From c8dfec6cf4272429f73246a2014ed25b8d1e9f99 Mon Sep 17 00:00:00 2001 From: CaoWangrenbo Date: Sun, 26 Oct 2025 10:24:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=BD=91=E9=A1=B5com?= =?UTF-8?q?ment=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cam_web.py | 33 ++++++++-- templates/list_images.html | 120 ++++++++++++++++++++++++++++++------- 2 files changed, 127 insertions(+), 26 deletions(-) diff --git a/cam_web.py b/cam_web.py index c8aa1c2..7198710 100644 --- a/cam_web.py +++ b/cam_web.py @@ -44,6 +44,7 @@ def init_db(): right_filename TEXT NOT NULL, timestamp REAL NOT NULL, metadata TEXT, + comment TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') @@ -89,7 +90,7 @@ def get_images_api(): conn = sqlite3.connect(DATABASE_PATH) cursor = conn.cursor() # 按时间倒序排列 - cursor.execute("SELECT id, left_filename, right_filename, timestamp, metadata, created_at FROM images ORDER BY timestamp DESC") + cursor.execute("SELECT id, left_filename, right_filename, timestamp, metadata, comment, created_at FROM images ORDER BY timestamp DESC") rows = cursor.fetchall() conn.close() @@ -101,7 +102,8 @@ def get_images_api(): "right_filename": row[2], "timestamp": row[3], "metadata": row[4], - "created_at": row[5] + "comment": row[5] or "", # 如果没有comment则显示空字符串 + "created_at": row[6] }) return jsonify(images) @@ -194,6 +196,7 @@ def upload_images(): left_file = request.files.get('left_image') right_file = request.files.get('right_image') metadata_str = request.form.get('metadata') # 如果需要处理元数据 + comment = request.form.get('comment', '') # 获取comment字段 if not left_file or not right_file: logger.warning("Received request without required image files.") @@ -244,9 +247,9 @@ def upload_images(): conn = sqlite3.connect(DATABASE_PATH) cursor = conn.cursor() cursor.execute(''' - INSERT INTO images (left_filename, right_filename, timestamp, metadata) - VALUES (?, ?, ?, ?) - ''', (left_filename, right_filename, float(timestamp_str), json.dumps(metadata))) + INSERT INTO images (left_filename, right_filename, timestamp, metadata, comment) + VALUES (?, ?, ?, ?, ?) + ''', (left_filename, right_filename, float(timestamp_str), json.dumps(metadata), comment)) conn.commit() image_id = cursor.lastrowid # 获取新插入记录的 ID conn.close() @@ -277,7 +280,25 @@ def upload_images(): logger.error(f"Error processing upload: {e}") return jsonify({"error": str(e)}), 500 -# --- 可选:添加一个简单的状态检查路由 --- +@app.route('/api/images/comment', methods=['PUT']) +def update_image_comment(): + """API: 更新图片的comment""" + data = request.json + image_id = data.get('id') + comment = data.get('comment', '') + + if not image_id: + return jsonify({"error": "Image ID is required"}), 400 + + conn = sqlite3.connect(DATABASE_PATH) + cursor = conn.cursor() + # 更新comment字段 + cursor.execute("UPDATE images SET comment = ? WHERE id = ?", (comment, image_id)) + conn.commit() + conn.close() + + return jsonify({"message": f"Comment for image {image_id} updated successfully"}) + @app.route('/status') def status(): with frame_lock: diff --git a/templates/list_images.html b/templates/list_images.html index 1be3a19..f58c7b7 100644 --- a/templates/list_images.html +++ b/templates/list_images.html @@ -1,11 +1,19 @@ + Saved Images List +

Saved Images

