From b510b4539f36c2310afbf977fae07d492de5e016 Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Wed, 8 Apr 2026 10:19:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 15 + src/api/modules/asset.ts | 157 ++-- src/api/modules/device.ts | 39 +- src/api/modules/exchange.ts | 4 +- src/assets/styles/el-ui.scss | 13 - src/components/core/tables/ArtTable.vue | 113 ++- src/components/device/SpeedLimitDialog.vue | 4 +- src/components/device/SwitchCardDialog.vue | 2 +- src/composables/useLogin.ts | 6 + src/config/constants/enableStatus.ts | 15 + src/config/constants/index.ts | 6 + src/config/constants/package.ts | 8 + src/config/constants/roleTypes.ts | 25 + src/locales/langs/en.json | 21 +- src/locales/langs/zh.json | 21 +- src/router/routes/asyncRoutes.ts | 52 +- src/router/routesAlias.ts | 149 ++-- src/types/api/asset.ts | 63 ++ src/types/api/device.ts | 3 + src/types/api/order.ts | 11 +- src/types/auto-imports.d.ts | 13 - src/types/common/index.ts | 2 +- src/types/components.d.ts | 20 - src/utils/constants/regionData.ts | 226 +----- .../account-management/account/index.vue | 125 +-- .../enterprise-customer/index.vue | 179 ++--- .../asset-information}/index.vue | 721 ++++++++++++++++-- .../asset-management/device-list/index.vue | 210 +++-- .../enterprise-devices/index.vue | 11 +- .../exchange-management/index.vue | 218 +++++- .../iot-card-management/index.vue | 345 ++++----- .../asset-management/iot-card-task/index.vue | 10 +- src/views/batch/device-import/index.vue | 8 +- src/views/batch/sim-import/index.vue | 10 +- src/views/common/account-list.vue | 7 +- .../commission/agent-commission/index.vue | 2 +- src/views/finance/refund/index.vue | 24 +- .../order-management/order-list/index.vue | 90 +-- .../package-list/detail.vue | 11 +- .../package-management/package-list/index.vue | 225 +++--- .../package-series/detail.vue | 9 +- .../package-series/index.vue | 149 +--- .../series-grants/detail.vue | 6 +- .../series-grants/index.vue | 125 +-- .../polling-management/data-cleanup/index.vue | 2 +- .../shop => shop-management/list}/index.vue | 188 ++--- .../carrier-management/index.vue | 113 +-- src/views/system/menu/index.vue | 661 ---------------- src/views/system/nested/menu1/index.vue | 5 - src/views/system/nested/menu2/index.vue | 5 - src/views/system/nested/menu3/index.vue | 5 - .../system/nested/menu3/menu3-2/index.vue | 5 - src/views/system/permission/index.vue | 115 +-- src/views/system/role/index.vue | 313 ++++---- 55 files changed, 2274 insertions(+), 2612 deletions(-) create mode 100644 src/config/constants/enableStatus.ts create mode 100644 src/config/constants/roleTypes.ts rename src/views/{my-simcard/single-card => asset-management/asset-information}/index.vue (82%) rename src/views/{product/shop => shop-management/list}/index.vue (89%) rename src/views/{finance => system}/carrier-management/index.vue (88%) delete mode 100644 src/views/system/menu/index.vue delete mode 100644 src/views/system/nested/menu1/index.vue delete mode 100644 src/views/system/nested/menu2/index.vue delete mode 100644 src/views/system/nested/menu3/index.vue delete mode 100644 src/views/system/nested/menu3/menu3-2/index.vue diff --git a/package.json b/package.json index b3b8a32..9d4c8c4 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "axios": "1.13.5", "crypto-js": "^4.2.0", "echarts": "^5.4.0", + "element-china-area-data": "^6.1.0", "element-plus": "2.11.3", "file-saver": "^2.0.5", "highlight.js": "^11.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 662806d..825ea9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: echarts: specifier: ^5.4.0 version: 5.5.1 + element-china-area-data: + specifier: ^6.1.0 + version: 6.1.0 element-plus: specifier: 2.11.3 version: 2.11.3(vue@3.5.15(typescript@5.6.3)) @@ -1638,6 +1641,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + china-division@2.7.0: + resolution: {integrity: sha512-4uUPAT+1WfqDh5jytq7omdCmHNk3j+k76zEG/2IqaGcYB90c2SwcixttcypdsZ3T/9tN1TTpBDoeZn+Yw/qBEA==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2056,6 +2062,9 @@ packages: echarts@5.5.1: resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==} + element-china-area-data@6.1.0: + resolution: {integrity: sha512-IkpcjwQv2A/2AxFiSoaISZ+oMw1rZCPUSOg5sOCwT5jKc96TaawmKZeY81xfxXsO0QbKxU5LLc6AirhG52hUmg==} + element-plus@2.11.3: resolution: {integrity: sha512-769xsjLR4B9Vf9cl5PDXnwTEdmFJvMgAkYtthdJKPhjVjU3hdAwTJ+gXKiO+PUyo2KWFwOYKZd4Ywh6PHfkbJg==} peerDependencies: @@ -6519,6 +6528,8 @@ snapshots: chardet@0.7.0: {} + china-division@2.7.0: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -6987,6 +6998,10 @@ snapshots: tslib: 2.3.0 zrender: 5.6.0 + element-china-area-data@6.1.0: + dependencies: + china-division: 2.7.0 + element-plus@2.11.3(vue@3.5.15(typescript@5.6.3)): dependencies: '@ctrl/tinycolor': 3.6.1 diff --git a/src/api/modules/asset.ts b/src/api/modules/asset.ts index bc12c83..7f3eff8 100644 --- a/src/api/modules/asset.ts +++ b/src/api/modules/asset.ts @@ -6,21 +6,18 @@ import { BaseService } from '../BaseService' import type { BaseResponse, - AssetType, AssetResolveResponse, AssetRealtimeStatusResponse, AssetRefreshResponse, - AssetPackageUsageRecord, AssetPackageListResponse, AssetPackageParams, AssetCurrentPackageResponse, DeviceStopResponse, - DeviceStartResponse, - CardStopResponse, - CardStartResponse, AssetWalletTransactionListResponse, AssetWalletTransactionParams, - AssetWalletResponse + AssetWalletResponse, + AssetOrdersParams, + AssetOrdersResponse } from '@/types/api' export class AssetService extends BaseService { @@ -36,148 +33,150 @@ export class AssetService extends BaseService { /** * 读取资产实时状态(直接读 DB/Redis,不调网关) - * GET /api/admin/assets/:asset_type/:id/realtime-status - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * GET /api/admin/assets/:identifier/realtime-status + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static getRealtimeStatus( - assetType: AssetType, - id: number - ): Promise> { + static getRealtimeStatus(identifier: string): Promise> { return this.getOne( - `/api/admin/assets/${assetType}/${id}/realtime-status` + `/api/admin/assets/${identifier}/realtime-status` ) } /** * 主动调网关拉取最新数据后返回 - * POST /api/admin/assets/:asset_type/:id/refresh + * POST /api/admin/assets/:identifier/refresh * 注意:设备有 30 秒冷却期,冷却中调用返回 429 - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static refreshAsset( - assetType: AssetType, - id: number - ): Promise> { + static refreshAsset(identifier: string): Promise> { return this.post>( - `/api/admin/assets/${assetType}/${id}/refresh`, + `/api/admin/assets/${identifier}/refresh`, {} ) } /** * 查询该资产所有套餐记录,含虚流量换算字段(分页) - * GET /api/admin/assets/:asset_type/:id/packages?page=1&page_size=50 - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * GET /api/admin/assets/:identifier/packages?page=1&page_size=50 + * @param identifier 资产标识符(ICCID 或 VirtualNo) * @param params 查询参数(可选分页参数) */ static getAssetPackages( - assetType: AssetType, - id: number, + identifier: string, params?: AssetPackageParams ): Promise> { return this.get>( - `/api/admin/assets/${assetType}/${id}/packages`, + `/api/admin/assets/${identifier}/packages`, params ) } /** * 查询当前生效中的主套餐 - * GET /api/admin/assets/:asset_type/:id/current-package + * GET /api/admin/assets/:identifier/current-package * 无生效套餐时返回 404 - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static getCurrentPackage( - assetType: AssetType, - id: number - ): Promise> { + static getCurrentPackage(identifier: string): Promise> { return this.getOne( - `/api/admin/assets/${assetType}/${id}/current-package` + `/api/admin/assets/${identifier}/current-package` ) } - // ========== 设备停复机操作 ========== + // ========== 资产停复机操作(统一接口)========== /** - * 批量停机设备下所有已实名卡 - * POST /api/admin/assets/device/:device_id/stop + * 停机资产(卡或设备) + * POST /api/admin/assets/:identifier/stop * 停机成功后设置 1 小时停机保护期(保护期内禁止复机) - * @param deviceId 设备ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static stopDevice(deviceId: number): Promise> { - return this.post>( - `/api/admin/assets/device/${deviceId}/stop`, + static stopAsset(identifier: string): Promise> { + return this.post>( + `/api/admin/assets/${identifier}/stop`, {} ) } /** - * 批量复机设备下所有已实名卡 - * POST /api/admin/assets/device/:device_id/start + * 复机资产(卡或设备) + * POST /api/admin/assets/:identifier/start * 复机成功后设置 1 小时复机保护期(保护期内禁止停机) - * @param deviceId 设备ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static startDevice(deviceId: number): Promise> { - return this.post>(`/api/admin/assets/device/${deviceId}/start`, {}) + static startAsset(identifier: string): Promise> { + return this.post>(`/api/admin/assets/${identifier}/start`, {}) } - // ========== 单卡停复机操作 ========== + // ========== 资产停用 ========== /** - * 手动停机单张卡(通过 ICCID) - * POST /api/admin/assets/card/:iccid/stop - * 若卡绑定的设备在复机保护期内,返回 403 - * @param iccid ICCID + * 手动停用资产(卡或设备) + * PATCH /api/admin/assets/:identifier/deactivate + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static stopCard(iccid: string): Promise> { - return this.post>(`/api/admin/assets/card/${iccid}/stop`, {}) - } - - /** - * 手动复机单张卡(通过 ICCID) - * POST /api/admin/assets/card/:iccid/start - * 若卡绑定的设备在停机保护期内,返回 403 - * @param iccid ICCID - */ - static startCard(iccid: string): Promise> { - return this.post>(`/api/admin/assets/card/${iccid}/start`, {}) + static deactivateAsset(identifier: string): Promise { + return this.patch(`/api/admin/assets/${identifier}/deactivate`, {}) } // ========== 钱包查询 ========== /** * 查询指定卡或设备的钱包余额概况 - * GET /api/admin/assets/:asset_type/:id/wallet + * GET /api/admin/assets/:identifier/wallet * 企业账号禁止调用 - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) */ - static getAssetWallet( - assetType: AssetType, - id: number - ): Promise> { - return this.getOne(`/api/admin/assets/${assetType}/${id}/wallet`) + static getAssetWallet(identifier: string): Promise> { + return this.getOne(`/api/admin/assets/${identifier}/wallet`) } /** * 分页查询指定资产的钱包收支流水 - * GET /api/admin/assets/:asset_type/:id/wallet/transactions + * GET /api/admin/assets/:identifier/wallet/transactions * 企业账号禁止调用 - * @param assetType 资产类型 (card 或 device) - * @param id 资产ID + * @param identifier 资产标识符(ICCID 或 VirtualNo) * @param params 查询参数 */ static getWalletTransactions( - assetType: AssetType, - id: number, + identifier: string, params?: AssetWalletTransactionParams ): Promise> { return this.get>( - `/api/admin/assets/${assetType}/${id}/wallet/transactions`, + `/api/admin/assets/${identifier}/wallet/transactions`, + params + ) + } + + // ========== 轮询状态更新 ========== + + /** + * 更新资产轮询状态 + * PATCH /api/admin/assets/:identifier/polling-status + * @param identifier 资产标识符(ICCID 或 VirtualNo) + * @param data 轮询状态数据 + */ + static updatePollingStatus( + identifier: string, + data: { enable_polling: boolean } + ): Promise { + return this.patch(`/api/admin/assets/${identifier}/polling-status`, data) + } + + // ========== 资产历史订单 ========== + + /** + * 查询资产历史订单 + * GET /api/admin/assets/:identifier/orders + * @param identifier 资产标识符(ICCID 或 VirtualNo) + * @param params 查询参数 + */ + static getAssetOrders( + identifier: string, + params?: AssetOrdersParams + ): Promise> { + return this.get>( + `/api/admin/assets/${identifier}/orders`, params ) } diff --git a/src/api/modules/device.ts b/src/api/modules/device.ts index 5d0f2da..ff7ba2a 100644 --- a/src/api/modules/device.ts +++ b/src/api/modules/device.ts @@ -56,45 +56,48 @@ export class DeviceService extends BaseService { /** * 删除设备 - * @param id 设备ID + * @param virtualNo 设备虚拟号 */ - static deleteDevice(id: number): Promise { - return this.remove(`/api/admin/devices/${id}`) + static deleteDevice(virtualNo: string): Promise { + return this.remove(`/api/admin/devices/${virtualNo}`) } // ========== 设备卡绑定管理 ========== /** * 获取设备绑定的卡列表 - * @param id 设备ID + * @param virtualNo 设备虚拟号 */ - static getDeviceCards(id: number): Promise> { - return this.getOne(`/api/admin/devices/${id}/cards`) + static getDeviceCards(virtualNo: string): Promise> { + return this.getOne(`/api/admin/devices/${virtualNo}/cards`) } /** * 绑定卡到设备 - * @param id 设备ID + * @param virtualNo 设备虚拟号 * @param data 绑定参数 */ static bindCard( - id: number, + virtualNo: string, data: BindCardToDeviceRequest ): Promise> { - return this.post>(`/api/admin/devices/${id}/cards`, data) + return this.post>( + `/api/admin/devices/${virtualNo}/cards`, + data + ) } /** * 解绑设备上的卡 - * @param deviceId 设备ID - * @param cardId IoT卡ID + * @param virtualNo 设备虚拟号 + * @param iccid 卡ICCID */ static unbindCard( - deviceId: number, - cardId: number + virtualNo: string, + iccid: string ): Promise> { return this.delete>( - `/api/admin/devices/${deviceId}/cards/${cardId}` + `/api/admin/devices/${virtualNo}/cards/${iccid}` ) } @@ -230,12 +233,4 @@ export class DeviceService extends BaseService { data ) } - - /** - * 手动停用设备 - * @param id 设备ID - */ - static deactivateDevice(id: number): Promise { - return this.patch(`/api/admin/devices/${id}/deactivate`, {}) - } } diff --git a/src/api/modules/exchange.ts b/src/api/modules/exchange.ts index 15f7c79..cabe1e8 100644 --- a/src/api/modules/exchange.ts +++ b/src/api/modules/exchange.ts @@ -66,10 +66,10 @@ export class ExchangeService extends BaseService { static getExchanges( params?: ExchangeQueryParams ): Promise< - BaseResponse<{ list: ExchangeResponse[]; page: number; page_size: number; total: number }> + BaseResponse<{ items: ExchangeResponse[]; page: number; page_size: number; total: number }> > { return this.get< - BaseResponse<{ list: ExchangeResponse[]; page: number; page_size: number; total: number }> + BaseResponse<{ items: ExchangeResponse[]; page: number; page_size: number; total: number }> >('/api/admin/exchanges', params) } diff --git a/src/assets/styles/el-ui.scss b/src/assets/styles/el-ui.scss index 4300a6b..85bb447 100644 --- a/src/assets/styles/el-ui.scss +++ b/src/assets/styles/el-ui.scss @@ -279,19 +279,6 @@ background-color: #fff !important; transform: scale(0.6) !important; } - - &::after { - width: 4px; - height: 8px; - left: 0; - right: 0; - top: 0; - bottom: 4px; - margin: auto; - border: 2px solid var(--el-checkbox-checked-icon-color); - border-left: 0; - border-top: 0; - } } } diff --git a/src/components/core/tables/ArtTable.vue b/src/components/core/tables/ArtTable.vue index c798c6d..5165fb9 100644 --- a/src/components/core/tables/ArtTable.vue +++ b/src/components/core/tables/ArtTable.vue @@ -47,6 +47,21 @@ + + + + + + diff --git a/src/views/my-simcard/single-card/index.vue b/src/views/asset-management/asset-information/index.vue similarity index 82% rename from src/views/my-simcard/single-card/index.vue rename to src/views/asset-management/asset-information/index.vue index bbe6092..58207d2 100644 --- a/src/views/my-simcard/single-card/index.vue +++ b/src/views/asset-management/asset-information/index.vue @@ -60,10 +60,31 @@ @@ -222,17 +243,17 @@ > - + - + - + - + + + + + + + {{ cardInfo?.asset_type === 'card' ? cardInfo?.iccid : cardInfo?.imei }} ({{ + cardInfo?.virtual_no + }}) + + + + + +
+ {{ pkg.package_name }} + + ¥{{ (pkg.cost_price / 100).toFixed(2) }} + +
+
+
+
+ + + + + + +
+ + +
+
+
+ +
+ + + + +
+ +
+
+ +
+

