fetch(add): 新增企业设备授权
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m25s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m25s
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
<title>君鸿卡管系统</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<!-- 引入小米字体 CSS 文件 -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Regular.min.css"/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="src/assets/img/logo.png" />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ export { DeviceService } from './device'
|
||||
export { CarrierService } from './carrier'
|
||||
export { PackageSeriesService } from './packageSeries'
|
||||
export { PackageManageService } from './packageManage'
|
||||
export { MyPackageService } from './myPackage'
|
||||
export { ShopPackageAllocationService } from './shopPackageAllocation'
|
||||
export { ShopSeriesAllocationService } from './shopSeriesAllocation'
|
||||
export { OrderService } from './order'
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* 代理可售套餐 API 服务
|
||||
*/
|
||||
|
||||
import { BaseService } from '../BaseService'
|
||||
import type {
|
||||
MyPackageResponse,
|
||||
MyPackageQueryParams,
|
||||
MySeriesAllocationResponse,
|
||||
BaseResponse,
|
||||
PaginationResponse
|
||||
} from '@/types/api'
|
||||
|
||||
export class MyPackageService extends BaseService {
|
||||
/**
|
||||
* 获取我的可售套餐列表
|
||||
* GET /api/admin/my-packages
|
||||
* @param params 查询参数
|
||||
*/
|
||||
static getMyPackages(
|
||||
params?: MyPackageQueryParams
|
||||
): Promise<PaginationResponse<MyPackageResponse>> {
|
||||
return this.getPage<MyPackageResponse>('/api/admin/my-packages', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的可售套餐详情
|
||||
* GET /api/admin/my-packages/{id}
|
||||
* @param id 套餐ID
|
||||
*/
|
||||
static getMyPackageDetail(id: number): Promise<BaseResponse<MyPackageResponse>> {
|
||||
return this.getOne<MyPackageResponse>(`/api/admin/my-packages/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的被分配系列列表
|
||||
* GET /api/admin/my-series-allocations
|
||||
* @param params 查询参数(支持分页)
|
||||
*/
|
||||
static getMySeriesAllocations(
|
||||
params?: Record<string, any>
|
||||
): Promise<PaginationResponse<MySeriesAllocationResponse>> {
|
||||
return this.getPage<MySeriesAllocationResponse>('/api/admin/my-series-allocations', params)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,17 +1,8 @@
|
||||
// 全局样式
|
||||
|
||||
@font-face {
|
||||
font-family: 'DMSans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../fonts/DMSans.woff2) format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../fonts/Montserrat.woff2) format('woff2');
|
||||
// 强制所有元素使用小米字体
|
||||
* {
|
||||
font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// --el-border-color: #E4E4E7 !important; // DCDFE6
|
||||
// 按钮粗度
|
||||
--el-font-weight-primary: 400 !important;
|
||||
// Element Plus 全局字体
|
||||
--el-font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
|
||||
|
||||
--el-component-custom-height: 36px !important;
|
||||
|
||||
@@ -180,6 +182,8 @@
|
||||
|
||||
// 修改el-button样式
|
||||
.el-button {
|
||||
font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
|
||||
|
||||
&.el-button--text {
|
||||
background-color: transparent !important;
|
||||
padding: 0 !important;
|
||||
@@ -198,6 +202,32 @@
|
||||
border-radius: 6px !important;
|
||||
font-weight: bold;
|
||||
transition: all 0s !important;
|
||||
font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
// 为所有 Element Plus 组件添加小米字体
|
||||
.el-input,
|
||||
.el-input__wrapper,
|
||||
.el-select,
|
||||
.el-select__wrapper,
|
||||
.el-form-item__label,
|
||||
.el-table,
|
||||
.el-pagination,
|
||||
.el-dialog,
|
||||
.el-message,
|
||||
.el-message-box,
|
||||
.el-dropdown-menu,
|
||||
.el-menu,
|
||||
.el-radio,
|
||||
.el-checkbox,
|
||||
.el-switch,
|
||||
.el-date-picker,
|
||||
.el-cascader,
|
||||
.el-tree-select,
|
||||
.el-upload,
|
||||
.el-card,
|
||||
.el-divider {
|
||||
font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
.el-checkbox-group {
|
||||
|
||||
@@ -34,9 +34,7 @@ h5 {
|
||||
body {
|
||||
color: var(--art-text-gray-700);
|
||||
text-align: left;
|
||||
font-family:
|
||||
Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
|
||||
'微软雅黑', Arial, sans-serif;
|
||||
font-family: 'MiSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 快速入口 -->
|
||||
<ArtFastEnter v-if="width >= 1200" />
|
||||
<!--<ArtFastEnter v-if="width >= 1200" />-->
|
||||
|
||||
<!-- 面包屑 -->
|
||||
<ArtBreadcrumb
|
||||
@@ -65,19 +65,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 通知 -->
|
||||
<div class="btn-box notice-btn" @click="visibleNotice">
|
||||
<div class="btn notice-button">
|
||||
<i class="iconfont-sys notice-btn"></i>
|
||||
<span class="count notice-btn"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div class="btn-box notice-btn" @click="visibleNotice">-->
|
||||
<!-- <div class="btn notice-button">-->
|
||||
<!-- <i class="iconfont-sys notice-btn"></i>-->
|
||||
<!-- <span class="count notice-btn"></span>-->
|
||||
<!-- </div>-->
|
||||
<!--</div>-->
|
||||
<!-- 聊天 -->
|
||||
<div class="btn-box chat-btn" @click="openChat">
|
||||
<div class="btn chat-button">
|
||||
<i class="iconfont-sys"></i>
|
||||
<span class="dot"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div class="btn-box chat-btn" @click="openChat">-->
|
||||
<!-- <div class="btn chat-button">-->
|
||||
<!-- <i class="iconfont-sys"></i>-->
|
||||
<!-- <span class="dot"></span>-->
|
||||
<!-- </div>-->
|
||||
<!--</div>-->
|
||||
<!-- 语言 -->
|
||||
<div class="btn-box" v-if="showLanguage">
|
||||
<el-dropdown @command="changeLanguage" popper-class="langDropDownStyle">
|
||||
|
||||
@@ -433,11 +433,12 @@
|
||||
},
|
||||
"assetManagement": {
|
||||
"title": "Asset Management",
|
||||
"cardSearch": "Card Search",
|
||||
"cardSearch": "IoT Card Query",
|
||||
"deviceSearch": "Device Search",
|
||||
"singleCard": "Single Card Info",
|
||||
"standaloneCardList": "Standalone Card List",
|
||||
"taskManagement": "Task Management",
|
||||
"standaloneCardList": "IoT Card Management",
|
||||
"iotCardTask": "IoT Card Tasks",
|
||||
"deviceTask": "Device Tasks",
|
||||
"taskDetail": "Task Details",
|
||||
"devices": "Device Management",
|
||||
"deviceDetail": "Device Details",
|
||||
|
||||
@@ -406,7 +406,6 @@
|
||||
"packageAssign": "单套餐分配",
|
||||
"seriesAssign": "套餐系列分配",
|
||||
"packageSeries": "套餐系列",
|
||||
"myPackages": "代理可售套餐",
|
||||
"packageCommission": "套餐佣金网卡"
|
||||
},
|
||||
"accountManagement": {
|
||||
@@ -437,11 +436,12 @@
|
||||
},
|
||||
"assetManagement": {
|
||||
"title": "资产管理",
|
||||
"cardSearch": "单卡查询",
|
||||
"cardSearch": "IoT卡查询",
|
||||
"deviceSearch": "设备查询",
|
||||
"singleCard": "单卡信息",
|
||||
"standaloneCardList": "单卡列表",
|
||||
"taskManagement": "任务管理",
|
||||
"standaloneCardList": "IoT卡管理",
|
||||
"iotCardTask": "IoT卡任务",
|
||||
"deviceTask": "设备任务",
|
||||
"taskDetail": "任务详情",
|
||||
"devices": "设备管理",
|
||||
"deviceDetail": "设备详情",
|
||||
|
||||
@@ -620,15 +620,15 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
icon: ''
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'card-detail',
|
||||
name: 'CardDetail',
|
||||
component: RoutesAlias.CardDetail,
|
||||
meta: {
|
||||
title: 'menus.cardManagement.cardDetail',
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
// {
|
||||
// path: 'card-detail',
|
||||
// name: 'CardDetail',
|
||||
// component: RoutesAlias.CardDetail,
|
||||
// meta: {
|
||||
// title: 'menus.cardManagement.cardDetail',
|
||||
// keepAlive: true
|
||||
// }
|
||||
// },
|
||||
{
|
||||
path: 'card-assign',
|
||||
name: 'CardAssign',
|
||||
@@ -721,15 +721,6 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'my-packages',
|
||||
name: 'MyPackages',
|
||||
component: RoutesAlias.MyPackages,
|
||||
meta: {
|
||||
title: 'menus.packageManagement.myPackages',
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'package-assign',
|
||||
name: 'PackageAssign',
|
||||
@@ -900,7 +891,7 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'card-search',
|
||||
path: 'iot-card-query',
|
||||
name: 'CardSearch',
|
||||
component: RoutesAlias.CardSearch,
|
||||
meta: {
|
||||
@@ -918,7 +909,7 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'card-list',
|
||||
path: 'iot-card-management',
|
||||
name: 'StandaloneCardList',
|
||||
component: RoutesAlias.StandaloneCardList,
|
||||
meta: {
|
||||
@@ -927,11 +918,20 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'task-management',
|
||||
name: 'TaskManagement',
|
||||
component: RoutesAlias.TaskManagement,
|
||||
path: 'iot-card-task',
|
||||
name: 'IotCardTask',
|
||||
component: RoutesAlias.IotCardTask,
|
||||
meta: {
|
||||
title: 'menus.assetManagement.taskManagement',
|
||||
title: 'menus.assetManagement.iotCardTask',
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'device-task',
|
||||
name: 'DeviceTask',
|
||||
component: RoutesAlias.DeviceTask,
|
||||
meta: {
|
||||
title: 'menus.assetManagement.deviceTask',
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -72,7 +72,6 @@ export enum RoutesAlias {
|
||||
PackageAssign = '/package-management/package-assign', // 单套餐分配
|
||||
SeriesAssign = '/package-management/series-assign', // 套餐系列分配
|
||||
PackageSeries = '/package-management/package-series', // 套餐系列
|
||||
MyPackages = '/package-management/my-packages', // 代理可售套餐
|
||||
PackageCommission = '/package-management/package-commission', // 套餐佣金网卡
|
||||
|
||||
// 账号管理
|
||||
@@ -92,10 +91,11 @@ export enum RoutesAlias {
|
||||
SimCardAssign = '/product/sim-card-assign', // 号卡分配
|
||||
|
||||
// 资产管理
|
||||
CardSearch = '/asset-management/card-search', // 单卡查询
|
||||
CardSearch = '/asset-management/iot-card-query', // IoT卡查询
|
||||
DeviceSearch = '/asset-management/device-search', // 设备查询
|
||||
StandaloneCardList = '/asset-management/card-list', // 单卡列表(未绑定设备)
|
||||
TaskManagement = '/asset-management/task-management', // 任务管理
|
||||
StandaloneCardList = '/asset-management/iot-card-management', // IoT卡管理
|
||||
IotCardTask = '/asset-management/iot-card-task', // IoT卡任务
|
||||
DeviceTask = '/asset-management/device-task', // 设备任务
|
||||
TaskDetail = '/asset-management/task-detail', // 任务详情
|
||||
DeviceList = '/asset-management/device-list', // 设备列表
|
||||
DeviceDetail = '/asset-management/device-detail', // 设备详情
|
||||
|
||||
@@ -256,8 +256,9 @@ export interface ImportIotCardResponse {
|
||||
export interface IotCardImportTask {
|
||||
id: number // 任务ID
|
||||
task_no: string // 任务编号
|
||||
batch_no: string // 批次号
|
||||
batch_no?: string // 批次号 (可选)
|
||||
carrier_id: number // 运营商ID
|
||||
carrier_type: string // 运营商类型 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信)
|
||||
carrier_name: string // 运营商名称
|
||||
file_name: string // 文件名
|
||||
status: IotCardImportTaskStatus // 任务状态
|
||||
@@ -266,10 +267,10 @@ export interface IotCardImportTask {
|
||||
success_count: number // 成功数
|
||||
fail_count: number // 失败数
|
||||
skip_count: number // 跳过数
|
||||
error_message: string // 错误信息
|
||||
error_message?: string // 错误信息 (可选)
|
||||
created_at: string // 创建时间
|
||||
started_at: string | null // 开始处理时间
|
||||
completed_at: string | null // 完成时间
|
||||
started_at?: string | null // 开始处理时间 (可选)
|
||||
completed_at?: string | null // 完成时间 (可选)
|
||||
}
|
||||
|
||||
// 导入任务查询参数
|
||||
@@ -323,24 +324,27 @@ export interface StandaloneCardQueryParams extends PaginationParams {
|
||||
export interface StandaloneIotCard {
|
||||
id: number // 卡ID
|
||||
iccid: string // ICCID
|
||||
imsi: string // IMSI
|
||||
msisdn: string // 卡接入号
|
||||
imsi?: string // IMSI (可选)
|
||||
msisdn?: string // 卡接入号 (可选)
|
||||
carrier_id: number // 运营商ID
|
||||
carrier_type: string // 运营商类型 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)
|
||||
carrier_name: string // 运营商名称
|
||||
card_type: string // 卡类型
|
||||
card_type: string // 卡类型 (physical:物理卡, data_card:流量卡, voice_card:语音卡等)
|
||||
card_category: string // 卡业务类型 (normal:普通卡, industry:行业卡)
|
||||
status: StandaloneCardStatus // 状态
|
||||
activation_status: number // 激活状态 (0:未激活, 1:已激活)
|
||||
network_status: number // 网络状态 (0:停机, 1:开机)
|
||||
real_name_status: number // 实名状态 (0:未实名, 1:已实名)
|
||||
batch_no: string // 批次号
|
||||
supplier: string // 供应商
|
||||
shop_id: number | null // 店铺ID
|
||||
shop_name: string // 店铺名称
|
||||
batch_no?: string // 批次号 (可选)
|
||||
supplier?: string // 供应商 (可选)
|
||||
shop_id?: number | null // 店铺ID (可选)
|
||||
shop_name?: string // 店铺名称 (可选)
|
||||
cost_price: number // 成本价(分)
|
||||
distribute_price: number // 分销价(分)
|
||||
data_usage_mb: number // 累计流量使用(MB)
|
||||
activated_at: string | null // 激活时间
|
||||
first_commission_paid: boolean // 是否已支付首次佣金
|
||||
accumulated_recharge: number // 累计充值金额(分)
|
||||
activated_at?: string | null // 激活时间 (可选)
|
||||
created_at: string // 创建时间
|
||||
updated_at: string // 更新时间
|
||||
}
|
||||
|
||||
@@ -144,58 +144,6 @@ export interface SeriesSelectOption {
|
||||
series_code: string
|
||||
}
|
||||
|
||||
// ==================== 代理可售套餐 ====================
|
||||
|
||||
/**
|
||||
* 我的可售套餐响应
|
||||
*/
|
||||
export interface MyPackageResponse {
|
||||
id: number
|
||||
package_code: string
|
||||
package_name: string
|
||||
series_id: number
|
||||
series_name: string
|
||||
package_type: string
|
||||
data_type: string
|
||||
real_data_mb: number // 真流量额度(MB)
|
||||
virtual_data_mb: number // 虚流量额度(MB)
|
||||
duration_months: number
|
||||
price: number // 套餐原价(分)
|
||||
cost_price: number // 成本价(分)
|
||||
suggested_retail_price: number // 建议售价(分)
|
||||
profit_margin: number // 利润空间(分)
|
||||
price_source: string // 价格来源:'series_pricing':系列加价, 'package_override':单套餐覆盖
|
||||
shelf_status: number
|
||||
status: number
|
||||
description?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的可售套餐查询参数
|
||||
*/
|
||||
export interface MyPackageQueryParams extends PaginationParams {
|
||||
series_id?: number // 系列ID筛选
|
||||
package_type?: string // 套餐类型筛选
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的被分配系列响应
|
||||
*/
|
||||
export interface MySeriesAllocationResponse {
|
||||
id: number // 分配ID
|
||||
series_id: number
|
||||
series_code: string
|
||||
series_name: string
|
||||
pricing_mode: string // 定价模式:'fixed':固定金额, 'percent':百分比
|
||||
pricing_value: number // 定价值
|
||||
allocator_shop_name: string // 分配者店铺名称
|
||||
package_count: number // 可售套餐数量
|
||||
status: number
|
||||
created_at: string
|
||||
}
|
||||
|
||||
// ==================== 单套餐分配 ====================
|
||||
|
||||
/**
|
||||
@@ -276,6 +224,28 @@ export interface TierCommissionConfig {
|
||||
tiers: TierEntry[] // 梯度档位数组
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性佣金梯度档位配置
|
||||
*/
|
||||
export interface OneTimeCommissionTierEntry {
|
||||
tier_type: 'sales_count' | 'sales_amount' // 梯度类型:销量或销售额
|
||||
threshold: number // 阈值
|
||||
mode: 'fixed' | 'percent' // 返佣模式
|
||||
value: number // 返佣值
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性佣金配置
|
||||
*/
|
||||
export interface OneTimeCommissionConfig {
|
||||
type: 'fixed' | 'tiered' // 佣金类型:'fixed':固定佣金, 'tiered':梯度佣金
|
||||
trigger: 'single_recharge' | 'accumulated_recharge' // 触发方式:'single_recharge':单笔充值, 'accumulated_recharge':累计充值
|
||||
threshold: number // 最低阈值(分)
|
||||
mode?: 'fixed' | 'percent' // 返佣模式(固定佣金时必填)
|
||||
value?: number // 返佣值(固定佣金时必填)
|
||||
tiers?: OneTimeCommissionTierEntry[] | null // 梯度档位数组(梯度佣金时必填)
|
||||
}
|
||||
|
||||
/**
|
||||
* 套餐系列分配响应
|
||||
*/
|
||||
@@ -288,8 +258,8 @@ export interface ShopSeriesAllocationResponse {
|
||||
allocator_shop_id: number // 分配者店铺ID
|
||||
allocator_shop_name: string // 分配者店铺名称
|
||||
base_commission: BaseCommissionConfig // 基础返佣配置
|
||||
enable_tier_commission: boolean // 是否启用梯度返佣
|
||||
tier_config?: TierCommissionConfig // 梯度返佣配置(可选)
|
||||
enable_one_time_commission: boolean // 是否启用一次性佣金
|
||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置(可选)
|
||||
status: number // 1:启用, 2:禁用
|
||||
created_at: string
|
||||
updated_at: string
|
||||
@@ -311,8 +281,8 @@ export interface CreateShopSeriesAllocationRequest {
|
||||
series_id: number // 套餐系列ID,必填
|
||||
shop_id: number // 店铺ID,必填
|
||||
base_commission: BaseCommissionConfig // 基础返佣配置,必填
|
||||
enable_tier_commission?: boolean // 是否启用梯度返佣,可选(默认false)
|
||||
tier_config?: TierCommissionConfig // 梯度返佣配置,当enable_tier_commission为true时必填
|
||||
enable_one_time_commission?: boolean // 是否启用一次性佣金,可选(默认false)
|
||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置,当enable_one_time_commission为true时必填
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,8 +290,8 @@ export interface CreateShopSeriesAllocationRequest {
|
||||
*/
|
||||
export interface UpdateShopSeriesAllocationRequest {
|
||||
base_commission?: BaseCommissionConfig // 基础返佣配置
|
||||
enable_tier_commission?: boolean // 是否启用梯度返佣
|
||||
tier_config?: TierCommissionConfig // 梯度返佣配置
|
||||
enable_one_time_commission?: boolean // 是否启用一次性佣金
|
||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
310
src/views/asset-management/device-task/index.vue
Normal file
310
src/views/asset-management/device-task/index.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="device-task-page" id="table-full-screen">
|
||||
<!-- 搜索栏 -->
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
:items="searchFormItems"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
|
||||
<ElCard shadow="never" class="art-table-card">
|
||||
<!-- 表格头部 -->
|
||||
<ArtTableHeader
|
||||
:columnList="columnOptions"
|
||||
v-model:columns="columnChecks"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:data="taskList"
|
||||
:currentPage="pagination.page"
|
||||
:pageSize="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DeviceService } from '@/api/modules'
|
||||
import { ElMessage, ElTag } from 'element-plus'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||
import type { DeviceImportTask, DeviceImportTaskStatus } from '@/types/api/device'
|
||||
|
||||
defineOptions({ name: 'DeviceTask' })
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const tableRef = ref()
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
status: undefined,
|
||||
batch_no: '',
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({ ...initialSearchState })
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 搜索表单配置
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '任务状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '全部'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '待处理', value: 1 },
|
||||
{ label: '处理中', value: 2 },
|
||||
{ label: '已完成', value: 3 },
|
||||
{ label: '失败', value: 4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '批次号',
|
||||
prop: 'batch_no',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请输入批次号'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'dateRange',
|
||||
type: 'daterange',
|
||||
config: {
|
||||
type: 'daterange',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间',
|
||||
valueFormat: 'YYYY-MM-DDTHH:mm:ssZ'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: '任务编号', prop: 'task_no' },
|
||||
{ label: '批次号', prop: 'batch_no' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '任务状态', prop: 'status' },
|
||||
{ label: '总数', prop: 'total_count' },
|
||||
{ label: '成功数', prop: 'success_count' },
|
||||
{ label: '失败数', prop: 'fail_count' },
|
||||
{ label: '跳过数', prop: 'skip_count' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '完成时间', prop: 'completed_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
]
|
||||
|
||||
const taskList = ref<DeviceImportTask[]>([])
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusType = (status: DeviceImportTaskStatus) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return 'info'
|
||||
case 2:
|
||||
return 'warning'
|
||||
case 3:
|
||||
return 'success'
|
||||
case 4:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const viewDetail = (row: DeviceImportTask) => {
|
||||
router.push({
|
||||
path: '/asset-management/task-detail',
|
||||
query: {
|
||||
id: row.id,
|
||||
task_type: 'device'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'task_no',
|
||||
label: '任务编号',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
prop: 'batch_no',
|
||||
label: '批次号',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 200
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
label: '任务状态',
|
||||
width: 100,
|
||||
formatter: (row: DeviceImportTask) => {
|
||||
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'total_count',
|
||||
label: '总数',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'success_count',
|
||||
label: '成功数',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'fail_count',
|
||||
label: '失败数',
|
||||
width: 80,
|
||||
formatter: (row: DeviceImportTask) => {
|
||||
const type = row.fail_count > 0 ? 'danger' : 'success'
|
||||
return h(ElTag, { type, size: 'small' }, () => row.fail_count)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'skip_count',
|
||||
label: '跳过数',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
width: 160,
|
||||
formatter: (row: DeviceImportTask) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'completed_at',
|
||||
label: '完成时间',
|
||||
width: 160,
|
||||
formatter: (row: DeviceImportTask) => (row.completed_at ? formatDateTime(row.completed_at) : '-')
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
formatter: (row: DeviceImportTask) => {
|
||||
return h(ArtButtonTable, {
|
||||
type: 'view',
|
||||
onClick: () => viewDetail(row)
|
||||
})
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
|
||||
// 获取设备任务列表
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: any = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize,
|
||||
status: searchForm.status,
|
||||
batch_no: searchForm.batch_no || undefined
|
||||
}
|
||||
|
||||
// 处理时间范围
|
||||
if (searchForm.dateRange && Array.isArray(searchForm.dateRange)) {
|
||||
params.start_time = searchForm.dateRange[0]
|
||||
params.end_time = searchForm.dateRange[1]
|
||||
}
|
||||
|
||||
// 清理空值
|
||||
Object.keys(params).forEach((key) => {
|
||||
if (params[key] === '' || params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const res = await DeviceService.getImportTasks(params)
|
||||
if (res.code === 0) {
|
||||
taskList.value = res.data.list || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('获取设备任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.assign(searchForm, { ...initialSearchState })
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
const handleRefresh = () => {
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.pageSize = newPageSize
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.page = newCurrentPage
|
||||
getTableData()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-task-page {
|
||||
// Device task page styles
|
||||
}
|
||||
</style>
|
||||
@@ -28,6 +28,11 @@
|
||||
<ElButton type="info" :disabled="selectedCards.length === 0" @click="showSeriesBindingDialog">
|
||||
批量设置套餐系列
|
||||
</ElButton>
|
||||
<ElButton type="primary" @click="cardDistribution">网卡分销</ElButton>
|
||||
<ElButton type="success" @click="batchRecharge">批量充值</ElButton>
|
||||
<ElButton type="danger" @click="cardRecycle">网卡回收</ElButton>
|
||||
<ElButton type="info" @click="batchDownload">批量下载</ElButton>
|
||||
<ElButton type="warning" @click="changePackage">变更套餐</ElButton>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
@@ -404,15 +409,10 @@
|
||||
const initialSearchState = {
|
||||
status: undefined,
|
||||
carrier_id: undefined,
|
||||
shop_id: undefined,
|
||||
iccid: '',
|
||||
msisdn: '',
|
||||
batch_no: '',
|
||||
package_id: undefined,
|
||||
is_distributed: undefined,
|
||||
is_replaced: undefined,
|
||||
iccid_start: '',
|
||||
iccid_end: ''
|
||||
is_distributed: undefined
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
@@ -603,14 +603,19 @@
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: 'ICCID', prop: 'iccid' },
|
||||
{ label: 'IMSI', prop: 'imsi' },
|
||||
{ label: '卡接入号', prop: 'msisdn' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
{ label: '卡类型', prop: 'card_type' },
|
||||
{ label: '卡业务类型', prop: 'card_category' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
{ label: '成本价', prop: 'cost_price' },
|
||||
{ label: '分销价', prop: 'distribute_price' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '批次号', prop: 'batch_no' },
|
||||
{ label: '店铺名称', prop: 'shop_name' },
|
||||
{ label: '激活时间', prop: 'activated_at' },
|
||||
{ label: '激活状态', prop: 'activation_status' },
|
||||
{ label: '网络状态', prop: 'network_status' },
|
||||
{ label: '实名状态', prop: 'real_name_status' },
|
||||
{ label: '累计流量(MB)', prop: 'data_usage_mb' },
|
||||
{ label: '首次佣金', prop: 'first_commission_paid' },
|
||||
{ label: '累计充值', prop: 'accumulated_recharge' },
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
@@ -653,28 +658,40 @@
|
||||
{
|
||||
prop: 'iccid',
|
||||
label: 'ICCID',
|
||||
minWidth: 180
|
||||
},
|
||||
{
|
||||
prop: 'imsi',
|
||||
label: 'IMSI',
|
||||
width: 150
|
||||
minWidth: 190
|
||||
},
|
||||
{
|
||||
prop: 'msisdn',
|
||||
label: '卡接入号',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'carrier_name',
|
||||
label: '运营商',
|
||||
width: 100
|
||||
width: 130
|
||||
},
|
||||
{
|
||||
prop: 'card_type',
|
||||
label: '卡类型',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'card_category',
|
||||
label: '卡业务类型',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'carrier_name',
|
||||
label: '运营商',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
prop: 'cost_price',
|
||||
label: '成本价',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => `¥${(row.cost_price / 100).toFixed(2)}`
|
||||
},
|
||||
{
|
||||
prop: 'distribute_price',
|
||||
label: '分销价',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => `¥${(row.distribute_price / 100).toFixed(2)}`
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
label: '状态',
|
||||
@@ -684,20 +701,55 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'batch_no',
|
||||
label: '批次号',
|
||||
prop: 'activation_status',
|
||||
label: '激活状态',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => {
|
||||
const type = row.activation_status === 1 ? 'success' : 'info'
|
||||
const text = row.activation_status === 1 ? '已激活' : '未激活'
|
||||
return h(ElTag, { type }, () => text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'network_status',
|
||||
label: '网络状态',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => {
|
||||
const type = row.network_status === 1 ? 'success' : 'danger'
|
||||
const text = row.network_status === 1 ? '开机' : '停机'
|
||||
return h(ElTag, { type }, () => text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'real_name_status',
|
||||
label: '实名状态',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => {
|
||||
const type = row.real_name_status === 1 ? 'success' : 'warning'
|
||||
const text = row.real_name_status === 1 ? '已实名' : '未实名'
|
||||
return h(ElTag, { type }, () => text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'data_usage_mb',
|
||||
label: '累计流量(MB)',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'shop_name',
|
||||
label: '店铺名称',
|
||||
width: 150
|
||||
prop: 'first_commission_paid',
|
||||
label: '首次佣金',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => {
|
||||
const type = row.first_commission_paid ? 'success' : 'info'
|
||||
const text = row.first_commission_paid ? '已支付' : '未支付'
|
||||
return h(ElTag, { type, size: 'small' }, () => text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'activated_at',
|
||||
label: '激活时间',
|
||||
width: 160,
|
||||
formatter: (row: StandaloneIotCard) => (row.activated_at ? formatDateTime(row.activated_at) : '-')
|
||||
prop: 'accumulated_recharge',
|
||||
label: '累计充值',
|
||||
width: 100,
|
||||
formatter: (row: StandaloneIotCard) => `¥${(row.accumulated_recharge / 100).toFixed(2)}`
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
@@ -729,7 +781,7 @@
|
||||
|
||||
const res = await CardService.getStandaloneIotCards(params)
|
||||
if (res.code === 0) {
|
||||
cardList.value = res.data.list || []
|
||||
cardList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1127,6 +1179,31 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 网卡分销 - 正在开发中
|
||||
const cardDistribution = () => {
|
||||
ElMessage.info('功能正在开发中')
|
||||
}
|
||||
|
||||
// 批量充值 - 正在开发中
|
||||
const batchRecharge = () => {
|
||||
ElMessage.info('功能正在开发中')
|
||||
}
|
||||
|
||||
// 网卡回收 - 正在开发中
|
||||
const cardRecycle = () => {
|
||||
ElMessage.info('功能正在开发中')
|
||||
}
|
||||
|
||||
// 批量下载 - 正在开发中
|
||||
const batchDownload = () => {
|
||||
ElMessage.info('功能正在开发中')
|
||||
}
|
||||
|
||||
// 变更套餐 - 正在开发中
|
||||
const changePackage = () => {
|
||||
ElMessage.info('功能正在开发中')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="task-management-page" id="table-full-screen">
|
||||
<div class="iot-card-task-page" id="table-full-screen">
|
||||
<!-- 搜索栏 -->
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
@@ -42,33 +42,26 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CardService, DeviceService } from '@/api/modules'
|
||||
import { CardService } from '@/api/modules'
|
||||
import { ElMessage, ElTag } from 'element-plus'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||
import type { IotCardImportTask, IotCardImportTaskStatus } from '@/types/api/card'
|
||||
import type { DeviceImportTask } from '@/types/api/device'
|
||||
|
||||
defineOptions({ name: 'TaskManagement' })
|
||||
defineOptions({ name: 'IotCardTask' })
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const tableRef = ref()
|
||||
|
||||
// 任务类型
|
||||
type TaskType = 'card' | 'device'
|
||||
type ImportTask = IotCardImportTask | DeviceImportTask
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
task_type: undefined as TaskType | undefined,
|
||||
status: undefined,
|
||||
carrier_id: undefined,
|
||||
batch_no: '',
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
dateRange: undefined as any
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
@@ -83,19 +76,6 @@
|
||||
|
||||
// 搜索表单配置
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '任务类型',
|
||||
prop: 'task_type',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '全部'
|
||||
},
|
||||
options: () => [
|
||||
{ label: 'ICCID导入', value: 'card' },
|
||||
{ label: '设备导入', value: 'device' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '任务状态',
|
||||
prop: 'status',
|
||||
@@ -150,21 +130,21 @@
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: '任务编号', prop: 'task_no' },
|
||||
{ label: '任务类型', prop: 'task_type' },
|
||||
{ label: '批次号', prop: 'batch_no' },
|
||||
{ label: '任务状态', prop: 'status' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '任务状态', prop: 'status' },
|
||||
{ label: '总数', prop: 'total_count' },
|
||||
{ label: '成功数', prop: 'success_count' },
|
||||
{ label: '失败数', prop: 'fail_count' },
|
||||
{ label: '跳过数', prop: 'skip_count' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '开始时间', prop: 'started_at' },
|
||||
{ label: '完成时间', prop: 'completed_at' },
|
||||
{ label: '错误信息', prop: 'error_message' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
]
|
||||
|
||||
const taskList = ref<ImportTask[]>([])
|
||||
const taskList = ref<IotCardImportTask[]>([])
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusType = (status: IotCardImportTaskStatus) => {
|
||||
@@ -182,27 +162,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务类型
|
||||
const getTaskType = (row: ImportTask): TaskType => {
|
||||
// 判断是否为设备导入任务(设备导入任务有 device_no 字段,卡导入任务有 carrier_name 字段)
|
||||
if ('device_no' in row || (row.batch_no && row.batch_no.startsWith('DEV-'))) {
|
||||
return 'device'
|
||||
}
|
||||
return 'card'
|
||||
}
|
||||
|
||||
// 获取任务类型文本
|
||||
const getTaskTypeText = (taskType: TaskType) => {
|
||||
return taskType === 'device' ? '设备导入' : 'ICCID导入'
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const viewDetail = (row: ImportTask) => {
|
||||
const viewDetail = (row: IotCardImportTask) => {
|
||||
router.push({
|
||||
path: '/asset-management/task-detail',
|
||||
query: {
|
||||
id: row.id,
|
||||
task_type: getTaskType(row)
|
||||
task_type: 'card'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -212,44 +178,26 @@
|
||||
{
|
||||
prop: 'task_no',
|
||||
label: '任务编号',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
prop: 'task_type',
|
||||
label: '任务类型',
|
||||
width: 100,
|
||||
formatter: (row: ImportTask) => {
|
||||
const taskType = getTaskType(row)
|
||||
const tagType = taskType === 'device' ? 'warning' : 'primary'
|
||||
return h(ElTag, { type: tagType, size: 'small' }, () => getTaskTypeText(taskType))
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'batch_no',
|
||||
label: '批次号',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'carrier_name',
|
||||
label: '运营商',
|
||||
width: 100,
|
||||
formatter: (row: ImportTask) => {
|
||||
return (row as IotCardImportTask).carrier_name || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 200
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
label: '任务状态',
|
||||
width: 100,
|
||||
formatter: (row: ImportTask) => {
|
||||
formatter: (row: IotCardImportTask) => {
|
||||
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'carrier_name',
|
||||
label: '运营商',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 250
|
||||
},
|
||||
{
|
||||
prop: 'total_count',
|
||||
label: '总数',
|
||||
@@ -264,7 +212,7 @@
|
||||
prop: 'fail_count',
|
||||
label: '失败数',
|
||||
width: 80,
|
||||
formatter: (row: ImportTask) => {
|
||||
formatter: (row: IotCardImportTask) => {
|
||||
const type = row.fail_count > 0 ? 'danger' : 'success'
|
||||
return h(ElTag, { type, size: 'small' }, () => row.fail_count)
|
||||
}
|
||||
@@ -275,25 +223,37 @@
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
prop: 'started_at',
|
||||
label: '开始时间',
|
||||
width: 160,
|
||||
formatter: (row: ImportTask) => formatDateTime(row.created_at)
|
||||
formatter: (row: IotCardImportTask) => (row.started_at ? formatDateTime(row.started_at) : '-')
|
||||
},
|
||||
{
|
||||
prop: 'completed_at',
|
||||
label: '完成时间',
|
||||
width: 160,
|
||||
formatter: (row: ImportTask) => (row.completed_at ? formatDateTime(row.completed_at) : '-')
|
||||
formatter: (row: IotCardImportTask) => (row.completed_at ? formatDateTime(row.completed_at) : '-')
|
||||
},
|
||||
{
|
||||
prop: 'error_message',
|
||||
label: '错误信息',
|
||||
minWidth: 200,
|
||||
formatter: (row: IotCardImportTask) => row.error_message || '-'
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
width: 160,
|
||||
formatter: (row: IotCardImportTask) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 100,
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
formatter: (row: ImportTask) => {
|
||||
formatter: (row: IotCardImportTask) => {
|
||||
return h(ArtButtonTable, {
|
||||
type: 'view',
|
||||
text: '查看详情',
|
||||
onClick: () => viewDetail(row)
|
||||
})
|
||||
}
|
||||
@@ -304,7 +264,7 @@
|
||||
getTableData()
|
||||
})
|
||||
|
||||
// 获取任务列表
|
||||
// 获取IoT卡任务列表
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -312,6 +272,7 @@
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize,
|
||||
status: searchForm.status,
|
||||
carrier_id: searchForm.carrier_id,
|
||||
batch_no: searchForm.batch_no || undefined
|
||||
}
|
||||
|
||||
@@ -328,52 +289,14 @@
|
||||
}
|
||||
})
|
||||
|
||||
// 根据任务类型获取不同的数据
|
||||
if (searchForm.task_type === 'device') {
|
||||
// 仅获取设备导入任务
|
||||
const res = await DeviceService.getImportTasks(params)
|
||||
if (res.code === 0) {
|
||||
taskList.value = res.data.list || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} else if (searchForm.task_type === 'card') {
|
||||
// 仅获取ICCID导入任务(需要carrier_id参数)
|
||||
const cardParams = {
|
||||
...params,
|
||||
carrier_id: searchForm.carrier_id
|
||||
}
|
||||
const res = await CardService.getIotCardImportTasks(cardParams)
|
||||
if (res.code === 0) {
|
||||
taskList.value = res.data.list || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} else {
|
||||
// 获取所有类型任务 - 分别调用两个API然后合并结果
|
||||
const [cardRes, deviceRes] = await Promise.all([
|
||||
CardService.getIotCardImportTasks({
|
||||
...params,
|
||||
carrier_id: searchForm.carrier_id
|
||||
}),
|
||||
DeviceService.getImportTasks(params)
|
||||
])
|
||||
|
||||
const cardTasks = cardRes.code === 0 ? cardRes.data.list || [] : []
|
||||
const deviceTasks = deviceRes.code === 0 ? deviceRes.data.list || [] : []
|
||||
|
||||
// 合并并按创建时间排序
|
||||
const allTasks = [...cardTasks, ...deviceTasks].sort((a, b) => {
|
||||
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
||||
})
|
||||
|
||||
// 前端分页
|
||||
const start = (pagination.page - 1) * pagination.pageSize
|
||||
const end = start + pagination.pageSize
|
||||
taskList.value = allTasks.slice(start, end)
|
||||
pagination.total = allTasks.length
|
||||
const res = await CardService.getIotCardImportTasks(params)
|
||||
if (res.code === 0) {
|
||||
taskList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('获取任务列表失败')
|
||||
ElMessage.error('获取IoT卡任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -410,7 +333,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-management-page {
|
||||
// Task management page styles
|
||||
.iot-card-task-page {
|
||||
// IoT card task page styles
|
||||
}
|
||||
</style>
|
||||
@@ -1,37 +1,38 @@
|
||||
<template>
|
||||
<div class="analysis-dashboard">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xl="14" :lg="15" :xs="24">
|
||||
<TodaySales />
|
||||
</el-col>
|
||||
<el-col :xl="10" :lg="9" :xs="24">
|
||||
<VisitorInsights />
|
||||
</el-col>
|
||||
</el-row>
|
||||
开发中敬请期待...
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :xl="14" :lg="15" :xs="24">-->
|
||||
<!-- <TodaySales />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :xl="10" :lg="9" :xs="24">-->
|
||||
<!-- <VisitorInsights />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20" class="mt-20">
|
||||
<el-col :xl="10" :lg="10" :xs="24">
|
||||
<TotalRevenue />
|
||||
</el-col>
|
||||
<el-col :xl="7" :lg="7" :xs="24">
|
||||
<CustomerSatisfaction />
|
||||
</el-col>
|
||||
<el-col :xl="7" :lg="7" :xs="24">
|
||||
<TargetVsReality />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20" class="mt-20">-->
|
||||
<!-- <el-col :xl="10" :lg="10" :xs="24">-->
|
||||
<!-- <TotalRevenue />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :xl="7" :lg="7" :xs="24">-->
|
||||
<!-- <CustomerSatisfaction />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :xl="7" :lg="7" :xs="24">-->
|
||||
<!-- <TargetVsReality />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20" class="mt-20">
|
||||
<el-col :xl="10" :lg="10" :xs="24">
|
||||
<TopProducts />
|
||||
</el-col>
|
||||
<el-col :xl="7" :lg="7" :xs="24">
|
||||
<SalesMappingByCountry />
|
||||
</el-col>
|
||||
<el-col :xl="7" :lg="7" :xs="24">
|
||||
<VolumeServiceLevel />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20" class="mt-20">-->
|
||||
<!-- <el-col :xl="10" :lg="10" :xs="24">-->
|
||||
<!-- <TopProducts />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :xl="7" :lg="7" :xs="24">-->
|
||||
<!-- <SalesMappingByCountry />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :xl="7" :lg="7" :xs="24">-->
|
||||
<!-- <VolumeServiceLevel />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
<template>
|
||||
<div class="console">
|
||||
<CardList></CardList>
|
||||
开发中敬请期待...
|
||||
<!--<CardList></CardList>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :md="12" :lg="10">
|
||||
<ActiveUser />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" :lg="14">
|
||||
<SalesOverview />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="10">-->
|
||||
<!-- <ActiveUser />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="14">-->
|
||||
<!-- <SalesOverview />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :md="24" :lg="12">
|
||||
<NewUser />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" :lg="6">
|
||||
<Dynamic />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" :lg="6">
|
||||
<TodoList />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="24" :md="24" :lg="12">-->
|
||||
<!-- <NewUser />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="6">-->
|
||||
<!-- <Dynamic />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="6">-->
|
||||
<!-- <TodoList />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<AboutProject />
|
||||
<!--<AboutProject />-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,59 +1,60 @@
|
||||
<template>
|
||||
<div class="ecommerce">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :md="24" :lg="16">
|
||||
<Banner />
|
||||
</el-col>
|
||||
<el-col :sm="12" :md="12" :lg="4">
|
||||
<TotalOrderVolume />
|
||||
</el-col>
|
||||
<el-col :sm="12" :md="12" :lg="4">
|
||||
<TotalProducts />
|
||||
</el-col>
|
||||
</el-row>
|
||||
开发中敬请期待...
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="24" :md="24" :lg="16">-->
|
||||
<!-- <Banner />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="12" :md="12" :lg="4">-->
|
||||
<!-- <TotalOrderVolume />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="12" :md="12" :lg="4">-->
|
||||
<!-- <TotalProducts />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="12" :md="12" :lg="8">
|
||||
<SalesTrend />
|
||||
</el-col>
|
||||
<el-col :sm="12" :md="12" :lg="8">
|
||||
<SalesClassification />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="24" :lg="8">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :md="12" :lg="12">
|
||||
<ProductSales />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" :lg="12">
|
||||
<SalesGrowth />
|
||||
</el-col>
|
||||
<el-col :span="24" class="no-margin-bottom">
|
||||
<CartConversionRate />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="12" :md="12" :lg="8">-->
|
||||
<!-- <SalesTrend />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="12" :md="12" :lg="8">-->
|
||||
<!-- <SalesClassification />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="24" :lg="8">-->
|
||||
<!-- <el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="12">-->
|
||||
<!-- <ProductSales />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="12">-->
|
||||
<!-- <SalesGrowth />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="24" class="no-margin-bottom">-->
|
||||
<!-- <CartConversionRate />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :md="12" :lg="8">
|
||||
<HotCommodity />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" :lg="8">
|
||||
<AnnualSales />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="24" :lg="8">
|
||||
<TransactionList />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="8">-->
|
||||
<!-- <HotCommodity />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="12" :lg="8">-->
|
||||
<!-- <AnnualSales />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :sm="24" :md="24" :lg="8">-->
|
||||
<!-- <TransactionList />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="24" :lg="8">
|
||||
<RecentTransaction />
|
||||
</el-col>
|
||||
<el-col :md="24" :lg="16" class="no-margin-bottom">
|
||||
<HotProductsList />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--<el-row :gutter="20">-->
|
||||
<!-- <el-col :md="24" :lg="8">-->
|
||||
<!-- <RecentTransaction />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :md="24" :lg="16" class="no-margin-bottom">-->
|
||||
<!-- <HotProductsList />-->
|
||||
<!-- </el-col>-->
|
||||
<!--</el-row>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="my-packages-page" id="table-full-screen">
|
||||
<!-- 搜索栏 -->
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
:items="searchFormItems"
|
||||
:show-expand="false"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
|
||||
<ElCard shadow="never" class="art-table-card">
|
||||
<!-- 表格头部 -->
|
||||
<ArtTableHeader
|
||||
:columnList="columnOptions"
|
||||
v-model:columns="columnChecks"
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
</ArtTableHeader>
|
||||
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:data="packageList"
|
||||
:currentPage="pagination.page"
|
||||
:pageSize="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<ElDialog v-model="detailDialogVisible" title="套餐详情" width="600px">
|
||||
<ElDescriptions :column="2" border v-if="currentPackage">
|
||||
<ElDescriptionsItem label="套餐编码">{{
|
||||
currentPackage.package_code
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="套餐名称">{{
|
||||
currentPackage.package_name
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="所属系列">{{
|
||||
currentPackage.series_name
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="套餐类型">
|
||||
<ElTag :type="getPackageTypeTag(currentPackage.package_type)">{{
|
||||
getPackageTypeLabel(currentPackage.package_type)
|
||||
}}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="流量类型">
|
||||
<ElTag :type="getDataTypeTag(currentPackage.data_type)">{{
|
||||
getDataTypeLabel(currentPackage.data_type)
|
||||
}}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="真流量">{{
|
||||
currentPackage.real_data_mb
|
||||
}}MB</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="虚流量">{{
|
||||
currentPackage.virtual_data_mb
|
||||
}}MB</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="有效期">{{
|
||||
currentPackage.duration_months
|
||||
}}月</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="套餐价格">¥{{
|
||||
(currentPackage.price / 100).toFixed(2)
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="上架状态">
|
||||
<ElTag :type="currentPackage.shelf_status === 1 ? 'success' : 'info'">{{
|
||||
currentPackage.shelf_status === 1 ? '上架' : '下架'
|
||||
}}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">
|
||||
<ElTag :type="currentPackage.status === 1 ? 'success' : 'danger'">{{
|
||||
currentPackage.status === 1 ? '启用' : '禁用'
|
||||
}}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">{{
|
||||
currentPackage.description || '无'
|
||||
}}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton type="primary" @click="detailDialogVisible = false">关闭</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { PackageManageService, PackageSeriesService } from '@/api/modules'
|
||||
import { ElMessage, ElTag, ElDescriptions, ElDescriptionsItem } from 'element-plus'
|
||||
import type { PackageResponse } from '@/types/api'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import {
|
||||
PACKAGE_TYPE_OPTIONS,
|
||||
getPackageTypeLabel,
|
||||
getPackageTypeTag,
|
||||
getDataTypeLabel,
|
||||
getDataTypeTag
|
||||
} from '@/config/constants'
|
||||
|
||||
defineOptions({ name: 'MyPackages' })
|
||||
|
||||
const loading = ref(false)
|
||||
const seriesLoading = ref(false)
|
||||
const detailDialogVisible = ref(false)
|
||||
const tableRef = ref()
|
||||
const currentPackage = ref<PackageResponse | null>(null)
|
||||
const seriesOptions = ref<any[]>([])
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
series_id: undefined as number | undefined,
|
||||
package_type: undefined as string | undefined
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({ ...initialSearchState })
|
||||
|
||||
// 搜索表单配置
|
||||
const searchFormItems = computed<SearchFormItem[]>(() => [
|
||||
{
|
||||
label: '套餐系列',
|
||||
prop: 'series_id',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
remote: true,
|
||||
remoteMethod: searchSeries,
|
||||
loading: seriesLoading.value,
|
||||
placeholder: '请选择或搜索套餐系列'
|
||||
},
|
||||
options: () =>
|
||||
seriesOptions.value.map((s: any) => ({
|
||||
label: s.series_name,
|
||||
value: s.id
|
||||
}))
|
||||
},
|
||||
{
|
||||
label: '套餐类型',
|
||||
prop: 'package_type',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请选择套餐类型'
|
||||
},
|
||||
options: () =>
|
||||
PACKAGE_TYPE_OPTIONS.map((o) => ({
|
||||
label: o.label,
|
||||
value: o.value
|
||||
}))
|
||||
}
|
||||
])
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: 'ID', prop: 'id' },
|
||||
{ label: '套餐编码', prop: 'package_code' },
|
||||
{ label: '套餐名称', prop: 'package_name' },
|
||||
{ label: '所属系列', prop: 'series_name' },
|
||||
{ label: '套餐类型', prop: 'package_type' },
|
||||
{ label: '流量类型', prop: 'data_type' },
|
||||
{ label: '真流量', prop: 'real_data_mb' },
|
||||
{ label: '虚流量', prop: 'virtual_data_mb' },
|
||||
{ label: '有效期', prop: 'duration_months' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
]
|
||||
|
||||
const packageList = ref<PackageResponse[]>([])
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'id',
|
||||
label: 'ID',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'package_code',
|
||||
label: '套餐编码',
|
||||
minWidth: 150
|
||||
},
|
||||
{
|
||||
prop: 'package_name',
|
||||
label: '套餐名称',
|
||||
minWidth: 180
|
||||
},
|
||||
{
|
||||
prop: 'series_name',
|
||||
label: '所属系列',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'package_type',
|
||||
label: '套餐类型',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: getPackageTypeTag(row.package_type), size: 'small' },
|
||||
() => getPackageTypeLabel(row.package_type)
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'data_type',
|
||||
label: '流量类型',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: getDataTypeTag(row.data_type), size: 'small' },
|
||||
() => getDataTypeLabel(row.data_type)
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'real_data_mb',
|
||||
label: '真流量',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => `${row.real_data_mb}MB`
|
||||
},
|
||||
{
|
||||
prop: 'virtual_data_mb',
|
||||
label: '虚流量',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => `${row.virtual_data_mb}MB`
|
||||
},
|
||||
{
|
||||
prop: 'duration_months',
|
||||
label: '有效期',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => `${row.duration_months}月`
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
formatter: (row: PackageResponse) => {
|
||||
return h(ArtButtonTable, {
|
||||
text: '查看详情',
|
||||
onClick: () => showDetail(row)
|
||||
})
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
loadSeriesOptions()
|
||||
getTableData()
|
||||
})
|
||||
|
||||
// 加载套餐系列选项(默认加载10条)
|
||||
const loadSeriesOptions = async (seriesName?: string) => {
|
||||
seriesLoading.value = true
|
||||
try {
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
status: 1
|
||||
}
|
||||
if (seriesName) {
|
||||
params.series_name = seriesName
|
||||
}
|
||||
const res = await PackageSeriesService.getPackageSeries(params)
|
||||
if (res.code === 0) {
|
||||
seriesOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系列选项失败:', error)
|
||||
} finally {
|
||||
seriesLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索系列
|
||||
const searchSeries = (query: string) => {
|
||||
if (query) {
|
||||
loadSeriesOptions(query)
|
||||
} else {
|
||||
loadSeriesOptions()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取我的可售套餐列表
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.page_size,
|
||||
series_id: searchForm.series_id || undefined,
|
||||
package_type: searchForm.package_type || undefined
|
||||
}
|
||||
const res = await PackageManageService.getPackages(params)
|
||||
if (res.code === 0) {
|
||||
packageList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.assign(searchForm, { ...initialSearchState })
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
const handleRefresh = () => {
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.page_size = newPageSize
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.page = newCurrentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 显示详情
|
||||
const showDetail = async (row: PackageResponse) => {
|
||||
try {
|
||||
const res = await PackageManageService.getPackageDetail(row.id)
|
||||
if (res.code === 0) {
|
||||
currentPackage.value = res.data
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('获取套餐详情失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-packages-page {
|
||||
// 可以添加特定样式
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -97,6 +97,7 @@
|
||||
:remote-method="searchPackage"
|
||||
:loading="packageLoading"
|
||||
clearable
|
||||
@change="handlePackageChange"
|
||||
>
|
||||
<ElOption
|
||||
v-for="pkg in packageOptions"
|
||||
@@ -273,7 +274,21 @@
|
||||
const rules = reactive<FormRules>({
|
||||
package_id: [{ required: true, message: '请选择套餐', trigger: 'change' }],
|
||||
shop_id: [{ required: true, message: '请选择店铺', trigger: 'change' }],
|
||||
cost_price: [{ required: true, message: '请输入成本价', trigger: 'blur' }]
|
||||
cost_price: [
|
||||
{ required: true, message: '请输入成本价', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback(new Error('请输入成本价'))
|
||||
} else if (form.package_base_price && value < form.package_base_price) {
|
||||
callback(new Error(`成本价不能低于套餐价格 ¥${(form.package_base_price / 100).toFixed(2)}`))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
@@ -281,7 +296,8 @@
|
||||
id: 0,
|
||||
package_id: undefined,
|
||||
shop_id: undefined,
|
||||
cost_price: 0
|
||||
cost_price: 0,
|
||||
package_base_price: 0 // 存储选中套餐的成本价,用于验证
|
||||
})
|
||||
|
||||
// 成本价表单验证规则
|
||||
@@ -589,11 +605,13 @@
|
||||
form.package_id = row.package_id
|
||||
form.shop_id = row.shop_id
|
||||
form.cost_price = row.cost_price
|
||||
form.package_base_price = 0
|
||||
} else {
|
||||
form.id = 0
|
||||
form.package_id = undefined
|
||||
form.shop_id = undefined
|
||||
form.cost_price = 0
|
||||
form.package_base_price = 0
|
||||
}
|
||||
|
||||
// 重置表单验证状态
|
||||
@@ -602,6 +620,23 @@
|
||||
})
|
||||
}
|
||||
|
||||
// 处理套餐选择变化
|
||||
const handlePackageChange = (packageId: number | undefined) => {
|
||||
if (packageId) {
|
||||
// 从套餐选项中找到选中的套餐
|
||||
const selectedPackage = packageOptions.value.find(pkg => pkg.id === packageId)
|
||||
if (selectedPackage) {
|
||||
// 将套餐的价格设置为成本价
|
||||
form.cost_price = selectedPackage.price
|
||||
form.package_base_price = selectedPackage.price
|
||||
}
|
||||
} else {
|
||||
// 清空时重置成本价
|
||||
form.cost_price = 0
|
||||
form.package_base_price = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 处理弹窗关闭事件
|
||||
const handleDialogClosed = () => {
|
||||
// 清除表单验证状态
|
||||
@@ -611,6 +646,7 @@
|
||||
form.package_id = undefined
|
||||
form.shop_id = undefined
|
||||
form.cost_price = 0
|
||||
form.package_base_price = 0
|
||||
}
|
||||
|
||||
// 删除分配
|
||||
|
||||
@@ -69,6 +69,20 @@
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<div v-if="dialogType === 'edit'" class="info-row">
|
||||
<div class="info-item">
|
||||
<span class="info-label">系列名称:</span>
|
||||
<span class="info-value">{{ form.series_name || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">店铺名称:</span>
|
||||
<span class="info-value">{{ form.shop_name || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">分配者店铺:</span>
|
||||
<span class="info-value">{{ form.allocator_shop_name || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ElFormItem label="选择店铺" prop="shop_id" v-if="dialogType === 'add'">
|
||||
<ElSelect
|
||||
v-model="form.shop_id"
|
||||
@@ -121,52 +135,97 @@
|
||||
</div>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 梯度返佣配置 -->
|
||||
<ElDivider content-position="left">梯度返佣设置(可选)</ElDivider>
|
||||
<ElFormItem label="启用梯度返佣">
|
||||
<ElSwitch v-model="form.enable_tier_commission" />
|
||||
<!-- 一次性佣金配置 -->
|
||||
<ElDivider content-position="left">一次性佣金设置(可选)</ElDivider>
|
||||
<ElFormItem label="启用一次性佣金">
|
||||
<ElSwitch v-model="form.enable_one_time_commission" />
|
||||
</ElFormItem>
|
||||
|
||||
<template v-if="form.enable_tier_commission">
|
||||
<ElFormItem label="周期类型" prop="tier_config.period_type">
|
||||
<ElSelect v-model="form.tier_config.period_type" placeholder="请选择周期类型" style="width: 100%">
|
||||
<ElOption label="月度" value="monthly" />
|
||||
<ElOption label="季度" value="quarterly" />
|
||||
<ElOption label="年度" value="yearly" />
|
||||
</ElSelect>
|
||||
<template v-if="form.enable_one_time_commission">
|
||||
<ElFormItem label="一次性佣金类型" prop="one_time_commission_config.type">
|
||||
<ElRadioGroup v-model="form.one_time_commission_config.type">
|
||||
<ElRadio value="fixed">固定</ElRadio>
|
||||
<ElRadio value="tiered">梯度</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="梯度类型" prop="tier_config.tier_type">
|
||||
<ElSelect v-model="form.tier_config.tier_type" placeholder="请选择梯度类型" style="width: 100%">
|
||||
<ElOption label="按销量" value="sales_count" />
|
||||
<ElOption label="按销售额" value="sales_amount" />
|
||||
</ElSelect>
|
||||
|
||||
<ElFormItem label="触发条件" prop="one_time_commission_config.trigger">
|
||||
<ElRadioGroup v-model="form.one_time_commission_config.trigger">
|
||||
<ElRadio value="single_recharge">单次充值</ElRadio>
|
||||
<ElRadio value="accumulated_recharge">累计充值</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="梯度档位">
|
||||
<div class="tier-list">
|
||||
<div v-for="(tier, index) in form.tier_config.tiers" :key="index" class="tier-item">
|
||||
<ElInputNumber
|
||||
v-model="tier.threshold"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
placeholder="阈值"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<ElSelect v-model="tier.mode" placeholder="模式" style="width: 100px">
|
||||
<ElOption label="固定" value="fixed" />
|
||||
<ElOption label="百分比" value="percent" />
|
||||
</ElSelect>
|
||||
<ElInputNumber
|
||||
v-model="tier.value"
|
||||
:min="0"
|
||||
:controls="false"
|
||||
placeholder="返佣值"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<ElButton type="danger" @click="removeTier(index)">删除</ElButton>
|
||||
|
||||
<ElFormItem label="最低阈值(分)" prop="one_time_commission_config.threshold">
|
||||
<ElInputNumber
|
||||
v-model="form.one_time_commission_config.threshold"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
placeholder="请输入最低阈值(分)"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 固定类型配置 -->
|
||||
<template v-if="form.one_time_commission_config.type === 'fixed'">
|
||||
<ElFormItem label="返佣模式" prop="one_time_commission_config.mode">
|
||||
<ElRadioGroup v-model="form.one_time_commission_config.mode">
|
||||
<ElRadio value="fixed">固定金额</ElRadio>
|
||||
<ElRadio value="percent">百分比</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem
|
||||
:label="form.one_time_commission_config.mode === 'fixed' ? '佣金金额(分)' : '佣金比例(千分比)'"
|
||||
prop="one_time_commission_config.value"
|
||||
>
|
||||
<ElInputNumber
|
||||
v-model="form.one_time_commission_config.value"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
:placeholder="
|
||||
form.one_time_commission_config.mode === 'fixed'
|
||||
? '请输入佣金金额(分)'
|
||||
: '请输入佣金比例的千分比(如200表示20%)'
|
||||
"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<!-- 梯度类型配置 -->
|
||||
<template v-if="form.one_time_commission_config.type === 'tiered'">
|
||||
<ElFormItem label="梯度档位">
|
||||
<div class="tier-list">
|
||||
<div v-for="(tier, index) in form.one_time_commission_config.tiers" :key="index" class="tier-item">
|
||||
<ElSelect v-model="tier.tier_type" placeholder="梯度类型" style="width: 120px">
|
||||
<ElOption label="销量" value="sales_count" />
|
||||
<ElOption label="销售额" value="sales_amount" />
|
||||
</ElSelect>
|
||||
<ElInputNumber
|
||||
v-model="tier.threshold"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
placeholder="阈值"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<ElSelect v-model="tier.mode" placeholder="返佣模式" style="width: 120px">
|
||||
<ElOption label="固定金额" value="fixed" />
|
||||
<ElOption label="百分比" value="percent" />
|
||||
</ElSelect>
|
||||
<ElInputNumber
|
||||
v-model="tier.value"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
placeholder="返佣值"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<ElButton type="danger" @click="removeTier(index)">删除</ElButton>
|
||||
</div>
|
||||
<ElButton type="primary" @click="addTier">添加档位</ElButton>
|
||||
</div>
|
||||
<ElButton type="primary" @click="addTier">添加档位</ElButton>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
</template>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
@@ -295,7 +354,7 @@
|
||||
{ label: '店铺名称', prop: 'shop_name' },
|
||||
{ label: '分配者店铺', prop: 'allocator_shop_name' },
|
||||
{ label: '基础返佣', prop: 'base_commission' },
|
||||
{ label: '梯度返佣', prop: 'enable_tier_commission' },
|
||||
{ label: '一次性佣金', prop: 'enable_one_time_commission' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
@@ -306,14 +365,20 @@
|
||||
id: 0,
|
||||
series_id: undefined,
|
||||
shop_id: undefined,
|
||||
series_name: '',
|
||||
shop_name: '',
|
||||
allocator_shop_name: '',
|
||||
base_commission: {
|
||||
mode: 'fixed',
|
||||
value: 0
|
||||
},
|
||||
enable_tier_commission: false,
|
||||
tier_config: {
|
||||
period_type: 'monthly',
|
||||
tier_type: 'sales_count',
|
||||
enable_one_time_commission: false,
|
||||
one_time_commission_config: {
|
||||
type: 'fixed',
|
||||
trigger: 'single_recharge',
|
||||
threshold: 0,
|
||||
mode: 'fixed',
|
||||
value: 0,
|
||||
tiers: []
|
||||
}
|
||||
})
|
||||
@@ -341,14 +406,27 @@
|
||||
]
|
||||
}
|
||||
|
||||
// 如果启用了梯度返佣,添加梯度返佣的验证规则
|
||||
if (form.enable_tier_commission) {
|
||||
baseRules['tier_config.period_type'] = [
|
||||
{ required: true, message: '请选择周期类型', trigger: 'change' }
|
||||
// 如果启用了一次性佣金,添加验证规则
|
||||
if (form.enable_one_time_commission) {
|
||||
baseRules['one_time_commission_config.type'] = [
|
||||
{ required: true, message: '请选择一次性佣金类型', trigger: 'change' }
|
||||
]
|
||||
baseRules['tier_config.tier_type'] = [
|
||||
{ required: true, message: '请选择梯度类型', trigger: 'change' }
|
||||
baseRules['one_time_commission_config.trigger'] = [
|
||||
{ required: true, message: '请选择触发条件', trigger: 'change' }
|
||||
]
|
||||
baseRules['one_time_commission_config.threshold'] = [
|
||||
{ required: true, message: '请输入最低阈值', trigger: 'blur' }
|
||||
]
|
||||
|
||||
// 固定类型验证
|
||||
if (form.one_time_commission_config.type === 'fixed') {
|
||||
baseRules['one_time_commission_config.mode'] = [
|
||||
{ required: true, message: '请选择返佣模式', trigger: 'change' }
|
||||
]
|
||||
baseRules['one_time_commission_config.value'] = [
|
||||
{ required: true, message: '请输入佣金值', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return baseRules
|
||||
@@ -397,14 +475,14 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'enable_tier_commission',
|
||||
label: '梯度返佣',
|
||||
width: 100,
|
||||
prop: 'enable_one_time_commission',
|
||||
label: '一次性佣金',
|
||||
width: 120,
|
||||
formatter: (row: ShopSeriesAllocationResponse) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: row.enable_tier_commission ? 'success' : 'info', size: 'small' },
|
||||
() => (row.enable_tier_commission ? '已启用' : '未启用')
|
||||
{ type: row.enable_one_time_commission ? 'success' : 'info', size: 'small' },
|
||||
() => (row.enable_one_time_commission ? '已启用' : '未启用')
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -647,7 +725,8 @@
|
||||
|
||||
// 添加档位
|
||||
const addTier = () => {
|
||||
form.tier_config.tiers.push({
|
||||
form.one_time_commission_config.tiers.push({
|
||||
tier_type: 'sales_count',
|
||||
threshold: 0,
|
||||
mode: 'fixed',
|
||||
value: 0
|
||||
@@ -656,7 +735,7 @@
|
||||
|
||||
// 删除档位
|
||||
const removeTier = (index: number) => {
|
||||
form.tier_config.tiers.splice(index, 1)
|
||||
form.one_time_commission_config.tiers.splice(index, 1)
|
||||
}
|
||||
|
||||
// 显示新增/编辑对话框
|
||||
@@ -668,21 +747,30 @@
|
||||
form.id = row.id
|
||||
form.series_id = row.series_id
|
||||
form.shop_id = row.shop_id
|
||||
form.series_name = row.series_name
|
||||
form.shop_name = row.shop_name
|
||||
form.allocator_shop_name = row.allocator_shop_name
|
||||
form.base_commission = {
|
||||
mode: row.base_commission.mode,
|
||||
value: row.base_commission.value
|
||||
}
|
||||
form.enable_tier_commission = row.enable_tier_commission
|
||||
if (row.enable_tier_commission && row.tier_config) {
|
||||
form.tier_config = {
|
||||
period_type: row.tier_config.period_type,
|
||||
tier_type: row.tier_config.tier_type,
|
||||
tiers: row.tier_config.tiers.map((t) => ({ ...t }))
|
||||
form.enable_one_time_commission = row.enable_one_time_commission
|
||||
if (row.enable_one_time_commission && row.one_time_commission_config) {
|
||||
form.one_time_commission_config = {
|
||||
type: row.one_time_commission_config.type,
|
||||
trigger: row.one_time_commission_config.trigger,
|
||||
threshold: row.one_time_commission_config.threshold,
|
||||
mode: row.one_time_commission_config.mode || 'fixed',
|
||||
value: row.one_time_commission_config.value || 0,
|
||||
tiers: row.one_time_commission_config.tiers?.map((t) => ({ ...t })) || []
|
||||
}
|
||||
} else {
|
||||
form.tier_config = {
|
||||
period_type: 'monthly',
|
||||
tier_type: 'sales_count',
|
||||
form.one_time_commission_config = {
|
||||
type: 'fixed',
|
||||
trigger: 'single_recharge',
|
||||
threshold: 0,
|
||||
mode: 'fixed',
|
||||
value: 0,
|
||||
tiers: []
|
||||
}
|
||||
}
|
||||
@@ -690,14 +778,20 @@
|
||||
form.id = 0
|
||||
form.series_id = undefined
|
||||
form.shop_id = undefined
|
||||
form.series_name = ''
|
||||
form.shop_name = ''
|
||||
form.allocator_shop_name = ''
|
||||
form.base_commission = {
|
||||
mode: 'fixed',
|
||||
value: 0
|
||||
}
|
||||
form.enable_tier_commission = false
|
||||
form.tier_config = {
|
||||
period_type: 'monthly',
|
||||
tier_type: 'sales_count',
|
||||
form.enable_one_time_commission = false
|
||||
form.one_time_commission_config = {
|
||||
type: 'fixed',
|
||||
trigger: 'single_recharge',
|
||||
threshold: 0,
|
||||
mode: 'fixed',
|
||||
value: 0,
|
||||
tiers: []
|
||||
}
|
||||
}
|
||||
@@ -716,14 +810,20 @@
|
||||
form.id = 0
|
||||
form.series_id = undefined
|
||||
form.shop_id = undefined
|
||||
form.series_name = ''
|
||||
form.shop_name = ''
|
||||
form.allocator_shop_name = ''
|
||||
form.base_commission = {
|
||||
mode: 'fixed',
|
||||
value: 0
|
||||
}
|
||||
form.enable_tier_commission = false
|
||||
form.tier_config = {
|
||||
period_type: 'monthly',
|
||||
tier_type: 'sales_count',
|
||||
form.enable_one_time_commission = false
|
||||
form.one_time_commission_config = {
|
||||
type: 'fixed',
|
||||
trigger: 'single_recharge',
|
||||
threshold: 0,
|
||||
mode: 'fixed',
|
||||
value: 0,
|
||||
tiers: []
|
||||
}
|
||||
}
|
||||
@@ -759,20 +859,22 @@
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
// 验证梯度档位
|
||||
if (form.enable_tier_commission) {
|
||||
if (form.tier_config.tiers.length === 0) {
|
||||
ElMessage.warning('启用梯度返佣时至少需要添加一个档位')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证档位阈值递增
|
||||
const thresholds = form.tier_config.tiers.map((t: any) => t.threshold)
|
||||
for (let i = 1; i < thresholds.length; i++) {
|
||||
if (thresholds[i] <= thresholds[i - 1]) {
|
||||
ElMessage.warning('档位阈值必须递增')
|
||||
// 验证一次性佣金配置
|
||||
if (form.enable_one_time_commission) {
|
||||
if (form.one_time_commission_config.type === 'tiered') {
|
||||
if (form.one_time_commission_config.tiers.length === 0) {
|
||||
ElMessage.warning('启用梯度类型时至少需要添加一个档位')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证档位阈值递增
|
||||
const thresholds = form.one_time_commission_config.tiers.map((t: any) => t.threshold)
|
||||
for (let i = 1; i < thresholds.length; i++) {
|
||||
if (thresholds[i] <= thresholds[i - 1]) {
|
||||
ElMessage.warning('档位阈值必须递增')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,15 +885,26 @@
|
||||
mode: form.base_commission.mode,
|
||||
value: form.base_commission.value
|
||||
},
|
||||
enable_tier_commission: form.enable_tier_commission
|
||||
enable_one_time_commission: form.enable_one_time_commission
|
||||
}
|
||||
|
||||
// 如果启用了梯度返佣,加入梯度配置
|
||||
if (form.enable_tier_commission) {
|
||||
data.tier_config = {
|
||||
period_type: form.tier_config.period_type,
|
||||
tier_type: form.tier_config.tier_type,
|
||||
tiers: form.tier_config.tiers.map((t: any) => ({
|
||||
// 如果启用了一次性佣金,加入配置
|
||||
if (form.enable_one_time_commission) {
|
||||
data.one_time_commission_config = {
|
||||
type: form.one_time_commission_config.type,
|
||||
trigger: form.one_time_commission_config.trigger,
|
||||
threshold: form.one_time_commission_config.threshold
|
||||
}
|
||||
|
||||
// 固定类型配置
|
||||
if (form.one_time_commission_config.type === 'fixed') {
|
||||
data.one_time_commission_config.mode = form.one_time_commission_config.mode
|
||||
data.one_time_commission_config.value = form.one_time_commission_config.value
|
||||
}
|
||||
// 梯度类型配置
|
||||
else if (form.one_time_commission_config.type === 'tiered') {
|
||||
data.one_time_commission_config.tiers = form.one_time_commission_config.tiers.map((t: any) => ({
|
||||
tier_type: t.tier_type,
|
||||
threshold: t.threshold,
|
||||
mode: t.mode,
|
||||
value: t.value
|
||||
@@ -864,4 +977,30 @@
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 18px;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: var(--art-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user