refactor: Images.vue uses shared composables, fix error handling

- Import statusType/statusText/fmtTime from useFileUtils composable
- Use useFilePreview composable for preview state and logic
- Remove duplicated preview refs, cleanupPreview, and utility functions
- Add try/finally to loadData for reliable loading state
- Properly handle cancel vs error in deleteFile and batchDelete

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 21:31:40 +08:00
parent 7e63dda522
commit 7e15431937
+14 -44
View File
@@ -121,9 +121,12 @@ import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Right } from '@element-plus/icons-vue' import { Right } from '@element-plus/icons-vue'
import { useProcessingStore } from '../../stores/processing' import { useProcessingStore } from '../../stores/processing'
import { statusType, statusText, fmtTime } from '../../composables/useFileUtils'
import { useFilePreview } from '../../composables/useFilePreview'
import api from '../../api' import api from '../../api'
const processingStore = useProcessingStore() const processingStore = useProcessingStore()
const { showPreview, previewType, previewSrc, previewRows, openPreview, cleanupPreview } = useFilePreview()
const items = ref<any[]>([]) const items = ref<any[]>([])
const total = ref(0) const total = ref(0)
@@ -135,65 +138,28 @@ const sortBy = ref('created_at')
const sortOrder = ref('desc') const sortOrder = ref('desc')
const uploadInput = ref<HTMLInputElement>() const uploadInput = ref<HTMLInputElement>()
const showPreview = ref(false)
const previewType = ref('')
const previewSrc = ref('')
const previewRows = ref<string[][]>([])
const showDetailDlg = ref(false) const showDetailDlg = ref(false)
const detailLogs = ref<string[]>([]) const detailLogs = ref<string[]>([])
function statusType(s: string) {
const m: Record<string, string> = { done: 'success', merged: 'success', excel_done: 'warning', ocr_done: 'info', pending: 'info' }
return m[s] || 'info'
}
function statusText(s: string) {
const m: Record<string, string> = { done: '已完成', merged: '已合并', excel_done: '已处理', ocr_done: '已OCR', pending: '待处理' }
return m[s] || s
}
function fmtTime(t: string): string {
if (!t) return '--'
return t.replace('T', ' ').slice(0, 19)
}
async function loadData() { async function loadData() {
loading.value = true loading.value = true
try { try {
const res = await api.get('/files/relations', { params: { view: 'images', page: page.value, page_size: pageSize, sort_by: sortBy.value, sort_order: sortOrder.value } }) const res = await api.get('/files/relations', { params: { view: 'images', page: page.value, page_size: pageSize, sort_by: sortBy.value, sort_order: sortOrder.value } })
items.value = res.data.items items.value = res.data.items
total.value = res.data.total total.value = res.data.total
} catch {} } catch {
ElMessage.error('加载文件列表失败')
} finally {
loading.value = false loading.value = false
} }
}
function onSelect(rows: any[]) { selected.value = rows } function onSelect(rows: any[]) { selected.value = rows }
async function previewFile(row: any) { async function previewFile(row: any) {
const token = localStorage.getItem('token')
const fname = row.input_image || row.output_excel || row.result_purchase const fname = row.input_image || row.output_excel || row.result_purchase
const dir = row.input_image ? 'input' : row.output_excel ? 'output' : 'result' const dir = row.input_image ? 'input' : row.output_excel ? 'output' : 'result'
try { await openPreview(dir, fname)
const resp = await fetch(`/api/files/preview/${dir}/${encodeURIComponent(fname)}`, { headers: { Authorization: `Bearer ${token}` } })
const ct = resp.headers.get('content-type') || ''
if (ct.includes('image')) {
previewType.value = 'image'
const blob = await resp.blob()
previewSrc.value = URL.createObjectURL(blob)
} else {
const data = await resp.json()
if (data.type === 'excel') { previewType.value = 'excel'; previewRows.value = data.rows }
}
showPreview.value = true
} catch { ElMessage.error('预览失败') }
}
function cleanupPreview() {
if (previewSrc.value && previewSrc.value.startsWith('blob:')) {
URL.revokeObjectURL(previewSrc.value)
}
previewSrc.value = ''
previewType.value = ''
previewRows.value = []
} }
function showDetail(row: any) { function showDetail(row: any) {
@@ -269,7 +235,9 @@ async function deleteFile(row: any) {
if (row.id) await api.delete('/files/relations', { data: { ids: [row.id] } }) if (row.id) await api.delete('/files/relations', { data: { ids: [row.id] } })
ElMessage.success('已删除') ElMessage.success('已删除')
loadData() loadData()
} catch {} } catch (err: any) {
if (err !== 'cancel') ElMessage.error('删除失败')
}
} }
async function batchPipeline() { async function batchPipeline() {
@@ -314,7 +282,9 @@ async function batchDelete() {
} }
ElMessage.success('批量删除完成') ElMessage.success('批量删除完成')
loadData() loadData()
} catch {} } catch (err: any) {
if (err !== 'cancel') ElMessage.error('批量删除失败')
}
} }
onMounted(loadData) onMounted(loadData)