+ 当前代 ({{ orderHistoryData.current_generation.identifier }}) +

+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+
+

+ 第{{ gen.generation }}代 ({{ gen.identifier }}) + + 换货单号: {{ gen.exchange_no }} + + + 换货时间: {{ formatDateTime(gen.exchanged_at) }} + +

+ + + + + + + + + + + + + + + + + + + + +
+ + + 换货链超过10代,仅显示最近10代的订单记录 + +
+
+
+ +
+
@@ -1220,18 +1462,29 @@ import { Loading, Search, Refresh } from '@element-plus/icons-vue' import { useRoute, useRouter } from 'vue-router' import { EnterpriseService } from '@/api/modules/enterprise' - import { CardService, AssetService, DeviceService } from '@/api/modules' + import { + CardService, + AssetService, + DeviceService, + OrderService, + PackageManageService + } from '@/api/modules' import { formatDateTime } from '@/utils/business/format' import type { AssetWalletTransactionItem, TransactionType, - AssetWalletResponse + AssetWalletResponse, + CreateOrderRequest, + PackageResponse, + PurchaseCheckRequest } from '@/types/api' + import { useUserStore } from '@/store/modules/user' defineOptions({ name: 'SingleCard' }) const route = useRoute() const router = useRouter() + const userStore = useUserStore() const loading = ref(false) const operationLoading = ref(false) const refreshLoading = ref(false) @@ -1243,6 +1496,10 @@ const disableCardLoading = ref(false) const manualDeactivateCardLoading = ref(false) + // 轮询状态 + const pollingEnabled = ref(false) + const pollingLoading = ref(false) + // 设备操作loading const rebootDeviceLoading = ref(false) const resetDeviceLoading = ref(false) @@ -1295,6 +1552,21 @@ ] }) + // 套餐充值相关 + const rechargeDialogVisible = ref(false) + const rechargeLoading = ref(false) + const rechargeFormRef = ref() + const rechargeForm = reactive({ + package_ids: [] as number[], + payment_method: 'wallet' as 'wallet' | 'offline' + }) + const rechargeRules: FormRules = { + package_ids: [{ required: true, message: '请选择套餐', trigger: 'change' }], + payment_method: [{ required: true, message: '请选择支付方式', trigger: 'change' }] + } + const packageOptions = ref([]) + const packageSearchLoading = ref(false) + // 流量详单相关 const dailyRecordsDialogVisible = ref(false) const dailyRecordsLoading = ref(false) @@ -1304,6 +1576,15 @@ const selectedMonth = ref('') const filteredRecords = ref([]) + // 往期订单相关 + const orderHistoryDialogVisible = ref(false) + const orderHistoryLoading = ref(false) + const orderHistoryData = ref(null) + const orderHistoryPagination = reactive({ + page: 1, + page_size: 20 + }) + // ICCID搜索相关 const searchIccid = ref('') const iccidInputFocused = ref(false) @@ -1443,15 +1724,19 @@ packageList: [] // 套餐列表需要单独调用API获取 } - // 如果有asset_id和asset_type,并行加载其他数据 - if (data.asset_type && data.asset_id) { + // 设置轮询状态 + pollingEnabled.value = data.enable_polling || false + + // 获取资产标识符并加载其他数据 + const identifier = data.asset_type === 'card' ? data.iccid : data.virtual_no + if (identifier) { // 并行调用5个接口: 当前生效套餐、套餐列表、实时状态、钱包概况、钱包流水 Promise.all([ - loadCurrentPackage(data.asset_type, data.asset_id), - loadPackageList(data.asset_type, data.asset_id), - loadRealtimeStatus(data.asset_type, data.asset_id), - loadAssetWallet(data.asset_type, data.asset_id), - loadWalletTransactions(data.asset_type, data.asset_id) + loadCurrentPackage(identifier), + loadPackageList(identifier), + loadRealtimeStatus(identifier, data.asset_type), + loadAssetWallet(identifier), + loadWalletTransactions(identifier) ]) } } else { @@ -1474,9 +1759,9 @@ } // 加载套餐列表 - const loadPackageList = async (assetType: string, assetId: number) => { + const loadPackageList = async (identifier: string) => { try { - const response = await AssetService.getAssetPackages(assetType, assetId, { + const response = await AssetService.getAssetPackages(identifier, { page: 1, page_size: 50 }) @@ -1490,12 +1775,12 @@ } // 加载当前生效套餐 - const loadCurrentPackage = async (assetType: string, assetId: number) => { + const loadCurrentPackage = async (identifier: string) => { try { // 清空之前的错误信息 currentPackageErrorMsg.value = '' - const response = await AssetService.getCurrentPackage(assetType, assetId) + const response = await AssetService.getCurrentPackage(identifier) if (response.code === 0) { if (response.data) { const pkg = response.data @@ -1549,9 +1834,9 @@ } // 加载实时状态 - const loadRealtimeStatus = async (assetType: string, assetId: number) => { + const loadRealtimeStatus = async (identifier: string, assetType: string) => { try { - const response = await AssetService.getRealtimeStatus(assetType, assetId) + const response = await AssetService.getRealtimeStatus(identifier) if (response.code === 0 && response.data) { const data = response.data // 更新实时状态数据 @@ -1605,9 +1890,9 @@ } // 加载钱包概况 - const loadAssetWallet = async (assetType: string, assetId: number) => { + const loadAssetWallet = async (identifier: string) => { try { - const response = await AssetService.getAssetWallet(assetType, assetId) + const response = await AssetService.getAssetWallet(identifier) if (response.code === 0 && response.data) { walletInfo.value = response.data } @@ -1985,12 +2270,14 @@ try { refreshLoading.value = true - await AssetService.refreshAsset(cardInfo.value.asset_type, cardInfo.value.asset_id) + const identifier = + cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no + await AssetService.refreshAsset(identifier) ElMessage.success('刷新成功') // 刷新成功后重新加载实时状态数据 - if (cardInfo.value.asset_type && cardInfo.value.asset_id) { - await loadRealtimeStatus(cardInfo.value.asset_type, cardInfo.value.asset_id) + if (identifier) { + await loadRealtimeStatus(identifier, cardInfo.value.asset_type) } } catch (error: any) { // 检查429错误(冷却期) @@ -2004,6 +2291,45 @@ } } + // 处理轮询状态变更 + const handlePollingStatusChange = async (value: boolean) => { + if (!cardInfo.value) { + ElMessage.warning('无法更新轮询状态:缺少资产信息') + // 恢复原状态 + pollingEnabled.value = !value + return + } + + 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: value + }) + + if (response.code === 0) { + ElMessage.success(value ? '已开启自动轮询' : '已关闭自动轮询') + // 更新本地状态 + if (cardInfo.value) { + cardInfo.value.enable_polling = value + } + } else { + ElMessage.error(response.msg || '更新轮询状态失败') + // 恢复原状态 + pollingEnabled.value = !value + } + } catch (error: any) { + console.error('更新轮询状态失败:', error) + ElMessage.error(error?.message || '更新轮询状态失败') + // 恢复原状态 + pollingEnabled.value = !value + } finally { + pollingLoading.value = false + } + } + // 处理操作按钮点击 const handleOperation = async (operation: string) => { if (!cardInfo.value) { @@ -2072,7 +2398,7 @@ ) stopLoading.value = true - await AssetService.stopCard(cardInfo.value.iccid) + await AssetService.stopAsset(cardInfo.value.iccid) ElMessage.success('停机操作成功') // 刷新卡片信息 @@ -2111,7 +2437,7 @@ ) startLoading.value = true - await AssetService.startCard(cardInfo.value.iccid) + await AssetService.startAsset(cardInfo.value.iccid) ElMessage.success('复机操作成功') // 刷新卡片信息 @@ -2150,7 +2476,7 @@ ) stopLoading.value = true - const response = await AssetService.stopDevice(cardInfo.value.asset_id) + const response = await AssetService.stopAsset(cardInfo.value.virtual_no) // 显示停机结果 if (response.code === 0 && response.data) { @@ -2201,7 +2527,7 @@ ) startLoading.value = true - await AssetService.startDevice(cardInfo.value.asset_id) + await AssetService.startAsset(cardInfo.value.virtual_no) ElMessage.success('设备复机操作成功') // 刷新设备信息 @@ -2220,14 +2546,10 @@ // ========== 钱包流水相关函数 ========== // 加载钱包流水列表 - const loadWalletTransactions = async (assetType: string, assetId: number) => { + const loadWalletTransactions = async (identifier: string) => { try { walletLoading.value = true - const response = await AssetService.getWalletTransactions( - assetType, - assetId, - walletQueryParams.value - ) + const response = await AssetService.getWalletTransactions(identifier, walletQueryParams.value) if (response.code === 0 && response.data) { walletTransactionList.value = response.data.list || [] walletTransactionTotal.value = response.data.total || 0 @@ -2263,8 +2585,10 @@ // 处理筛选条件变化 const handleWalletFilterChange = () => { walletQueryParams.value.page = 1 // 重置到第一页 - if (cardInfo.value?.asset_type && cardInfo.value?.asset_id) { - loadWalletTransactions(cardInfo.value.asset_type, cardInfo.value.asset_id) + const identifier = + cardInfo.value?.asset_type === 'card' ? cardInfo.value?.iccid : cardInfo.value?.virtual_no + if (identifier) { + loadWalletTransactions(identifier) } } @@ -2290,8 +2614,10 @@ end_time: null } walletDateRange.value = null - if (cardInfo.value?.asset_type && cardInfo.value?.asset_id) { - loadWalletTransactions(cardInfo.value.asset_type, cardInfo.value.asset_id) + const identifier = + cardInfo.value?.asset_type === 'card' ? cardInfo.value?.iccid : cardInfo.value?.virtual_no + if (identifier) { + loadWalletTransactions(identifier) } } @@ -2299,16 +2625,20 @@ const handleWalletPageSizeChange = (size: number) => { walletQueryParams.value.page_size = size walletQueryParams.value.page = 1 // 重置到第一页 - if (cardInfo.value?.asset_type && cardInfo.value?.asset_id) { - loadWalletTransactions(cardInfo.value.asset_type, cardInfo.value.asset_id) + const identifier = + cardInfo.value?.asset_type === 'card' ? cardInfo.value?.iccid : cardInfo.value?.virtual_no + if (identifier) { + loadWalletTransactions(identifier) } } // 处理页码变化 const handleWalletPageChange = (page: number) => { walletQueryParams.value.page = page - if (cardInfo.value?.asset_type && cardInfo.value?.asset_id) { - loadWalletTransactions(cardInfo.value.asset_type, cardInfo.value.asset_id) + const identifier = + cardInfo.value?.asset_type === 'card' ? cardInfo.value?.iccid : cardInfo.value?.virtual_no + if (identifier) { + loadWalletTransactions(identifier) } } @@ -2335,7 +2665,7 @@ }) enableCardLoading.value = true - const res = await CardService.enableIotCard(cardInfo.value.iccid) + const res = await AssetService.startAsset(cardInfo.value.iccid) if (res.code === 0) { ElMessage.success('启用成功') await handleRefreshAsset() @@ -2358,7 +2688,7 @@ }) disableCardLoading.value = true - const res = await CardService.disableIotCard(cardInfo.value.iccid) + const res = await AssetService.stopAsset(cardInfo.value.iccid) if (res.code === 0) { ElMessage.success('停用成功') await handleRefreshAsset() @@ -2387,7 +2717,7 @@ ) manualDeactivateCardLoading.value = true - const res = await CardService.deactivateIotCard(cardInfo.value.asset_id) + const res = await AssetService.deactivateAsset(cardInfo.value.iccid) if (res.code === 0) { ElMessage.success('手动停用成功') await handleRefreshAsset() @@ -2411,7 +2741,7 @@ type: 'warning' }) - const res = await CardService.enableIotCard(card.iccid) + const res = await AssetService.startAsset(card.iccid) if (res.code === 0) { ElMessage.success('启用成功') await handleRefreshAsset() @@ -2431,7 +2761,7 @@ type: 'warning' }) - const res = await CardService.disableIotCard(card.iccid) + const res = await AssetService.stopAsset(card.iccid) if (res.code === 0) { ElMessage.success('停用成功') await handleRefreshAsset() @@ -2457,7 +2787,7 @@ } ) - const res = await CardService.deactivateIotCard(card.id) + const res = await AssetService.deactivateAsset(card.iccid) if (res.code === 0) { ElMessage.success('手动停用成功') await handleRefreshAsset() @@ -2644,6 +2974,221 @@ }) } + // ========== 套餐充值 ========== + + // 显示套餐充值对话框 + const showRechargeDialog = async () => { + if (!cardInfo.value) { + ElMessage.warning('请先查询资产信息') + return + } + + rechargeDialogVisible.value = true + // 加载可用套餐列表 + await loadRechargePackages() + } + + // 加载可充值套餐列表 + const loadRechargePackages = async () => { + if (!cardInfo.value) return + + try { + packageSearchLoading.value = true + + const response = await PackageManageService.getPackages({ + series_id: cardInfo.value.series_id, + page: 1, + page_size: 20, + status: 1, // 只获取启用的套餐 + shelf_status: 1 // 只获取已上架的套餐 + }) + + if (response.code === 0 && response.data) { + packageOptions.value = response.data.items || [] + } + } catch (error: any) { + console.error('加载套餐列表失败:', error) + ElMessage.error(error?.message || '加载套餐列表失败') + } finally { + packageSearchLoading.value = false + } + } + + // 搜索套餐 + const handleRechargePackageSearch = async (query: string) => { + if (!cardInfo.value) return + + try { + packageSearchLoading.value = true + + const response = await PackageManageService.getPackages({ + package_name: query || undefined, + series_id: cardInfo.value.series_id, + page: 1, + page_size: 20, + status: 1, // 只获取启用的套餐 + shelf_status: 1 // 只获取已上架的套餐 + }) + + if (response.code === 0 && response.data) { + packageOptions.value = response.data.items || [] + } + } catch (error: any) { + console.error('搜索套餐失败:', error) + } finally { + packageSearchLoading.value = false + } + } + + // 提交充值订单 + const handleRechargeOrder = async (formEl: FormInstance | undefined) => { + if (!formEl || !cardInfo.value) return + + await formEl.validate(async (valid) => { + if (valid) { + rechargeLoading.value = true + try { + // 获取订单类型和资源ID + const orderType: 'single_card' | 'device' = + cardInfo.value.asset_type === 'card' ? 'single_card' : 'device' + const resourceId = cardInfo.value.asset_id + const identifier = + cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no + + // 调用套餐购买预检接口 + const checkData: PurchaseCheckRequest = { + order_type: orderType, + resource_id: resourceId, + package_ids: rechargeForm.package_ids + } + + const checkResponse = await OrderService.purchaseCheck(checkData) + + // 检查预检结果 + if (checkResponse.code === 0 && checkResponse.data) { + const checkResult = checkResponse.data + + // 如果需要强充,显示确认对话框 + if (checkResult.need_force_recharge) { + const confirmMessage = `${checkResult.message || '钱包余额不足'}\n\n套餐总价: ¥${(checkResult.total_package_amount! / 100).toFixed(2)}\n需要强充: ¥${(checkResult.force_recharge_amount! / 100).toFixed(2)}\n钱包到账: ¥${(checkResult.wallet_credit! / 100).toFixed(2)}\n实际支付: ¥${(checkResult.actual_payment! / 100).toFixed(2)}\n\n是否继续创建订单?` + + await ElMessageBox.confirm(confirmMessage, '购买预检提示', { + confirmButtonText: '继续创建', + cancelButtonText: '取消', + type: 'warning' + }) + } + } + + // 预检通过或用户确认后,创建订单 + const data: CreateOrderRequest = { + identifier, + package_ids: rechargeForm.package_ids, + payment_method: rechargeForm.payment_method + } + + await OrderService.createOrder(data) + + // 根据支付方式显示不同的成功消息 + if (rechargeForm.payment_method === 'wallet') { + ElMessage.success('订单创建成功,已自动完成支付') + } else { + ElMessage.success('订单创建成功') + } + + rechargeDialogVisible.value = false + formEl.resetFields() + + // 刷新资产信息 + if (searchIccid.value) { + await fetchCardDetailByIccid(searchIccid.value) + } + } catch (error: any) { + // 用户取消确认对话框 + if (error === 'cancel') { + return + } + console.error('创建订单失败:', error) + } finally { + rechargeLoading.value = false + } + } + }) + } + + // 关闭充值对话框 + const handleRechargeDialogClosed = () => { + rechargeFormRef.value?.resetFields() + rechargeForm.package_ids = [] + rechargeForm.payment_method = 'wallet' + packageOptions.value = [] + } + + // ========== 往期订单 ========== + + // 显示往期订单对话框 + const showOrderHistoryDialog = async () => { + if (!cardInfo.value) { + ElMessage.warning('请先查询资产信息') + return + } + + orderHistoryDialogVisible.value = true + orderHistoryPagination.page = 1 + orderHistoryPagination.page_size = 20 + await loadOrderHistory() + } + + // 加载往期订单 + const loadOrderHistory = async () => { + if (!cardInfo.value) return + + try { + orderHistoryLoading.value = true + + // 使用资产标识符(ICCID 或 VirtualNo) + const identifier = + cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no + + const response = await AssetService.getAssetOrders(identifier, { + page: orderHistoryPagination.page, + page_size: orderHistoryPagination.page_size, + include_previous: true // 包含换货前代的订单 + }) + + if (response.code === 0 && response.data) { + orderHistoryData.value = response.data + } + } catch (error: any) { + console.error('加载往期订单失败:', error) + ElMessage.error(error?.message || '加载往期订单失败') + } finally { + orderHistoryLoading.value = false + } + } + + // 分页变化 + const handleOrderHistorySizeChange = (size: number) => { + orderHistoryPagination.page_size = size + loadOrderHistory() + } + + const handleOrderHistoryPageChange = (page: number) => { + orderHistoryPagination.page = page + loadOrderHistory() + } + + // 获取支付状态标签类型 + const getPaymentStatusType = (status: number): 'success' | 'warning' | 'danger' | 'info' => { + const statusMap: Record = { + 1: 'warning', // 待支付 + 2: 'success', // 已支付 + 3: 'danger', // 已取消 + 4: 'info' // 已关闭 + } + return statusMap[status] || 'info' + } + // 手动停用设备 const handleManualDeactivateDevice = async () => { try { @@ -2658,7 +3203,9 @@ ) manualDeactivateDeviceLoading.value = true - const res = await DeviceService.deactivateDevice(cardInfo.value.asset_id) + 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('手动停用成功') await handleRefreshAsset() @@ -2965,10 +3512,53 @@ display: flex; gap: 8px; align-items: center; + justify-content: space-between; font-size: 16px; font-weight: 600; color: var(--el-text-color-primary, #1f2937); + .card-header-left { + display: flex; + gap: 8px; + align-items: center; + } + + .card-header-right { + display: flex; + gap: 12px; + align-items: center; + + .polling-switch-wrapper { + display: flex; + gap: 10px; + align-items: center; + + .polling-label { + font-size: 15px; + font-weight: 500; + color: var(--el-text-color-regular); + } + + :deep(.el-switch) { + height: 26px; + + .el-switch__core { + height: 26px; + min-width: 50px; + + .el-switch__inner { + font-size: 13px; + } + } + + .el-switch__action { + width: 20px; + height: 20px; + } + } + } + } + .header-title { color: var(--el-text-color-primary); } @@ -3439,4 +4029,17 @@ } } } + + // 往期订单样式 + .generation-section { + padding: 16px; + background: var(--el-fill-color-lighter); + border-radius: 8px; + + h3 { + font-size: 16px; + font-weight: 600; + color: var(--el-text-color-primary); + } + } diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index a069081..f98bd37 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -53,13 +53,11 @@ :pageSize="pagination.pageSize" :total="pagination.total" :marginTop="10" - :row-class-name="getRowClassName" + :actions="getActions" + :actionsWidth="180" @size-change="handleSizeChange" @current-change="handleCurrentChange" @selection-change="handleSelectionChange" - @row-contextmenu="handleRowContextMenu" - @cell-mouse-enter="handleCellMouseEnter" - @cell-mouse-leave="handleCellMouseLeave" > - - - -
+
@@ -296,17 +291,17 @@ }} {{ - currentDeviceDetail.device_name || '--' + currentDeviceDetail.device_name || '-' }} {{ - currentDeviceDetail.device_model || '--' + currentDeviceDetail.device_model || '-' }} {{ - currentDeviceDetail.device_type || '--' + currentDeviceDetail.device_type || '-' }} {{ - currentDeviceDetail.manufacturer || '--' + currentDeviceDetail.manufacturer || '-' }} {{ currentDeviceDetail.max_sim_slots @@ -321,11 +316,11 @@ {{ - currentDeviceDetail.batch_no || '--' + currentDeviceDetail.batch_no || '-' }} {{ - formatDateTime(currentDeviceDetail.created_at) || '--' + formatDateTime(currentDeviceDetail.created_at) || '-' }}
@@ -398,7 +393,7 @@ -
+
@@ -406,15 +401,15 @@ - - + +