296 lines
9.1 KiB
Vue
296 lines
9.1 KiB
Vue
<template>
|
|
<ArtTableFullScreen>
|
|
<div class="task-detail-page" id="table-full-screen">
|
|
<ElCard shadow="never" class="art-table-card">
|
|
<!-- 页面头部 -->
|
|
<div class="detail-header">
|
|
<ElButton @click="goBack">
|
|
<template #icon>
|
|
<ElIcon><ArrowLeft /></ElIcon>
|
|
</template>
|
|
返回
|
|
</ElButton>
|
|
<h2 class="detail-title">任务详情</h2>
|
|
</div>
|
|
|
|
<!-- 使用 DetailPage 组件显示任务信息 -->
|
|
<DetailPage v-if="taskDetail" :sections="detailSections" :data="taskDetail" />
|
|
|
|
<!-- 失败记录 -->
|
|
<div class="failure-section" v-if="taskDetail?.fail_count && taskDetail.fail_count > 0">
|
|
<ElDivider content-position="left">
|
|
<span class="section-title">失败记录 ({{ taskDetail.fail_count }})</span>
|
|
</ElDivider>
|
|
<ElTable :data="taskDetail.failed_items" border style="width: 100%">
|
|
<ElTableColumn prop="line" label="行号" width="100" />
|
|
<ElTableColumn v-if="taskType === 'card'" prop="iccid" label="ICCID" min-width="180" />
|
|
<ElTableColumn v-else prop="virtual_no" label="设备号" min-width="180" />
|
|
<ElTableColumn prop="reason" label="失败原因" min-width="300" />
|
|
</ElTable>
|
|
</div>
|
|
|
|
<!-- 跳过记录 -->
|
|
<div class="skipped-section" v-if="taskDetail?.skip_count && taskDetail.skip_count > 0">
|
|
<ElDivider content-position="left">
|
|
<span class="section-title">跳过记录 ({{ taskDetail.skip_count }})</span>
|
|
</ElDivider>
|
|
<ElTable :data="taskDetail.skipped_items" border style="width: 100%">
|
|
<ElTableColumn prop="line" label="行号" width="100" />
|
|
<ElTableColumn v-if="taskType === 'card'" prop="iccid" label="ICCID" min-width="180" />
|
|
<ElTableColumn v-else prop="virtual_no" label="设备号" min-width="180" />
|
|
<ElTableColumn prop="reason" label="跳过原因" min-width="300" />
|
|
</ElTable>
|
|
</div>
|
|
</ElCard>
|
|
</div>
|
|
</ArtTableFullScreen>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, h } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { ref, onMounted } from 'vue'
|
|
import { CardService, DeviceService } from '@/api/modules'
|
|
import { ElDivider, ElIcon, ElMessage, ElTable, ElTableColumn, ElTag } from 'element-plus'
|
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
|
import { formatDateTime } from '@/utils/business/format'
|
|
import type { IotCardImportTaskDetail, IotCardImportTaskStatus } from '@/types/api/card'
|
|
import type { DeviceImportTaskDetail } from '@/types/api/device'
|
|
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
|
import DetailPage from '@/components/common/DetailPage.vue'
|
|
|
|
defineOptions({ name: 'TaskDetail' })
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
type TaskType = 'card' | 'device'
|
|
type TaskDetail = IotCardImportTaskDetail | DeviceImportTaskDetail
|
|
|
|
const taskDetail = ref<TaskDetail | null>(null)
|
|
const loading = ref(false)
|
|
const taskType = ref<TaskType>('card')
|
|
|
|
// 获取状态标签类型
|
|
const getStatusType = (status?: IotCardImportTaskStatus) => {
|
|
if (!status) return 'info'
|
|
switch (status) {
|
|
case 1:
|
|
return 'info'
|
|
case 2:
|
|
return 'warning'
|
|
case 3:
|
|
return 'success'
|
|
case 4:
|
|
return 'danger'
|
|
default:
|
|
return 'info'
|
|
}
|
|
}
|
|
|
|
// 详情页配置
|
|
const detailSections = computed((): DetailSection[] => {
|
|
return [
|
|
{
|
|
title: '任务基本信息',
|
|
fields: [
|
|
{ label: '任务编号', prop: 'task_no', formatter: (value: string) => value || '-' },
|
|
{
|
|
label: '任务类型',
|
|
render: () => {
|
|
return h(
|
|
ElTag,
|
|
{ type: taskType.value === 'device' ? 'warning' : 'primary', size: 'small' },
|
|
() => (taskType.value === 'device' ? '设备导入' : 'ICCID导入')
|
|
)
|
|
}
|
|
},
|
|
{ label: '批次号', prop: 'batch_no', formatter: (value: string) => value || '-' },
|
|
{ label: '文件名', prop: 'file_name', formatter: (value: string) => value || '-' },
|
|
{
|
|
label: '任务状态',
|
|
render: (data: TaskDetail) => {
|
|
return h(ElTag, { type: getStatusType(data.status) }, () => data.status_text)
|
|
}
|
|
},
|
|
...(taskType.value === 'card'
|
|
? [
|
|
{
|
|
label: '运营商',
|
|
prop: 'carrier_name',
|
|
formatter: (value: any) => value || '-'
|
|
},
|
|
{
|
|
label: '卡业务类型',
|
|
render: (data: any) => {
|
|
if (!data.card_category) return h('span', '-')
|
|
return h(
|
|
ElTag,
|
|
{
|
|
type: data.card_category === 'industry' ? 'warning' : 'success',
|
|
size: 'small'
|
|
},
|
|
() =>
|
|
data.card_category === 'normal'
|
|
? '普通卡'
|
|
: data.card_category === 'industry'
|
|
? '行业卡'
|
|
: data.card_category
|
|
)
|
|
}
|
|
}
|
|
]
|
|
: []),
|
|
{
|
|
label: '创建时间',
|
|
prop: 'created_at',
|
|
formatter: (value: Date) => (value ? formatDateTime(value) : '-')
|
|
},
|
|
{
|
|
label: '开始处理时间',
|
|
prop: 'started_at',
|
|
formatter: (value: Date) => (value ? formatDateTime(value) : '-')
|
|
},
|
|
{
|
|
label: '完成时间',
|
|
prop: 'completed_at',
|
|
formatter: (value: Date) => (value ? formatDateTime(value) : '-')
|
|
},
|
|
...(taskDetail.value?.error_message
|
|
? [
|
|
{
|
|
label: '错误信息',
|
|
prop: 'error_message',
|
|
fullWidth: true,
|
|
render: (data: TaskDetail) => {
|
|
return h(
|
|
'span',
|
|
{ style: { color: 'var(--el-color-danger)' } },
|
|
data.error_message || ''
|
|
)
|
|
}
|
|
}
|
|
]
|
|
: [])
|
|
]
|
|
},
|
|
{
|
|
title: '统计信息',
|
|
columns: 2,
|
|
fields: [
|
|
{
|
|
label: '总数',
|
|
render: (data: TaskDetail) => {
|
|
return h(
|
|
'span',
|
|
{
|
|
style: {
|
|
fontSize: '16px',
|
|
fontWeight: 'bold',
|
|
color: 'var(--el-color-primary)'
|
|
}
|
|
},
|
|
String(data.total_count || 0)
|
|
)
|
|
}
|
|
},
|
|
{
|
|
label: '成功数',
|
|
render: (data: TaskDetail) => {
|
|
return h(ElTag, { type: 'success' }, () => String(data.success_count || 0))
|
|
}
|
|
},
|
|
{
|
|
label: '失败数',
|
|
render: (data: TaskDetail) => {
|
|
return h(ElTag, { type: 'danger' }, () => String(data.fail_count || 0))
|
|
}
|
|
},
|
|
{
|
|
label: '跳过数',
|
|
render: (data: TaskDetail) => {
|
|
return h(ElTag, { type: 'warning' }, () => String(data.skip_count || 0))
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
|
|
// 返回列表
|
|
const goBack = () => {
|
|
router.back()
|
|
}
|
|
|
|
// 获取任务详情
|
|
const getTaskDetail = async () => {
|
|
const taskId = route.query.id
|
|
const queryTaskType = route.query.task_type as TaskType | undefined
|
|
|
|
if (!taskId) {
|
|
ElMessage.error('缺少任务ID参数')
|
|
goBack()
|
|
return
|
|
}
|
|
|
|
// 设置任务类型
|
|
if (queryTaskType) {
|
|
taskType.value = queryTaskType
|
|
}
|
|
|
|
loading.value = true
|
|
try {
|
|
if (taskType.value === 'device') {
|
|
// 获取设备导入任务详情
|
|
const res = await DeviceService.getImportTaskDetail(Number(taskId))
|
|
if (res.code === 0) {
|
|
taskDetail.value = res.data
|
|
}
|
|
} else {
|
|
// 获取ICCID导入任务详情
|
|
const res = await CardService.getIotCardImportTaskDetail(Number(taskId))
|
|
if (res.code === 0) {
|
|
taskDetail.value = res.data
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
console.log('获取任务详情失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
getTaskDetail()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.task-detail-page {
|
|
.detail-header {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: center;
|
|
padding-bottom: 16px;
|
|
|
|
.detail-title {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: var(--el-text-color-primary);
|
|
}
|
|
}
|
|
|
|
.failure-section,
|
|
.skipped-section {
|
|
margin-top: 20px;
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
</style>
|