@@ -41,18 +85,20 @@ + - + @@ -82,6 +128,7 @@ } // 渲染表格 + // 修改渲染表格的函数 function renderTable(images) { const tbody = document.getElementById('imagesTableBody'); tbody.innerHTML = ''; // 清空现有内容 @@ -90,14 +137,14 @@ const row = tbody.insertRow(); row.insertCell(0).innerHTML = ``; row.insertCell(1).textContent = image.id; - + const leftCell = row.insertCell(2); const leftImg = document.createElement('img'); // 修改这里:使用 Flask 静态文件路径 leftImg.src = `/static/received/left/${image.left_filename}`; leftImg.alt = "Left Image"; leftImg.className = 'image-preview'; - leftImg.onerror = function() { this.src = 'data:image/svg+xml;utf8,No Image'; }; + leftImg.onerror = function () { this.src = 'data:image/svg+xml;utf8,No Image'; }; leftCell.appendChild(leftImg); const rightCell = row.insertCell(3); @@ -106,15 +153,47 @@ rightImg.src = `/static/received/right/${image.right_filename}`; rightImg.alt = "Right Image"; rightImg.className = 'image-preview'; - rightImg.onerror = function() { this.src = 'data:image/svg+xml;utf8,No Image'; }; + rightImg.onerror = function () { this.src = 'data:image/svg+xml;utf8,No Image'; }; rightCell.appendChild(rightImg); row.insertCell(4).textContent = new Date(image.timestamp * 1000).toISOString(); - row.insertCell(5).textContent = image.created_at; + + // 添加可编辑的comment单元格 + const commentCell = row.insertCell(5); + const commentInput = document.createElement('input'); + commentInput.type = 'text'; + commentInput.value = image.comment || ''; + commentInput.dataset.id = image.id; + commentInput.className = 'comment-input'; + commentInput.style.width = '100%'; + commentInput.addEventListener('change', function () { + updateComment(image.id, this.value); + }); + commentCell.appendChild(commentInput); + row.insertCell(6).innerHTML = ``; }); } + // 添加更新comment的函数 + async function updateComment(id, comment) { + try { + const response = await fetch('/api/images/comment', { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ id: id, comment: comment }) + }); + if (!response.ok) { + const errorData = await response.json().catch(() => ({ error: response.statusText })); + throw new Error(errorData.error || `HTTP error! status: ${response.status}`); + } + document.getElementById('status').textContent = `Comment for image ID ${id} updated.`; + } catch (error) { + console.error('Error updating comment:', error); + document.getElementById('status').textContent = 'Error updating comment: ' + error.message; + } + } + // 删除图片 async function deleteImage(id) { if (!confirm(`Are you sure you want to delete image ID ${id}?`)) return; @@ -165,7 +244,7 @@ document.getElementById('refreshBtn').addEventListener('click', loadImages); // --- 修改:使用 fetch API 发起导出请求 --- - document.getElementById('exportBtn').addEventListener('click', async function() { + document.getElementById('exportBtn').addEventListener('click', async function () { const selectedIds = getSelectedIds(); if (selectedIds.length === 0) { alert('Please select at least one image to export.'); @@ -205,7 +284,7 @@ } }); - document.getElementById('deleteSelectedBtn').addEventListener('click', async function() { + document.getElementById('deleteSelectedBtn').addEventListener('click', async function () { const selectedIds = getSelectedIds(); if (selectedIds.length === 0) { alert('Please select at least one image to delete.'); @@ -236,7 +315,7 @@ }); // 全选复选框事件监听 - document.getElementById('selectAllCheckbox').addEventListener('change', function() { + document.getElementById('selectAllCheckbox').addEventListener('change', function () { const isChecked = this.checked; const checkboxes = document.querySelectorAll('.selectCheckbox'); checkboxes.forEach(checkbox => { @@ -246,7 +325,7 @@ }); // 监听单个复选框变化以更新全选框和按钮状态 - document.getElementById('imagesTable').addEventListener('change', function(e) { + document.getElementById('imagesTable').addEventListener('change', function (e) { if (e.target.classList.contains('selectCheckbox')) { updateSelectAllCheckbox(); } @@ -257,4 +336,5 @@ + \ No newline at end of file
- + ID Left Image Right Image TimestampCreated AtComment Actions