This commit is contained in:
@@ -159,7 +159,7 @@ export interface AssetExchangeTrace {
|
||||
export interface AssetResolveResponse {
|
||||
asset_type: AssetType // 资产类型:card 或 device
|
||||
asset_id: number // 数据库 ID
|
||||
associated_phones?: string[] // 当前关联手机号列表(完整手机号,无关联时为空数组)
|
||||
associated_phones?: string[] // 当前关联手机号列表(按资产数据范围返回完整手机号,无关联时为空数组)
|
||||
identifier?: string // 原样回传本次查询所用的标识符
|
||||
virtual_no: string // 虚拟号
|
||||
status: number // 资产状态
|
||||
|
||||
@@ -384,7 +384,7 @@ export interface StandaloneIotCard {
|
||||
gateway_card_imei?: string // 网关侧返回的卡 IMEI(可空)
|
||||
id: number // 卡ID
|
||||
iccid: string // ICCID
|
||||
associated_phones?: string[] // 当前关联手机号列表(完整手机号,无关联时为空数组)
|
||||
associated_phones?: string[] // 当前关联手机号列表(按资产数据范围返回完整手机号,无关联时为空数组)
|
||||
imsi?: string // IMSI (可选)
|
||||
msisdn?: string // 卡接入号 (可选)
|
||||
virtual_no?: string // 卡虚拟号(可空)
|
||||
|
||||
@@ -39,7 +39,7 @@ export type SwitchMode = '0' | '1' // 0=自动, 1=手动
|
||||
export interface Device {
|
||||
id: number // 设备ID
|
||||
virtual_no: string // 虚拟号(原 device_no)
|
||||
associated_phones?: string[] // 当前关联手机号列表(完整手机号,无关联时为空数组)
|
||||
associated_phones?: string[] // 当前关联手机号列表(按资产数据范围返回完整手机号,无关联时为空数组)
|
||||
device_name: string // 设备名称
|
||||
device_model: string // 设备型号
|
||||
device_type: string // 设备类型
|
||||
|
||||
@@ -183,7 +183,7 @@ export type CloseEmployeeCollectionBillResult = EmployeeCollectionBill
|
||||
|
||||
// ========== 核销申请 ==========
|
||||
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已撤销或已关闭 */
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已关闭 */
|
||||
export type EmployeeCollectionApplicationStatus = number
|
||||
|
||||
/** 审批尝试记录(历史材料不被覆盖) */
|
||||
|
||||
@@ -1413,6 +1413,10 @@
|
||||
content: '\e776';
|
||||
}
|
||||
|
||||
:deep(.asset-label--associated-phones::before) {
|
||||
content: '\e776';
|
||||
}
|
||||
|
||||
:deep(.asset-label--operator-account::before) {
|
||||
content: '\e70d';
|
||||
}
|
||||
|
||||
@@ -365,7 +365,6 @@
|
||||
return
|
||||
}
|
||||
console.error('创建订单失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -836,7 +836,6 @@
|
||||
console.error('设置限速失败:', error)
|
||||
speedLimitDialogVisible.value = false
|
||||
await nextTick()
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
speedTierSubmitting.value = false
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface AssetInfo {
|
||||
iccid?: string
|
||||
imei?: string
|
||||
msisdn?: string
|
||||
associated_phones?: string[] // 当前关联手机号(完整值)
|
||||
associated_phones?: string[] // 当前关联手机号(按资产数据范围返回完整值)
|
||||
carrier_type?: string
|
||||
carrier_name?: string
|
||||
real_name_status?: number
|
||||
|
||||
@@ -142,34 +142,41 @@
|
||||
</ElDialog>
|
||||
|
||||
<!-- CSV 解绑导入 -->
|
||||
<ElDialog
|
||||
<ElDrawer
|
||||
v-model="importDialogVisible"
|
||||
title="CSV 解绑导入"
|
||||
width="520px"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleImportDialogClosed"
|
||||
>
|
||||
<div class="import-tips">
|
||||
<span>模板表头:</span>
|
||||
<ElButton link type="primary" @click="downloadTemplate">下载模板</ElButton>
|
||||
<div class="import-tips-detail">
|
||||
资产标识,备注(备注可选);支持 UTF-8(可带 BOM),非 UTF-8 按 GBK 解码;无行数上限。
|
||||
</div>
|
||||
</div>
|
||||
<ElAlert type="info" :closable="false" style="margin-bottom: 20px">
|
||||
<template #title>
|
||||
<div class="import-tips">
|
||||
<p><strong>导入说明:</strong></p>
|
||||
<p>1. 请先下载 CSV 模板,按模板填写资产标识和备注。</p>
|
||||
<p>2. 仅支持 UTF-8(可带 BOM)编码,非 UTF-8 文件按 GBK 解码。</p>
|
||||
<p>3. 备注为可选字段,文件不限制数据行数,大小限制以服务端校验为准。</p>
|
||||
<p>
|
||||
4. 模板表头:资产标识,备注;
|
||||
<ElButton link type="primary" @click="downloadTemplate">下载模板</ElButton>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</ElAlert>
|
||||
<ElForm ref="importFormRef" :model="importForm" :rules="importRules" label-width="90px">
|
||||
<ElFormItem label="CSV 文件" prop="file">
|
||||
<ElUpload
|
||||
<VoucherUpload
|
||||
ref="uploadRef"
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
accept=".csv"
|
||||
:on-change="handleFileChange"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
<ElIcon class="el-icon--upload"><UploadFilled /></ElIcon>
|
||||
<div class="el-upload__text">拖拽文件到此处,或<em>点击选择 .csv 文件</em></div>
|
||||
</ElUpload>
|
||||
v-model="importFileKeys"
|
||||
voucher-name="CSV 文件"
|
||||
:max-count="1"
|
||||
purpose="phone_unbind_import"
|
||||
content-type="text/csv"
|
||||
accept=".csv,text/csv"
|
||||
tip="仅支持 UTF-8 CSV(可带 BOM),大小限制以服务端校验为准"
|
||||
@uploading-change="importUploading = $event"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="解除原因" prop="reason">
|
||||
<ElInput
|
||||
@@ -187,27 +194,24 @@
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="importDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="importLoading" @click="confirmImport"
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="importLoading"
|
||||
:disabled="importUploading || !importFileKeys.length"
|
||||
@click="confirmImport"
|
||||
>创建导入任务</ElButton
|
||||
>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</ElDrawer>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
ElMessage,
|
||||
ElTag,
|
||||
FormInstance,
|
||||
FormRules,
|
||||
UploadFile,
|
||||
UploadInstance
|
||||
} from 'element-plus'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import { PhoneAssetAssociationService, StorageService } from '@/api/modules'
|
||||
import { ElMessage, ElTag, FormInstance, FormRules } from 'element-plus'
|
||||
import { PhoneAssetAssociationService } from '@/api/modules'
|
||||
import VoucherUpload from '@/components/business/VoucherUpload.vue'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
@@ -407,7 +411,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取手机号资产关联列表失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取手机号资产关联列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -522,7 +525,6 @@
|
||||
getTableData()
|
||||
} catch (error) {
|
||||
console.error('解除关联失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '解除关联失败')
|
||||
} finally {
|
||||
unbindLoading.value = false
|
||||
}
|
||||
@@ -594,7 +596,6 @@
|
||||
getTableData()
|
||||
} catch (error) {
|
||||
console.error('批量解绑失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '批量解绑失败')
|
||||
} finally {
|
||||
batchUnbindLoading.value = false
|
||||
}
|
||||
@@ -604,10 +605,11 @@
|
||||
|
||||
const importDialogVisible = ref(false)
|
||||
const importLoading = ref(false)
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const importUploading = ref(false)
|
||||
const uploadRef = ref<InstanceType<typeof VoucherUpload>>()
|
||||
const importFormRef = ref<FormInstance>()
|
||||
const importFileKeys = ref<string[]>([])
|
||||
const importForm = reactive({
|
||||
file: null as File | null,
|
||||
reason: '',
|
||||
confirmed: false
|
||||
})
|
||||
@@ -615,7 +617,7 @@
|
||||
file: [
|
||||
{
|
||||
validator: (_rule, _value, callback) => {
|
||||
if (!importForm.file) callback(new Error('请选择 CSV 文件'))
|
||||
if (!importFileKeys.value.length) callback(new Error('请选择 CSV 文件'))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'change'
|
||||
@@ -637,7 +639,7 @@
|
||||
}
|
||||
|
||||
const openImportDialog = () => {
|
||||
importForm.file = null
|
||||
importFileKeys.value = []
|
||||
importForm.reason = ''
|
||||
importForm.confirmed = false
|
||||
importDialogVisible.value = true
|
||||
@@ -645,22 +647,8 @@
|
||||
|
||||
const handleImportDialogClosed = () => {
|
||||
uploadRef.value?.clearFiles()
|
||||
}
|
||||
|
||||
const handleFileChange = (file: UploadFile) => {
|
||||
const rawFile = file.raw
|
||||
if (!rawFile) return
|
||||
if (!rawFile.name.toLowerCase().endsWith('.csv')) {
|
||||
ElMessage.error('解绑导入仅支持 .csv 文件')
|
||||
uploadRef.value?.clearFiles()
|
||||
importForm.file = null
|
||||
return
|
||||
}
|
||||
importForm.file = rawFile
|
||||
}
|
||||
|
||||
const handleFileExceed = () => {
|
||||
ElMessage.warning('仅支持上传一个 CSV 文件')
|
||||
importFileKeys.value = []
|
||||
importUploading.value = false
|
||||
}
|
||||
|
||||
const downloadTemplate = () => {
|
||||
@@ -680,24 +668,12 @@
|
||||
|
||||
const confirmImport = async () => {
|
||||
if (!(await importFormRef.value?.validate().catch(() => false))) return
|
||||
const file = importForm.file
|
||||
if (!file) return
|
||||
const fileKey = importFileKeys.value[0]
|
||||
if (!fileKey) return
|
||||
importLoading.value = true
|
||||
try {
|
||||
const uploadUrlRes = await StorageService.getUploadUrl({
|
||||
file_name: file.name,
|
||||
content_type: 'text/csv',
|
||||
purpose: 'phone_unbind_import'
|
||||
})
|
||||
if (uploadUrlRes.code !== 0 || !uploadUrlRes.data) {
|
||||
ElMessage.error(uploadUrlRes.msg || '获取上传地址失败')
|
||||
return
|
||||
}
|
||||
const { upload_url, file_key } = uploadUrlRes.data
|
||||
await StorageService.uploadFile(upload_url, file, 'text/csv')
|
||||
|
||||
const res = await PhoneAssetAssociationService.createUnbindImport({
|
||||
file_key,
|
||||
file_key: fileKey,
|
||||
reason: importForm.reason.trim(),
|
||||
confirmed: importForm.confirmed
|
||||
})
|
||||
@@ -710,7 +686,6 @@
|
||||
goUnbindImportTasks()
|
||||
} catch (error) {
|
||||
console.error('创建解绑导入任务失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '创建解绑导入任务失败')
|
||||
} finally {
|
||||
importLoading.value = false
|
||||
}
|
||||
@@ -737,13 +712,12 @@
|
||||
}
|
||||
|
||||
.import-tips {
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
.import-tips-detail {
|
||||
margin-top: 4px;
|
||||
line-height: 1.6;
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -349,7 +349,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备批量任务目标失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
targetLoading.value = false
|
||||
}
|
||||
@@ -425,7 +424,6 @@
|
||||
taskListTotal.value = res.data.total || 0
|
||||
} catch (error) {
|
||||
console.error('获取设备批量任务失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
taskListLoading.value = false
|
||||
}
|
||||
@@ -527,7 +525,6 @@
|
||||
ElMessage.success(`任务已创建:${res.data.task_no}`)
|
||||
} catch (error) {
|
||||
console.error('创建设备批量任务失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
|
||||
@@ -408,7 +408,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -539,7 +538,6 @@
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.error('设备导入失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || error.message || '设备导入失败')
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
|
||||
@@ -35,11 +35,6 @@
|
||||
{{ row.asset_type ? (row.asset_type === 'iot_card' ? '物联网卡' : '设备') : '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="asset_id" label="资产ID" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.asset_id ? row.asset_id : '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="status" label="行状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<ElTag :type="row.status === 3 ? 'success' : 'danger'" size="small">
|
||||
@@ -130,7 +125,6 @@
|
||||
startPollingIfNeeded()
|
||||
} catch (error) {
|
||||
console.error('获取解绑导入任务详情失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取解绑导入任务详情失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -219,7 +219,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取解绑导入任务列表失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取解绑导入任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const BILL_STATUS = {
|
||||
CLOSED: 3
|
||||
} as const
|
||||
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已撤销或已关闭 */
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已关闭 */
|
||||
export const APPLICATION_STATUS = {
|
||||
PENDING: 0,
|
||||
APPROVED: 1,
|
||||
@@ -77,7 +77,7 @@ export const APPLICATION_STATUS_OPTIONS = [
|
||||
{ label: '审批中', value: APPLICATION_STATUS.PENDING },
|
||||
{ label: '已通过', value: APPLICATION_STATUS.APPROVED },
|
||||
{ label: '已驳回', value: APPLICATION_STATUS.REJECTED },
|
||||
{ label: '已撤销或已关闭', value: APPLICATION_STATUS.REVOKED }
|
||||
{ label: '已关闭', value: APPLICATION_STATUS.REVOKED }
|
||||
]
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
:items="searchFormItems"
|
||||
label-width="90"
|
||||
:show-expand="false"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
@@ -36,7 +35,7 @@
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:actions="getActions"
|
||||
:actionsWidth="150"
|
||||
:actionsWidth="120"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
@@ -199,7 +198,7 @@
|
||||
packageOptions.value.map((item) => ({ label: item.package_name, value: item.id }))
|
||||
},
|
||||
{
|
||||
label: '启用状态',
|
||||
label: '状态',
|
||||
prop: 'enabled',
|
||||
type: 'select',
|
||||
config: { clearable: true, placeholder: '全部' },
|
||||
@@ -236,7 +235,7 @@
|
||||
},
|
||||
{
|
||||
prop: 'enabled',
|
||||
label: '启用状态',
|
||||
label: '状态',
|
||||
width: 100,
|
||||
formatter: (row: PackageTrafficAlertRuleItem) =>
|
||||
h(
|
||||
@@ -264,7 +263,7 @@
|
||||
{ label: '套餐名称', prop: 'package_name' },
|
||||
{ label: '当前真流量额度', prop: 'real_data_mb' },
|
||||
{ label: '预警阈值', prop: 'threshold_percent' },
|
||||
{ label: '启用状态', prop: 'enabled' },
|
||||
{ label: '状态', prop: 'enabled' },
|
||||
{ label: '备注', prop: 'remark' },
|
||||
{ label: '更新时间', prop: 'updated_at' }
|
||||
]
|
||||
@@ -289,7 +288,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取真流量预警规则列表失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取真流量预警规则列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -375,7 +373,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新真流量预警规则失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +469,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存真流量预警规则失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '操作失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
|
||||
@@ -394,7 +394,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取真流量达量预警记录失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取真流量达量预警记录失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -506,7 +505,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建真流量达量预警导出任务失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '创建导出任务失败')
|
||||
} finally {
|
||||
exportSubmitting.value = false
|
||||
}
|
||||
|
||||
@@ -226,7 +226,6 @@
|
||||
}
|
||||
const PAGE_OPTIONS: Array<{ label: string; value: H5PopupPage }> = [
|
||||
{ label: '首页', value: 'home' },
|
||||
{ label: '资产详情', value: 'asset_detail' },
|
||||
{ label: '套餐购买', value: 'package_purchase' },
|
||||
{ label: '资产钱包充值', value: 'asset_wallet_recharge' }
|
||||
]
|
||||
@@ -441,7 +440,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取运营弹窗配置列表失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '获取运营弹窗配置列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -513,7 +511,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('启用运营弹窗配置失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '启用失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +534,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('停用运营弹窗配置失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '停用失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +744,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存运营弹窗配置失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message || '保存失败')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user