diff --git a/src/api/modules/device.ts b/src/api/modules/device.ts index ff7ba2a..e5b3df3 100644 --- a/src/api/modules/device.ts +++ b/src/api/modules/device.ts @@ -233,4 +233,19 @@ export class DeviceService extends BaseService { data ) } + + /** + * 设置切卡模式 + * @param identifier 设备标识(虚拟号 / IMEI / SN) + * @param switchMode 切卡模式(0=自动切卡,1=手动切卡) + */ + static setSwitchMode( + identifier: string, + switchMode: number + ): Promise> { + return this.post>( + `/api/admin/devices/by-identifier/${identifier}/switch-mode`, + { switch_mode: switchMode } + ) + } } diff --git a/src/api/modules/index.ts b/src/api/modules/index.ts index 66115ed..bdca63d 100644 --- a/src/api/modules/index.ts +++ b/src/api/modules/index.ts @@ -36,6 +36,7 @@ export { PollingConcurrencyService } from './pollingConcurrency' export { PollingConfigService } from './pollingConfig' export { ManualTriggerService } from './manualTrigger' export { PollingMonitorService } from './pollingMonitor' +export { SuperAdminService } from './superAdmin' // TODO: 按需添加其他业务模块 // export { SettingService } from './setting' diff --git a/src/api/modules/superAdmin.ts b/src/api/modules/superAdmin.ts new file mode 100644 index 0000000..7d2b642 --- /dev/null +++ b/src/api/modules/superAdmin.ts @@ -0,0 +1,36 @@ +/** + * 超级管理员操作密码 API + */ + +import { BaseService } from '../BaseService' +import type { BaseResponse } from '@/types/api' + +interface OperationPasswordStatusResponse { + is_set: boolean +} + +interface SetOperationPasswordParams { + password: string + confirm_password: string +} + +export class SuperAdminService extends BaseService { + /** + * 查询操作密码是否已设置 + * GET /api/admin/super-admin/operation-password/status + */ + static getOperationPasswordStatus(): Promise> { + return this.get>( + '/api/admin/super-admin/operation-password/status' + ) + } + + /** + * 设置/重置操作密码 + * POST /api/admin/super-admin/operation-password + * @param params 包含 password 和 confirm_password + */ + static setOperationPassword(params: SetOperationPasswordParams): Promise { + return this.post('/api/admin/super-admin/operation-password', params) + } +} diff --git a/src/locales/langs/zh.json b/src/locales/langs/zh.json index faa5c93..3f60b0e 100644 --- a/src/locales/langs/zh.json +++ b/src/locales/langs/zh.json @@ -402,7 +402,8 @@ "title": "设置管理", "wechatPayConfiguration": "微信配置", "detailsOfPaymentConfiguration": "支付配置详情", - "withdrawalSettings": "提现配置" + "withdrawalSettings": "提现配置", + "passwordSettings": "密码设置" } }, "table": { diff --git a/src/router/routes/asyncRoutes.ts b/src/router/routes/asyncRoutes.ts index 6ce165e..71d0752 100644 --- a/src/router/routes/asyncRoutes.ts +++ b/src/router/routes/asyncRoutes.ts @@ -609,6 +609,17 @@ export const asyncRoutes: AppRouteRecord[] = [ isHide: true, keepAlive: false } + }, + // 操作密码设置 + { + path: 'operation-password', + name: 'OperationPasswordSettings', + component: RoutesAlias.OperationPasswordSettings, + meta: { + title: 'menus.settings.passwordSettings', + keepAlive: true, + roles: ['R_SUPER'] + } } ] }, diff --git a/src/router/routesAlias.ts b/src/router/routesAlias.ts index c4c1128..7ceae5d 100644 --- a/src/router/routesAlias.ts +++ b/src/router/routesAlias.ts @@ -79,6 +79,7 @@ export enum RoutesAlias { WithdrawalSettings = '/settings/withdrawal-settings', // 提现配置 WechatConfig = '/settings/wechat-config', // 微信配置 WechatConfigDetail = '/settings/wechat-config/detail', // 微信支付配置详情 + OperationPasswordSettings = '/settings/operation-password', // 操作密码设置 // 轮询管理 DataCleanup = '/polling-management/data-cleanup', // 数据清理 diff --git a/src/types/api/agentRecharge.ts b/src/types/api/agentRecharge.ts index 6e462d8..b49ea9f 100644 --- a/src/types/api/agentRecharge.ts +++ b/src/types/api/agentRecharge.ts @@ -31,6 +31,7 @@ export interface AgentRecharge { payment_channel: AgentRechargePaymentChannel payment_config_id: number | null payment_transaction_id: string + payment_voucher_key?: string // 凭证 file_key(仅 offline 支付时有值) created_at: string paid_at: string | null completed_at: string | null @@ -62,6 +63,7 @@ export interface CreateAgentRechargeRequest { amount: number // 充值金额(单位:分),范围 10000 ~ 100000000 payment_method: AgentRechargePaymentMethod shop_id: number + payment_voucher_key?: string // 线下支付凭证(payment_method=offline 时必填) } // 确认线下充值请求 diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 74fcce1..3761f1e 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -74,6 +74,7 @@ declare module 'vue' { BoxStyleSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue')['default'] CardOperationDialog: typeof import('./../components/business/CardOperationDialog.vue')['default'] CardStatusTag: typeof import('./../components/business/CardStatusTag.vue')['default'] + ChunkErrorBoundary: typeof import('./../components/core/others/ChunkErrorBoundary.vue')['default'] CodeGeneratorButton: typeof import('./../components/business/CodeGeneratorButton.vue')['default'] ColorSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/ColorSettings.vue')['default'] CommentItem: typeof import('./../components/custom/comment-widget/widget/CommentItem.vue')['default'] diff --git a/src/views/asset-management/asset-information/components/BasicInfoCard.vue b/src/views/asset-management/asset-information/components/BasicInfoCard.vue index d23e40a..cb9032b 100644 --- a/src/views/asset-management/asset-information/components/BasicInfoCard.vue +++ b/src/views/asset-management/asset-information/components/BasicInfoCard.vue @@ -379,6 +379,7 @@
+ 设置切卡模式 重启设备 恢复出厂 切换SIM卡 @@ -506,6 +507,7 @@ (e: 'resetDevice'): void (e: 'showSpeedLimit'): void (e: 'showSwitchCard'): void + (e: 'showSwitchMode'): void (e: 'show-set-wifi'): void (e: 'enableBindingCard', payload: { card: BindingCard }): void (e: 'disableBindingCard', payload: { card: BindingCard }): void @@ -600,6 +602,10 @@ emit('showSwitchCard') } + const handleShowSwitchMode = () => { + emit('showSwitchMode') + } + const handleShowSetWiFi = () => { emit('show-set-wifi') } diff --git a/src/views/asset-management/asset-information/components/dialogs/SwitchModeDialog.vue b/src/views/asset-management/asset-information/components/dialogs/SwitchModeDialog.vue new file mode 100644 index 0000000..08c82fe --- /dev/null +++ b/src/views/asset-management/asset-information/components/dialogs/SwitchModeDialog.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/views/asset-management/asset-information/components/dialogs/index.ts b/src/views/asset-management/asset-information/components/dialogs/index.ts index 1bd3cef..4dea605 100644 --- a/src/views/asset-management/asset-information/components/dialogs/index.ts +++ b/src/views/asset-management/asset-information/components/dialogs/index.ts @@ -1,5 +1,6 @@ export { default as SpeedLimitDialog } from './SpeedLimitDialog.vue' export { default as SwitchCardDialog } from './SwitchCardDialog.vue' +export { default as SwitchModeDialog } from './SwitchModeDialog.vue' export { default as WiFiConfigDialog } from './WiFiConfigDialog.vue' export { default as PackageRechargeDialog } from './PackageRechargeDialog.vue' export { default as DailyRecordsDialog } from './DailyRecordsDialog.vue' diff --git a/src/views/asset-management/asset-information/index.vue b/src/views/asset-management/asset-information/index.vue index 31523e5..8fec977 100644 --- a/src/views/asset-management/asset-information/index.vue +++ b/src/views/asset-management/asset-information/index.vue @@ -23,6 +23,7 @@ @reboot-device="handleRebootDevice" @reset-device="handleResetDevice" @show-switch-card="showSwitchCardDialog" + @show-switch-mode="showSwitchModeDialog" @show-set-wifi="showSetWiFiDialog" @enable-binding-card="handleEnableBindingCard" @disable-binding-card="handleDisableBindingCard" @@ -88,6 +89,12 @@ :cards="cardInfo?.cards" @confirm="handleConfirmSwitchCard" /> + { + switchModeDialogVisible.value = true + } + + /** + * 确认切换模式 + */ + const handleConfirmSwitchMode = async (form: { switch_mode: number }) => { + try { + const { DeviceService } = await import('@/api/modules') + const identifier = cardInfo.value?.virtual_no || '' + const res = await DeviceService.setSwitchMode(identifier, form.switch_mode) + if (res.code === 0) { + ElMessage.success('设置切卡模式成功') + switchModeDialogVisible.value = false + await refreshAsset(cardInfo.value!.identifier, cardInfo.value!.asset_type) + } else { + ElMessage.error(res.msg || '设置切卡模式失败') + } + } catch (error: any) { + console.error('设置切卡模式失败:', error) + } + } + /** * 显示WiFi配置对话框 */ diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index 2ac5397..f418e26 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -554,6 +554,44 @@ + + + + + + {{ currentOperatingDeviceIdentifier }} + + + + {{ currentDeviceSwitchMode === 0 ? '自动切卡' : '手动切卡' }} + + + + + 自动切卡 + 手动切卡 + + + +
+

