This commit is contained in:
@@ -12,11 +12,12 @@
|
||||
<template #title>
|
||||
<div style="line-height: 1.8">
|
||||
<p><strong>导入说明:</strong></p>
|
||||
<p>1. 请先下载模板文件,按照模板格式填写设备信息</p>
|
||||
<p>2. 支持 Excel 格式(.xlsx, .xls),单次最多导入 500 条</p>
|
||||
<p>3. 必填字段:设备编号、设备名称、设备类型、ICCID(绑定网卡)</p>
|
||||
<p>4. ICCID 必须在系统中已存在,否则导入失败</p>
|
||||
<p>5. 设备编号重复将自动跳过</p>
|
||||
<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>6. 设备号重复将自动跳过,导入后可在任务管理中查看详情</p>
|
||||
</div>
|
||||
</template>
|
||||
</ElAlert>
|
||||
@@ -31,18 +32,15 @@
|
||||
<ElUpload
|
||||
ref="uploadRef"
|
||||
drag
|
||||
:action="uploadUrl"
|
||||
:on-change="handleFileChange"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:limit="1"
|
||||
accept=".xlsx,.xls"
|
||||
accept=".csv"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__text">将 CSV 文件拖到此处,或<em>点击选择</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传 xlsx/xls 文件,且不超过 5MB</div>
|
||||
<div class="el-upload__tip">只能上传 CSV 文件,且不超过 10MB</div>
|
||||
</template>
|
||||
</ElUpload>
|
||||
<div style="margin-top: 16px; text-align: center">
|
||||
@@ -124,7 +122,7 @@
|
||||
<ElOption label="完成" value="success" />
|
||||
<ElOption label="失败" value="failed" />
|
||||
</ElSelect>
|
||||
<ElButton size="small" @click="refreshList">刷新</ElButton>
|
||||
<ElButton @click="refreshList">刷新</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -239,6 +237,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
Download,
|
||||
UploadFilled,
|
||||
@@ -249,10 +248,14 @@
|
||||
CircleCloseFilled,
|
||||
TrendCharts
|
||||
} from '@element-plus/icons-vue'
|
||||
import type { UploadInstance, UploadRawFile } from 'element-plus'
|
||||
import type { UploadInstance } from 'element-plus'
|
||||
import { StorageService } from '@/api/modules/storage'
|
||||
import { DeviceService } from '@/api/modules'
|
||||
|
||||
defineOptions({ name: 'DeviceImport' })
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
interface FailReason {
|
||||
row: number
|
||||
deviceCode: string
|
||||
@@ -276,8 +279,7 @@
|
||||
}
|
||||
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const uploadUrl = ref('/api/batch/device-import')
|
||||
const fileList = ref<UploadRawFile[]>([])
|
||||
const fileList = ref<File[]>([])
|
||||
const uploading = ref(false)
|
||||
const detailDialogVisible = ref(false)
|
||||
const statusFilter = ref('')
|
||||
@@ -366,8 +368,25 @@
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const handleFileChange = (file: any, files: any[]) => {
|
||||
fileList.value = files
|
||||
const handleFileChange = (uploadFile: any) => {
|
||||
// 验证文件大小(10MB限制)
|
||||
const maxSize = 10 * 1024 * 1024
|
||||
if (uploadFile.raw && uploadFile.raw.size > maxSize) {
|
||||
ElMessage.error('文件大小不能超过 10MB')
|
||||
uploadRef.value?.clearFiles()
|
||||
fileList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
if (uploadFile.raw && !uploadFile.raw.name.endsWith('.csv')) {
|
||||
ElMessage.error('只能上传 CSV 文件')
|
||||
uploadRef.value?.clearFiles()
|
||||
fileList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
fileList.value = uploadFile.raw ? [uploadFile.raw] : []
|
||||
}
|
||||
|
||||
const clearFiles = () => {
|
||||
@@ -375,56 +394,73 @@
|
||||
fileList.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 三步上传流程
|
||||
* 1. 调用 StorageService.getUploadUrl() 获取预签名 URL 和 file_key
|
||||
* 2. 调用 StorageService.uploadFile() 上传文件到对象存储
|
||||
* 3. 调用 DeviceService.importDevices() 触发后端导入任务
|
||||
*/
|
||||
const submitUpload = async () => {
|
||||
if (!fileList.value.length) {
|
||||
ElMessage.warning('请先选择文件')
|
||||
ElMessage.warning('请先选择CSV文件')
|
||||
return
|
||||
}
|
||||
|
||||
const file = fileList.value[0]
|
||||
uploading.value = true
|
||||
ElMessage.info('正在导入设备并绑定ICCID,请稍候...')
|
||||
|
||||
// 模拟上传和导入过程
|
||||
setTimeout(() => {
|
||||
const newRecord: ImportRecord = {
|
||||
id: Date.now().toString(),
|
||||
batchNo: `DEV${new Date().getTime()}`,
|
||||
fileName: fileList.value[0].name,
|
||||
totalCount: 100,
|
||||
successCount: 95,
|
||||
failCount: 5,
|
||||
bindCount: 95,
|
||||
status: 'success',
|
||||
progress: 100,
|
||||
importTime: new Date().toLocaleString('zh-CN'),
|
||||
operator: 'admin',
|
||||
failReasons: [
|
||||
{
|
||||
row: 12,
|
||||
deviceCode: 'TEST001',
|
||||
iccid: '89860123456789012351',
|
||||
message: 'ICCID 不存在'
|
||||
},
|
||||
{ row: 34, deviceCode: 'TEST002', iccid: '89860123456789012352', message: '设备类型无效' }
|
||||
]
|
||||
try {
|
||||
// 第一步:获取预签名上传 URL
|
||||
ElMessage.info('正在准备上传...')
|
||||
const uploadUrlRes = await StorageService.getUploadUrl({
|
||||
file_name: file.name,
|
||||
content_type: 'text/csv',
|
||||
purpose: 'iot_import'
|
||||
})
|
||||
|
||||
if (uploadUrlRes.code !== 0) {
|
||||
throw new Error(uploadUrlRes.msg || '获取上传地址失败')
|
||||
}
|
||||
|
||||
importRecords.value.unshift(newRecord)
|
||||
uploading.value = false
|
||||
const { upload_url, file_key } = uploadUrlRes.data
|
||||
|
||||
// 第二步:上传文件到对象存储
|
||||
ElMessage.info('正在上传文件...')
|
||||
await StorageService.uploadFile(upload_url, file, 'text/csv')
|
||||
|
||||
// 第三步:调用设备导入API
|
||||
ElMessage.info('正在创建导入任务...')
|
||||
const importRes = await DeviceService.importDevices({
|
||||
file_key,
|
||||
batch_no: `DEV-${Date.now()}`
|
||||
})
|
||||
|
||||
if (importRes.code !== 0) {
|
||||
throw new Error(importRes.msg || '创建导入任务失败')
|
||||
}
|
||||
|
||||
const taskNo = importRes.data.task_no
|
||||
|
||||
// 清空文件列表
|
||||
clearFiles()
|
||||
ElMessage.success(
|
||||
`导入完成!成功 ${newRecord.successCount} 条,失败 ${newRecord.failCount} 条,已绑定 ${newRecord.bindCount} 个ICCID`
|
||||
)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const handleUploadSuccess = () => {
|
||||
ElMessage.success('上传成功')
|
||||
}
|
||||
// 显示成功消息并提供跳转链接
|
||||
ElMessage.success({
|
||||
message: `导入任务已创建!任务编号:${taskNo}`,
|
||||
duration: 5000,
|
||||
showClose: true
|
||||
})
|
||||
|
||||
const handleUploadError = () => {
|
||||
uploading.value = false
|
||||
ElMessage.error('上传失败')
|
||||
// 3秒后跳转到任务管理页面
|
||||
setTimeout(() => {
|
||||
router.push('/asset-management/task-management')
|
||||
}, 3000)
|
||||
} catch (error: any) {
|
||||
console.error('设备导入失败:', error)
|
||||
ElMessage.error(error.message || '设备导入失败')
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const refreshList = () => {
|
||||
|
||||
Reference in New Issue
Block a user