feat: add download button to Tables/Images views, add task history delete/clear-all
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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`)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}?`, '确认')
|
||||||
|
|||||||
Reference in New Issue
Block a user