修复bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m59s

This commit is contained in:
sexygoat
2026-04-09 17:47:32 +08:00
parent 472099bf96
commit c2c1644799
13 changed files with 166 additions and 172 deletions

View File

@@ -19,7 +19,9 @@ import type {
BaseResponse,
PaginationResponse,
ListResponse,
GatewayRealnameLinkResponse
GatewayRealnameLinkResponse,
ImportIotCardRequest,
ImportIotCardResponse
} from '@/types/api'
export class CardService extends BaseService {
@@ -286,15 +288,8 @@ export class CardService extends BaseService {
* 批量导入ICCID新版使用 JSON 格式)
* @param data 导入请求参数
*/
static importIotCards(data: {
carrier_id: number
file_key: string
batch_no?: string
}): Promise<BaseResponse<{ task_id: number; task_no: string; message: string }>> {
return this.post<BaseResponse<{ task_id: number; task_no: string; message: string }>>(
'/api/admin/iot-cards/import',
data
)
static importIotCards(data: ImportIotCardRequest): Promise<BaseResponse<ImportIotCardResponse>> {
return this.post<BaseResponse<ImportIotCardResponse>>('/api/admin/iot-cards/import', data)
}
/**

View File

@@ -93,82 +93,6 @@ export class CommissionService extends BaseService {
)
}
// ==================== 我的佣金(已废弃,仅保留用于向后兼容) ====================
/**
* @deprecated 使用 getShopCommissionRecords 代替
* 获取我的佣金明细
* GET /api/admin/my/commission-records
*/
static getMyCommissionRecords(
params?: CommissionRecordQueryParams
): Promise<BaseResponse<MyCommissionRecordPageResult>> {
return this.get<BaseResponse<MyCommissionRecordPageResult>>(
'/api/admin/my/commission-records',
params
)
}
/**
* @deprecated 使用 getShopFundSummary 代替
* 获取我的佣金概览
* GET /api/admin/my/commission-summary
*/
static getMyCommissionSummary(): Promise<BaseResponse<MyCommissionSummary>> {
return this.get<BaseResponse<MyCommissionSummary>>('/api/admin/my/commission-summary')
}
/**
* @deprecated 使用 getShopWithdrawalRequests 代替
* 获取我的提现记录
* GET /api/admin/my/withdrawal-requests
*/
static getMyWithdrawalRequests(
params?: WithdrawalRequestQueryParams
): Promise<BaseResponse<WithdrawalRequestPageResult>> {
return this.get<BaseResponse<WithdrawalRequestPageResult>>(
'/api/admin/my/withdrawal-requests',
params
)
}
/**
* @deprecated 使用 submitShopWithdrawalRequest 代替
* 发起提现申请
* POST /api/admin/my/withdrawal-requests
*/
static submitWithdrawalRequest(params: SubmitWithdrawalParams): Promise<BaseResponse> {
return this.post<BaseResponse>('/api/admin/my/withdrawal-requests', params)
}
/**
* @deprecated 使用 getShopCommissionStats 代替
* 获取我的佣金统计
* GET /api/admin/my/commission-stats
*/
static getMyCommissionStats(
params?: ShopCommissionStatsQueryParams
): Promise<BaseResponse<CommissionStatsResponse>> {
return this.get<BaseResponse<CommissionStatsResponse>>(
'/api/admin/my/commission-stats',
params
)
}
/**
* @deprecated 使用 getShopDailyCommissionStats 代替
* 获取我的每日佣金统计
* GET /api/admin/my/commission-daily-stats
*/
static getMyDailyCommissionStats(
params?: DailyCommissionStatsQueryParams
): Promise<BaseResponse<DailyCommissionStatsItem[]>> {
return this.get<BaseResponse<DailyCommissionStatsItem[]>>(
'/api/admin/my/commission-daily-stats',
params
)
}
// ==================== 代理商佣金管理 ====================
/**
@@ -212,17 +136,6 @@ export class CommissionService extends BaseService {
)
}
/**
* @deprecated 使用 getShopFundSummary 代替
* 获取代理商佣金汇总列表
* GET /api/admin/shops/commission-summary
*/
static getShopCommissionSummary(
params?: ShopFundSummaryQueryParams
): Promise<BaseResponse<ShopFundSummaryPageResult>> {
return this.getShopFundSummary(params)
}
/**
* 获取代理商佣金统计
* GET /api/admin/shops/{shop_id}/commission-stats

View File

@@ -42,6 +42,12 @@ export enum CardType {
FLOW = 'flow' // 流量卡
}
// 卡业务类型
export enum CardCategory {
NORMAL = 'normal', // 普通卡
INDUSTRY = 'industry' // 行业卡
}
// 号卡商品实体
export interface SimCardProduct {
id: string | number
@@ -107,6 +113,21 @@ export interface CardQueryParams extends PaginationParams {
importTimeRange?: [string, string]
}
// IoT 卡导入请求
export interface ImportIotCardRequest {
carrier_id: number // 运营商ID
file_key: string // 对象存储文件路径
batch_no?: string // 批次号
card_category?: CardCategory // 卡业务类型(新增,可选,默认 normal)
}
// IoT 卡导入响应
export interface ImportIotCardResponse {
task_id: number // 导入任务ID
task_no: string // 任务编号
message: string // 提示信息
}
// 网卡导入批次信息
export interface CardImportBatch extends ImportTask {
importBatch: string // 导入批次号
@@ -118,6 +139,7 @@ export interface CardImportBatch extends ImportTask {
cardCompanyName?: string
operator: Operator
cardType: CardType
cardCategory?: CardCategory // 卡业务类型(新增)
operatorUser?: string // 操作人
}

View File

@@ -151,6 +151,7 @@ export interface CommissionRecordQueryParams extends PaginationParams {
* 我的佣金概览
*/
export interface MyCommissionSummary {
main_balance: number // 预充值余额(分)
total_commission: number // 总佣金(分)
available_commission: number // 可提现佣金(分)
frozen_commission: number // 冻结中佣金(分)

View File

@@ -92,7 +92,7 @@ export interface CreateOrderRequest {
iot_card_id?: number | null // IoT卡ID
device_id?: number | null // 设备ID
identifier?: string // 资产标识符ICCID 或 VirtualNo
package_id: number // 套餐ID(单选
package_ids: number[] // 套餐ID列表(前端限制单选,后端支持1-10个
payment_method: OrderPaymentMethod // 支付方式
payment_voucher_key?: string // 线下支付凭证(仅 offline 必填)
}

View File

@@ -1498,7 +1498,7 @@
const rechargeLoading = ref(false)
const rechargeFormRef = ref<FormInstance>()
const rechargeForm = reactive({
package_id: 0,
package_id: undefined as number | undefined, // 前端使用单选
payment_method: 'wallet' as 'wallet' | 'offline'
})
const rechargeRules: FormRules = {
@@ -2658,14 +2658,20 @@
if (valid) {
rechargeLoading.value = true
try {
// 验证必填字段
if (!rechargeForm.package_id) {
ElMessage.error('请选择套餐')
return
}
// 获取资产标识符
const identifier =
cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no
// 创建订单
// 创建订单(后端接收数组,前端单选转为单元素数组)
const data: CreateOrderRequest = {
identifier,
package_id: rechargeForm.package_id,
package_ids: [rechargeForm.package_id],
payment_method: rechargeForm.payment_method
}
@@ -2701,7 +2707,7 @@
// 关闭充值对话框
const handleRechargeDialogClosed = () => {
rechargeFormRef.value?.resetFields()
rechargeForm.package_id = 0
rechargeForm.package_id = undefined
rechargeForm.payment_method = 'wallet'
packageOptions.value = []
}

View File

@@ -60,13 +60,10 @@
<p>1. 请先下载 Excel 模板文件按照模板格式填写设备信息</p>
<p>2. 仅支持 Excel 格式.xlsx单次最多导入 1000 </p>
<p>3. 列格式请设置为文本格式避免长数字被转为科学计数法</p>
<p>4. <strong>重要列顺序固定不可调整</strong>系统按位置读取不识别列名</p>
<p>5. 必填列虚拟号第1列</p>
<p
>4.
必填列virtual_no虚拟号device_name设备名称device_model设备型号device_type设备类型imei设备IMEI号</p
>
<p
>5. 可选列manufacturer制造商max_sim_slots最大插槽数默认4iccid_1 ~
iccid_4绑定的卡ICCID</p
>6. 可选列设备名称设备型号设备类型IMEI制造商最大SIM槽数默认4有效范围1-4卡1~卡4 ICCID</p
>
</div>
</template>
@@ -89,7 +86,7 @@
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text"> Excel 文件拖到此处<em>点击选择</em></div>
<template #tip>
<div class="el-upload__tip">只能上传 .xlsx 格式的 Excel 文件且不超过 10MB</div>
<div class="el-upload__tip">只能上传 .xlsx 格式的 Excel 文件且不超过 300MB</div>
</template>
</ElUpload>
@@ -411,33 +408,33 @@
// 动态导入 xlsx 库
const XLSX = await import('xlsx')
// 创建示例数据
// 创建示例数据(按固定列顺序)
const templateData = [
{
virtual_no: '862639070731999',
device_name: '智能水表01',
device_model: 'WM-2000',
device_type: '智能水表',
imei: '860123456789012',
manufacturer: '华为',
max_sim_slots: 4,
iccid_1: '89860123456789012345',
iccid_2: '',
iccid_3: '',
iccid_4: ''
虚拟号: '862639070731999',
设备名称: '智能水表01',
设备型号: 'WM-2000',
设备类型: '智能水表',
IMEI: '860123456789012',
制造商: '华为',
最大SIM槽数: 4,
'卡1 ICCID': '89860123456789012345',
'卡2 ICCID': '',
'卡3 ICCID': '',
'卡4 ICCID': ''
},
{
virtual_no: '862639070750932',
device_name: 'GPS定位器01',
device_model: 'GPS-3000',
device_type: '定位设备',
imei: '860123456789013',
manufacturer: '小米',
max_sim_slots: 2,
iccid_1: '89860123456789012346',
iccid_2: '89860123456789012347',
iccid_3: '',
iccid_4: ''
虚拟号: '862639070750932',
设备名称: 'GPS定位器01',
设备型号: 'GPS-3000',
设备类型: '定位设备',
IMEI: '860123456789013',
制造商: '小米',
最大SIM槽数: 2,
'卡1 ICCID': '89860123456789012346',
'卡2 ICCID': '89860123456789012347',
'卡3 ICCID': '',
'卡4 ICCID': ''
}
]
@@ -447,17 +444,17 @@
// 设置列宽
ws['!cols'] = [
{ wch: 20 }, // virtual_no
{ wch: 20 }, // device_name
{ wch: 15 }, // device_model
{ wch: 15 }, // device_type
{ wch: 18 }, // imei
{ wch: 15 }, // manufacturer
{ wch: 15 }, // max_sim_slots
{ wch: 22 }, // iccid_1
{ wch: 22 }, // iccid_2
{ wch: 22 }, // iccid_3
{ wch: 22 } // iccid_4
{ wch: 20 }, // 虚拟号
{ wch: 15 }, // 设备名称
{ wch: 15 }, // 设备型号
{ wch: 15 }, // 设备类型
{ wch: 18 }, // IMEI
{ wch: 12 }, // 制造商
{ wch: 15 }, // 最大SIM槽数
{ wch: 25 }, // 卡1 ICCID
{ wch: 25 }, // 卡2 ICCID
{ wch: 25 }, // 卡3 ICCID
{ wch: 25 } // 卡4 ICCID
]
// 将所有单元格设置为文本格式
@@ -484,9 +481,9 @@
// 文件选择变化
const handleFileChange = (uploadFile: any) => {
const maxSize = 10 * 1024 * 1024
const maxSize = 300 * 1024 * 1024
if (uploadFile.raw && uploadFile.raw.size > maxSize) {
ElMessage.error('文件大小不能超过 10MB')
ElMessage.error('文件大小不能超过 300MB')
uploadRef.value?.clearFiles()
fileList.value = []
return

View File

@@ -139,6 +139,16 @@
</ElSelect>
</ElFormItem>
<ElFormItem label="卡业务类型" style="margin-bottom: 20px">
<ElSelect v-model="selectedCardCategory" placeholder="请选择卡业务类型" style="width: 100%">
<ElOption label="普通卡" value="normal" />
<ElOption label="行业卡" value="industry" />
</ElSelect>
<div style="margin-top: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
默认为普通卡可选择行业卡
</div>
</ElFormItem>
<ElUpload
ref="uploadRef"
drag
@@ -150,7 +160,7 @@
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text"> Excel 文件拖到此处<em>点击选择</em></div>
<template #tip>
<div class="el-upload__tip">只能上传 .xlsx 格式的 Excel 文件且不超过 10MB</div>
<div class="el-upload__tip">只能上传 .xlsx 格式的 Excel 文件且不超过 300MB</div>
</template>
</ElUpload>
@@ -207,6 +217,7 @@
const uploading = ref(false)
const importDialogVisible = ref(false)
const selectedCarrierId = ref<number>()
const selectedCardCategory = ref<'normal' | 'industry'>('normal') // 默认普通卡
const carrierList = ref<Carrier[]>([])
const carrierLoading = ref(false)
const failDataDialogVisible = ref(false)
@@ -292,6 +303,7 @@
{ label: '任务编号', prop: 'task_no' },
{ label: '任务状态', prop: 'status' },
{ label: '运营商', prop: 'carrier_name' },
{ label: '卡业务类型', prop: 'card_category' },
{ label: '文件名', prop: 'file_name' },
{ label: '总数', prop: 'total_count' },
{ label: '成功数', prop: 'success_count' },
@@ -374,6 +386,19 @@
label: '运营商',
width: 120
},
{
prop: 'card_category',
label: '卡业务类型',
width: 110,
formatter: (row: IotCardImportTask) => {
if (!row.card_category) return '-'
return row.card_category === 'normal'
? '普通卡'
: row.card_category === 'industry'
? '行业卡'
: row.card_category
}
},
{
prop: 'file_name',
label: '文件名',
@@ -577,22 +602,22 @@
// 动态导入 xlsx 库
const XLSX = await import('xlsx')
// 创建示例数据
// 创建示例数据按固定列顺序ICCID、MSISDN、虚拟号
const templateData = [
{
ICCID: '89860123456789012345',
MSISDN: '13800138000',
virtual_no: 'V001'
'MSISDN(接入号)': '13800138000',
虚拟号: 'V001'
},
{
ICCID: '89860123456789012346',
MSISDN: '13800138001',
virtual_no: 'V002'
'MSISDN(接入号)': '13800138001',
虚拟号: 'V002'
},
{
ICCID: '89860123456789012347',
MSISDN: '13800138002',
virtual_no: 'V003'
'MSISDN(接入号)': '13800138002',
虚拟号: 'V003'
}
]
@@ -603,8 +628,8 @@
// 设置列宽
ws['!cols'] = [
{ wch: 25 }, // ICCID
{ wch: 15 }, // MSISDN
{ wch: 15 } // virtual_no
{ wch: 20 }, // MSISDN(接入号)
{ wch: 15 } // 虚拟号
]
// 将所有单元格设置为文本格式,防止科学计数法
@@ -631,9 +656,9 @@
// 文件选择变化
const handleFileChange = (uploadFile: any) => {
const maxSize = 10 * 1024 * 1024
const maxSize = 300 * 1024 * 1024
if (uploadFile.raw && uploadFile.raw.size > maxSize) {
ElMessage.error('文件大小不能超过 10MB')
ElMessage.error('文件大小不能超过 300MB')
uploadRef.value?.clearFiles()
fileList.value = []
return
@@ -659,6 +684,7 @@
// 取消导入
const handleCancelImport = () => {
clearFiles()
selectedCardCategory.value = 'normal' // 重置为默认值
importDialogVisible.value = false
}
@@ -728,7 +754,8 @@
const importRes = await CardService.importIotCards({
carrier_id: selectedCarrierId.value,
file_key,
batch_no: `IOT-${Date.now()}`
batch_no: `IOT-${Date.now()}`,
card_category: selectedCardCategory.value // 新增卡业务类型
})
if (importRes.code !== 0) {

View File

@@ -119,6 +119,17 @@
label: '运营商',
prop: 'carrier_name',
formatter: (value: any) => value || '-'
},
{
label: '卡业务类型',
render: (data: any) => {
if (!data.card_category) return h('span', '-')
return h(
ElTag,
{ type: data.card_category === 'industry' ? 'warning' : 'success', size: 'small' },
() => (data.card_category === 'normal' ? '普通卡' : data.card_category === 'industry' ? '行业卡' : data.card_category)
)
}
}
]
: []),

View File

@@ -10,6 +10,20 @@
<!-- 正常显示 -->
<div v-else class="commission-summary-widget">
<div class="stats-container">
<ElCard shadow="hover" class="stat-card-wrapper">
<div class="stat-card">
<div
class="stat-icon"
style="background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%)"
>
<i class="iconfont-sys">&#xe722;</i>
</div>
<div class="stat-content">
<div class="stat-label">预充值余额</div>
<div class="stat-value">{{ formatMoney(summary.main_balance) }}</div>
</div>
</div>
</ElCard>
<ElCard shadow="hover" class="stat-card-wrapper">
<div class="stat-card">
<div
@@ -102,6 +116,7 @@
// 佣金概览
const summary = ref<MyCommissionSummary>({
main_balance: 0,
total_commission: 0,
available_commission: 0,
frozen_commission: 0,
@@ -127,6 +142,7 @@
const myShopData = res.data.items.find(item => item.shop_id === currentShopId.value)
if (myShopData) {
summary.value = {
main_balance: myShopData.main_balance || 0,
total_commission: myShopData.total_commission,
available_commission: myShopData.available_commission,
frozen_commission: myShopData.frozen_commission,

View File

@@ -527,13 +527,13 @@
return baseRules
})
const createForm = reactive<CreateOrderRequest>({
order_type: 'single_card',
package_id: 0,
iot_card_id: null,
device_id: null,
payment_method: 'wallet', // 默认使用钱包支付
payment_voucher_key: undefined // 线下支付凭证
const createForm = reactive({
order_type: 'single_card' as OrderType,
package_id: undefined as number | undefined, // 前端使用单选
iot_card_id: null as number | null,
device_id: null as number | null,
payment_method: 'wallet' as OrderPaymentMethod, // 默认使用钱包支付
payment_voucher_key: undefined as string | undefined // 线下支付凭证
})
const orderList = ref<Order[]>([])
@@ -975,7 +975,7 @@
// 重置表单数据到初始值
createForm.order_type = 'single_card'
createForm.package_id = 0
createForm.package_id = undefined
createForm.iot_card_id = null
createForm.device_id = null
createForm.payment_method = 'wallet'
@@ -1028,10 +1028,16 @@
identifier = selectedDevice.virtual_no
}
// 创建订单
// 验证必填字段
if (!createForm.package_id) {
ElMessage.error('请选择套餐')
return
}
// 创建订单(后端接收数组,前端单选转为单元素数组)
const data: CreateOrderRequest = {
identifier,
package_id: createForm.package_id,
package_ids: [createForm.package_id],
payment_method: createForm.payment_method
}

View File

@@ -490,7 +490,7 @@
page_size: 20,
carrier_name: carrierName
})
carrierList.value = data.items || data.list || []
carrierList.value = data.items || []
} catch (error) {
console.error('加载运营商列表失败', error)
}

View File

@@ -709,7 +709,7 @@
page_size: 20,
carrier_name: carrierName
})
carrierList.value = data.items || data.list || []
carrierList.value = data.items || []
} catch (error) {
console.error('加载运营商列表失败', error)
}
@@ -727,7 +727,7 @@
const loadShops = async () => {
try {
const { data } = await ShopService.getShops()
shopTreeData.value = data.items || data.list || []
shopTreeData.value = data.items || []
} catch (error) {
console.error('加载店铺列表失败', error)
}
@@ -741,7 +741,7 @@
page_size: 20,
iccid: iccid
})
cardList.value = data.items || data.list || []
cardList.value = data.items || []
} catch (error) {
console.error('加载卡列表失败', error)
}