feat: add download button to Tables/Images views, add task history delete/clear-all

This commit is contained in:
2026-05-14 16:12:09 +08:00
parent 0e273111a2
commit d585a6baaa
5 changed files with 86 additions and 4 deletions
+23
View File
@@ -112,6 +112,29 @@ async def get_task(
return task return task
@router.delete("/{task_id}")
async def delete_task(
task_id: str,
current_user: dict = Depends(get_current_user),
):
"""Delete a single task by ID."""
loop = asyncio.get_event_loop()
deleted = await loop.run_in_executor(None, lambda: db_schema.delete_task(task_id))
if not deleted:
raise HTTPException(status_code=404, detail="任务不存在")
return {"message": "已删除"}
@router.delete("")
async def clear_all_tasks(
current_user: dict = Depends(get_current_user),
):
"""Clear all task history records."""
loop = asyncio.get_event_loop()
count = await loop.run_in_executor(None, db_schema.clear_task_history)
return {"message": f"已清除 {count} 条记录", "count": count}
@router.post("/{task_id}/retry") @router.post("/{task_id}/retry")
async def retry_task( async def retry_task(
task_id: str, task_id: str,
+22
View File
@@ -333,6 +333,28 @@ def query_task_stats() -> dict:
conn.close() conn.close()
def delete_task(task_id: str) -> bool:
"""Delete a single task by ID. Returns True if deleted."""
conn = sqlite3.connect(_db_path)
try:
cur = conn.execute("DELETE FROM task_history WHERE id = ?", (task_id,))
conn.commit()
return cur.rowcount > 0
finally:
conn.close()
def clear_task_history() -> int:
"""Delete all task history records. Returns number of deleted rows."""
conn = sqlite3.connect(_db_path)
try:
cur = conn.execute("DELETE FROM task_history")
conn.commit()
return cur.rowcount
finally:
conn.close()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Query functions — File metadata # Query functions — File metadata
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
+28 -2
View File
@@ -62,6 +62,7 @@
<template #prefix><el-icon><Search /></el-icon></template> <template #prefix><el-icon><Search /></el-icon></template>
</el-input> </el-input>
<el-button size="small" @click="loadData" :icon="Refresh">刷新</el-button> <el-button size="small" @click="loadData" :icon="Refresh">刷新</el-button>
<el-button size="small" type="danger" @click="clearAllTasks">清除全部</el-button>
</div> </div>
</div> </div>
@@ -88,10 +89,11 @@
<span class="time-cell">{{ formatTime(row.created_at) }}</span> <span class="time-cell">{{ formatTime(row.created_at) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="140" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link size="small" @click="showDetail(row)">详情</el-button> <el-button type="primary" link size="small" @click="showDetail(row)">详情</el-button>
<el-button v-if="row.status === 'failed'" type="warning" link size="small" @click="retryTask(row)">重试</el-button> <el-button v-if="row.status === 'failed'" type="warning" link size="small" @click="retryTask(row)">重试</el-button>
<el-button type="danger" link size="small" @click="deleteTask(row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -137,7 +139,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from 'vue' import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Timer, CircleCheck, CircleClose, Loading, Search, Refresh } from '@element-plus/icons-vue' import { Timer, CircleCheck, CircleClose, Loading, Search, Refresh } from '@element-plus/icons-vue'
import api from '../api' import api from '../api'
import { useDebounce } from '../composables/useDebounce' import { useDebounce } from '../composables/useDebounce'
@@ -214,6 +216,30 @@ async function copyPath(text: string) {
} }
} }
async function deleteTask(row: any) {
try {
await ElMessageBox.confirm(`确定删除任务 #${row.id}`, '确认删除')
await api.delete(`/tasks/${row.id}`)
ElMessage.success('已删除')
loadData()
loadStats()
} catch (err: any) {
if (err !== 'cancel') ElMessage.error(err.response?.data?.detail || '删除失败')
}
}
async function clearAllTasks() {
try {
await ElMessageBox.confirm('确定清除所有任务历史记录?此操作不可撤销。', '确认清除', { type: 'warning' })
await api.delete('/tasks')
ElMessage.success('已清除所有任务历史')
loadData()
loadStats()
} catch (err: any) {
if (err !== 'cancel') ElMessage.error(err.response?.data?.detail || '清除失败')
}
}
async function retryTask(row: any) { async function retryTask(row: any) {
try { try {
await api.post(`/tasks/${row.id}/retry`) await api.post(`/tasks/${row.id}/retry`)
+2 -1
View File
@@ -81,10 +81,11 @@
<span class="time-text">{{ fmtTime(row.created_at) }}</span> <span class="time-text">{{ fmtTime(row.created_at) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="320" fixed="right"> <el-table-column label="操作" width="370" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" size="small" @click="previewFile(row)">预览</el-button> <el-button link type="primary" size="small" @click="previewFile(row)">预览</el-button>
<el-button link type="primary" size="small" @click="showDetail(row)">详情</el-button> <el-button link type="primary" size="small" @click="showDetail(row)">详情</el-button>
<el-button link type="primary" size="small" @click="downloadFile(row)">下载</el-button>
<el-button link type="primary" size="small" @click="pipelineFile(row)">生成采购单</el-button> <el-button link type="primary" size="small" @click="pipelineFile(row)">生成采购单</el-button>
<el-button link type="primary" size="small" @click="ocrFile(row)">仅OCR</el-button> <el-button link type="primary" size="small" @click="ocrFile(row)">仅OCR</el-button>
<el-button link type="danger" size="small" @click="deleteFile(row)">删除</el-button> <el-button link type="danger" size="small" @click="deleteFile(row)">删除</el-button>
+11 -1
View File
@@ -78,10 +78,11 @@
<span class="time-text">{{ fmtTime(row.created_at) }}</span> <span class="time-text">{{ fmtTime(row.created_at) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="280" fixed="right"> <el-table-column label="操作" width="320" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" size="small" @click="previewFile(row)">预览</el-button> <el-button link type="primary" size="small" @click="previewFile(row)">预览</el-button>
<el-button link type="primary" size="small" @click="showDetail(row)">详情</el-button> <el-button link type="primary" size="small" @click="showDetail(row)">详情</el-button>
<el-button link type="primary" size="small" @click="downloadFile(row)">下载</el-button>
<el-button link type="primary" size="small" @click="processFile(row)">处理</el-button> <el-button link type="primary" size="small" @click="processFile(row)">处理</el-button>
<el-button link type="danger" size="small" @click="deleteFile(row)">删除</el-button> <el-button link type="danger" size="small" @click="deleteFile(row)">删除</el-button>
</template> </template>
@@ -210,6 +211,15 @@ async function processFile(row: any) {
} }
} }
async function downloadFile(row: any) {
const token = localStorage.getItem('token')
if (row.output_excel) {
window.open(`/api/files/download/output/${encodeURIComponent(row.output_excel)}?token=${token}`, '_blank')
} else if (row.result_purchase) {
window.open(`/api/files/download/result/${encodeURIComponent(row.result_purchase)}?token=${token}`, '_blank')
}
}
async function deleteFile(row: any) { async function deleteFile(row: any) {
try { try {
await ElMessageBox.confirm(`确定删除 ${row.output_excel}`, '确认') await ElMessageBox.confirm(`确定删除 ${row.output_excel}`, '确认')