feat: 已完成待办可重新打开

- 新增 /api/items/<id>/reopen 接口
- 前端列表已完成待办显示重新打开按钮
- 恢复为 pending 或 in_progress 状态
This commit is contained in:
2026-04-16 11:05:34 +08:00
parent 061bfa3b55
commit 6b775c99f9

View File

@@ -112,6 +112,28 @@ def complete_item(item_id):
return jsonify({'success': True, 'data': item})
@app.route('/api/items/<int:item_id>/reopen', methods=['POST'])
def reopen_item(item_id):
"""重新打开待办"""
item = db.get_item(item_id)
if not item:
return jsonify({'success': False, 'error': '条目不存在'}), 404
if item['type'] != 'todo':
return jsonify({'success': False, 'error': '不是待办事项'}), 400
# 获取请求中的目标状态,默认为 pending
data = request.get_json() or {}
new_status = data.get('status', 'pending')
if new_status not in ['pending', 'in_progress']:
return jsonify({'success': False, 'error': '无效状态'}), 400
db.update_item(item_id, status=new_status)
item = db.get_item(item_id)
return jsonify({'success': True, 'data': item})
@app.route('/api/tags', methods=['GET'])
def list_tags():
"""列出标签"""
@@ -1017,8 +1039,9 @@ function renderItems(items) {
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div style="flex: 1; min-width: 0;">
<h6 class="card-title text-truncate mb-1">
<h6 class="card-title text-truncate mb-1 ${item.type === 'todo' && item.status === 'completed' ? 'text-muted' : ''}">
${getTypeIcon(item.type)} ${item.title || truncate(item.content || item.url, 30)}
${item.type === 'todo' && item.status === 'completed' ? '' : ''}
</h6>
<p class="card-text text-muted small mb-0 text-truncate" style="font-size:12px;">
${item.url ? truncate(item.url, 50) : item.content ? truncate(item.content, 50) : item.note ? truncate(item.note, 50) : ''}
@@ -1030,6 +1053,7 @@ function renderItems(items) {
<button class="btn btn-sm btn-outline-info py-0 px-1" onclick="showSendEmailModal(${item.id})" title="发送邮件"><i class="bi bi-envelope" style="font-size:11px;"></i></button>
<button class="btn btn-sm btn-outline-primary py-0 px-1" onclick="openEditModal(${item.id})" title="编辑"><i class="bi bi-pencil" style="font-size:11px;"></i></button>
${item.type === 'todo' && item.status !== 'completed' ? `<button class="btn btn-sm btn-outline-success py-0 px-1" onclick="completeItem(${item.id})" title="完成"><i class="bi bi-check-lg" style="font-size:11px;"></i></button>` : ''}
${item.type === 'todo' && item.status === 'completed' ? `<button class="btn btn-sm btn-outline-warning py-0 px-1" onclick="reopenItem(${item.id})" title="重新打开"><i class="bi bi-arrow-counterclockwise" style="font-size:11px;"></i></button>` : ''}
<button class="btn btn-sm btn-outline-danger py-0 px-1" onclick="deleteItem(${item.id})" title="删除"><i class="bi bi-trash" style="font-size:11px;"></i></button>
</div>
</div>
@@ -1138,6 +1162,20 @@ async function completeItem(id) {
refreshData();
}
// 重新打开待办
async function reopenItem(id) {
const res = await fetch(`${API_BASE}/items/${id}/reopen`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'pending' })
});
if (res.ok) {
refreshData();
loadReminders(); // 刷新提醒
}
}
// 删除条目
async function deleteItem(id) {
if (!confirm('确认删除?')) return;