diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8d2569 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ + +# Go workspace file +go.work + +# IDE specific files +.idea +.vscode +*.swp +*.swo + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes + +# Build +bin/ +dist/ + +# Environment variables +/apps/**/.env diff --git a/cmd/server/main.go b/cmd/server/main.go index 4d7ecd3..0926b4d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -183,6 +183,14 @@ func main() { } }) + http.HandleFunc("/api/upload", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + customerHandler.UploadScreenshots(w, r) + } else { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } + }) + http.HandleFunc("/api/customers/", func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path @@ -340,6 +348,9 @@ func main() { // Serve static files http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./frontend")))) + // Serve uploaded files + http.Handle("/static/uploads/", http.StripPrefix("/static/uploads/", http.FileServer(http.Dir("./frontend/uploads")))) + // Serve index page http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, filepath.Join("./frontend", "index.html")) diff --git a/frontend/css/style.css b/frontend/css/style.css index 6585bbb..17e2d78 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -603,6 +603,24 @@ body { box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1); } +/* Select placeholder styling - Simple and direct approach */ +select { + color: var(--dark-gray); +} + +select option { + color: var(--dark-gray); +} + +select option[disabled] { + color: #999; +} + +/* This will be controlled by JavaScript for placeholder state */ +select.placeholder-active { + color: #999 !important; +} + .form-group textarea { resize: vertical; min-height: 80px; @@ -716,6 +734,7 @@ th { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.5px; + vertical-align: middle; } td { @@ -728,6 +747,12 @@ td { text-overflow: ellipsis; cursor: pointer; position: relative; + vertical-align: middle; + height: inherit; /* Ensure td takes full height of tr */ +} + +#customerTable td { + display: table-cell; /* Force correct display mode */ } tr:hover td { @@ -2258,35 +2283,20 @@ tr:hover .action-cell { /* Font Awesome check icon */ } -/* Table refresh animation */ -@keyframes tableRefresh { - 0% { - opacity: 0.3; - } - - 50% { - opacity: 0.6; - } - - 100% { - opacity: 1; - } -} - +/* Table refresh animation simplified */ .table-refreshing { - animation: tableRefresh 0.5s ease-out; + opacity: 0.8; + transition: opacity 0.3s ease; } -/* Row highlight on refresh */ +/* Row highlight on refresh - removed transform */ @keyframes rowHighlight { 0% { - background-color: rgba(255, 107, 53, 0.2); - transform: translateX(-5px); + background-color: rgba(255, 107, 53, 0.1); } 100% { background-color: transparent; - transform: translateX(0); } } @@ -2699,4 +2709,174 @@ tr:hover .action-cell { gap: 8px; min-width: auto; } +} + +/* Screenshot Preview & List Styles */ +.screenshot-wrapper { + display: flex; + gap: 5px; + align-items: center; + max-width: 100%; + overflow-x: auto; + padding: 4px 0; + scrollbar-width: thin; + min-height: 24px; +} + +.screenshot-column-cell { + min-width: 120px; +} + +.screenshot-wrapper::-webkit-scrollbar { + height: 4px; +} + +.screenshot-wrapper::-webkit-scrollbar-thumb { + background: rgba(255, 107, 53, 0.3); + border-radius: 10px; +} + +.screenshot-thumbnail { + width: 40px; + height: 40px; + border-radius: 4px; + object-fit: cover; + cursor: pointer; + border: 1px solid var(--border-color); + transition: transform 0.2s ease; +} + +.screenshot-thumbnail:hover { + transform: scale(1.1); + border-color: var(--primary-orange); +} + +.no-screenshots { + color: #ccc; + font-size: 0.8rem; +} + +/* Upload Container */ +.screenshot-upload-container { + display: flex; + flex-direction: column; + gap: 10px; + padding: 10px; + border: 1px solid var(--border-color); + border-radius: 8px; + background-color: #fafafa; +} + +.screenshot-preview-list { + display: flex; + flex-wrap: wrap; + gap: 10px; + min-height: 50px; +} + +.preview-item { + position: relative; + width: 80px; + height: 80px; +} + +.preview-item img { + width: 100%; + height: 100%; + border-radius: 6px; + object-fit: cover; + border: 1px solid var(--border-color); +} + +.remove-preview { + position: absolute; + top: -8px; + right: -8px; + width: 20px; + height: 20px; + background-color: #ff4d4f; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 12px; + border: 2px solid white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.screenshot-upload-btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background-color: var(--white); + border: 1px dashed var(--primary-orange); + border-radius: 6px; + color: var(--primary-orange); + font-size: 0.9rem; + cursor: pointer; + transition: all 0.3s ease; + width: fit-content; +} + +.screenshot-upload-btn:hover { + background-color: rgba(255, 107, 53, 0.05); + transform: translateY(-1px); +} + +/* Lightbox / Image Viewer */ +.image-viewer-modal { + position: fixed; + z-index: 5000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.9); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + animation: fadeIn 0.3s ease; +} + +.image-viewer-content { + max-width: 90%; + max-height: 80%; + border-radius: 4px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); + animation: zoomIn 0.3s ease; +} + +.image-viewer-close { + position: absolute; + top: 20px; + right: 35px; + color: #f1f1f1; + font-size: 40px; + font-weight: bold; + cursor: pointer; + transition: 0.3s; +} + +.image-viewer-close:hover { + color: var(--primary-orange); +} + +#imageViewerCaption { + margin: 15px; + color: #ccc; + font-size: 1rem; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes zoomIn { + from { transform: scale(0.9); } + to { transform: scale(1); } } \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index b7428c3..2f16d4f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -222,6 +222,7 @@ 类型 模块 状态与进度 + 截图 操作 @@ -529,13 +530,13 @@
@@ -546,7 +547,7 @@ - +
@@ -656,10 +657,11 @@
@@ -667,7 +669,7 @@
- + @@ -699,6 +701,17 @@
+
+ +
+
+ +
+