fetch(modify):修改原来的bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m53s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m53s
This commit is contained in:
@@ -15,8 +15,14 @@
|
||||
<p>1. 请先下载 CSV 模板文件,按照模板格式填写设备信息</p>
|
||||
<p>2. 支持 CSV 格式(.csv),单次最多导入 1000 条</p>
|
||||
<p>3. CSV 文件编码:UTF-8(推荐)或 GBK</p>
|
||||
<p>4. 必填字段:device_no(设备号)、device_name(设备名称)、device_model(设备型号)</p>
|
||||
<p>5. 可选字段:device_type(设备类型)、manufacturer(制造商)、max_sim_slots(最大插槽数,默认1)</p>
|
||||
<p
|
||||
>4.
|
||||
必填字段:device_no(设备号)、device_name(设备名称)、device_model(设备型号)</p
|
||||
>
|
||||
<p
|
||||
>5.
|
||||
可选字段:device_type(设备类型)、manufacturer(制造商)、max_sim_slots(最大插槽数,默认1)</p
|
||||
>
|
||||
<p>6. 设备号重复将自动跳过,导入后可在任务管理中查看详情</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -59,52 +65,6 @@
|
||||
</ElRow>
|
||||
</ElCard>
|
||||
|
||||
<!-- 导入统计 -->
|
||||
<ElRow :gutter="20" style="margin-top: 20px">
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElCard shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">今日导入</div>
|
||||
<div class="stat-value">1,250</div>
|
||||
</div>
|
||||
<el-icon class="stat-icon" style="color: var(--el-color-primary)"><Upload /></el-icon>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElCard shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">成功绑定</div>
|
||||
<div class="stat-value" style="color: var(--el-color-success)">1,180</div>
|
||||
</div>
|
||||
<el-icon class="stat-icon" style="color: var(--el-color-success)"
|
||||
><SuccessFilled
|
||||
/></el-icon>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElCard shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">导入失败</div>
|
||||
<div class="stat-value" style="color: var(--el-color-danger)">70</div>
|
||||
</div>
|
||||
<el-icon class="stat-icon" style="color: var(--el-color-danger)"
|
||||
><CircleCloseFilled
|
||||
/></el-icon>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElCard shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">成功率</div>
|
||||
<div class="stat-value">94.4%</div>
|
||||
</div>
|
||||
<el-icon class="stat-icon" style="color: var(--el-color-warning)"
|
||||
><TrendCharts
|
||||
/></el-icon>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<!-- 导入记录 -->
|
||||
<ElCard shadow="never" style="margin-top: 20px">
|
||||
<template #header>
|
||||
@@ -117,71 +77,27 @@
|
||||
style="width: 120px; margin-right: 12px"
|
||||
clearable
|
||||
>
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption label="处理中" value="processing" />
|
||||
<ElOption label="完成" value="success" />
|
||||
<ElOption label="失败" value="failed" />
|
||||
<ElOption label="全部" :value="null" />
|
||||
<ElOption label="待处理" :value="1" />
|
||||
<ElOption label="处理中" :value="2" />
|
||||
<ElOption label="已完成" :value="3" />
|
||||
<ElOption label="失败" :value="4" />
|
||||
</ElSelect>
|
||||
<ElButton @click="refreshList">刷新</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ArtTable :data="filteredRecords" index>
|
||||
<ArtTable
|
||||
rowKey="id"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:data="filteredRecords"
|
||||
:marginTop="10"
|
||||
:stripe="false"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="导入批次号" prop="batchNo" width="180" />
|
||||
<ElTableColumn label="文件名" prop="fileName" min-width="200" show-overflow-tooltip />
|
||||
<ElTableColumn label="设备总数" prop="totalCount" width="100" />
|
||||
<ElTableColumn label="成功数" prop="successCount" width="100">
|
||||
<template #default="scope">
|
||||
<span style="color: var(--el-color-success)">{{ scope.row.successCount }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="失败数" prop="failCount" width="100">
|
||||
<template #default="scope">
|
||||
<span style="color: var(--el-color-danger)">{{ scope.row.failCount }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="已绑定ICCID" prop="bindCount" width="120">
|
||||
<template #default="scope">
|
||||
<ElTag type="success" size="small">{{ scope.row.bindCount }}</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="导入状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<ElTag v-if="scope.row.status === 'processing'" type="warning">
|
||||
<el-icon class="is-loading"><Loading /></el-icon>
|
||||
处理中
|
||||
</ElTag>
|
||||
<ElTag v-else-if="scope.row.status === 'success'" type="success">完成</ElTag>
|
||||
<ElTag v-else-if="scope.row.status === 'failed'" type="danger">失败</ElTag>
|
||||
<ElTag v-else type="info">待处理</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="导入进度" prop="progress" width="150">
|
||||
<template #default="scope">
|
||||
<ElProgress
|
||||
:percentage="scope.row.progress"
|
||||
:status="scope.row.status === 'failed' ? 'exception' : undefined"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="导入时间" prop="importTime" width="180" />
|
||||
<ElTableColumn label="操作人" prop="operator" width="120" />
|
||||
<ElTableColumn fixed="right" label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<el-button link :icon="View" @click="viewDetail(scope.row)">查看详情</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.failCount > 0"
|
||||
link
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="downloadFailData(scope.row)"
|
||||
>
|
||||
失败数据
|
||||
</el-button>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
@@ -189,20 +105,31 @@
|
||||
<!-- 导入详情对话框 -->
|
||||
<ElDialog v-model="detailDialogVisible" title="设备导入详情" width="900px" align-center>
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="任务编号" :span="2">{{
|
||||
currentDetail.taskNo
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">{{ currentDetail.statusText }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="批次号">{{ currentDetail.batchNo }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="文件名">{{ currentDetail.fileName }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="设备总数">{{ currentDetail.totalCount }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="成功导入">
|
||||
<ElDescriptionsItem label="文件名" :span="2">{{
|
||||
currentDetail.fileName
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="总数">{{ currentDetail.totalCount }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="成功数">
|
||||
<span style="color: var(--el-color-success)">{{ currentDetail.successCount }}</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="导入失败">
|
||||
<ElDescriptionsItem label="跳过数">{{ currentDetail.skipCount }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="失败数">
|
||||
<span style="color: var(--el-color-danger)">{{ currentDetail.failCount }}</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="已绑定ICCID">
|
||||
<ElTag type="success">{{ currentDetail.bindCount }}</ElTag>
|
||||
<ElDescriptionsItem label="警告数">
|
||||
<span style="color: var(--el-color-warning)">{{ currentDetail.warningCount }}</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="导入时间">{{ currentDetail.importTime }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="操作人">{{ currentDetail.operator }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="开始时间">{{ currentDetail.startedAt }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="完成时间">{{ currentDetail.completedAt }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间">{{ currentDetail.createdAt }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="错误信息" :span="2">{{
|
||||
currentDetail.errorMessage
|
||||
}}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<ElDivider content-position="left">失败明细</ElDivider>
|
||||
@@ -235,22 +162,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { h, computed, watch } from 'vue'
|
||||
import { ElMessage, ElTag, ElProgress, ElIcon, ElButton } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
Download,
|
||||
UploadFilled,
|
||||
View,
|
||||
Loading,
|
||||
Upload,
|
||||
SuccessFilled,
|
||||
CircleCloseFilled,
|
||||
TrendCharts
|
||||
} from '@element-plus/icons-vue'
|
||||
import { Download, UploadFilled, View, Loading } from '@element-plus/icons-vue'
|
||||
import type { UploadInstance } from 'element-plus'
|
||||
import { StorageService } from '@/api/modules/storage'
|
||||
import { DeviceService } from '@/api/modules'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||
|
||||
defineOptions({ name: 'DeviceImport' })
|
||||
|
||||
@@ -265,107 +186,213 @@
|
||||
|
||||
interface ImportRecord {
|
||||
id: string
|
||||
taskNo: string
|
||||
status: number
|
||||
statusText: string
|
||||
batchNo: string
|
||||
fileName: string
|
||||
totalCount: number
|
||||
successCount: number
|
||||
skipCount: number
|
||||
failCount: number
|
||||
bindCount: number
|
||||
status: 'pending' | 'processing' | 'success' | 'failed'
|
||||
progress: number
|
||||
importTime: string
|
||||
operator: string
|
||||
warningCount: number
|
||||
startedAt: string
|
||||
completedAt: string
|
||||
errorMessage: string
|
||||
createdAt: string
|
||||
failReasons?: FailReason[]
|
||||
}
|
||||
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const tableRef = ref()
|
||||
const fileList = ref<File[]>([])
|
||||
const uploading = ref(false)
|
||||
const detailDialogVisible = ref(false)
|
||||
const statusFilter = ref('')
|
||||
const statusFilter = ref<number | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const importRecords = ref<ImportRecord[]>([
|
||||
{
|
||||
id: '1',
|
||||
batchNo: 'DEV20260109001',
|
||||
fileName: '设备导入模板_20260109.xlsx',
|
||||
totalCount: 300,
|
||||
successCount: 285,
|
||||
failCount: 15,
|
||||
bindCount: 285,
|
||||
status: 'success',
|
||||
progress: 100,
|
||||
importTime: '2026-01-09 11:30:00',
|
||||
operator: 'admin',
|
||||
failReasons: [
|
||||
{ row: 12, deviceCode: 'DEV001', iccid: '89860123456789012345', message: 'ICCID 不存在' },
|
||||
{ row: 23, deviceCode: 'DEV002', iccid: '89860123456789012346', message: '设备编号已存在' },
|
||||
{ row: 45, deviceCode: '', iccid: '89860123456789012347', message: '设备编号为空' },
|
||||
{ row: 67, deviceCode: 'DEV003', iccid: '', message: 'ICCID 为空' },
|
||||
{ row: 89, deviceCode: 'DEV004', iccid: '89860123456789012348', message: '设备类型不存在' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
batchNo: 'DEV20260108001',
|
||||
fileName: '智能水表设备批量导入.xlsx',
|
||||
totalCount: 150,
|
||||
successCount: 150,
|
||||
failCount: 0,
|
||||
bindCount: 150,
|
||||
status: 'success',
|
||||
progress: 100,
|
||||
importTime: '2026-01-08 14:20:00',
|
||||
operator: 'admin'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
batchNo: 'DEV20260107001',
|
||||
fileName: 'GPS定位器导入.xlsx',
|
||||
totalCount: 200,
|
||||
successCount: 180,
|
||||
failCount: 20,
|
||||
bindCount: 180,
|
||||
status: 'success',
|
||||
progress: 100,
|
||||
importTime: '2026-01-07 10:15:00',
|
||||
operator: 'operator01',
|
||||
failReasons: [
|
||||
{
|
||||
row: 10,
|
||||
deviceCode: 'GPS001',
|
||||
iccid: '89860123456789012349',
|
||||
message: 'ICCID 已被其他设备绑定'
|
||||
},
|
||||
{ row: 20, deviceCode: 'GPS002', iccid: '89860123456789012350', message: 'ICCID 状态异常' }
|
||||
]
|
||||
}
|
||||
])
|
||||
const importRecords = ref<ImportRecord[]>([])
|
||||
|
||||
const currentDetail = ref<ImportRecord>({
|
||||
id: '',
|
||||
taskNo: '',
|
||||
status: 1,
|
||||
statusText: '',
|
||||
batchNo: '',
|
||||
fileName: '',
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
skipCount: 0,
|
||||
failCount: 0,
|
||||
bindCount: 0,
|
||||
status: 'pending',
|
||||
progress: 0,
|
||||
importTime: '',
|
||||
operator: ''
|
||||
warningCount: 0,
|
||||
startedAt: '',
|
||||
completedAt: '',
|
||||
errorMessage: '',
|
||||
createdAt: ''
|
||||
})
|
||||
|
||||
const filteredRecords = computed(() => {
|
||||
if (!statusFilter.value) return importRecords.value
|
||||
if (statusFilter.value === null) return importRecords.value
|
||||
return importRecords.value.filter((item) => item.status === statusFilter.value)
|
||||
})
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'taskNo',
|
||||
label: '任务编号',
|
||||
width: 200,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
label: '状态',
|
||||
width: 120,
|
||||
formatter: (row: ImportRecord) => {
|
||||
if (row.status === 1) {
|
||||
return h(ElTag, { type: 'info' }, () => '待处理')
|
||||
} else if (row.status === 2) {
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: 'warning' },
|
||||
{
|
||||
default: () => [
|
||||
h(ElIcon, { class: 'is-loading' }, () => h(Loading)),
|
||||
h('span', { style: { marginLeft: '4px' } }, '处理中')
|
||||
]
|
||||
}
|
||||
)
|
||||
} else if (row.status === 3) {
|
||||
return h(ElTag, { type: 'success' }, () => '已完成')
|
||||
} else if (row.status === 4) {
|
||||
return h(ElTag, { type: 'danger' }, () => '失败')
|
||||
} else {
|
||||
return h(ElTag, { type: 'info' }, () => row.statusText || '-')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'batchNo',
|
||||
label: '批次号',
|
||||
width: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'fileName',
|
||||
label: '文件名',
|
||||
minWidth: 200,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'totalCount',
|
||||
label: '总数',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'successCount',
|
||||
label: '成功数',
|
||||
width: 100,
|
||||
formatter: (row: ImportRecord) => {
|
||||
return h('span', { style: { color: 'var(--el-color-success)' } }, row.successCount)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'skipCount',
|
||||
label: '跳过数',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'failCount',
|
||||
label: '失败数',
|
||||
width: 100,
|
||||
formatter: (row: ImportRecord) => {
|
||||
return h('span', { style: { color: 'var(--el-color-danger)' } }, row.failCount)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'warningCount',
|
||||
label: '警告数',
|
||||
width: 100,
|
||||
formatter: (row: ImportRecord) => {
|
||||
return h('span', { style: { color: 'var(--el-color-warning)' } }, row.warningCount)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'startedAt',
|
||||
label: '开始时间',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
prop: 'completedAt',
|
||||
label: '完成时间',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
prop: 'errorMessage',
|
||||
label: '错误信息',
|
||||
minWidth: 200,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'createdAt',
|
||||
label: '创建时间',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
formatter: (row: ImportRecord) => {
|
||||
const buttons = []
|
||||
|
||||
// 显示"查看详情"按钮
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '详情',
|
||||
onClick: () => viewDetail(row)
|
||||
})
|
||||
),
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '失败数据',
|
||||
type: 'danger',
|
||||
onClick: () => downloadFailData(row)
|
||||
})
|
||||
)
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const downloadTemplate = () => {
|
||||
ElMessage.success('模板下载中...')
|
||||
setTimeout(() => {
|
||||
ElMessage.success('设备导入模板下载成功')
|
||||
}, 1000)
|
||||
// CSV模板内容 - 包含表头和示例数据
|
||||
const csvContent = [
|
||||
// 表头
|
||||
'device_no,device_name,device_model,device_type,manufacturer,max_sim_slots',
|
||||
// 示例数据
|
||||
'DEV001,智能水表01,WM-2000,智能水表,华为,1',
|
||||
'DEV002,GPS定位器01,GPS-3000,定位设备,小米,2',
|
||||
'DEV003,智能燃气表01,GM-1500,智能燃气表,海尔,1'
|
||||
].join('\n')
|
||||
|
||||
// 添加 BOM 头确保 Excel 正确识别 UTF-8 编码
|
||||
const BOM = '\uFEFF'
|
||||
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' })
|
||||
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a')
|
||||
const url = URL.createObjectURL(blob)
|
||||
link.setAttribute('href', url)
|
||||
link.setAttribute('download', '设备导入模板.csv')
|
||||
link.style.visibility = 'hidden'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
ElMessage.success('设备导入模板下载成功')
|
||||
}
|
||||
|
||||
const handleFileChange = (uploadFile: any) => {
|
||||
@@ -444,17 +471,15 @@
|
||||
// 清空文件列表
|
||||
clearFiles()
|
||||
|
||||
// 显示成功消息并提供跳转链接
|
||||
// 刷新任务列表
|
||||
await fetchImportTasks()
|
||||
|
||||
// 显示成功消息
|
||||
ElMessage.success({
|
||||
message: `导入任务已创建!任务编号:${taskNo}`,
|
||||
duration: 5000,
|
||||
duration: 3000,
|
||||
showClose: true
|
||||
})
|
||||
|
||||
// 3秒后跳转到任务管理页面
|
||||
setTimeout(() => {
|
||||
router.push('/asset-management/task-management')
|
||||
}, 3000)
|
||||
} catch (error: any) {
|
||||
console.error('设备导入失败:', error)
|
||||
ElMessage.error(error.message || '设备导入失败')
|
||||
@@ -463,21 +488,133 @@
|
||||
}
|
||||
}
|
||||
|
||||
const refreshList = () => {
|
||||
ElMessage.success('刷新成功')
|
||||
// 获取导入任务列表
|
||||
const fetchImportTasks = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 100
|
||||
}
|
||||
|
||||
// 如果有状态筛选,添加到参数
|
||||
if (statusFilter.value !== null) {
|
||||
params.status = statusFilter.value
|
||||
}
|
||||
|
||||
const res = await DeviceService.getImportTasks(params)
|
||||
if (res.code === 0 && res.data) {
|
||||
// 将API返回的数据映射到本地格式
|
||||
importRecords.value = res.data.items.map((item: any) => ({
|
||||
id: item.id.toString(),
|
||||
taskNo: item.task_no || '-',
|
||||
status: item.status,
|
||||
statusText: item.status_text || '-',
|
||||
batchNo: item.batch_no || '-',
|
||||
fileName: item.file_name || '-',
|
||||
totalCount: item.total_count || 0,
|
||||
successCount: item.success_count || 0,
|
||||
skipCount: item.skip_count || 0,
|
||||
failCount: item.fail_count || 0,
|
||||
warningCount: item.warning_count || 0,
|
||||
startedAt: item.started_at ? formatDateTime(item.started_at) : '-',
|
||||
completedAt: item.completed_at ? formatDateTime(item.completed_at) : '-',
|
||||
errorMessage: item.error_message || '-',
|
||||
createdAt: item.created_at ? formatDateTime(item.created_at) : '-'
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取导入任务列表失败:', error)
|
||||
ElMessage.error('获取导入任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const viewDetail = (row: ImportRecord) => {
|
||||
currentDetail.value = { ...row }
|
||||
detailDialogVisible.value = true
|
||||
const refreshList = () => {
|
||||
fetchImportTasks()
|
||||
}
|
||||
|
||||
const viewDetail = async (row: ImportRecord) => {
|
||||
try {
|
||||
const res = await DeviceService.getImportTaskDetail(Number(row.id))
|
||||
if (res.code === 0 && res.data) {
|
||||
const detail = res.data
|
||||
currentDetail.value = {
|
||||
id: detail.id.toString(),
|
||||
taskNo: detail.task_no || '-',
|
||||
status: detail.status,
|
||||
statusText: detail.status_text || '-',
|
||||
batchNo: detail.batch_no || '-',
|
||||
fileName: detail.file_name || '-',
|
||||
totalCount: detail.total_count || 0,
|
||||
successCount: detail.success_count || 0,
|
||||
skipCount: detail.skip_count || 0,
|
||||
failCount: detail.fail_count || 0,
|
||||
warningCount: detail.warning_count || 0,
|
||||
startedAt: detail.started_at ? formatDateTime(detail.started_at) : '-',
|
||||
completedAt: detail.completed_at ? formatDateTime(detail.completed_at) : '-',
|
||||
errorMessage: detail.error_message || '-',
|
||||
createdAt: detail.created_at ? formatDateTime(detail.created_at) : '-',
|
||||
failReasons:
|
||||
detail.failed_items?.map((item: any, index: number) => ({
|
||||
row: index + 1,
|
||||
deviceCode: item.device_no || '-',
|
||||
iccid: item.iccid || '-',
|
||||
message: item.reason || item.error || '未知错误'
|
||||
})) || []
|
||||
}
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取任务详情失败:', error)
|
||||
ElMessage.error('获取任务详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
const downloadFailData = (row: ImportRecord) => {
|
||||
ElMessage.info(`正在下载批次 ${row.batchNo} 的失败数据...`)
|
||||
setTimeout(() => {
|
||||
ElMessage.success('失败数据下载完成')
|
||||
}, 1000)
|
||||
if (!row.failReasons || row.failReasons.length === 0) {
|
||||
ElMessage.warning('没有失败数据可下载')
|
||||
return
|
||||
}
|
||||
|
||||
// 生成失败数据CSV
|
||||
const headers = ['行号', '设备编号', 'ICCID', '失败原因']
|
||||
const csvRows = [
|
||||
headers.join(','),
|
||||
...row.failReasons.map((item) =>
|
||||
[item.row, item.deviceCode, item.iccid, `"${item.message}"`].join(',')
|
||||
)
|
||||
]
|
||||
const csvContent = csvRows.join('\n')
|
||||
|
||||
// 添加 BOM 头确保 Excel 正确识别 UTF-8 编码
|
||||
const BOM = '\uFEFF'
|
||||
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' })
|
||||
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a')
|
||||
const url = URL.createObjectURL(blob)
|
||||
link.setAttribute('href', url)
|
||||
link.setAttribute('download', `导入失败数据_${row.batchNo}.csv`)
|
||||
link.style.visibility = 'hidden'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
ElMessage.success('失败数据下载成功')
|
||||
}
|
||||
|
||||
// 页面加载时获取任务列表
|
||||
onMounted(() => {
|
||||
fetchImportTasks()
|
||||
})
|
||||
|
||||
// 监听状态筛选变化
|
||||
watch(statusFilter, () => {
|
||||
fetchImportTasks()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user