This commit is contained in:
@@ -122,7 +122,7 @@
|
||||
:key="item.device_id"
|
||||
style="margin-bottom: 8px; font-size: 12px; color: #f56c6c"
|
||||
>
|
||||
设备号: {{ item.device_no }} - {{ item.reason }}
|
||||
设备号: {{ item.virtual_no }} - {{ item.reason }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,7 +178,7 @@
|
||||
:key="item.device_id"
|
||||
style="margin-bottom: 8px; font-size: 12px; color: #f56c6c"
|
||||
>
|
||||
设备号: {{ item.device_no }} - {{ item.reason }}
|
||||
设备号: {{ item.virtual_no }} - {{ item.reason }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,7 +260,7 @@
|
||||
:key="item.device_id"
|
||||
style="margin-bottom: 8px; font-size: 12px; color: #f56c6c"
|
||||
>
|
||||
设备号: {{ item.device_no }} - {{ item.reason }}
|
||||
设备号: {{ item.virtual_no }} - {{ item.reason }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -292,7 +292,7 @@
|
||||
<ElDescriptions :column="3" border style="margin-bottom: 20px">
|
||||
<ElDescriptionsItem label="设备ID">{{ currentDeviceDetail.id }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="设备号" :span="2">{{
|
||||
currentDeviceDetail.device_no
|
||||
currentDeviceDetail.virtual_no
|
||||
}}</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="设备名称">{{
|
||||
@@ -332,10 +332,72 @@
|
||||
</ElDialog>
|
||||
|
||||
<!-- 绑定卡片列表弹窗 -->
|
||||
<ElDialog v-model="deviceCardsDialogVisible" title="绑定的卡片" width="900px">
|
||||
<div style="margin-bottom: 10px; text-align: right">
|
||||
<ElButton type="primary" @click="handleBindCard">绑定新卡</ElButton>
|
||||
</div>
|
||||
<ElDialog v-model="deviceCardsDialogVisible" title="设备绑定的卡" width="60%">
|
||||
<!-- 绑定新卡表单 -->
|
||||
<ElCard shadow="never" class="bind-card-section" style="margin-bottom: 20px">
|
||||
<template #header>
|
||||
<div style="font-weight: bold">绑定新卡</div>
|
||||
</template>
|
||||
<ElForm
|
||||
ref="bindCardFormRef"
|
||||
:model="bindCardForm"
|
||||
:rules="bindCardRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="10">
|
||||
<ElFormItem label="IoT卡" prop="iot_card_id">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.iot_card_id"
|
||||
placeholder="请选择或搜索IoT卡(支持ICCID搜索)"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:remote-method="searchIotCards"
|
||||
:loading="iotCardSearchLoading"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="card in iotCardList"
|
||||
:key="card.id"
|
||||
:label="`${card.iccid} ${card.msisdn ? '(' + card.msisdn + ')' : ''}`"
|
||||
:value="card.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="10">
|
||||
<ElFormItem label="插槽位置" prop="slot_position">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.slot_position"
|
||||
placeholder="请选择插槽位置"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="i in currentDeviceDetail?.max_sim_slots || 4"
|
||||
:key="i"
|
||||
:label="`插槽 ${i}`"
|
||||
:value="i"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="4">
|
||||
<ElButton
|
||||
type="primary"
|
||||
@click="handleConfirmBindCard"
|
||||
:loading="bindCardLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
确认绑定
|
||||
</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElForm>
|
||||
</ElCard>
|
||||
|
||||
<!-- 已绑定卡片列表 -->
|
||||
<div v-if="deviceCardsLoading" style="text-align: center; padding: 40px 0">
|
||||
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
|
||||
</div>
|
||||
@@ -368,7 +430,7 @@
|
||||
v-if="deviceCards.length === 0"
|
||||
style="text-align: center; padding: 20px; color: #909399"
|
||||
>
|
||||
暂无绑定的卡片
|
||||
暂无设备绑定的卡
|
||||
</div>
|
||||
</div>
|
||||
</ElDialog>
|
||||
@@ -381,57 +443,6 @@
|
||||
@select="handleDeviceOperationMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 绑定卡弹窗 -->
|
||||
<ElDialog v-model="bindCardDialogVisible" title="绑定卡到设备" width="500px">
|
||||
<ElForm
|
||||
ref="bindCardFormRef"
|
||||
:model="bindCardForm"
|
||||
:rules="bindCardRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<ElFormItem label="IoT卡" prop="iot_card_id">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.iot_card_id"
|
||||
placeholder="请选择或搜索IoT卡(支持ICCID搜索)"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:remote-method="searchIotCards"
|
||||
:loading="iotCardSearchLoading"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="card in iotCardList"
|
||||
:key="card.id"
|
||||
:label="`${card.iccid} ${card.msisdn ? '(' + card.msisdn + ')' : ''}`"
|
||||
:value="card.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="插槽位置" prop="slot_position">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.slot_position"
|
||||
placeholder="请选择插槽位置"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="i in currentDeviceDetail?.max_sim_slots || 4"
|
||||
:key="i"
|
||||
:label="`插槽 ${i}`"
|
||||
:value="i"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="bindCardDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleConfirmBindCard" :loading="bindCardLoading">
|
||||
确认绑定
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 设置限速对话框 -->
|
||||
<ElDialog v-model="speedLimitDialogVisible" title="设置限速" width="500px">
|
||||
<ElForm
|
||||
@@ -552,7 +563,13 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DeviceService, ShopService, CardService, PackageSeriesService } from '@/api/modules'
|
||||
import {
|
||||
DeviceService,
|
||||
ShopService,
|
||||
CardService,
|
||||
PackageSeriesService,
|
||||
AssetService
|
||||
} from '@/api/modules'
|
||||
import {
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
@@ -635,7 +652,6 @@
|
||||
const deviceCardsDialogVisible = ref(false)
|
||||
|
||||
// 绑定卡相关
|
||||
const bindCardDialogVisible = ref(false)
|
||||
const bindCardLoading = ref(false)
|
||||
const bindCardFormRef = ref<FormInstance>()
|
||||
const iotCardList = ref<any[]>([])
|
||||
@@ -704,7 +720,7 @@
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
device_no: '',
|
||||
virtual_no: '',
|
||||
device_name: '',
|
||||
status: undefined as DeviceStatus | undefined,
|
||||
batch_no: '',
|
||||
@@ -719,7 +735,7 @@
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '设备号',
|
||||
prop: 'device_no',
|
||||
prop: 'virtual_no',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true,
|
||||
@@ -788,7 +804,7 @@
|
||||
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: '设备号', prop: 'device_no' },
|
||||
{ label: '设备号', prop: 'virtual_no' },
|
||||
{ label: '设备名称', prop: 'device_name' },
|
||||
{ label: '设备型号', prop: 'device_model' },
|
||||
{ label: '设备类型', prop: 'device_type' },
|
||||
@@ -825,7 +841,7 @@
|
||||
router.push({
|
||||
path: '/asset-management/single-card',
|
||||
query: {
|
||||
device_no: deviceNo
|
||||
virtual_no: deviceNo
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -833,12 +849,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 查看设备绑定的卡片
|
||||
// 查看设备设备绑定的卡
|
||||
const handleViewCards = async (device: Device) => {
|
||||
currentDeviceDetail.value = device
|
||||
deviceCards.value = []
|
||||
deviceCardsDialogVisible.value = true
|
||||
await loadDeviceCards(device.id)
|
||||
// 重置绑定卡表单
|
||||
bindCardForm.iot_card_id = undefined
|
||||
bindCardForm.slot_position = 1
|
||||
// 加载设备卡列表和默认IoT卡列表
|
||||
await Promise.all([loadDeviceCards(device.id), loadDefaultIotCards()])
|
||||
}
|
||||
|
||||
// 加载设备绑定的卡列表
|
||||
@@ -856,15 +876,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 打开绑定卡弹窗
|
||||
const handleBindCard = async () => {
|
||||
bindCardForm.iot_card_id = undefined
|
||||
bindCardForm.slot_position = 1
|
||||
bindCardDialogVisible.value = true
|
||||
// 加载默认的IoT卡列表
|
||||
await loadDefaultIotCards()
|
||||
}
|
||||
|
||||
// 加载默认的IoT卡列表
|
||||
const loadDefaultIotCards = async () => {
|
||||
iotCardSearchLoading.value = true
|
||||
@@ -920,22 +931,20 @@
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('绑定成功')
|
||||
bindCardDialogVisible.value = false
|
||||
// 重置表单
|
||||
bindCardForm.iot_card_id = undefined
|
||||
bindCardForm.slot_position = 1
|
||||
bindCardFormRef.value.resetFields()
|
||||
// 重新加载卡列表
|
||||
await loadDeviceCards(currentDeviceDetail.value.id)
|
||||
// 刷新设备详情以更新绑定卡数量
|
||||
const detailRes = await DeviceService.getDeviceByImei(
|
||||
currentDeviceDetail.value.device_no
|
||||
)
|
||||
const detailRes = await AssetService.resolveAsset(currentDeviceDetail.value.virtual_no)
|
||||
if (detailRes.code === 0 && detailRes.data) {
|
||||
currentDeviceDetail.value = detailRes.data
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message || '绑定失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('绑定卡失败:', error)
|
||||
ElMessage.error(error?.message || '绑定失败')
|
||||
} finally {
|
||||
bindCardLoading.value = false
|
||||
}
|
||||
@@ -958,9 +967,7 @@
|
||||
// 重新加载卡列表
|
||||
await loadDeviceCards(currentDeviceDetail.value.id)
|
||||
// 刷新设备详情以更新绑定卡数量
|
||||
const detailRes = await DeviceService.getDeviceByImei(
|
||||
currentDeviceDetail.value.device_no
|
||||
)
|
||||
const detailRes = await AssetService.resolveAsset(currentDeviceDetail.value.virtual_no)
|
||||
if (detailRes.code === 0 && detailRes.data) {
|
||||
currentDeviceDetail.value = detailRes.data
|
||||
}
|
||||
@@ -1013,7 +1020,7 @@
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'device_no',
|
||||
prop: 'virtual_no',
|
||||
label: '设备号',
|
||||
minWidth: 150,
|
||||
showOverflowTooltip: true,
|
||||
@@ -1024,10 +1031,10 @@
|
||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
goToDeviceSearchDetail(row.device_no)
|
||||
goToDeviceSearchDetail(row.virtual_no)
|
||||
}
|
||||
},
|
||||
row.device_no
|
||||
row.virtual_no
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -1156,7 +1163,7 @@
|
||||
const params = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize,
|
||||
device_no: searchForm.device_no || undefined,
|
||||
virtual_no: searchForm.virtual_no || undefined,
|
||||
device_name: searchForm.device_name || undefined,
|
||||
status: searchForm.status,
|
||||
batch_no: searchForm.batch_no || undefined,
|
||||
@@ -1212,11 +1219,15 @@
|
||||
|
||||
// 删除设备
|
||||
const deleteDevice = (row: Device) => {
|
||||
ElMessageBox.confirm(`确定删除设备 ${row.device_no} 吗?删除后将自动解绑所有卡。`, '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
})
|
||||
ElMessageBox.confirm(
|
||||
`确定删除设备 ${row.virtual_no} 吗?删除后将自动解绑所有卡。`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
try {
|
||||
await DeviceService.deleteDevice(row.id)
|
||||
@@ -1449,9 +1460,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 通过设备号查看卡片
|
||||
// 通过设备号设备绑定的卡
|
||||
const handleViewCardsByDeviceNo = (deviceNo: string) => {
|
||||
const device = deviceList.value.find((d) => d.device_no === deviceNo)
|
||||
const device = deviceList.value.find((d) => d.virtual_no === deviceNo)
|
||||
if (device) {
|
||||
handleViewCards(device)
|
||||
} else {
|
||||
@@ -1462,7 +1473,7 @@
|
||||
// 通过设备号删除设备
|
||||
const handleDeleteDeviceByNo = async (deviceNo: string) => {
|
||||
// 先根据设备号找到设备对象
|
||||
const device = deviceList.value.find((d) => d.device_no === deviceNo)
|
||||
const device = deviceList.value.find((d) => d.virtual_no === deviceNo)
|
||||
if (device) {
|
||||
deleteDevice(device)
|
||||
} else {
|
||||
@@ -1636,11 +1647,11 @@
|
||||
const deviceOperationMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
// 添加查看卡片到菜单最前面
|
||||
// 添加设备绑定的卡到菜单最前面
|
||||
if (hasAuth('device:view_cards')) {
|
||||
items.push({
|
||||
key: 'view-cards',
|
||||
label: '查看卡片'
|
||||
label: '设备绑定的卡'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1707,7 +1718,7 @@
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: Device, column: any, event: MouseEvent) => {
|
||||
showDeviceOperationMenu(event, row.device_no)
|
||||
showDeviceOperationMenu(event, row.virtual_no)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<ElDescriptions :column="3" border>
|
||||
<ElDescriptionsItem label="设备ID">{{ deviceDetail.id }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="设备号" :span="2">{{
|
||||
deviceDetail.device_no
|
||||
deviceDetail.virtual_no
|
||||
}}</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="设备名称">{{
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DeviceService } from '@/api/modules/device'
|
||||
import { AssetService } from '@/api/modules'
|
||||
|
||||
defineOptions({ name: 'DeviceSearch' })
|
||||
|
||||
@@ -106,12 +106,12 @@
|
||||
deviceDetail.value = null
|
||||
|
||||
try {
|
||||
const res = await DeviceService.getDeviceByImei(searchForm.imei.trim())
|
||||
const res = await AssetService.resolveAsset(searchForm.imei.trim())
|
||||
if (res.code === 0 && res.data) {
|
||||
deviceDetail.value = res.data
|
||||
ElMessage.success('查询成功')
|
||||
} else {
|
||||
ElMessage.error(res.message || '查询失败')
|
||||
ElMessage.error(res.msg || '查询失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('查询设备详情失败:', error)
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<p>3. 列格式请设置为文本格式,避免长数字被转为科学计数法</p>
|
||||
<p
|
||||
>4.
|
||||
必填列:device_no(设备号)、device_name(设备名称)、device_model(设备型号)、device_type(设备类型)</p
|
||||
必填列:virtual_no(设备号)、device_name(设备名称)、device_model(设备型号)、device_type(设备类型)</p
|
||||
>
|
||||
<p
|
||||
>5. 可选列:manufacturer(制造商)、max_sim_slots(最大插槽数,默认4)、iccid_1 ~
|
||||
@@ -441,7 +441,7 @@
|
||||
// 创建示例数据
|
||||
const templateData = [
|
||||
{
|
||||
device_no: '862639070731999',
|
||||
virtual_no: '862639070731999',
|
||||
device_name: '智能水表01',
|
||||
device_model: 'WM-2000',
|
||||
device_type: '智能水表',
|
||||
@@ -453,7 +453,7 @@
|
||||
iccid_4: ''
|
||||
},
|
||||
{
|
||||
device_no: '862639070750932',
|
||||
virtual_no: '862639070750932',
|
||||
device_name: 'GPS定位器01',
|
||||
device_model: 'GPS-3000',
|
||||
device_type: '定位设备',
|
||||
@@ -472,7 +472,7 @@
|
||||
|
||||
// 设置列宽
|
||||
ws['!cols'] = [
|
||||
{ wch: 20 }, // device_no
|
||||
{ wch: 20 }, // virtual_no
|
||||
{ wch: 20 }, // device_name
|
||||
{ wch: 15 }, // device_model
|
||||
{ wch: 15 }, // device_type
|
||||
@@ -607,7 +607,7 @@
|
||||
const failReasons =
|
||||
detail.failed_items?.map((item: any) => ({
|
||||
line: item.line || '-',
|
||||
deviceNo: item.device_no || '-',
|
||||
deviceNo: item.virtual_no || '-',
|
||||
message: item.reason || '未知错误'
|
||||
})) || []
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
:rules="allocateRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<ElFormItem :label="$t('enterpriseDevices.form.deviceNos')" prop="device_nos">
|
||||
<ElFormItem :label="$t('enterpriseDevices.form.deviceNos')" prop="virtual_nos">
|
||||
<ElInput
|
||||
v-model="deviceNosText"
|
||||
type="textarea"
|
||||
@@ -93,7 +93,7 @@
|
||||
<div style="margin-top: 4px; font-size: 12px; color: var(--el-color-info)">
|
||||
{{
|
||||
$t('enterpriseDevices.form.selectedCount', {
|
||||
count: allocateForm.device_nos?.length || 0
|
||||
count: allocateForm.virtual_nos?.length || 0
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
@@ -171,7 +171,7 @@
|
||||
}}</ElDivider>
|
||||
<ElTable :data="operationResult.failed_items" border max-height="300">
|
||||
<ElTableColumn
|
||||
prop="device_no"
|
||||
prop="virtual_no"
|
||||
:label="$t('enterpriseDevices.result.deviceNo')"
|
||||
width="180"
|
||||
/>
|
||||
@@ -191,7 +191,7 @@
|
||||
}}</ElDivider>
|
||||
<ElTable :data="operationResult.authorized_devices" border max-height="200">
|
||||
<ElTableColumn
|
||||
prop="device_no"
|
||||
prop="virtual_no"
|
||||
:label="$t('enterpriseDevices.result.deviceNo')"
|
||||
width="180"
|
||||
/>
|
||||
@@ -267,7 +267,7 @@
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
device_no: ''
|
||||
virtual_no: ''
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
@@ -275,13 +275,13 @@
|
||||
|
||||
// 授权表单
|
||||
const allocateForm = reactive({
|
||||
device_nos: [] as string[],
|
||||
virtual_nos: [] as string[],
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 授权表单验证规则
|
||||
const allocateRules = reactive<FormRules>({
|
||||
device_nos: [
|
||||
virtual_nos: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
@@ -315,7 +315,7 @@
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: t('enterpriseDevices.searchForm.deviceNo'),
|
||||
prop: 'device_no',
|
||||
prop: 'virtual_no',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true,
|
||||
@@ -327,7 +327,7 @@
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: t('enterpriseDevices.table.deviceId'), prop: 'device_id' },
|
||||
{ label: t('enterpriseDevices.table.deviceNo'), prop: 'device_no' },
|
||||
{ label: t('enterpriseDevices.table.deviceNo'), prop: 'virtual_no' },
|
||||
{ label: t('enterpriseDevices.table.deviceName'), prop: 'device_name' },
|
||||
{ label: t('enterpriseDevices.table.deviceModel'), prop: 'device_model' },
|
||||
{ label: t('enterpriseDevices.table.cardCount'), prop: 'card_count' },
|
||||
@@ -344,7 +344,7 @@
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'device_no',
|
||||
prop: 'virtual_no',
|
||||
label: t('enterpriseDevices.table.deviceNo'),
|
||||
minWidth: 150
|
||||
},
|
||||
@@ -407,7 +407,7 @@
|
||||
const params: any = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize,
|
||||
device_no: searchForm.device_no || undefined
|
||||
virtual_no: searchForm.virtual_no || undefined
|
||||
}
|
||||
|
||||
// 清理空值
|
||||
@@ -473,7 +473,7 @@
|
||||
const showAllocateDialog = () => {
|
||||
allocateDialogVisible.value = true
|
||||
deviceNosText.value = ''
|
||||
allocateForm.device_nos = []
|
||||
allocateForm.virtual_nos = []
|
||||
allocateForm.remark = ''
|
||||
if (allocateFormRef.value) {
|
||||
allocateFormRef.value.resetFields()
|
||||
@@ -487,7 +487,7 @@
|
||||
.split(/[,\s\n]+/)
|
||||
.map((no) => no.trim())
|
||||
.filter((no) => no.length > 0)
|
||||
allocateForm.device_nos = deviceNos
|
||||
allocateForm.virtual_nos = deviceNos
|
||||
}
|
||||
|
||||
// 执行授权
|
||||
@@ -496,12 +496,12 @@
|
||||
|
||||
await allocateFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (allocateForm.device_nos.length === 0) {
|
||||
if (allocateForm.virtual_nos.length === 0) {
|
||||
ElMessage.warning(t('enterpriseDevices.messages.deviceNosEmpty'))
|
||||
return
|
||||
}
|
||||
|
||||
if (allocateForm.device_nos.length > 100) {
|
||||
if (allocateForm.virtual_nos.length > 100) {
|
||||
ElMessage.warning(t('enterpriseDevices.messages.deviceNosMaxLimit'))
|
||||
return
|
||||
}
|
||||
@@ -509,7 +509,7 @@
|
||||
allocateLoading.value = true
|
||||
try {
|
||||
const res = await EnterpriseService.allocateDevices(enterpriseId.value, {
|
||||
device_nos: allocateForm.device_nos,
|
||||
virtual_nos: allocateForm.virtual_nos,
|
||||
remark: allocateForm.remark || undefined
|
||||
})
|
||||
|
||||
@@ -582,7 +582,7 @@
|
||||
recallLoading.value = true
|
||||
try {
|
||||
const res = await EnterpriseService.recallDevices(enterpriseId.value, {
|
||||
device_nos: selectedDevices.value.map((device) => device.device_no)
|
||||
virtual_nos: selectedDevices.value.map((device) => device.virtual_no)
|
||||
})
|
||||
|
||||
if (res.code === 0) {
|
||||
|
||||
@@ -471,12 +471,15 @@
|
||||
</div>
|
||||
|
||||
<ElDescriptions v-else-if="flowUsageData" :column="1" border>
|
||||
<ElDescriptionsItem label="已用流量">{{
|
||||
flowUsageData.usedFlow || 0
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="流量单位">{{
|
||||
flowUsageData.unit || 'MB'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="套餐总流量"
|
||||
>{{ flowUsageData.totalFlow || 0 }} MB</ElDescriptionsItem
|
||||
>
|
||||
<ElDescriptionsItem label="已用流量"
|
||||
>{{ flowUsageData.usedFlow || 0 }} MB</ElDescriptionsItem
|
||||
>
|
||||
<ElDescriptionsItem label="剩余流量"
|
||||
>{{ flowUsageData.remainFlow || 0 }} MB</ElDescriptionsItem
|
||||
>
|
||||
<ElDescriptionsItem v-if="flowUsageData.extend" label="扩展信息">{{
|
||||
flowUsageData.extend
|
||||
}}</ElDescriptionsItem>
|
||||
@@ -538,40 +541,6 @@
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 实名认证链接对话框 -->
|
||||
<ElDialog v-model="realnameLinkDialogVisible" title="实名认证链接" width="500px">
|
||||
<div v-if="realnameLinkLoading" style="text-align: center; padding: 40px">
|
||||
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
|
||||
<div style="margin-top: 16px">获取中...</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="realnameLinkData && realnameLinkData.link" style="text-align: center">
|
||||
<div style="margin-bottom: 16px">
|
||||
<img v-if="qrcodeDataURL" :src="qrcodeDataURL" alt="实名认证二维码" />
|
||||
</div>
|
||||
<ElDescriptions :column="1" border>
|
||||
<ElDescriptionsItem label="实名链接">
|
||||
<a
|
||||
:href="realnameLinkData.link"
|
||||
target="_blank"
|
||||
style="color: var(--el-color-primary)"
|
||||
>
|
||||
{{ realnameLinkData.link }}
|
||||
</a>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem v-if="realnameLinkData.extend" label="扩展信息">{{
|
||||
realnameLinkData.extend
|
||||
}}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton type="primary" @click="realnameLinkDialogVisible = false">关闭</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 更多操作右键菜单 -->
|
||||
<ArtMenuRight
|
||||
ref="moreMenuRef"
|
||||
@@ -595,11 +564,10 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CardService, ShopService, PackageSeriesService } from '@/api/modules'
|
||||
import { CardService, ShopService, PackageSeriesService, AssetService } from '@/api/modules'
|
||||
import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import QRCode from 'qrcode'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
@@ -690,11 +658,6 @@
|
||||
const cardStatusLoading = ref(false)
|
||||
const cardStatusData = ref<any>(null)
|
||||
|
||||
const realnameLinkDialogVisible = ref(false)
|
||||
const realnameLinkLoading = ref(false)
|
||||
const realnameLinkData = ref<any>(null)
|
||||
const qrcodeDataURL = ref<string>('')
|
||||
|
||||
// 更多操作右键菜单
|
||||
const moreMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const cardOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
@@ -712,6 +675,7 @@
|
||||
carrier_id: undefined,
|
||||
iccid: '',
|
||||
msisdn: '',
|
||||
virtual_no: '',
|
||||
is_distributed: undefined
|
||||
}
|
||||
|
||||
@@ -861,6 +825,15 @@
|
||||
placeholder: '请输入卡接入号'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '虚拟号',
|
||||
prop: 'virtual_no',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请输入虚拟号'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '是否已分销',
|
||||
prop: 'is_distributed',
|
||||
@@ -880,6 +853,7 @@
|
||||
const columnOptions = [
|
||||
{ label: 'ICCID', prop: 'iccid' },
|
||||
{ label: '卡接入号', prop: 'msisdn' },
|
||||
{ label: '虚拟号', prop: 'virtual_no' },
|
||||
{ label: '卡业务类型', prop: 'card_category' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
{ label: '店铺名称', prop: 'shop_name' },
|
||||
@@ -1010,6 +984,12 @@
|
||||
label: '卡接入号',
|
||||
width: 130
|
||||
},
|
||||
{
|
||||
prop: 'virtual_no',
|
||||
label: '虚拟号',
|
||||
width: 130,
|
||||
formatter: (row: StandaloneIotCard) => row.virtual_no || '-'
|
||||
},
|
||||
{
|
||||
prop: 'card_category',
|
||||
label: '卡业务类型',
|
||||
@@ -1569,24 +1549,17 @@
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('iot_card:get_realname_link')) {
|
||||
items.push({
|
||||
key: 'realname-link',
|
||||
label: '获取实名链接'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('iot_card:start_card')) {
|
||||
items.push({
|
||||
key: 'start-card',
|
||||
label: '启用卡片'
|
||||
label: '启用此卡'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('iot_card:stop_card')) {
|
||||
items.push({
|
||||
key: 'stop-card',
|
||||
label: '停用卡片'
|
||||
label: '停用此卡'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1681,9 +1654,6 @@
|
||||
case 'card-status':
|
||||
showCardStatusDialog(iccid)
|
||||
break
|
||||
case 'realname-link':
|
||||
showRealnameLinkDialog(iccid)
|
||||
break
|
||||
case 'start-card':
|
||||
handleStartCard(iccid)
|
||||
break
|
||||
@@ -1700,9 +1670,16 @@
|
||||
flowUsageData.value = null
|
||||
|
||||
try {
|
||||
const res = await CardService.getGatewayFlow(iccid)
|
||||
if (res.code === 0) {
|
||||
flowUsageData.value = res.data
|
||||
// 通过 ICCID 解析获取资产信息,resolveAsset 接口已包含所有需要的数据
|
||||
const res = await AssetService.resolveAsset(iccid)
|
||||
if (res.code === 0 && res.data) {
|
||||
// 直接使用 resolveAsset 返回的流量信息
|
||||
flowUsageData.value = {
|
||||
totalFlow: res.data.package_total_mb || 0,
|
||||
usedFlow: res.data.package_used_mb || 0,
|
||||
remainFlow: res.data.package_remain_mb || 0,
|
||||
extend: res.data.extend
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message || '查询失败')
|
||||
flowUsageDialogVisible.value = false
|
||||
@@ -1723,9 +1700,26 @@
|
||||
realnameStatusData.value = null
|
||||
|
||||
try {
|
||||
const res = await CardService.getGatewayRealname(iccid)
|
||||
if (res.code === 0) {
|
||||
realnameStatusData.value = res.data
|
||||
// 通过 ICCID 解析获取资产信息,resolveAsset 接口已包含所有需要的数据
|
||||
const res = await AssetService.resolveAsset(iccid)
|
||||
if (res.code === 0 && res.data) {
|
||||
// 直接使用 resolveAsset 返回的实名状态,real_name_status: 0未实名 1实名中 2已实名
|
||||
let statusText = '未知'
|
||||
switch (res.data.real_name_status) {
|
||||
case 0:
|
||||
statusText = '未实名'
|
||||
break
|
||||
case 1:
|
||||
statusText = '实名中'
|
||||
break
|
||||
case 2:
|
||||
statusText = '已实名'
|
||||
break
|
||||
}
|
||||
realnameStatusData.value = {
|
||||
status: statusText,
|
||||
extend: res.data.extend
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message || '查询失败')
|
||||
realnameStatusDialogVisible.value = false
|
||||
@@ -1746,9 +1740,15 @@
|
||||
cardStatusData.value = null
|
||||
|
||||
try {
|
||||
const res = await CardService.getGatewayStatus(iccid)
|
||||
if (res.code === 0) {
|
||||
cardStatusData.value = res.data
|
||||
// 通过 ICCID 解析获取资产信息,resolveAsset 接口已包含所有需要的数据
|
||||
const res = await AssetService.resolveAsset(iccid)
|
||||
if (res.code === 0 && res.data) {
|
||||
// 直接使用 resolveAsset 返回的网络状态
|
||||
cardStatusData.value = {
|
||||
iccid: iccid,
|
||||
cardStatus: res.data.network_status === 1 ? '开机' : '停机',
|
||||
extend: res.data.extend
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message || '查询失败')
|
||||
cardStatusDialogVisible.value = false
|
||||
@@ -1762,36 +1762,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 获取实名认证链接
|
||||
const showRealnameLinkDialog = async (iccid: string) => {
|
||||
realnameLinkDialogVisible.value = true
|
||||
realnameLinkLoading.value = true
|
||||
realnameLinkData.value = null
|
||||
qrcodeDataURL.value = ''
|
||||
|
||||
try {
|
||||
const res = await CardService.getRealnameLink(iccid)
|
||||
if (res.code === 0 && res.data?.link) {
|
||||
realnameLinkData.value = res.data
|
||||
// 生成二维码
|
||||
qrcodeDataURL.value = await QRCode.toDataURL(res.data.link, {
|
||||
width: 200,
|
||||
margin: 1
|
||||
})
|
||||
} else {
|
||||
ElMessage.error(res.message || '获取失败')
|
||||
realnameLinkDialogVisible.value = false
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('获取实名链接失败:', error)
|
||||
ElMessage.error(error?.message || '获取失败')
|
||||
realnameLinkDialogVisible.value = false
|
||||
} finally {
|
||||
realnameLinkLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 启用卡片(复机)
|
||||
// 启用此卡(复机)
|
||||
const handleStartCard = (iccid: string) => {
|
||||
ElMessageBox.confirm('确定要启用该卡片吗?', '确认启用', {
|
||||
confirmButtonText: '确定',
|
||||
@@ -1800,16 +1771,14 @@
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
const res = await CardService.startCard(iccid)
|
||||
// 使用新接口:直接通过 ICCID 启用此卡
|
||||
const res = await AssetService.startCard(iccid)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('启用成功')
|
||||
getTableData()
|
||||
} else {
|
||||
ElMessage.error(res.message || '启用失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('启用卡片失败:', error)
|
||||
ElMessage.error(error?.message || '启用失败')
|
||||
console.error('启用此卡失败:', error)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -1817,7 +1786,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
// 停用卡片(停机)
|
||||
// 停用此卡(停机)
|
||||
const handleStopCard = (iccid: string) => {
|
||||
ElMessageBox.confirm('确定要停用该卡片吗?', '确认停用', {
|
||||
confirmButtonText: '确定',
|
||||
@@ -1826,16 +1795,14 @@
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
const res = await CardService.stopCard(iccid)
|
||||
// 使用新接口:直接通过 ICCID 停用此卡
|
||||
const res = await AssetService.stopCard(iccid)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('停用成功')
|
||||
getTableData()
|
||||
} else {
|
||||
ElMessage.error(res.message || '停用失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('停用卡片失败:', error)
|
||||
ElMessage.error(error?.message || '停用失败')
|
||||
console.error('停用此卡失败:', error)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -64,6 +64,51 @@
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<!-- 失败数据对话框 -->
|
||||
<ElDialog
|
||||
v-model="failDataDialogVisible"
|
||||
title="失败数据详情"
|
||||
width="900px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
>
|
||||
<div v-if="failDataLoading" style="text-align: center; padding: 40px">
|
||||
<ElSkeleton :rows="5" animated />
|
||||
</div>
|
||||
<div v-else-if="failedItems.length > 0">
|
||||
<ElAlert type="warning" :closable="false" style="margin-bottom: 20px">
|
||||
<template #title>
|
||||
<div style="line-height: 1.8">
|
||||
<p><strong>失败数据汇总:</strong></p>
|
||||
<p>共 {{ failedItems.length }} 条记录导入失败</p>
|
||||
</div>
|
||||
</template>
|
||||
</ElAlert>
|
||||
|
||||
<ElTable :data="failedItems" max-height="400" border>
|
||||
<ElTableColumn prop="line" label="行号" width="80" />
|
||||
<ElTableColumn prop="iccid" label="ICCID" width="200" show-overflow-tooltip />
|
||||
<ElTableColumn prop="msisdn" label="MSISDN" width="150" show-overflow-tooltip />
|
||||
<ElTableColumn prop="message" label="失败原因" min-width="200" show-overflow-tooltip />
|
||||
</ElTable>
|
||||
</div>
|
||||
<div v-else style="text-align: center; padding: 40px">
|
||||
<ElEmpty description="暂无失败数据" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="failDataDialogVisible = false">关闭</ElButton>
|
||||
<ElButton
|
||||
v-if="failedItems.length > 0"
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="downloadCurrentFailData"
|
||||
>
|
||||
下载失败数据
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<ElDialog v-model="importDialogVisible" title="批量导入IoT卡" width="700px" align-center>
|
||||
<ElAlert type="info" :closable="false" style="margin-bottom: 20px">
|
||||
@@ -73,7 +118,7 @@
|
||||
<p>1. 请先下载 Excel 模板文件,按照模板格式填写IoT卡信息</p>
|
||||
<p>2. 仅支持 Excel 格式(.xlsx),单次最多导入 1000 条</p>
|
||||
<p>3. 列格式请设置为文本格式,避免长数字被转为科学计数法</p>
|
||||
<p>4. 必填字段:ICCID、MSISDN(手机号)</p>
|
||||
<p>4. 必填字段:ICCID、MSISDN(手机号);可选字段:virtual_no(虚拟号)</p>
|
||||
<p>5. 必须选择运营商</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -139,7 +184,15 @@
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CardService, CarrierService } from '@/api/modules'
|
||||
import { ElMessage, ElTag, ElFormItem, ElSelect, ElOption } from 'element-plus'
|
||||
import {
|
||||
ElMessage,
|
||||
ElTag,
|
||||
ElFormItem,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElSkeleton,
|
||||
ElEmpty
|
||||
} from 'element-plus'
|
||||
import { Download, UploadFilled, Upload } from '@element-plus/icons-vue'
|
||||
import type { UploadInstance } from 'element-plus'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
@@ -181,6 +234,10 @@
|
||||
const carrierLoading = ref(false)
|
||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentRow = ref<IotCardImportTask | null>(null)
|
||||
const failDataDialogVisible = ref(false)
|
||||
const failDataLoading = ref(false)
|
||||
const failedItems = ref<any[]>([])
|
||||
const currentFailTask = ref<IotCardImportTask | null>(null)
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
@@ -473,13 +530,18 @@
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 从行数据下载失败数据
|
||||
const downloadFailDataByRow = async (row: IotCardImportTask) => {
|
||||
// 显示失败数据弹窗
|
||||
const showFailDataDialog = async (row: IotCardImportTask) => {
|
||||
try {
|
||||
failDataDialogVisible.value = true
|
||||
failDataLoading.value = true
|
||||
failedItems.value = []
|
||||
currentFailTask.value = row
|
||||
|
||||
const res = await CardService.getIotCardImportTaskDetail(row.id)
|
||||
if (res.code === 0 && res.data) {
|
||||
const detail = res.data
|
||||
const failReasons =
|
||||
failedItems.value =
|
||||
detail.failed_items?.map((item: any) => ({
|
||||
line: item.line || '-',
|
||||
iccid: item.iccid || '-',
|
||||
@@ -487,35 +549,49 @@
|
||||
message: item.reason || item.error || '未知错误'
|
||||
})) || []
|
||||
|
||||
if (failReasons.length === 0) {
|
||||
ElMessage.warning('没有失败数据可下载')
|
||||
return
|
||||
if (failedItems.value.length === 0) {
|
||||
ElMessage.warning('没有失败数据')
|
||||
}
|
||||
|
||||
const headers = ['行号', 'ICCID', 'MSISDN', '失败原因']
|
||||
const csvRows = [
|
||||
headers.join(','),
|
||||
...failReasons.map((item: any) =>
|
||||
[item.line, item.iccid, item.msisdn, `"${item.message}"`].join(',')
|
||||
)
|
||||
]
|
||||
const csvContent = csvRows.join('\n')
|
||||
|
||||
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', `IoT卡导入失败数据_${row.task_no}.csv`)
|
||||
link.style.visibility = 'hidden'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
ElMessage.success('失败数据下载成功')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取失败数据失败:', error)
|
||||
ElMessage.error('获取失败数据失败')
|
||||
} finally {
|
||||
failDataLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 下载当前失败数据
|
||||
const downloadCurrentFailData = () => {
|
||||
if (!currentFailTask.value || failedItems.value.length === 0) {
|
||||
ElMessage.warning('没有失败数据可下载')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const headers = ['行号', 'ICCID', 'MSISDN', '失败原因']
|
||||
const csvRows = [
|
||||
headers.join(','),
|
||||
...failedItems.value.map((item: any) =>
|
||||
[item.line, item.iccid, item.msisdn, `"${item.message}"`].join(',')
|
||||
)
|
||||
]
|
||||
const csvContent = csvRows.join('\n')
|
||||
|
||||
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', `IoT卡导入失败数据_${currentFailTask.value.task_no}.csv`)
|
||||
link.style.visibility = 'hidden'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
ElMessage.success('失败数据下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载失败数据失败:', error)
|
||||
ElMessage.error('下载失败数据失败')
|
||||
@@ -532,15 +608,18 @@
|
||||
const templateData = [
|
||||
{
|
||||
ICCID: '89860123456789012345',
|
||||
MSISDN: '13800138000'
|
||||
MSISDN: '13800138000',
|
||||
virtual_no: 'V001'
|
||||
},
|
||||
{
|
||||
ICCID: '89860123456789012346',
|
||||
MSISDN: '13800138001'
|
||||
MSISDN: '13800138001',
|
||||
virtual_no: 'V002'
|
||||
},
|
||||
{
|
||||
ICCID: '89860123456789012347',
|
||||
MSISDN: '13800138002'
|
||||
MSISDN: '13800138002',
|
||||
virtual_no: 'V003'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -551,7 +630,8 @@
|
||||
// 设置列宽
|
||||
ws['!cols'] = [
|
||||
{ wch: 25 }, // ICCID
|
||||
{ wch: 15 } // MSISDN
|
||||
{ wch: 15 }, // MSISDN
|
||||
{ wch: 15 } // virtual_no
|
||||
]
|
||||
|
||||
// 将所有单元格设置为文本格式,防止科学计数法
|
||||
@@ -727,7 +807,7 @@
|
||||
|
||||
switch (item.key) {
|
||||
case 'failData':
|
||||
downloadFailDataByRow(currentRow.value)
|
||||
showFailDataDialog(currentRow.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<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="device_no" label="设备号" min-width="180" />
|
||||
<ElTableColumn v-else prop="virtual_no" label="设备号" min-width="180" />
|
||||
<ElTableColumn prop="reason" label="失败原因" min-width="300" />
|
||||
</ElTable>
|
||||
</div>
|
||||
@@ -37,7 +37,7 @@
|
||||
<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="device_no" label="设备号" min-width="180" />
|
||||
<ElTableColumn v-else prop="virtual_no" label="设备号" min-width="180" />
|
||||
<ElTableColumn prop="reason" label="跳过原因" min-width="300" />
|
||||
</ElTable>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user