2026-07-10 16:45:55 +08:00
|
|
|
<script setup lang="ts">
|
2026-07-23 15:10:13 +08:00
|
|
|
import { onMounted, ref } from 'vue'
|
2026-07-10 16:45:55 +08:00
|
|
|
import { useRouter } from 'vue-router'
|
2026-07-23 15:10:13 +08:00
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
2026-07-10 16:45:55 +08:00
|
|
|
import DataTablePage from '@/components/DataTablePage.vue'
|
2026-07-23 15:10:13 +08:00
|
|
|
import { deleteDataProcessTask, getDataProcessTasks } from '@/api/modules/dataProcess'
|
2026-07-28 10:56:05 +08:00
|
|
|
import { usePolling } from '@/composables/usePolling'
|
2026-07-23 15:10:13 +08:00
|
|
|
import type { DataProcessTask, DataProcessType } from '@/types/dataProcess'
|
2026-07-27 10:03:52 +08:00
|
|
|
import { dataProcessDisplayStatus } from '@/utils/dataProcessStatus.js'
|
2026-07-27 09:50:26 +08:00
|
|
|
|
2026-07-23 15:10:13 +08:00
|
|
|
const processTypeMap: Record<DataProcessType, string> = {
|
2026-07-13 11:47:33 +08:00
|
|
|
structured: '结构化数据',
|
|
|
|
|
unstructured: '非结构化数据',
|
|
|
|
|
external: '外来数据源拉取',
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-10 16:45:55 +08:00
|
|
|
const router = useRouter()
|
2026-07-23 15:10:13 +08:00
|
|
|
const dataList = ref<DataProcessTask[]>([])
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const deletingId = ref<string | number | null>(null)
|
|
|
|
|
const loadError = ref('')
|
2026-07-10 16:45:55 +08:00
|
|
|
|
2026-07-23 15:10:13 +08:00
|
|
|
async function loadData(silent = false) {
|
|
|
|
|
if (!silent) loading.value = true
|
|
|
|
|
loadError.value = ''
|
|
|
|
|
try {
|
|
|
|
|
const response = await getDataProcessTasks({ page: 1, page_size: 200 })
|
|
|
|
|
dataList.value = response.items
|
2026-07-28 10:56:05 +08:00
|
|
|
if (dataList.value.some((item) => (
|
|
|
|
|
item.status === 'running'
|
|
|
|
|
|| item.preview_status === 'running'
|
|
|
|
|
|| item.preview_status === 'queued'
|
|
|
|
|
))) startPolling()
|
|
|
|
|
else stopPolling()
|
2026-07-23 15:10:13 +08:00
|
|
|
} catch {
|
|
|
|
|
loadError.value = '数据处理任务加载失败,请稍后重试。'
|
|
|
|
|
} finally {
|
|
|
|
|
if (!silent) loading.value = false
|
|
|
|
|
}
|
2026-07-10 16:45:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-13 11:04:55 +08:00
|
|
|
/** 查看任务详情 */
|
|
|
|
|
function viewDetail(row: unknown) {
|
2026-07-28 10:56:05 +08:00
|
|
|
const task = row as DataProcessTask
|
|
|
|
|
const routeName = task.status === 'completed' && task.results_confirmed !== false
|
|
|
|
|
? 'data-process-detail'
|
|
|
|
|
: 'data-process-workflow'
|
|
|
|
|
router.push({ name: routeName, params: { id: task.id } })
|
2026-07-10 16:45:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-23 15:10:13 +08:00
|
|
|
/** 删除由后端再次校验任务状态以及发布锁。 */
|
|
|
|
|
async function handleDelete(row: DataProcessTask) {
|
|
|
|
|
try {
|
|
|
|
|
await ElMessageBox.confirm(
|
|
|
|
|
`确定删除数据处理任务“${row.name}”吗?删除后无法恢复。`,
|
|
|
|
|
'确认删除',
|
|
|
|
|
{
|
|
|
|
|
type: 'warning',
|
|
|
|
|
confirmButtonText: '删除',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
confirmButtonClass: 'el-button--danger',
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
} catch {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deletingId.value = row.id
|
|
|
|
|
try {
|
|
|
|
|
await deleteDataProcessTask(row.id)
|
|
|
|
|
dataList.value = dataList.value.filter((item) => item.id !== row.id)
|
|
|
|
|
ElMessage.success('数据处理任务已删除')
|
|
|
|
|
} catch {
|
|
|
|
|
// 统一请求层已展示后端返回的失败原因。
|
|
|
|
|
} finally {
|
|
|
|
|
deletingId.value = null
|
|
|
|
|
}
|
2026-07-10 16:45:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDateTime(value?: string) {
|
|
|
|
|
if (!value) return '-'
|
2026-07-23 15:10:13 +08:00
|
|
|
const date = new Date(value)
|
|
|
|
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false })
|
2026-07-10 16:45:55 +08:00
|
|
|
}
|
2026-07-23 15:10:13 +08:00
|
|
|
|
2026-07-27 09:50:26 +08:00
|
|
|
function safeCount(value: unknown) {
|
|
|
|
|
const count = Number(value)
|
|
|
|
|
return Number.isFinite(count) ? Math.max(0, Math.trunc(count)) : 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function documentCountLabel(task: DataProcessTask) {
|
|
|
|
|
return `${safeCount(task.source_file_count)} 个`
|
2026-07-23 15:10:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-27 09:50:26 +08:00
|
|
|
function generatedCountLabel(task: DataProcessTask) {
|
|
|
|
|
return `${safeCount(task.output_count)} 条`
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 10:56:05 +08:00
|
|
|
const { start: startPolling, stop: stopPolling } = usePolling(
|
|
|
|
|
() => loadData(true),
|
|
|
|
|
3000,
|
|
|
|
|
{ immediate: false },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
onMounted(() => void loadData())
|
2026-07-10 16:45:55 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="data-process-page" style="height: 100%;">
|
|
|
|
|
<DataTablePage
|
|
|
|
|
title=""
|
2026-07-10 23:57:07 +08:00
|
|
|
:data="dataList"
|
2026-07-23 15:10:13 +08:00
|
|
|
:loading="loading"
|
2026-07-10 16:45:55 +08:00
|
|
|
searchable
|
|
|
|
|
:search-fields="['name']"
|
2026-07-13 16:39:16 +08:00
|
|
|
create-text="新建数据处理"
|
2026-07-10 16:45:55 +08:00
|
|
|
create-to="/data-process/create"
|
|
|
|
|
row-key="id"
|
|
|
|
|
:page-size="10"
|
2026-07-23 15:10:13 +08:00
|
|
|
:empty-text="loadError || '暂无数据处理任务'"
|
2026-07-10 16:45:55 +08:00
|
|
|
>
|
|
|
|
|
<template #columns>
|
2026-07-11 10:39:54 +08:00
|
|
|
<el-table-column label="任务ID" prop="id" align="center" width="100" />
|
2026-07-10 16:45:55 +08:00
|
|
|
<el-table-column label="任务名称" prop="name" align="center" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="任务状态" align="center" width="110">
|
|
|
|
|
<template #default="{ row }">
|
2026-07-27 09:50:26 +08:00
|
|
|
<el-tag
|
2026-07-27 10:03:52 +08:00
|
|
|
:type="dataProcessDisplayStatus(row as DataProcessTask).type"
|
2026-07-27 09:50:26 +08:00
|
|
|
size="small"
|
|
|
|
|
effect="light"
|
2026-07-27 10:03:52 +08:00
|
|
|
>{{ dataProcessDisplayStatus(row as DataProcessTask).label }}</el-tag>
|
2026-07-10 16:45:55 +08:00
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="处理类型" align="center" width="140">
|
2026-07-13 11:47:33 +08:00
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag v-if="row.process_type" size="small" type="info" effect="plain">
|
2026-07-23 15:10:13 +08:00
|
|
|
{{ processTypeMap[row.process_type as DataProcessType] || row.process_type }}
|
2026-07-13 11:47:33 +08:00
|
|
|
</el-tag>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
2026-07-10 16:45:55 +08:00
|
|
|
</el-table-column>
|
2026-07-27 09:50:26 +08:00
|
|
|
<el-table-column label="文档数量" align="center" width="120">
|
|
|
|
|
<template #default="{ row }">{{ documentCountLabel(row as DataProcessTask) }}</template>
|
2026-07-10 16:45:55 +08:00
|
|
|
</el-table-column>
|
2026-07-27 09:50:26 +08:00
|
|
|
<el-table-column label="生成个数" align="center" width="120">
|
|
|
|
|
<template #default="{ row }">{{ generatedCountLabel(row as DataProcessTask) }}</template>
|
2026-07-10 16:45:55 +08:00
|
|
|
</el-table-column>
|
2026-08-21 09:49:48 +08:00
|
|
|
<el-table-column label="创建时间" align="center" width="190">
|
|
|
|
|
<template #default="{ row }">{{ formatDateTime(row.create_time || row.created_at) }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="上传/创建人" align="center" width="150" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row }">{{ row.creator_name || row.created_by || '-' }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="最后处理人" align="center" width="150" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row }">{{ row.processor_name || row.updated_by || '-' }}</template>
|
2026-07-10 16:45:55 +08:00
|
|
|
</el-table-column>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template #actions="{ row }">
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<el-button type="primary" link size="small" @click="viewDetail(row)">
|
|
|
|
|
<i class="fa fa-file-text-o" style="margin-right: 4px" />详情
|
|
|
|
|
</el-button>
|
2026-07-23 15:10:13 +08:00
|
|
|
<el-button
|
|
|
|
|
type="danger"
|
|
|
|
|
link
|
|
|
|
|
size="small"
|
|
|
|
|
:loading="deletingId === row.id"
|
|
|
|
|
@click="handleDelete(row as DataProcessTask)"
|
|
|
|
|
>
|
2026-07-10 16:45:55 +08:00
|
|
|
<i class="fa fa-trash-o" style="margin-right: 4px" />删除
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</DataTablePage>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.action-buttons {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|