From 9a6f085cdead297d5b73bc567307db42cfdb20e2 Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Fri, 10 Apr 2026 09:50:00 +0800 Subject: [PATCH] fix(operator): fix operator edit issue --- src/types/api/carrier.ts | 6 + .../components/AssetSearchCard.vue | 189 + .../components/BasicInfoCard.vue | 604 ++++ .../components/CurrentPackageCard.vue | 226 ++ .../components/PackageListCard.vue | 220 ++ .../components/WalletOverviewCard.vue | 140 + .../components/WalletTransactionCard.vue | 326 ++ .../components/dialogs/DailyRecordsDialog.vue | 205 ++ .../components/dialogs/OrderHistoryDialog.vue | 232 ++ .../dialogs/PackageRechargeDialog.vue | 237 ++ .../components/dialogs/SpeedLimitDialog.vue | 94 + .../components/dialogs/SwitchCardDialog.vue | 165 + .../components/dialogs/WiFiConfigDialog.vue | 117 + .../components/dialogs/index.ts | 6 + .../composables/useAssetFormatters.ts | 249 ++ .../composables/useAssetInfo.ts | 352 ++ .../composables/useAssetOperations.ts | 319 ++ .../asset-information/index.vue | 3026 ++--------------- .../asset-information/types.ts | 141 + src/views/system/carrier-management/index.vue | 10 +- 20 files changed, 4082 insertions(+), 2782 deletions(-) create mode 100644 src/views/asset-management/asset-information/components/AssetSearchCard.vue create mode 100644 src/views/asset-management/asset-information/components/BasicInfoCard.vue create mode 100644 src/views/asset-management/asset-information/components/CurrentPackageCard.vue create mode 100644 src/views/asset-management/asset-information/components/PackageListCard.vue create mode 100644 src/views/asset-management/asset-information/components/WalletOverviewCard.vue create mode 100644 src/views/asset-management/asset-information/components/WalletTransactionCard.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/OrderHistoryDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/PackageRechargeDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/SpeedLimitDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/SwitchCardDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/WiFiConfigDialog.vue create mode 100644 src/views/asset-management/asset-information/components/dialogs/index.ts create mode 100644 src/views/asset-management/asset-information/composables/useAssetFormatters.ts create mode 100644 src/views/asset-management/asset-information/composables/useAssetInfo.ts create mode 100644 src/views/asset-management/asset-information/composables/useAssetOperations.ts create mode 100644 src/views/asset-management/asset-information/types.ts diff --git a/src/types/api/carrier.ts b/src/types/api/carrier.ts index 94f4323..20e7ca4 100644 --- a/src/types/api/carrier.ts +++ b/src/types/api/carrier.ts @@ -20,6 +20,8 @@ export interface Carrier { carrier_type: CarrierType description?: string data_reset_day?: number | null // 上游流量重置日(1-28) + realname_link_type?: string // 实名链接类型 + realname_link_template?: string // 实名链接模板 status: number created_at: string updated_at: string @@ -39,6 +41,8 @@ export interface CreateCarrierParams { carrier_type: CarrierType description?: string data_reset_day?: number | null // 上游流量重置日(1-28) + realname_link_type?: string // 实名链接类型 + realname_link_template?: string // 实名链接模板 } // 更新运营商请求参数 @@ -46,6 +50,8 @@ export interface UpdateCarrierParams { carrier_name?: string description?: string data_reset_day?: number | null // 上游流量重置日(1-28) + realname_link_type?: string // 实名链接类型 + realname_link_template?: string // 实名链接模板 } // 更新运营商状态参数 diff --git a/src/views/asset-management/asset-information/components/AssetSearchCard.vue b/src/views/asset-management/asset-information/components/AssetSearchCard.vue new file mode 100644 index 0000000..07893ad --- /dev/null +++ b/src/views/asset-management/asset-information/components/AssetSearchCard.vue @@ -0,0 +1,189 @@ + + + + + 资产查询 + + + + + + + + + + + {{ formatIccidWithDashes(searchIccid) }} + + + + + + 查询 + + + + + 刷新 + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/BasicInfoCard.vue b/src/views/asset-management/asset-information/components/BasicInfoCard.vue new file mode 100644 index 0000000..9a4674b --- /dev/null +++ b/src/views/asset-management/asset-information/components/BasicInfoCard.vue @@ -0,0 +1,604 @@ + + + + + + {{ cardInfo?.asset_type === 'card' ? '卡信息' : '设备信息' }} + + {{ cardInfo?.asset_type === 'card' ? 'IoT卡' : '设备' }} + + + + + + 自动轮询 + + + + + + + + + + {{ cardInfo?.iccid || '-' }} + {{ + cardInfo?.carrier_name || '-' + }} + + + {{ getRealNameStatusName(cardInfo?.real_name_status) }} + + + + {{ cardInfo?.shop_name || '-' }} + {{ cardInfo?.series_name || '-' }} + {{ cardInfo?.virtual_no || '-' }} + + + {{ cardInfo?.network_status === 1 ? '正常' : '停机' }} + + + + {{ + formatDataSize(cardInfo?.current_month_usage_mb || 0) + }} + {{ + cardInfo?.real_name_at ? formatDateTime(cardInfo.real_name_at) : '-' + }} + + {{ cardInfo?.msisdn || '-' }} + + + {{ getAssetStatusName(cardInfo?.status) }} + + + + + + + {{ cardInfo?.imei || '-' }} + {{ cardInfo?.device_type || '-' }} + + + {{ getRealNameStatusName(currentCard?.real_name_status) }} + + + + {{ cardInfo?.shop_name || '-' }} + {{ cardInfo?.device_name || '-' }} + + + {{ getAssetStatusName(cardInfo?.status) }} + + + + {{ cardInfo?.virtual_no || '-' }} + {{ cardInfo?.sn || '-' }} + {{ + cardInfo?.device_model || '-' + }} + + {{ cardInfo?.supplier || '-' }} + {{ + cardInfo?.bound_card_count || 0 + }} + {{ + cardInfo?.max_sim_slots || '-' + }} + + {{ + cardInfo?.activated_at || '-' + }} + {{ + cardInfo?.real_name_at ? formatDateTime(cardInfo.real_name_at) : '-' + }} + + + {{ calculateSignalStrength() }} + + + + + + {{ getOnlineStatusName(cardInfo?.online_status) }} + + + {{ + cardInfo?.software_version || '-' + }} + {{ + getSwitchModeName(cardInfo?.switch_mode) + }} + + {{ + formatDateTime(cardInfo?.last_online_time) || '-' + }} + {{ + formatDateTime(cardInfo?.last_gateway_sync_at) || '-' + }} + {{ cardInfo?.manufacturer || '-' }} + + + + + + 绑定卡列表 + + + + + {{ scope.row.iccid }} + + + + + + + + + SIM-{{ scope.row.slot_position }} + 当前 + + + + + + + {{ scope.row.network_status === 1 ? '正常' : '停机' }} + + + + + + + {{ getRealNameStatusName(scope.row.real_name_status) }} + + + + + + {{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }} + + + + + + + 启用此卡 + + + 停用此卡 + + + + + + + + 设备实时信息 + + + + + + {{ deviceRealtime.online_status === 1 ? '在线' : '离线' }} + + + + + {{ deviceRealtime.status === 1 ? '正常' : '禁用' }} + + + + {{ + deviceRealtime.battery_level !== null && deviceRealtime.battery_level !== undefined + ? `${deviceRealtime.battery_level}%` + : '-' + }} + + + + + {{ formatDuration(deviceRealtime.run_time) }} + + + {{ formatDuration(deviceRealtime.connect_time) }} + + + {{ formatDateTime(deviceRealtime.last_online_time) || '-' }} + + + + + {{ deviceRealtime.rssi || '-' }} + + + {{ deviceRealtime.rsrp ? `${deviceRealtime.rsrp} dBm` : '-' }} + + + {{ deviceRealtime.rsrq ? `${deviceRealtime.rsrq} dB` : '-' }} + + + + {{ deviceRealtime.sinr ? `${deviceRealtime.sinr} dB` : '-' }} + + + {{ deviceRealtime.max_clients || '-' }} + + + {{ deviceRealtime.current_iccid || '-' }} + + + + + + {{ deviceRealtime.wifi_enabled ? '已开启' : '已关闭' }} + + + + {{ deviceRealtime.ssid || '-' }} + + + {{ deviceRealtime.wifi_password || '-' }} + + + + + {{ deviceRealtime.ip_address || '-' }} + + + {{ deviceRealtime.wan_ip || '-' }} + + + {{ deviceRealtime.lan_ip || '-' }} + + + + {{ deviceRealtime.mac_address || '-' }} + + + {{ deviceRealtime.imei || '-' }} + + + {{ deviceRealtime.imsi || '-' }} + + + + + {{ formatDataSize(Number(deviceRealtime.daily_usage || 0) / (1024 * 1024)) }} + + + {{ formatDataSize(Number(deviceRealtime.dl_stats || 0) / (1024 * 1024)) }} + + + {{ formatDataSize(Number(deviceRealtime.ul_stats || 0) / (1024 * 1024)) }} + + + + + {{ deviceRealtime.limit_speed ? `${deviceRealtime.limit_speed} KB/s` : '不限速' }} + + + {{ deviceRealtime.software_version || '-' }} + + + {{ + deviceRealtime.switch_mode === '0' + ? '自动' + : deviceRealtime.switch_mode === '1' + ? '手动' + : '-' + }} + + + + {{ deviceRealtime.sync_interval ? `${deviceRealtime.sync_interval} 秒` : '-' }} + + + {{ deviceRealtime.device_id || '-' }} + + + {{ formatDateTime(deviceRealtime.last_update_time) || '-' }} + + + + + + Gateway 不可达或设备离线,无法获取实时信息 + + + + + + + 启用此卡 + 停用此卡 + 手动停用 + + + + + 重启设备 + 恢复出厂 + 切换SIM卡 + 设置WiFi + + + + + + + diff --git a/src/views/asset-management/asset-information/components/CurrentPackageCard.vue b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue new file mode 100644 index 0000000..317ae9c --- /dev/null +++ b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue @@ -0,0 +1,226 @@ + + + + + + 当前生效套餐 + + {{ currentPackage?.status_name || '-' }} + + + + 套餐充值 + + + + + + + + + + + + + {{ currentPackage.package_name || '-' }} + + + {{ formatAmount(currentPackage.package_price) }} + + + {{ formatDataSize(currentPackage.package_total_data || 0) }} + + + + + {{ currentPackage.duration_days }}天 + + + + + + + 真流量 + + 已用 {{ formatDataSize(currentPackage.real_data_used || 0) }} / 剩余 + {{ formatDataSize(currentPackage.real_data_remaining || 0) }} / 总量 + {{ formatDataSize(currentPackage.real_data_total || 0) }} + + + + + + + + + 虚流量 + + 已用 {{ formatDataSize(currentPackage.virtual_data_used || 0) }} / 剩余 + {{ formatDataSize(currentPackage.virtual_data_remaining || 0) }} / 总量 + {{ formatDataSize(currentPackage.virtual_data_total || 0) }} + + + + + + + + + {{ formatDateTime(currentPackage.start_time) || '-' }} + + + {{ formatDateTime(currentPackage.expire_time) || '-' }} + + + + {{ currentPackage.status_name || '-' }} + + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/PackageListCard.vue b/src/views/asset-management/asset-information/components/PackageListCard.vue new file mode 100644 index 0000000..79838e8 --- /dev/null +++ b/src/views/asset-management/asset-information/components/PackageListCard.vue @@ -0,0 +1,220 @@ + + + + + 套餐列表 + + + + + + + + {{ scope.row.package_name }} + + + + + + {{ formatAmount(scope.row.package_price) }} + + + + + + {{ scope.row.status_name }} + + + + + + {{ formatDataSize(scope.row.data_limit_mb) }} + + + + + + + 已用: {{ formatDataSize(scope.row.data_usage_mb) }} + + 剩余: {{ formatDataSize(scope.row.data_limit_mb - scope.row.data_usage_mb) }} + + 总量: {{ formatDataSize(scope.row.data_limit_mb) }} + + + + + + + + + + 已用: {{ formatDataSize(scope.row.virtual_used_mb || 0) }} + + 剩余: {{ formatDataSize(scope.row.virtual_remain_mb || 0) }} + + + 总量: {{ formatDataSize(scope.row.virtual_limit_mb || 0) }} + + + + + + + + + {{ formatDateTime(scope.row.activated_at) }} + + + + + {{ formatDateTime(scope.row.expires_at) }} + + + + + + 流量详单 + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/WalletOverviewCard.vue b/src/views/asset-management/asset-information/components/WalletOverviewCard.vue new file mode 100644 index 0000000..a67bca0 --- /dev/null +++ b/src/views/asset-management/asset-information/components/WalletOverviewCard.vue @@ -0,0 +1,140 @@ + + + + + 钱包概况 + + {{ walletInfo.status_text || '-' }} + + + + + + + + 💰 + + 总余额 + {{ formatAmount(walletInfo.balance) }} + + + + ✅ + + 可用余额 + {{ formatAmount(walletInfo.available_balance) }} + + + + 🔒 + + 冻结余额 + {{ formatAmount(walletInfo.frozen_balance) }} + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/WalletTransactionCard.vue b/src/views/asset-management/asset-information/components/WalletTransactionCard.vue new file mode 100644 index 0000000..2dc8126 --- /dev/null +++ b/src/views/asset-management/asset-information/components/WalletTransactionCard.vue @@ -0,0 +1,326 @@ + + + + + + 钱包流水 + 共 {{ total }} 条 + + + + + + + + + 查询 + 重置 + + + + + + + + + + {{ scope.row.transaction_type_text }} + + + + + + + {{ formatAmount(scope.row.amount) }} + + + + + + {{ formatAmount(scope.row.balance_before) }} + + + + + {{ formatAmount(scope.row.balance_after) }} + + + + + + {{ scope.row.reference_type === 'recharge' ? '充值' : '订单' }} + + -- + + + + + + {{ scope.row.reference_no }} + + -- + + + + + {{ scope.row.remark || '-' }} + + + + + {{ formatDateTime(scope.row.created_at) }} + + + + + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue b/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue new file mode 100644 index 0000000..ecf0276 --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue @@ -0,0 +1,205 @@ + + + + + + + {{ data.package_name }} + + + {{ data.package_usage_id }} + + + {{ formatDataSize(data.total_usage_mb) }} + + + + + + 筛选方式: + + 全部 + 按日 + 按月 + + + + + + + + + + + {{ formatDataSize(row.daily_usage_mb) }} + + + + + {{ formatDataSize(row.cumulative_usage_mb) }} + + + + + + + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/OrderHistoryDialog.vue b/src/views/asset-management/asset-information/components/dialogs/OrderHistoryDialog.vue new file mode 100644 index 0000000..65653bd --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/OrderHistoryDialog.vue @@ -0,0 +1,232 @@ + + + + + + + + + 当前代 ({{ data.current_generation.identifier }}) + + + + + + + {{ row.payment_status_text }} + + + + + ¥{{ (row.total_amount / 100).toFixed(2) }} + + + + + {{ row.paid_at ? formatDateTime(row.paid_at) : '-' }} + + + + + + {{ item.package_name }} × {{ item.quantity }} + + + + + + {{ formatDateTime(row.created_at) }} + + + + + + + + + + + + + + + 第{{ gen.generation }}代 ({{ gen.identifier }}) + + 换货单号: {{ gen.exchange_no }} + + + 换货时间: {{ formatDateTime(gen.exchanged_at) }} + + + + + + + + + {{ row.payment_status_text }} + + + + + ¥{{ (row.total_amount / 100).toFixed(2) }} + + + + + {{ row.paid_at ? formatDateTime(row.paid_at) : '-' }} + + + + + + {{ item.package_name }} × {{ item.quantity }} + + + + + + {{ formatDateTime(row.created_at) }} + + + + + + + 换货链超过10代,仅显示最近10代的订单记录 + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/PackageRechargeDialog.vue b/src/views/asset-management/asset-information/components/dialogs/PackageRechargeDialog.vue new file mode 100644 index 0000000..ed3c99e --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/PackageRechargeDialog.vue @@ -0,0 +1,237 @@ + + + + + + {{ assetIdentifier }} + + + + + + + {{ pkg.package_name }} + + ¥{{ ((pkg.cost_price || 0) / 100).toFixed(2) }} + + + + + + + + + + + + + 提示: 使用钱包支付时,订单将直接完成 + + + 提示: 线下支付订单需要手动确认支付 + + + + + + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/SpeedLimitDialog.vue b/src/views/asset-management/asset-information/components/dialogs/SpeedLimitDialog.vue new file mode 100644 index 0000000..140e708 --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/SpeedLimitDialog.vue @@ -0,0 +1,94 @@ + + + + + + 单位: KB/s + + + + 单位: KB/s + + + + 取消 + 确认设置 + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/SwitchCardDialog.vue b/src/views/asset-management/asset-information/components/dialogs/SwitchCardDialog.vue new file mode 100644 index 0000000..605cf1b --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/SwitchCardDialog.vue @@ -0,0 +1,165 @@ + + + + + 加载设备绑定的卡列表中... + + + + + + 当前设备没有绑定任何SIM卡,无法进行切换操作。 + 请先为设备绑定SIM卡后再进行切换。 + + + + + + + + + {{ card.iccid }} + 插槽{{ card.slot_position }} + + + + + 当前设备共绑定 {{ bindingCards.length }} 张SIM卡 + + + + + + + 取消 + + 确认切换 + + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/WiFiConfigDialog.vue b/src/views/asset-management/asset-information/components/dialogs/WiFiConfigDialog.vue new file mode 100644 index 0000000..b7be13f --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/WiFiConfigDialog.vue @@ -0,0 +1,117 @@ + + + + + + 启用 + 禁用 + + + + + + + + + + + 取消 + 确认设置 + + + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/index.ts b/src/views/asset-management/asset-information/components/dialogs/index.ts new file mode 100644 index 0000000..1bd3cef --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/index.ts @@ -0,0 +1,6 @@ +export { default as SpeedLimitDialog } from './SpeedLimitDialog.vue' +export { default as SwitchCardDialog } from './SwitchCardDialog.vue' +export { default as WiFiConfigDialog } from './WiFiConfigDialog.vue' +export { default as PackageRechargeDialog } from './PackageRechargeDialog.vue' +export { default as DailyRecordsDialog } from './DailyRecordsDialog.vue' +export { default as OrderHistoryDialog } from './OrderHistoryDialog.vue' diff --git a/src/views/asset-management/asset-information/composables/useAssetFormatters.ts b/src/views/asset-management/asset-information/composables/useAssetFormatters.ts new file mode 100644 index 0000000..3d82d71 --- /dev/null +++ b/src/views/asset-management/asset-information/composables/useAssetFormatters.ts @@ -0,0 +1,249 @@ +/** + * 资产信息格式化工具函数 + */ + +import type { Ref } from 'vue' + +export function useAssetFormatters(deviceRealtime?: Ref) { + // 格式化数据大小(MB转GB/MB) + const formatDataSize = (mb: number) => { + if (mb >= 1024) { + return `${(mb / 1024).toFixed(2)}GB` + } + return `${mb.toFixed(2)}MB` + } + + // 格式化金额(分转元) + const formatAmount = (amount: number | undefined) => { + if (amount === undefined || amount === null) return '¥0.00' + const yuan = amount / 100 + return `¥${yuan.toFixed(2)}` + } + + // 格式化时长(秒转为时分秒) + const formatDuration = (seconds?: string) => { + if (!seconds) return '-' + const sec = Number(seconds) + if (isNaN(sec)) return '-' + + const hours = Math.floor(sec / 3600) + const minutes = Math.floor((sec % 3600) / 60) + const secs = sec % 60 + + if (hours > 0) { + return `${hours}小时${minutes}分${secs}秒` + } else if (minutes > 0) { + return `${minutes}分${secs}秒` + } else { + return `${secs}秒` + } + } + + // 格式化ICCID为4位一组,用横杠分隔 + const formatIccidWithDashes = (iccid: string) => { + if (!iccid) return '' + return iccid.replace(/(\d{4})(?=\d)/g, '$1-') + } + + // 获取实名状态名称 + const getRealNameStatusName = (status?: number) => { + if (status === undefined || status === null) return '未知' + const statusMap: Record = { + 0: '未实名', + 1: '已实名' + } + return statusMap[status] || '未知' + } + + // 获取实名状态标签类型 + const getRealNameStatusType = (status?: number): 'info' | 'success' | 'warning' | 'danger' => { + if (status === undefined || status === null) return 'info' + const map: Record = { + 0: 'info', + 1: 'success' + } + return map[status] || 'info' + } + + // 获取资产状态名称 + const getAssetStatusName = (status?: number) => { + if (status === undefined || status === null) return '未知' + const map: Record = { + 1: '在库', + 2: '已分销', + 3: '已激活', + 4: '已停用' + } + return map[status] || '未知' + } + + // 获取资产状态标签类型 + const getAssetStatusType = ( + status?: number + ): 'primary' | 'success' | 'warning' | 'info' | 'danger' => { + if (status === undefined || status === null) return 'info' + const map: Record = { + 1: 'info', // 在库 + 2: 'success', // 已分销 + 3: 'success', // 已激活 + 4: 'danger' // 已停用 + } + return map[status] || 'info' + } + + // 获取在线状态名称 + const getOnlineStatusName = (status?: number) => { + const map: Record = { + 0: '未知', + 1: '在线', + 2: '离线' + } + return map[status ?? 0] || '未知' + } + + // 获取在线状态标签类型 + const getOnlineStatusType = ( + status?: number + ): 'primary' | 'success' | 'warning' | 'info' | 'danger' => { + const map: Record = { + 0: 'info', // 未知 + 1: 'success', // 在线 + 2: 'danger' // 离线 + } + return map[status ?? 0] || 'info' + } + + // 获取钱包状态标签类型 + const getWalletStatusType = (status?: number) => { + const map: Record = { + 1: 'success', // 正常 + 2: 'warning', // 冻结 + 3: 'danger' // 关闭 + } + return map[status || 1] || 'info' + } + + // 获取套餐状态标签类型 + const getPackageStatusTagType = ( + status?: number + ): 'primary' | 'success' | 'warning' | 'info' | 'danger' => { + if (status === undefined || status === null) return 'info' + const map: Record = { + 0: 'info', // 待生效 + 1: 'success', // 生效中 + 2: 'warning', // 已用完 + 3: 'danger', // 已过期 + 4: 'info' // 已失效 + } + return map[status] || 'info' + } + + // 计算信号强度等级(基于RSSI、RSRP、RSRQ) + const calculateSignalStrength = () => { + if (!deviceRealtime?.value) return '-' + + const rssi = deviceRealtime.value.rssi ? Number(deviceRealtime.value.rssi) : null + const rsrp = deviceRealtime.value.rsrp ? Number(deviceRealtime.value.rsrp) : null + + // 优先使用RSRP判断(更准确) + // RSRP范围通常在-140到-44 dBm之间,0或正值表示无效数据 + if (rsrp !== null && !isNaN(rsrp) && rsrp < 0) { + if (rsrp >= -80) return '优秀' + if (rsrp >= -90) return '良好' + if (rsrp >= -100) return '一般' + if (rsrp >= -110) return '较差' + return '极差' + } + + // 其次使用RSSI + // RSSI范围通常在-113到-51 dBm之间,0或正值表示无效数据 + if (rssi !== null && !isNaN(rssi) && rssi < 0) { + if (rssi >= -70) return '优秀' + if (rssi >= -85) return '良好' + if (rssi >= -100) return '一般' + if (rssi >= -110) return '较差' + return '极差' + } + + return '-' + } + + // 获取信号强度标签类型 + const getSignalStrengthType = (): 'primary' | 'success' | 'warning' | 'info' | 'danger' => { + const strength = calculateSignalStrength() + const map: Record = { + 优秀: 'success', + 良好: 'success', + 一般: 'warning', + 较差: 'danger', + 极差: 'danger' + } + return map[strength] || 'info' + } + + // 获取切卡模式名称 + const getSwitchModeName = (mode?: string) => { + const map: Record = { + '0': '自动', + '1': '手动' + } + return map[mode || ''] || '-' + } + + // 获取交易类型标签颜色 + const getTransactionTypeTag = (type: string | undefined) => { + const map: Record = { + recharge: 'success', // 充值 + deduct: 'danger', // 扣款 + refund: 'warning' // 退款 + } + return map[type || ''] || 'info' + } + + // 获取支付状态标签类型 + const getPaymentStatusType = (status: number): 'success' | 'warning' | 'danger' | 'info' => { + const statusMap: Record = { + 1: 'warning', // 待支付 + 2: 'success', // 已支付 + 3: 'danger', // 已取消 + 4: 'info' // 已关闭 + } + return statusMap[status] || 'info' + } + + // 计算使用进度百分比 + const getUsageProgress = (cumulative: number, total: number): number => { + if (!total || total <= 0) return 0 + const percentage = (cumulative / total) * 100 + return Math.min(Math.round(percentage * 100) / 100, 100) + } + + // 根据百分比获取进度条颜色 + const getProgressColorByPercentage = (percentage: number): string => { + if (percentage < 50) return '#67c23a' + if (percentage < 80) return '#e6a23c' + return '#f56c6c' + } + + return { + formatDataSize, + formatAmount, + formatDuration, + formatIccidWithDashes, + getRealNameStatusName, + getRealNameStatusType, + getAssetStatusName, + getAssetStatusType, + getOnlineStatusName, + getOnlineStatusType, + getWalletStatusType, + getPackageStatusTagType, + calculateSignalStrength, + getSignalStrengthType, + getSwitchModeName, + getTransactionTypeTag, + getPaymentStatusType, + getUsageProgress, + getProgressColorByPercentage + } +} diff --git a/src/views/asset-management/asset-information/composables/useAssetInfo.ts b/src/views/asset-management/asset-information/composables/useAssetInfo.ts new file mode 100644 index 0000000..da9237b --- /dev/null +++ b/src/views/asset-management/asset-information/composables/useAssetInfo.ts @@ -0,0 +1,352 @@ +/** + * 资产信息数据管理 Composable + * 负责资产详情数据的加载和状态管理 + */ + +import { ref } from 'vue' +import { ElMessage } from 'element-plus' +import { AssetService } from '@/api/modules' +import type { AssetWalletResponse } from '@/types/api' + +/** + * 格式化数据大小(MB -> GB/MB) + */ +const formatDataSize = (mb: number) => { + if (mb >= 1024) { + return `${(mb / 1024).toFixed(2)}GB` + } + return `${mb.toFixed(2)}MB` +} + +export function useAssetInfo() { + // 状态管理 + const loading = ref(false) + const cardInfo = ref(null) // 使用 any 以保持与原始实现一致 + const deviceRealtime = ref(null) + const currentPackage = ref(null) + const currentPackageLoading = ref(false) + const currentPackageErrorMsg = ref('') + const packageList = ref([]) + const walletInfo = ref(null) + const walletLoading = ref(false) + + /** + * 获取资产详情 + * @param identifier - 资产标识符(ICCID、IMEI、SN、虚拟号或MSISDN) + */ + const fetchAssetDetail = async (identifier: string) => { + try { + loading.value = true + const response = await AssetService.resolveAsset(identifier) + + if (response.code === 0 && response.data) { + const data = response.data + + // 映射API数据到页面显示格式 + cardInfo.value = { + // 基本信息 + asset_type: data.asset_type, // card 或 device + asset_id: data.asset_id, + virtual_no: data.virtual_no, + status: data.status, + batch_no: data.batch_no, + shop_id: data.shop_id, + shop_name: data.shop_name, + series_id: data.series_id, + series_name: data.series_name, + real_name_status: data.real_name_status, + activated_at: data.activated_at, + created_at: data.created_at, + updated_at: data.updated_at, + accumulated_recharge: data.accumulated_recharge, + first_commission_paid: data.first_commission_paid, + + // 卡专属字段 (asset_type === 'card') + iccid: data.iccid || '', + msisdn: data.msisdn || '', + imsi: data.imsi || '', + carrier_id: data.carrier_id, + carrier_type: data.carrier_type || '', + carrier_name: data.carrier_name || '', + supplier: data.supplier || '', + network_status: data.network_status, + bound_device_id: data.bound_device_id, + bound_device_no: data.bound_device_no || '', + bound_device_name: data.bound_device_name || '', + card_category: data.card_category || '', + activation_status: data.activation_status, + enable_polling: data.enable_polling, + + // 设备专属字段 (asset_type === 'device') + device_name: data.device_name || '', + device_model: data.device_model || '', + device_type: data.device_type || '', + manufacturer: data.manufacturer || '', + imei: data.imei || '', + sn: data.sn || '', + max_sim_slots: data.max_sim_slots || 0, + bound_card_count: data.bound_card_count || 0, + cards: data.cards || [], + device_protect_status: data.device_protect_status, + online_status: data.online_status, + last_online_time: data.last_online_time, + software_version: data.software_version, + switch_mode: data.switch_mode, + last_gateway_sync_at: data.last_gateway_sync_at, + + // 流量/套餐信息 + current_package: data.current_package || '', + packageSeries: data.series_name || '-', + packageTotalFlow: formatDataSize(data.package_total_mb || 0), + usedFlow: formatDataSize(data.package_used_mb || 0), + remainFlow: formatDataSize(data.package_remain_mb || 0), + usedFlowPercentage: + data.package_total_mb > 0 + ? `${((data.package_used_mb / data.package_total_mb) * 100).toFixed(2)}%` + : '0.00%', + packageList: [] // 套餐列表需要单独调用API获取 + } + + // 获取资产标识符并加载其他数据 + const assetIdentifier = data.asset_type === 'card' ? data.iccid : data.virtual_no + if (assetIdentifier) { + // 并行调用多个接口: 当前生效套餐、套餐列表、实时状态、钱包概况 + Promise.all([ + loadCurrentPackage(assetIdentifier), + loadPackageList(assetIdentifier), + loadRealtimeStatus(assetIdentifier, data.asset_type), + loadAssetWallet(assetIdentifier) + ]) + } + } else { + ElMessage.error(response.msg || '查询失败') + cardInfo.value = null + } + } catch (error: any) { + console.error('获取卡片信息失败:', error) + if (error?.response?.status === 404) { + ElMessage.error('未找到该资产') + } else if (error?.response?.status === 403) { + ElMessage.error('无权限访问该资产') + } else { + ElMessage.error(error?.message || '获取卡片信息失败') + } + cardInfo.value = null + } finally { + loading.value = false + } + } + + /** + * 加载套餐列表 + * @param identifier - 资产标识符 + */ + const loadPackageList = async (identifier: string) => { + try { + const response = await AssetService.getAssetPackages(identifier, { + page: 1, + page_size: 50 + }) + if (response.code === 0 && response.data) { + // 使用分页响应中的 items 数组 + cardInfo.value.packageList = response.data.items || [] + } + } catch (error) { + console.error('获取套餐列表失败:', error) + } + } + + /** + * 加载当前生效套餐 + * @param identifier - 资产标识符 + */ + const loadCurrentPackage = async (identifier: string) => { + try { + currentPackageLoading.value = true + // 清空之前的错误信息 + currentPackageErrorMsg.value = '' + + const response = await AssetService.getCurrentPackage(identifier) + if (response.code === 0) { + if (response.data) { + const pkg = response.data + // 保存完整的当前套餐数据 + cardInfo.value.currentPackageDetail = { + package_usage_id: pkg.package_usage_id, + package_id: pkg.package_id, + package_name: pkg.package_name, + package_type: pkg.package_type, + status: pkg.status, + status_name: pkg.status_name, + data_limit_mb: pkg.data_limit_mb, + data_usage_mb: pkg.data_usage_mb, + virtual_limit_mb: pkg.virtual_limit_mb, + virtual_used_mb: pkg.virtual_used_mb, + virtual_remain_mb: pkg.virtual_remain_mb, + virtual_ratio: pkg.virtual_ratio, + activated_at: pkg.activated_at, + expires_at: pkg.expires_at, + created_at: pkg.created_at, + master_usage_id: pkg.master_usage_id, + priority: pkg.priority, + usage_type: pkg.usage_type + } + // 同时更新流量显示信息 + cardInfo.value.current_package = pkg.package_name + cardInfo.value.packageTotalFlow = formatDataSize(pkg.virtual_limit_mb || 0) + cardInfo.value.usedFlow = formatDataSize(pkg.virtual_used_mb || 0) + cardInfo.value.remainFlow = formatDataSize(pkg.virtual_remain_mb || 0) + cardInfo.value.usedFlowPercentage = + (pkg.virtual_limit_mb || 0) > 0 + ? `${(((pkg.virtual_used_mb || 0) / (pkg.virtual_limit_mb || 1)) * 100).toFixed(2)}%` + : '0.00%' + } else { + // code 为 0 但 data 为空,表示暂无当前生效套餐(正常情况) + currentPackageErrorMsg.value = '暂无当前生效套餐' + } + } else { + // 接口返回 code 不为 0,保存错误信息 + currentPackageErrorMsg.value = response.msg || '获取当前套餐失败' + } + } catch (error: any) { + // 404表示无当前生效套餐,这是正常情况 + if (error?.response?.status === 404) { + currentPackageErrorMsg.value = '暂无当前生效套餐' + } else { + console.error('获取当前套餐失败:', error) + currentPackageErrorMsg.value = error?.message || '获取当前套餐失败' + } + } finally { + currentPackageLoading.value = false + } + } + + /** + * 加载实时状态 + * @param identifier - 资产标识符 + * @param assetType - 资产类型(card/device) + */ + const loadRealtimeStatus = async (identifier: string, assetType: string) => { + try { + const response = await AssetService.getRealtimeStatus(identifier) + if (response.code === 0 && response.data) { + const data = response.data + // 更新实时状态数据 + if (assetType === 'card') { + // 卡的实时状态 + if (data.network_status !== undefined) { + cardInfo.value.network_status = data.network_status + } + if (data.real_name_status !== undefined) { + cardInfo.value.real_name_status = data.real_name_status + } + if (data.last_sync_time !== undefined) { + cardInfo.value.last_sync_time = data.last_sync_time + } + if (data.current_month_usage_mb !== undefined) { + cardInfo.value.current_month_usage_mb = data.current_month_usage_mb + } + } else if (assetType === 'device') { + // 设备的实时状态 + if (data.device_protect_status !== undefined) { + cardInfo.value.device_protect_status = data.device_protect_status + } + if (data.cards && data.cards.length > 0) { + cardInfo.value.cards = data.cards + } + if (data.online_status !== undefined) { + cardInfo.value.online_status = data.online_status + } + if (data.last_online_time !== undefined) { + cardInfo.value.last_online_time = data.last_online_time + } + if (data.software_version !== undefined) { + cardInfo.value.software_version = data.software_version + } + if (data.switch_mode !== undefined) { + cardInfo.value.switch_mode = data.switch_mode + } + if (data.last_gateway_sync_at !== undefined) { + cardInfo.value.last_gateway_sync_at = data.last_gateway_sync_at + } + // 设备实时信息(Gateway 数据) + if (data.device_realtime !== undefined) { + deviceRealtime.value = data.device_realtime + console.log('设备实时信息:', deviceRealtime.value) + } + } + } + } catch (error) { + console.error('获取实时状态失败:', error) + } + } + + /** + * 加载钱包概况 + * @param identifier - 资产标识符 + */ + const loadAssetWallet = async (identifier: string) => { + try { + walletLoading.value = true + const response = await AssetService.getAssetWallet(identifier) + if (response.code === 0 && response.data) { + walletInfo.value = response.data + } + } catch (error: any) { + if (error?.response?.status === 403) { + // 企业账号禁止查看钱包信息,不显示错误 + walletInfo.value = null + } else { + console.error('获取钱包概况失败:', error) + } + } finally { + walletLoading.value = false + } + } + + /** + * 刷新资产数据(调用refresh接口后重新加载) + * @param identifier - 资产标识符 + * @param assetType - 资产类型(card/device) + */ + const refreshAsset = async (identifier: string, assetType: string) => { + try { + await AssetService.refreshAsset(identifier) + ElMessage.success('刷新成功') + + // 刷新成功后重新加载实时状态数据 + if (identifier) { + await loadRealtimeStatus(identifier, assetType) + } + } catch (error: any) { + // 检查429错误(冷却期) + if (error?.response?.status === 429) { + ElMessage.warning('刷新过于频繁,请稍后再试') + } else { + ElMessage.error(error?.message || '刷新失败') + } + throw error + } + } + + return { + // State + loading, + cardInfo, + deviceRealtime, + currentPackage, + currentPackageLoading, + currentPackageErrorMsg, + packageList, + walletInfo, + walletLoading, + + // Methods + fetchAssetDetail, + loadPackageList, + loadCurrentPackage, + loadRealtimeStatus, + loadAssetWallet, + refreshAsset + } +} diff --git a/src/views/asset-management/asset-information/composables/useAssetOperations.ts b/src/views/asset-management/asset-information/composables/useAssetOperations.ts new file mode 100644 index 0000000..30c5a8d --- /dev/null +++ b/src/views/asset-management/asset-information/composables/useAssetOperations.ts @@ -0,0 +1,319 @@ +import { ref } from 'vue' +import type { Ref } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import { AssetService, DeviceService } from '@/api/modules' + +/** + * Asset operations composable + * Manages asset operation methods (enable/disable cards, device operations, etc.) + */ +export function useAssetOperations(cardInfo: Ref, refreshAssetFn?: () => Promise) { + // Loading states + const enableCardLoading = ref(false) + const disableCardLoading = ref(false) + const manualDeactivateCardLoading = ref(false) + const rebootDeviceLoading = ref(false) + const resetDeviceLoading = ref(false) + const speedLimitLoading = ref(false) + const switchCardLoading = ref(false) + const setWiFiLoading = ref(false) + const pollingLoading = ref(false) + + /** + * Enable card + */ + const enableCard = async () => { + try { + await ElMessageBox.confirm('确认要启用此卡吗?', '提示', { + type: 'warning' + }) + + enableCardLoading.value = true + const res = await AssetService.startAsset(cardInfo.value.iccid) + if (res.code === 0) { + ElMessage.success('启用成功') + if (refreshAssetFn) { + await refreshAssetFn() + } + } + } catch (error: any) { + if (error !== 'cancel') { + console.error('启用失败:', error) + ElMessage.error(error?.message || '启用失败') + } + } finally { + enableCardLoading.value = false + } + } + + /** + * Disable card with confirmation + */ + const disableCard = async () => { + try { + await ElMessageBox.confirm('确认要停用此卡吗?', '提示', { + type: 'warning' + }) + + disableCardLoading.value = true + const res = await AssetService.stopAsset(cardInfo.value.iccid) + if (res.code === 0) { + ElMessage.success('停用成功') + if (refreshAssetFn) { + await refreshAssetFn() + } + } + } catch (error: any) { + if (error !== 'cancel') { + console.error('停用失败:', error) + ElMessage.error(error?.message || '停用失败') + } + } finally { + disableCardLoading.value = false + } + } + + /** + * Manually deactivate card with strong warning confirmation + */ + const manualDeactivateCard = async () => { + try { + await ElMessageBox.confirm( + '手动停用后卡片将永久停用,无法再次使用。此操作不可逆,请谨慎操作。确认要手动停用吗?', + '警告', + { + type: 'error', + confirmButtonText: '确认停用', + cancelButtonText: '取消' + } + ) + + manualDeactivateCardLoading.value = true + const identifier = + cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no + const res = await AssetService.deactivateAsset(identifier) + if (res.code === 0) { + ElMessage.success('手动停用成功') + if (refreshAssetFn) { + await refreshAssetFn() + } + } + } catch (error: any) { + if (error !== 'cancel') { + console.error('手动停用失败:', error) + ElMessage.error(error?.message || '手动停用失败') + } + } finally { + manualDeactivateCardLoading.value = false + } + } + + /** + * Reboot device with confirmation + */ + const rebootDevice = async () => { + try { + await ElMessageBox.confirm('确认要重启设备吗?', '提示', { + type: 'warning' + }) + + rebootDeviceLoading.value = true + const res = await DeviceService.rebootDevice(cardInfo.value.imei) + if (res.code === 0) { + ElMessage.success(res.data?.message || '重启指令已发送') + if (refreshAssetFn) { + await refreshAssetFn() + } + } + } catch (error: any) { + if (error !== 'cancel') { + console.error('重启失败:', error) + ElMessage.error(error?.message || '重启失败') + } + } finally { + rebootDeviceLoading.value = false + } + } + + /** + * Factory reset device with strong warning confirmation + */ + const resetDevice = async () => { + try { + await ElMessageBox.confirm('恢复出厂设置将清除设备所有数据和配置,确认要继续吗?', '警告', { + type: 'error', + confirmButtonText: '确认恢复', + cancelButtonText: '取消' + }) + + resetDeviceLoading.value = true + const res = await DeviceService.resetDevice(cardInfo.value.imei) + if (res.code === 0) { + ElMessage.success(res.data?.message || '恢复出厂设置指令已发送') + if (refreshAssetFn) { + await refreshAssetFn() + } + } + } catch (error: any) { + if (error !== 'cancel') { + console.error('恢复出厂失败:', error) + ElMessage.error(error?.message || '恢复出厂失败') + } + } finally { + resetDeviceLoading.value = false + } + } + + /** + * Set device speed limit + */ + const setSpeedLimit = async (form: { download_speed: number; upload_speed: number }) => { + try { + speedLimitLoading.value = true + const res = await DeviceService.setSpeedLimit(cardInfo.value.imei, { + download_speed: form.download_speed, + upload_speed: form.upload_speed + }) + if (res.code === 0) { + ElMessage.success('限速设置成功') + if (refreshAssetFn) { + await refreshAssetFn() + } + return true + } else { + ElMessage.error(res.msg || '设置失败') + return false + } + } catch (error: any) { + console.error('设置限速失败:', error) + ElMessage.error(error?.message || '设置失败') + return false + } finally { + speedLimitLoading.value = false + } + } + + /** + * Switch to different card + */ + const switchCard = async (form: { target_iccid: string }) => { + try { + switchCardLoading.value = true + const res = await DeviceService.switchCard(cardInfo.value.imei, { + target_iccid: form.target_iccid + }) + if (res.code === 0) { + ElMessage.success('切换SIM卡指令已发送') + if (refreshAssetFn) { + await refreshAssetFn() + } + return true + } else { + ElMessage.error(res.msg || '切换失败') + return false + } + } catch (error: any) { + console.error('切换SIM卡失败:', error) + ElMessage.error(error?.message || '切换失败') + return false + } finally { + switchCardLoading.value = false + } + } + + /** + * Configure WiFi settings + */ + const setWiFi = async (form: { enabled: number; ssid: string; password: string }) => { + try { + setWiFiLoading.value = true + const res = await DeviceService.setWiFi(cardInfo.value.imei, { + enabled: form.enabled, + ssid: form.ssid, + password: form.password + }) + if (res.code === 0) { + ElMessage.success('WiFi设置成功') + if (refreshAssetFn) { + await refreshAssetFn() + } + return true + } else { + ElMessage.error(res.msg || '设置失败') + return false + } + } catch (error: any) { + console.error('设置WiFi失败:', error) + ElMessage.error(error?.message || '设置WiFi失败') + return false + } finally { + setWiFiLoading.value = false + } + } + + /** + * Update polling status + */ + const updatePollingStatus = async (enabled: boolean) => { + if (!cardInfo.value) { + ElMessage.warning('无法更新轮询状态:缺少资产信息') + return false + } + + try { + pollingLoading.value = true + const identifier = + cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no + + const response = await AssetService.updatePollingStatus(identifier, { + enable_polling: enabled + }) + + if (response.code === 0) { + ElMessage.success(enabled ? '已开启自动轮询' : '已关闭自动轮询') + // Update local state + if (cardInfo.value) { + cardInfo.value.enable_polling = enabled + } + return true + } else { + ElMessage.error(response.msg || '更新轮询状态失败') + return false + } + } catch (error: any) { + console.error('更新轮询状态失败:', error) + ElMessage.error(error?.message || '更新轮询状态失败') + return false + } finally { + pollingLoading.value = false + } + } + + return { + // Loading states + enableCardLoading, + disableCardLoading, + manualDeactivateCardLoading, + rebootDeviceLoading, + resetDeviceLoading, + speedLimitLoading, + switchCardLoading, + setWiFiLoading, + pollingLoading, + + // IoT card operations + enableCard, + disableCard, + manualDeactivateCard, + + // Device operations + rebootDevice, + resetDevice, + setSpeedLimit, + switchCard, + setWiFi, + + // Polling + updatePollingStatus + } +} diff --git a/src/views/asset-management/asset-information/index.vue b/src/views/asset-management/asset-information/index.vue index 0b5c13d..7c792f2 100644 --- a/src/views/asset-management/asset-information/index.vue +++ b/src/views/asset-management/asset-information/index.vue @@ -1,2025 +1,329 @@ - - - - 资产查询 - - - - - - - - - - - {{ formatIccidWithDashes(searchIccid) }} - - - - - 查询 - - - - - 刷新 - - - - + - - + - - - - - {{ cardInfo?.asset_type === 'card' ? '卡信息' : '设备信息' }} - - {{ cardInfo?.asset_type === 'card' ? 'IoT卡' : '设备' }} - - - - - - 自动轮询 - - - - - - - - - - {{ cardInfo?.iccid || '-' }} - {{ - cardInfo?.carrier_name || '-' - }} - - - {{ getRealNameStatusName(cardInfo?.real_name_status) }} - - - - {{ - cardInfo?.shop_name || '-' - }} - {{ - cardInfo?.series_name || '-' - }} - {{ - cardInfo?.virtual_no || '-' - }} - - - {{ cardInfo?.network_status === 1 ? '正常' : '停机' }} - - - - {{ - formatDataSize(cardInfo?.current_month_usage_mb || 0) - }} - {{ - cardInfo?.real_name_at ? formatDateTime(cardInfo.real_name_at) : '-' - }} - - {{ - cardInfo?.msisdn || '-' - }} - - - - - {{ cardInfo?.imei || '-' }} - {{ - cardInfo?.device_type || '-' - }} - - - {{ getRealNameStatusName(currentCard?.real_name_status) }} - - - - {{ - cardInfo?.shop_name || '-' - }} - {{ - cardInfo?.device_name || '-' - }} - - - {{ getAssetStatusName(cardInfo?.status) }} - - - - {{ - cardInfo?.virtual_no || '-' - }} - {{ cardInfo?.sn || '-' }} - {{ - cardInfo?.device_model || '-' - }} - - {{ - cardInfo?.supplier || '-' - }} - {{ - cardInfo?.bound_card_count || 0 - }} - {{ - cardInfo?.max_sim_slots || '-' - }} - - {{ - cardInfo?.activated_at || '-' - }} - {{ - cardInfo?.real_name_at ? formatDateTime(cardInfo.real_name_at) : '-' - }} - - - {{ calculateSignalStrength() }} - - - - - - {{ getOnlineStatusName(cardInfo?.online_status) }} - - - {{ - cardInfo?.software_version || '-' - }} - {{ - getSwitchModeName(cardInfo?.switch_mode) - }} - - {{ - formatDateTime(cardInfo?.last_online_time) || '-' - }} - {{ - formatDateTime(cardInfo?.last_gateway_sync_at) || '-' - }} - {{ - cardInfo?.manufacturer || '-' - }} - - - - - - 绑定卡列表 - - - - - {{ scope.row.iccid }} - - - - - - - - - SIM-{{ scope.row.slot_position }} - 当前 - - - - - - - {{ scope.row.network_status === 1 ? '正常' : '停机' }} - - - - - - - {{ getRealNameStatusName(scope.row.real_name_status) }} - - - - - - {{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }} - - - - - - - 启用此卡 - - - 停用此卡 - - - 手动停用 - - - - - - - - 设备实时信息 - - - - - - {{ deviceRealtime.online_status === 1 ? '在线' : '离线' }} - - - - - {{ deviceRealtime.status === 1 ? '正常' : '禁用' }} - - - - {{ - deviceRealtime.battery_level !== null && - deviceRealtime.battery_level !== undefined - ? `${deviceRealtime.battery_level}%` - : '' - }} - - - - - {{ formatDuration(deviceRealtime.run_time) }} - - - {{ formatDuration(deviceRealtime.connect_time) }} - - - {{ formatDateTime(deviceRealtime.last_online_time) || '-' }} - - - - - {{ deviceRealtime.rssi || '-' }} - - - {{ deviceRealtime.rsrp ? `${deviceRealtime.rsrp} dBm` : '-' }} - - - {{ deviceRealtime.rsrq ? `${deviceRealtime.rsrq} dB` : '-' }} - - - - {{ deviceRealtime.sinr ? `${deviceRealtime.sinr} dB` : '-' }} - - - {{ deviceRealtime.max_clients || '-' }} - - - {{ deviceRealtime.current_iccid || '-' }} - - - - - - {{ deviceRealtime.wifi_enabled ? '已开启' : '已关闭' }} - - - - {{ deviceRealtime.ssid || '-' }} - - - {{ deviceRealtime.wifi_password || '-' }} - - - - - {{ deviceRealtime.ip_address || '-' }} - - - {{ deviceRealtime.wan_ip || '-' }} - - - {{ deviceRealtime.lan_ip || '-' }} - - - - {{ deviceRealtime.mac_address || '-' }} - - - {{ deviceRealtime.imei || '-' }} - - - {{ deviceRealtime.imsi || '-' }} - - - - - {{ formatDataSize(Number(deviceRealtime.daily_usage || 0) / (1024 * 1024)) }} - - - {{ formatDataSize(Number(deviceRealtime.dl_stats || 0) / (1024 * 1024)) }} - - - {{ formatDataSize(Number(deviceRealtime.ul_stats || 0) / (1024 * 1024)) }} - - - - - {{ - deviceRealtime.limit_speed ? `${deviceRealtime.limit_speed} KB/s` : '不限速' - }} - - - {{ deviceRealtime.software_version || '-' }} - - - {{ - deviceRealtime.switch_mode === '0' - ? '自动' - : deviceRealtime.switch_mode === '1' - ? '手动' - : '-' - }} - - - - {{ deviceRealtime.sync_interval ? `${deviceRealtime.sync_interval} 秒` : '-' }} - - - {{ deviceRealtime.device_id || '-' }} - - - {{ formatDateTime(deviceRealtime.last_update_time) || '-' }} - - - - - - Gateway 不可达或设备离线,无法获取实时信息 - - - - - - - - 启用此卡 - - - 停用此卡 - - - 手动停用 - - - - - - - 重启设备 - - - 恢复出厂 - - - - - - - - - - 切换SIM卡 - - - 设置WiFi - - - 手动停用 - - - + - - - - 当前生效套餐 - - {{ cardInfo.currentPackageDetail?.status_name || '-' }} - - - - - - - {{ - cardInfo.currentPackageDetail.package_name || '-' - }} - - - {{ - cardInfo.currentPackageDetail.package_type === 'formal' - ? '正式套餐' - : '加油包' - }} - - - - - {{ - formatDataSize(cardInfo.currentPackageDetail.data_limit_mb || 0) - }} - {{ - formatDataSize(cardInfo.currentPackageDetail.data_usage_mb || 0) - }} - {{ - cardInfo.currentPackageDetail.virtual_ratio || '-' - }} - - - {{ - formatDataSize(cardInfo.currentPackageDetail.virtual_limit_mb || 0) - }} - {{ - formatDataSize(cardInfo.currentPackageDetail.virtual_used_mb || 0) - }} - {{ - formatDataSize(cardInfo.currentPackageDetail.virtual_remain_mb || 0) - }} - - - {{ - formatDateTime(cardInfo.currentPackageDetail.activated_at) - }} - {{ - formatDateTime(cardInfo.currentPackageDetail.expires_at) - }} - {{ - formatDateTime(cardInfo.currentPackageDetail.created_at) - }} - - - - - 套餐充值 - 更改过期时间 - 往期订单 - - - - + - - - - - - - 钱包概况 - - {{ walletInfo.status_text || '-' }} - - - - - - - - 💰 - - 总余额 - {{ formatAmount(walletInfo.balance) }} - - - - ✅ - - 可用余额 - {{ - formatAmount(walletInfo.available_balance) - }} - - - - 🔒 - - 冻结余额 - {{ formatAmount(walletInfo.frozen_balance) }} - - - - - - + + + - - - - - - - 套餐列表 - - - - - - - - {{ scope.row.package_name }} - - - - - - - {{ scope.row.package_type === 'formal' ? '正式套餐' : '加油包' }} - - - - - - - {{ scope.row.usage_type === 'single_card' ? '单卡' : '设备' }} - - - - - - - {{ scope.row.status_name }} - - - - - - {{ formatDataSize(scope.row.data_limit_mb) }} - - - - - {{ formatDataSize(scope.row.data_usage_mb) }} - - - - - {{ formatDataSize(scope.row.virtual_limit_mb || 0) }} - - - - - {{ formatDataSize(scope.row.virtual_used_mb || 0) }} - - - - - {{ formatDataSize(scope.row.virtual_remain_mb || 0) }} - - - - - {{ scope.row.virtual_ratio?.toFixed(2) || '1.00' }} - - - - - - {{ formatDateTime(scope.row.activated_at) }} - - - - - {{ formatDateTime(scope.row.expires_at) }} - - - - - {{ formatDateTime(scope.row.created_at) }} - - - - - - + + - + - - - - - 钱包流水 - 共 {{ walletTransactionTotal }} 条 - - - - - - - - - 查询 - 重置 - - - - - - - - - - {{ scope.row.transaction_type_text }} - - - - - - - {{ formatAmount(scope.row.amount) }} - - - - - - {{ formatAmount(scope.row.balance_before) }} - - - - - {{ formatAmount(scope.row.balance_after) }} - - - - - - {{ scope.row.reference_type === 'recharge' ? '充值' : '订单' }} - - -- - - - - - - {{ scope.row.reference_no }} - - -- - - - - - {{ scope.row.remark || '-' }} - - - - - {{ formatDateTime(scope.row.created_at) }} - - - - - - - - - - - + - - + + + + - - - - - {{ - cardInfo?.imei || cardInfo?.virtual_no - }} - - - - 单位: KB/s - - - - 单位: KB/s - - - - 取消 - - 确认设置 - - - - - - - - - 加载设备绑定的卡列表中... - - - - - - 当前设备没有绑定任何SIM卡,无法进行切换操作。 - 请先为设备绑定SIM卡后再进行切换。 - - - - - - {{ - cardInfo?.imei || cardInfo?.virtual_no - }} - - - - - - {{ card.iccid }} - 插槽{{ card.slot_position }} - - - - - 当前设备共绑定 {{ deviceBindingCards.length }} 张SIM卡 - - - - - - - 取消 - - 确认切换 - - - - - - - - - {{ - cardInfo?.imei || cardInfo?.virtual_no - }} - - - - 启用 - 禁用 - - - - - - - - - - - 取消 - - 确认设置 - - - - - - + + + - - - - {{ cardInfo?.asset_type === 'card' ? cardInfo?.iccid : cardInfo?.imei }} ({{ - cardInfo?.virtual_no - }}) - - - - - - - {{ pkg.package_name }} - - ¥{{ ((pkg.cost_price || 0) / 100).toFixed(2) }} - - - - - - - - - - - - - - 提示: 使用钱包支付时,订单将直接完成 - - - 提示: 线下支付订单需要手动确认支付 - - - - - - - - - - - + - - - - - {{ dailyRecordsData.package_name }} - - - {{ dailyRecordsData.package_usage_id }} - - - {{ formatDataSize(dailyRecordsData.total_usage_mb) }} - - - - - - 筛选方式: - - 全部 - 按日 - 按月 - - - - - - - - - - - {{ formatDataSize(row.daily_usage_mb) }} - - - - - {{ formatDataSize(row.cumulative_usage_mb) }} - - - - - - - - - - - - - - - - - - - - - - - - 当前代 ({{ orderHistoryData.current_generation.identifier }}) - - - - - - - - {{ row.payment_status_text }} - - - - - ¥{{ (row.total_amount / 100).toFixed(2) }} - - - - - {{ row.paid_at ? formatDateTime(row.paid_at) : '-' }} - - - - - - {{ item.package_name }} × {{ item.quantity }} - - - - - - {{ formatDateTime(row.created_at) }} - - - - - - - - - - - - - - - 第{{ gen.generation }}代 ({{ gen.identifier }}) - - 换货单号: {{ gen.exchange_no }} - - - 换货时间: {{ formatDateTime(gen.exchanged_at) }} - - - - - - - - - {{ row.payment_status_text }} - - - - - ¥{{ (row.total_amount / 100).toFixed(2) }} - - - - - {{ row.paid_at ? formatDateTime(row.paid_at) : '-' }} - - - - - - {{ item.package_name }} × {{ item.quantity }} - - - - - - {{ formatDateTime(row.created_at) }} - - - - - - - 换货链超过10代,仅显示最近10代的订单记录 - - - - - - - + :package-usage-id="selectedPackageUsageId" + /> +