Files
one-pipe-system/src/views/asset-management/task-management/index.vue
sexygoat 5c6312c407
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 6s
fetch(add): 新增
2026-01-27 09:18:45 +08:00

417 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ArtTableFullScreen>
<div class="task-management-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
/>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="taskList"
:currentPage="pagination.page"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { useRouter } from 'vue-router'
import { CardService, DeviceService } from '@/api/modules'
import { ElMessage, ElTag } from 'element-plus'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { formatDateTime } from '@/utils/business/format'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import type { IotCardImportTask, IotCardImportTaskStatus } from '@/types/api/card'
import type { DeviceImportTask } from '@/types/api/device'
defineOptions({ name: 'TaskManagement' })
const router = useRouter()
const loading = ref(false)
const tableRef = ref()
// 任务类型
type TaskType = 'card' | 'device'
type ImportTask = IotCardImportTask | DeviceImportTask
// 搜索表单初始值
const initialSearchState = {
task_type: undefined as TaskType | undefined,
status: undefined,
carrier_id: undefined,
batch_no: '',
start_time: '',
end_time: ''
}
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: '任务类型',
prop: 'task_type',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: 'ICCID导入', value: 'card' },
{ label: '设备导入', value: 'device' }
]
},
{
label: '任务状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '待处理', value: 1 },
{ label: '处理中', value: 2 },
{ label: '已完成', value: 3 },
{ label: '失败', value: 4 }
]
},
{
label: '运营商',
prop: 'carrier_id',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '中国移动', value: 1 },
{ label: '中国联通', value: 2 },
{ label: '中国电信', value: 3 }
]
},
{
label: '批次号',
prop: 'batch_no',
type: 'input',
config: {
clearable: true,
placeholder: '请输入批次号'
}
},
{
label: '创建时间',
prop: 'dateRange',
type: 'daterange',
config: {
type: 'daterange',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
valueFormat: 'YYYY-MM-DDTHH:mm:ssZ'
}
}
]
// 列配置
const columnOptions = [
{ label: '任务编号', prop: 'task_no' },
{ label: '任务类型', prop: 'task_type' },
{ label: '批次号', prop: 'batch_no' },
{ label: '运营商', prop: 'carrier_name' },
{ label: '文件名', prop: 'file_name' },
{ label: '任务状态', prop: 'status' },
{ label: '总数', prop: 'total_count' },
{ label: '成功数', prop: 'success_count' },
{ label: '失败数', prop: 'fail_count' },
{ label: '跳过数', prop: 'skip_count' },
{ label: '创建时间', prop: 'created_at' },
{ label: '完成时间', prop: 'completed_at' },
{ label: '操作', prop: 'operation' }
]
const taskList = ref<ImportTask[]>([])
// 获取状态标签类型
const getStatusType = (status: IotCardImportTaskStatus) => {
switch (status) {
case 1:
return 'info'
case 2:
return 'warning'
case 3:
return 'success'
case 4:
return 'danger'
default:
return 'info'
}
}
// 获取任务类型
const getTaskType = (row: ImportTask): TaskType => {
// 判断是否为设备导入任务(设备导入任务有 device_no 字段,卡导入任务有 carrier_name 字段)
if ('device_no' in row || (row.batch_no && row.batch_no.startsWith('DEV-'))) {
return 'device'
}
return 'card'
}
// 获取任务类型文本
const getTaskTypeText = (taskType: TaskType) => {
return taskType === 'device' ? '设备导入' : 'ICCID导入'
}
// 查看详情
const viewDetail = (row: ImportTask) => {
router.push({
path: '/asset-management/task-detail',
query: {
id: row.id,
task_type: getTaskType(row)
}
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'task_no',
label: '任务编号',
width: 150
},
{
prop: 'task_type',
label: '任务类型',
width: 100,
formatter: (row: ImportTask) => {
const taskType = getTaskType(row)
const tagType = taskType === 'device' ? 'warning' : 'primary'
return h(ElTag, { type: tagType, size: 'small' }, () => getTaskTypeText(taskType))
}
},
{
prop: 'batch_no',
label: '批次号',
width: 120
},
{
prop: 'carrier_name',
label: '运营商',
width: 100,
formatter: (row: ImportTask) => {
return (row as IotCardImportTask).carrier_name || '-'
}
},
{
prop: 'file_name',
label: '文件名',
minWidth: 200
},
{
prop: 'status',
label: '任务状态',
width: 100,
formatter: (row: ImportTask) => {
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_text)
}
},
{
prop: 'total_count',
label: '总数',
width: 80
},
{
prop: 'success_count',
label: '成功数',
width: 80
},
{
prop: 'fail_count',
label: '失败数',
width: 80,
formatter: (row: ImportTask) => {
const type = row.fail_count > 0 ? 'danger' : 'success'
return h(ElTag, { type, size: 'small' }, () => row.fail_count)
}
},
{
prop: 'skip_count',
label: '跳过数',
width: 80
},
{
prop: 'created_at',
label: '创建时间',
width: 160,
formatter: (row: ImportTask) => formatDateTime(row.created_at)
},
{
prop: 'completed_at',
label: '完成时间',
width: 160,
formatter: (row: ImportTask) => (row.completed_at ? formatDateTime(row.completed_at) : '-')
},
{
prop: 'operation',
label: '操作',
width: 100,
fixed: 'right',
formatter: (row: ImportTask) => {
return h(ArtButtonTable, {
type: 'view',
onClick: () => viewDetail(row)
})
}
}
])
onMounted(() => {
getTableData()
})
// 获取任务列表
const getTableData = async () => {
loading.value = true
try {
const params: any = {
page: pagination.page,
page_size: pagination.pageSize,
status: searchForm.status,
batch_no: searchForm.batch_no || undefined
}
// 处理时间范围
if (searchForm.dateRange && Array.isArray(searchForm.dateRange)) {
params.start_time = searchForm.dateRange[0]
params.end_time = searchForm.dateRange[1]
}
// 清理空值
Object.keys(params).forEach((key) => {
if (params[key] === '' || params[key] === undefined) {
delete params[key]
}
})
// 根据任务类型获取不同的数据
if (searchForm.task_type === 'device') {
// 仅获取设备导入任务
const res = await DeviceService.getImportTasks(params)
if (res.code === 0) {
taskList.value = res.data.list || []
pagination.total = res.data.total || 0
}
} else if (searchForm.task_type === 'card') {
// 仅获取ICCID导入任务需要carrier_id参数
const cardParams = {
...params,
carrier_id: searchForm.carrier_id
}
const res = await CardService.getIotCardImportTasks(cardParams)
if (res.code === 0) {
taskList.value = res.data.list || []
pagination.total = res.data.total || 0
}
} else {
// 获取所有类型任务 - 分别调用两个API然后合并结果
const [cardRes, deviceRes] = await Promise.all([
CardService.getIotCardImportTasks({
...params,
carrier_id: searchForm.carrier_id
}),
DeviceService.getImportTasks(params)
])
const cardTasks = cardRes.code === 0 ? cardRes.data.list || [] : []
const deviceTasks = deviceRes.code === 0 ? deviceRes.data.list || [] : []
// 合并并按创建时间排序
const allTasks = [...cardTasks, ...deviceTasks].sort((a, b) => {
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
})
// 前端分页
const start = (pagination.page - 1) * pagination.pageSize
const end = start + pagination.pageSize
taskList.value = allTasks.slice(start, end)
pagination.total = allTasks.length
}
} catch (error) {
console.error(error)
ElMessage.error('获取任务列表失败')
} finally {
loading.value = false
}
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
</script>
<style lang="scss" scoped>
.task-management-page {
// Task management page styles
}
</style>