feat: 增加网页comment字段
This commit is contained in:
33
cam_web.py
33
cam_web.py
@@ -44,6 +44,7 @@ def init_db():
|
|||||||
right_filename TEXT NOT NULL,
|
right_filename TEXT NOT NULL,
|
||||||
timestamp REAL NOT NULL,
|
timestamp REAL NOT NULL,
|
||||||
metadata TEXT,
|
metadata TEXT,
|
||||||
|
comment TEXT,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
@@ -89,7 +90,7 @@ def get_images_api():
|
|||||||
conn = sqlite3.connect(DATABASE_PATH)
|
conn = sqlite3.connect(DATABASE_PATH)
|
||||||
cursor = conn.cursor()
|
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()
|
rows = cursor.fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@@ -101,7 +102,8 @@ def get_images_api():
|
|||||||
"right_filename": row[2],
|
"right_filename": row[2],
|
||||||
"timestamp": row[3],
|
"timestamp": row[3],
|
||||||
"metadata": row[4],
|
"metadata": row[4],
|
||||||
"created_at": row[5]
|
"comment": row[5] or "", # 如果没有comment则显示空字符串
|
||||||
|
"created_at": row[6]
|
||||||
})
|
})
|
||||||
return jsonify(images)
|
return jsonify(images)
|
||||||
|
|
||||||
@@ -194,6 +196,7 @@ def upload_images():
|
|||||||
left_file = request.files.get('left_image')
|
left_file = request.files.get('left_image')
|
||||||
right_file = request.files.get('right_image')
|
right_file = request.files.get('right_image')
|
||||||
metadata_str = request.form.get('metadata') # 如果需要处理元数据
|
metadata_str = request.form.get('metadata') # 如果需要处理元数据
|
||||||
|
comment = request.form.get('comment', '') # 获取comment字段
|
||||||
|
|
||||||
if not left_file or not right_file:
|
if not left_file or not right_file:
|
||||||
logger.warning("Received request without required image files.")
|
logger.warning("Received request without required image files.")
|
||||||
@@ -244,9 +247,9 @@ def upload_images():
|
|||||||
conn = sqlite3.connect(DATABASE_PATH)
|
conn = sqlite3.connect(DATABASE_PATH)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT INTO images (left_filename, right_filename, timestamp, metadata)
|
INSERT INTO images (left_filename, right_filename, timestamp, metadata, comment)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
''', (left_filename, right_filename, float(timestamp_str), json.dumps(metadata)))
|
''', (left_filename, right_filename, float(timestamp_str), json.dumps(metadata), comment))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
image_id = cursor.lastrowid # 获取新插入记录的 ID
|
image_id = cursor.lastrowid # 获取新插入记录的 ID
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -277,7 +280,25 @@ def upload_images():
|
|||||||
logger.error(f"Error processing upload: {e}")
|
logger.error(f"Error processing upload: {e}")
|
||||||
return jsonify({"error": str(e)}), 500
|
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')
|
@app.route('/status')
|
||||||
def status():
|
def status():
|
||||||
with frame_lock:
|
with frame_lock:
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Saved Images List</title>
|
<title>Saved Images List</title>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js "></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js "></script>
|
||||||
<style>
|
<style>
|
||||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
body {
|
||||||
.controls { margin-bottom: 20px; }
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
@@ -14,23 +22,59 @@
|
|||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
button:hover { background-color: #0056b3; }
|
|
||||||
button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
button:hover {
|
||||||
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
background-color: #0056b3;
|
||||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
}
|
||||||
th { background-color: #f2f2f2; }
|
|
||||||
.image-preview { width: 100px; height: auto; }
|
button:disabled {
|
||||||
input[type="checkbox"] { transform: scale(1.2); }
|
background-color: #cccccc;
|
||||||
#status { padding: 10px; margin: 10px 0; background-color: #f0f0f0; }
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
width: 100px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#status {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #007bff;
|
color: #007bff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Saved Images</h1>
|
<h1>Saved Images</h1>
|
||||||
<!-- 添加导航链接 -->
|
<!-- 添加导航链接 -->
|
||||||
@@ -41,18 +85,20 @@
|
|||||||
<button id="exportBtn" disabled>Export Selected</button>
|
<button id="exportBtn" disabled>Export Selected</button>
|
||||||
<button id="deleteSelectedBtn" disabled>Delete Selected</button>
|
<button id="deleteSelectedBtn" disabled>Delete Selected</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 修改表格头部 -->
|
||||||
<table id="imagesTable">
|
<table id="imagesTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<input type="checkbox" id="selectAllCheckbox">
|
<input type="checkbox" id="selectAllCheckbox">
|
||||||
<label for="selectAllCheckbox" style="display: inline-block; margin-left: 5px; cursor: pointer;">Select All</label>
|
<label for="selectAllCheckbox"
|
||||||
|
style="display: inline-block; margin-left: 5px; cursor: pointer;">Select All</label>
|
||||||
</th>
|
</th>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Left Image</th>
|
<th>Left Image</th>
|
||||||
<th>Right Image</th>
|
<th>Right Image</th>
|
||||||
<th>Timestamp</th>
|
<th>Timestamp</th>
|
||||||
<th>Created At</th>
|
<th>Comment</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -82,6 +128,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染表格
|
// 渲染表格
|
||||||
|
// 修改渲染表格的函数
|
||||||
function renderTable(images) {
|
function renderTable(images) {
|
||||||
const tbody = document.getElementById('imagesTableBody');
|
const tbody = document.getElementById('imagesTableBody');
|
||||||
tbody.innerHTML = ''; // 清空现有内容
|
tbody.innerHTML = ''; // 清空现有内容
|
||||||
@@ -110,11 +157,43 @@
|
|||||||
rightCell.appendChild(rightImg);
|
rightCell.appendChild(rightImg);
|
||||||
|
|
||||||
row.insertCell(4).textContent = new Date(image.timestamp * 1000).toISOString();
|
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 = `<button onclick="deleteImage(${image.id})">Delete</button>`;
|
row.insertCell(6).innerHTML = `<button onclick="deleteImage(${image.id})">Delete</button>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加更新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) {
|
async function deleteImage(id) {
|
||||||
if (!confirm(`Are you sure you want to delete image ID ${id}?`)) return;
|
if (!confirm(`Are you sure you want to delete image ID ${id}?`)) return;
|
||||||
@@ -257,4 +336,5 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user