说明:

+

自动切卡:设备根据信号强度自动切换到最优的SIM卡

+

手动切卡:需要手动触发切换SIM卡操作

+
+
+
+ +
@@ -704,6 +742,19 @@ ] }) + // 切换模式相关 + const switchModeDialogVisible = ref(false) + const switchModeLoading = ref(false) + const switchModeFormRef = ref() + const currentOperatingDeviceIdentifier = ref('') + const currentDeviceSwitchMode = ref(0) + const switchModeForm = reactive({ + switch_mode: 0 + }) + const switchModeRules = reactive({ + switch_mode: [{ required: true, message: '请选择切卡模式', trigger: 'change' }] + }) + // 搜索表单初始值 const initialSearchState = { virtual_no: '', @@ -1547,6 +1598,9 @@ case 'clear-series': await handleClearDeviceSeries(deviceNo) break + case 'switch-mode': + showSwitchModeDialog(deviceNo, device?.switch_mode) + break } } @@ -1758,6 +1812,42 @@ }) } + // 显示切换模式对话框 + const showSwitchModeDialog = (deviceIdentifier: string, currentMode?: number) => { + currentOperatingDeviceIdentifier.value = deviceIdentifier + currentDeviceSwitchMode.value = currentMode ?? 0 + switchModeForm.switch_mode = currentMode ?? 0 + switchModeDialogVisible.value = true + } + + // 确认切换模式 + const handleConfirmSwitchMode = async () => { + if (!switchModeFormRef.value) return + + await switchModeFormRef.value.validate(async (valid) => { + if (valid) { + switchModeLoading.value = true + try { + const res = await DeviceService.setSwitchMode( + currentOperatingDeviceIdentifier.value, + switchModeForm.switch_mode + ) + if (res.code === 0) { + ElMessage.success('设置切卡模式成功') + switchModeDialogVisible.value = false + await getTableData() + } else { + ElMessage.error(res.msg || '设置切卡模式失败') + } + } catch (error: any) { + console.error('设置切卡模式失败:', error) + } finally { + switchModeLoading.value = false + } + } + }) + } + // 设备操作菜单项配置 // 表格操作列配置 const getActions = (row: Device) => { @@ -1783,6 +1873,14 @@ }) } + if (hasAuth('device:switch_mode')) { + moreActions.push({ + label: '设置切卡模式', + handler: () => handleDeviceOperation('switch-mode', row.virtual_no, row), + type: 'primary' + }) + } + if (hasAuth('device:reboot')) { moreActions.push({ label: '重启设备', diff --git a/src/views/finance/agent-recharge/index.vue b/src/views/finance/agent-recharge/index.vue index 027879d..5e17076 100644 --- a/src/views/finance/agent-recharge/index.vue +++ b/src/views/finance/agent-recharge/index.vue @@ -75,14 +75,7 @@ placeholder="请选择支付方式" style="width: 100%" > - - - - + @@ -97,6 +90,26 @@ @change="handleShopChange" /> + + + + + +