fetch(modify):修改套餐接口
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m40s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m40s
This commit is contained in:
82
openspec/changes/update-package-management-api/proposal.md
Normal file
82
openspec/changes/update-package-management-api/proposal.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Change: 更新套餐管理API以支持新的佣金配置模型
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
根据最新的API文档(`docs/修改原来的套餐管理.md`),后端API规范发生了重大变更,主要涉及:
|
||||||
|
|
||||||
|
1. **套餐系列** - 新增一次性佣金配置支持(包括固定/梯度佣金、强制充值、时效类型等复杂配置)
|
||||||
|
2. **系列分配** - 佣金配置模型完全重构,从简单的base_commission改为更细粒度的配置
|
||||||
|
3. **单套餐分配** - 新增系列关联字段和分配者信息字段
|
||||||
|
|
||||||
|
当前前端实现的类型定义(`src/types/api/packageManagement.ts`)和API服务与新规范不匹配,需要更新以支持新的数据结构。
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
### 1. 类型定义重构
|
||||||
|
|
||||||
|
**修改**: `src/types/api/packageManagement.ts`
|
||||||
|
|
||||||
|
**套餐系列相关类型**:
|
||||||
|
- `PackageSeriesResponse` - 新增 `enable_one_time_commission` 和 `one_time_commission_config`
|
||||||
|
- `CreatePackageSeriesRequest` - 新增一次性佣金配置字段
|
||||||
|
- `UpdatePackageSeriesRequest` - 新增一次性佣金配置字段
|
||||||
|
- 新增 `SeriesOneTimeCommissionConfig` - 一次性佣金配置(支持固定/梯度模式)
|
||||||
|
- 新增 `OneTimeCommissionTier` - 梯度佣金档位配置
|
||||||
|
|
||||||
|
**系列分配相关类型**:
|
||||||
|
- **BREAKING**: `ShopSeriesAllocationResponse` - 完全重构字段结构
|
||||||
|
- 移除: `base_commission` (BaseCommissionConfig)
|
||||||
|
- 新增: `series_code`, `enable_force_recharge`, `enable_one_time_commission`, `force_recharge_amount`, `force_recharge_trigger_type`, `one_time_commission_amount`, `one_time_commission_threshold`, `one_time_commission_trigger`
|
||||||
|
- **BREAKING**: `CreateShopSeriesAllocationRequest` - 重构请求字段
|
||||||
|
- **BREAKING**: `UpdateShopSeriesAllocationRequest` - 重构请求字段
|
||||||
|
- 移除不再使用的类型: `BaseCommissionConfig`, `TierEntry`, `TierCommissionConfig`, `OneTimeCommissionTierEntry`, `OneTimeCommissionConfig`(旧版)
|
||||||
|
|
||||||
|
**单套餐分配相关类型**:
|
||||||
|
- `ShopPackageAllocationResponse` - 字段调整
|
||||||
|
- 新增: `series_id`, `series_name`, `series_allocation_id`, `allocator_shop_id`, `allocator_shop_name`
|
||||||
|
- 移除: `allocation_id` (重命名为 `series_allocation_id`), `calculated_cost_price`
|
||||||
|
- `ShopPackageAllocationQueryParams` - 新增 `series_allocation_id`, `allocator_shop_id` 筛选参数
|
||||||
|
|
||||||
|
### 2. API服务层无需修改
|
||||||
|
|
||||||
|
现有的API服务类(`PackageSeriesService`, `ShopSeriesAllocationService`, `ShopPackageAllocationService`)的方法签名保持不变,只是底层数据类型发生变化。
|
||||||
|
|
||||||
|
### 3. 页面/组件适配(实施阶段处理)
|
||||||
|
|
||||||
|
以下文件需要适配新的数据结构(本提案阶段不编写代码):
|
||||||
|
- 套餐系列管理页面 - 需支持一次性佣金配置表单
|
||||||
|
- 系列分配页面 - 需重构佣金配置表单UI
|
||||||
|
- 单套餐分配页面 - 需展示新增的关联字段
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
### 受影响的规范
|
||||||
|
|
||||||
|
- `package-series-management` - MODIFIED (新增一次性佣金配置能力)
|
||||||
|
- `shop-series-allocation` - MODIFIED (佣金配置模型重构)
|
||||||
|
- `shop-package-allocation` - MODIFIED (新增系列关联和分配者字段)
|
||||||
|
|
||||||
|
### 受影响的代码
|
||||||
|
|
||||||
|
- `src/types/api/packageManagement.ts` - **BREAKING CHANGE** 类型定义重构
|
||||||
|
- 所有使用 `ShopSeriesAllocationResponse` 的页面/组件 - 需适配新字段结构
|
||||||
|
- 所有使用 `PackageSeriesResponse` 的页面/组件 - 需处理新增的一次性佣金配置
|
||||||
|
|
||||||
|
### 依赖关系
|
||||||
|
|
||||||
|
- 后端API已按新规范实现(`docs/修改原来的套餐管理.md`)
|
||||||
|
- 前端需要先完成类型定义更新,再更新UI组件
|
||||||
|
|
||||||
|
### 风险评估
|
||||||
|
|
||||||
|
- **高风险**: 这是一个BREAKING CHANGE,会影响所有使用系列分配的功能
|
||||||
|
- **兼容性**: 需要确保后端API已更新,否则会导致运行时错误
|
||||||
|
- **迁移成本**: 现有页面中的表单组件需要重写以支持新的数据结构
|
||||||
|
- **测试需求**: 需要全面测试套餐系列、系列分配、单套餐分配的所有CRUD操作
|
||||||
|
|
||||||
|
### 建议迁移策略
|
||||||
|
|
||||||
|
1. 先更新类型定义,确保TypeScript编译通过
|
||||||
|
2. 逐个页面/组件适配新数据结构
|
||||||
|
3. 与后端联调确认新API规范工作正常
|
||||||
|
4. 完成后删除旧的、不再使用的类型定义
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# Package Series Management Specification Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Package Series Data Model
|
||||||
|
|
||||||
|
套餐系列的数据模型SHALL支持一次性佣金配置,包括固定佣金、梯度佣金、强制充值、时效类型等高级功能。
|
||||||
|
|
||||||
|
**字段定义**:
|
||||||
|
- `id` (number) - 系列ID
|
||||||
|
- `series_code` (string) - 系列编码,唯一标识
|
||||||
|
- `series_name` (string) - 系列名称
|
||||||
|
- `description` (string, optional) - 系列描述
|
||||||
|
- `enable_one_time_commission` (boolean) - 是否启用一次性佣金
|
||||||
|
- `one_time_commission_config` (SeriesOneTimeCommissionConfig, optional) - 一次性佣金配置对象
|
||||||
|
- `status` (number) - 状态 (1:启用, 2:禁用)
|
||||||
|
- `created_at` (string) - 创建时间
|
||||||
|
- `updated_at` (string) - 更新时间
|
||||||
|
|
||||||
|
**一次性佣金配置结构** (`SeriesOneTimeCommissionConfig`):
|
||||||
|
- `enable` (boolean) - 是否启用一次性佣金
|
||||||
|
- `commission_type` (string) - 佣金类型: "fixed"(固定) | "tiered"(梯度)
|
||||||
|
- `commission_amount` (number, optional) - 固定佣金金额(分),commission_type=fixed时使用
|
||||||
|
- `threshold` (number) - 触发阈值(分)
|
||||||
|
- `trigger_type` (string) - 触发类型: "first_recharge"(首次充值) | "accumulated_recharge"(累计充值)
|
||||||
|
- `tiers` (OneTimeCommissionTier[], optional) - 梯度配置列表,commission_type=tiered时使用
|
||||||
|
- `enable_force_recharge` (boolean) - 是否启用强充
|
||||||
|
- `force_amount` (number, optional) - 强充金额(分)
|
||||||
|
- `force_calc_type` (string, optional) - 强充计算类型: "fixed"(固定) | "dynamic"(动态)
|
||||||
|
- `validity_type` (string) - 时效类型: "permanent"(永久) | "fixed_date"(固定日期) | "relative"(相对时长)
|
||||||
|
- `validity_value` (string, optional) - 时效值(日期字符串或月数)
|
||||||
|
|
||||||
|
**梯度档位结构** (`OneTimeCommissionTier`):
|
||||||
|
- `threshold` (number) - 达标阈值
|
||||||
|
- `dimension` (string) - 统计维度: "sales_count"(销量) | "sales_amount"(销售额)
|
||||||
|
- `amount` (number) - 佣金金额(分)
|
||||||
|
- `stat_scope` (string, optional) - 统计范围: "self"(仅自己) | "self_and_sub"(自己+下级)
|
||||||
|
|
||||||
|
#### Scenario: 创建套餐系列并启用固定一次性佣金
|
||||||
|
|
||||||
|
- **GIVEN** 管理员登录系统
|
||||||
|
- **WHEN** 创建套餐系列时提供以下配置:
|
||||||
|
- `series_code`: "SERIES001"
|
||||||
|
- `series_name`: "标准流量系列"
|
||||||
|
- `enable_one_time_commission`: true
|
||||||
|
- `one_time_commission_config`:
|
||||||
|
- `enable`: true
|
||||||
|
- `commission_type`: "fixed"
|
||||||
|
- `commission_amount`: 5000 (50元)
|
||||||
|
- `threshold`: 10000 (100元)
|
||||||
|
- `trigger_type`: "first_recharge"
|
||||||
|
- `validity_type`: "permanent"
|
||||||
|
- **THEN** 系统SHALL创建套餐系列,并保存一次性佣金配置
|
||||||
|
|
||||||
|
#### Scenario: 创建套餐系列并启用梯度一次性佣金
|
||||||
|
|
||||||
|
- **GIVEN** 管理员登录系统
|
||||||
|
- **WHEN** 创建套餐系列时提供以下配置:
|
||||||
|
- `series_code`: "SERIES002"
|
||||||
|
- `series_name`: "高级流量系列"
|
||||||
|
- `enable_one_time_commission`: true
|
||||||
|
- `one_time_commission_config`:
|
||||||
|
- `enable`: true
|
||||||
|
- `commission_type`: "tiered"
|
||||||
|
- `threshold`: 5000
|
||||||
|
- `trigger_type`: "accumulated_recharge"
|
||||||
|
- `tiers`: [
|
||||||
|
{ threshold: 10, dimension: "sales_count", amount: 3000 },
|
||||||
|
{ threshold: 50, dimension: "sales_count", amount: 8000 }
|
||||||
|
]
|
||||||
|
- `validity_type`: "relative"
|
||||||
|
- `validity_value`: "12" (12个月)
|
||||||
|
- **THEN** 系统SHALL创建套餐系列,并保存梯度佣金配置
|
||||||
|
|
||||||
|
#### Scenario: 查询启用一次性佣金的套餐系列
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个套餐系列,部分启用了一次性佣金
|
||||||
|
- **WHEN** 使用查询参数 `enable_one_time_commission=true` 查询列表
|
||||||
|
- **THEN** 系统SHALL返回所有启用一次性佣金的套餐系列
|
||||||
|
|
||||||
|
#### Scenario: 更新套餐系列的一次性佣金配置
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为1的套餐系列
|
||||||
|
- **WHEN** 更新该系列时提供新的一次性佣金配置:
|
||||||
|
- `one_time_commission_config.commission_amount`: 8000 (从5000更新到8000)
|
||||||
|
- `one_time_commission_config.enable_force_recharge`: true
|
||||||
|
- `one_time_commission_config.force_amount`: 20000
|
||||||
|
- `one_time_commission_config.force_calc_type`: "fixed"
|
||||||
|
- **THEN** 系统SHALL更新套餐系列的一次性佣金配置,并返回更新后的数据
|
||||||
|
|
||||||
|
#### Scenario: 获取套餐系列详情时包含完整的一次性佣金配置
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为1的套餐系列,已配置一次性佣金
|
||||||
|
- **WHEN** 请求获取该系列的详情 (GET /api/admin/package-series/1)
|
||||||
|
- **THEN** 系统SHALL返回包含完整 `one_time_commission_config` 对象的响应数据
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: One-Time Commission Query Filter
|
||||||
|
|
||||||
|
系统SHALL支持按一次性佣金启用状态筛选套餐系列列表。
|
||||||
|
|
||||||
|
#### Scenario: 按一次性佣金启用状态筛选
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个套餐系列
|
||||||
|
- **WHEN** 使用查询参数 `enable_one_time_commission=true`
|
||||||
|
- **THEN** 系统SHALL只返回 `enable_one_time_commission` 为 true 的套餐系列
|
||||||
|
- **AND** 分页信息正确反映筛选后的结果数量
|
||||||
|
|
||||||
|
### Requirement: One-Time Commission Configuration Validation
|
||||||
|
|
||||||
|
系统SHALL验证一次性佣金配置的完整性和逻辑一致性。
|
||||||
|
|
||||||
|
#### Scenario: 固定佣金模式的验证
|
||||||
|
|
||||||
|
- **GIVEN** 管理员创建套餐系列
|
||||||
|
- **WHEN** `commission_type` 为 "fixed"
|
||||||
|
- **THEN** 系统SHALL要求 `commission_amount` 字段必填
|
||||||
|
- **AND** `tiers` 字段不应被使用
|
||||||
|
|
||||||
|
#### Scenario: 梯度佣金模式的验证
|
||||||
|
|
||||||
|
- **GIVEN** 管理员创建套餐系列
|
||||||
|
- **WHEN** `commission_type` 为 "tiered"
|
||||||
|
- **THEN** 系统SHALL要求 `tiers` 数组不为空
|
||||||
|
- **AND** 每个梯度档位的 `threshold`, `dimension`, `amount` 字段必填
|
||||||
|
|
||||||
|
#### Scenario: 强制充值配置的验证
|
||||||
|
|
||||||
|
- **GIVEN** 管理员创建套餐系列
|
||||||
|
- **WHEN** `enable_force_recharge` 为 true
|
||||||
|
- **THEN** 系统SHALL要求 `force_amount` 和 `force_calc_type` 字段必填
|
||||||
|
|
||||||
|
#### Scenario: 时效配置的验证
|
||||||
|
|
||||||
|
- **GIVEN** 管理员创建套餐系列
|
||||||
|
- **WHEN** `validity_type` 为 "fixed_date"
|
||||||
|
- **THEN** 系统SHALL要求 `validity_value` 字段必填,且格式为有效的日期字符串
|
||||||
|
- **WHEN** `validity_type` 为 "relative"
|
||||||
|
- **THEN** 系统SHALL要求 `validity_value` 字段必填,且为表示月数的数字字符串
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# Shop Package Allocation Specification Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Shop Package Allocation Data Model
|
||||||
|
|
||||||
|
单套餐分配的数据模型SHALL新增系列关联字段和分配者信息字段,以支持更完整的分配关系追溯。
|
||||||
|
|
||||||
|
**字段定义**:
|
||||||
|
- `id` (number) - 分配ID
|
||||||
|
- `package_id` (number) - 套餐ID
|
||||||
|
- `package_code` (string) - 套餐编码
|
||||||
|
- `package_name` (string) - 套餐名称
|
||||||
|
- `series_id` (number) - 套餐系列ID
|
||||||
|
- `series_name` (string) - 套餐系列名称
|
||||||
|
- `shop_id` (number) - 被分配的店铺ID
|
||||||
|
- `shop_name` (string) - 被分配的店铺名称
|
||||||
|
- `allocator_shop_id` (number) - 分配者店铺ID,0表示平台分配
|
||||||
|
- `allocator_shop_name` (string) - 分配者店铺名称
|
||||||
|
- `series_allocation_id` (number, nullable) - 关联的系列分配ID(可选)
|
||||||
|
- `cost_price` (number) - 该代理的成本价(分)
|
||||||
|
- `status` (number) - 状态 (1:启用, 2:禁用)
|
||||||
|
- `created_at` (string) - 创建时间
|
||||||
|
- `updated_at` (string) - 更新时间
|
||||||
|
|
||||||
|
**查询参数** (`ShopPackageAllocationQueryParams`):
|
||||||
|
- `page` (number, optional) - 页码
|
||||||
|
- `page_size` (number, optional) - 每页数量
|
||||||
|
- `shop_id` (number, optional) - 被分配的店铺ID
|
||||||
|
- `package_id` (number, optional) - 套餐ID
|
||||||
|
- `series_allocation_id` (number, optional) - 系列分配ID
|
||||||
|
- `allocator_shop_id` (number, optional) - 分配者店铺ID
|
||||||
|
- `status` (number, optional) - 状态
|
||||||
|
|
||||||
|
#### Scenario: 创建单套餐分配时包含系列信息
|
||||||
|
|
||||||
|
- **GIVEN** 平台管理员登录系统
|
||||||
|
- **AND** 系统中存在ID为10的套餐,该套餐属于系列ID为2,系列名称为"标准流量系列"
|
||||||
|
- **AND** 系统中存在ID为100的店铺
|
||||||
|
- **WHEN** 创建单套餐分配:
|
||||||
|
- `package_id`: 10
|
||||||
|
- `shop_id`: 100
|
||||||
|
- `cost_price`: 15000
|
||||||
|
- **THEN** 系统SHALL创建单套餐分配记录
|
||||||
|
- **AND** 响应数据中自动填充 `series_id` 为 2
|
||||||
|
- **AND** 响应数据中自动填充 `series_name` 为 "标准流量系列"
|
||||||
|
- **AND** 响应数据中 `allocator_shop_id` 为 0(平台分配)
|
||||||
|
|
||||||
|
#### Scenario: 创建单套餐分配并关联系列分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为20的系列分配,关联系列ID为2,店铺ID为100
|
||||||
|
- **AND** 系列2下存在套餐ID为10
|
||||||
|
- **WHEN** 为店铺100创建套餐10的分配
|
||||||
|
- **THEN** 系统MAY自动关联系列分配,设置 `series_allocation_id` 为 20
|
||||||
|
|
||||||
|
#### Scenario: 查询单套餐分配列表并显示完整关联信息
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个单套餐分配
|
||||||
|
- **WHEN** 查询单套餐分配列表 (GET /api/admin/shop-package-allocations)
|
||||||
|
- **THEN** 系统SHALL返回所有分配记录
|
||||||
|
- **AND** 每条记录包含 `series_id`, `series_name`, `allocator_shop_id`, `allocator_shop_name`
|
||||||
|
- **AND** 如果存在关联的系列分配,`series_allocation_id` 不为空
|
||||||
|
|
||||||
|
#### Scenario: 按系列分配ID筛选单套餐分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个单套餐分配,部分关联了系列分配ID为20的系列分配
|
||||||
|
- **WHEN** 使用查询参数 `series_allocation_id=20`
|
||||||
|
- **THEN** 系统SHALL返回所有 `series_allocation_id` 为 20 的单套餐分配记录
|
||||||
|
|
||||||
|
#### Scenario: 按分配者店铺ID筛选单套餐分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个单套餐分配
|
||||||
|
- **WHEN** 使用查询参数 `allocator_shop_id=50`
|
||||||
|
- **THEN** 系统SHALL返回所有由店铺ID为50的代理商创建的单套餐分配记录
|
||||||
|
|
||||||
|
#### Scenario: 获取单套餐分配详情时显示完整信息
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为100的单套餐分配
|
||||||
|
- **WHEN** 请求获取该分配的详情 (GET /api/admin/shop-package-allocations/100)
|
||||||
|
- **THEN** 响应数据SHALL包含所有字段,包括:
|
||||||
|
- `series_id`, `series_name` - 套餐所属系列信息
|
||||||
|
- `allocator_shop_id`, `allocator_shop_name` - 分配者信息
|
||||||
|
- `series_allocation_id` - 关联的系列分配ID(如果存在)
|
||||||
|
|
||||||
|
## RENAMED Requirements
|
||||||
|
|
||||||
|
- FROM: `allocation_id`
|
||||||
|
- TO: `series_allocation_id`
|
||||||
|
|
||||||
|
**说明**: 将字段名从 `allocation_id` 重命名为 `series_allocation_id`,使其语义更加明确,表示关联的系列分配ID。
|
||||||
|
|
||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
### Requirement: Calculated Cost Price Field
|
||||||
|
|
||||||
|
旧版的 `calculated_cost_price` 字段已被移除。
|
||||||
|
|
||||||
|
**Reason**: 该字段仅用于参考,增加了数据模型的复杂性,且前端很少使用。成本价信息应直接使用 `cost_price` 字段。
|
||||||
|
|
||||||
|
**Migration**: 移除所有使用 `calculated_cost_price` 字段的代码。如果需要查看原始成本价信息,可以通过关联的系列分配或套餐系列获取。
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Series Information in Package Allocation
|
||||||
|
|
||||||
|
系统SHALL在单套餐分配响应数据中包含套餐所属系列的ID和名称。
|
||||||
|
|
||||||
|
#### Scenario: 创建分配时自动关联系列信息
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在套餐,且该套餐属于某个系列
|
||||||
|
- **WHEN** 创建单套餐分配
|
||||||
|
- **THEN** 系统SHALL自动从套餐数据中获取 `series_id` 和 `series_name`
|
||||||
|
- **AND** 填充到分配响应数据中
|
||||||
|
|
||||||
|
### Requirement: Allocator Information Tracking in Package Allocation
|
||||||
|
|
||||||
|
系统SHALL记录单套餐分配的创建者(分配者)信息,包括分配者店铺ID和名称。
|
||||||
|
|
||||||
|
#### Scenario: 平台创建的单套餐分配标记为平台分配
|
||||||
|
|
||||||
|
- **GIVEN** 平台管理员登录系统
|
||||||
|
- **WHEN** 创建单套餐分配
|
||||||
|
- **THEN** 系统SHALL自动设置 `allocator_shop_id` 为 0
|
||||||
|
- **AND** `allocator_shop_name` 为 "平台" 或相应的平台标识
|
||||||
|
|
||||||
|
#### Scenario: 代理商创建的单套餐分配记录代理商信息
|
||||||
|
|
||||||
|
- **GIVEN** 代理商A登录系统,shop_id为50,shop_name为"代理商A"
|
||||||
|
- **WHEN** 创建单套餐分配给下级代理
|
||||||
|
- **THEN** 系统SHALL自动设置 `allocator_shop_id` 为 50
|
||||||
|
- **AND** `allocator_shop_name` 为 "代理商A"
|
||||||
|
|
||||||
|
### Requirement: Query by Allocator Shop ID
|
||||||
|
|
||||||
|
系统SHALL支持按分配者店铺ID筛选单套餐分配列表。
|
||||||
|
|
||||||
|
#### Scenario: 查看平台创建的所有分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个单套餐分配
|
||||||
|
- **WHEN** 使用查询参数 `allocator_shop_id=0`
|
||||||
|
- **THEN** 系统SHALL返回所有由平台创建的单套餐分配记录
|
||||||
|
|
||||||
|
#### Scenario: 查看特定代理商创建的所有分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个单套餐分配
|
||||||
|
- **WHEN** 使用查询参数 `allocator_shop_id=50`
|
||||||
|
- **THEN** 系统SHALL返回所有由店铺ID为50的代理商创建的单套餐分配记录
|
||||||
|
|
||||||
|
### Requirement: Query by Series Allocation ID
|
||||||
|
|
||||||
|
系统SHALL支持按系列分配ID筛选单套餐分配列表,以便查找由特定系列分配派生的所有单套餐分配。
|
||||||
|
|
||||||
|
#### Scenario: 查找系列分配的所有派生单套餐分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在系列分配ID为20的系列分配
|
||||||
|
- **AND** 该系列分配下有多个单套餐分配
|
||||||
|
- **WHEN** 使用查询参数 `series_allocation_id=20`
|
||||||
|
- **THEN** 系统SHALL返回所有关联该系列分配的单套餐分配记录
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
# Shop Series Allocation Specification Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Shop Series Allocation Data Model
|
||||||
|
|
||||||
|
套餐系列分配的数据模型SHALL重构为更细粒度的佣金配置结构,移除旧的 `base_commission` 模式,采用独立的一次性佣金和强制充值配置字段。
|
||||||
|
|
||||||
|
**字段定义**:
|
||||||
|
- `id` (number) - 分配ID
|
||||||
|
- `series_id` (number) - 套餐系列ID
|
||||||
|
- `series_name` (string) - 套餐系列名称
|
||||||
|
- `series_code` (string) - 套餐系列编码
|
||||||
|
- `shop_id` (number) - 被分配的店铺ID
|
||||||
|
- `shop_name` (string) - 被分配的店铺名称
|
||||||
|
- `allocator_shop_id` (number) - 分配者店铺ID,0表示平台分配
|
||||||
|
- `allocator_shop_name` (string) - 分配者店铺名称
|
||||||
|
- `enable_one_time_commission` (boolean) - 是否启用一次性佣金
|
||||||
|
- `one_time_commission_amount` (number) - 该代理能拿的一次性佣金金额上限(分)
|
||||||
|
- `one_time_commission_threshold` (number, optional) - 一次性佣金触发阈值(分)
|
||||||
|
- `one_time_commission_trigger` (string, optional) - 一次性佣金触发类型: "first_recharge" | "accumulated_recharge"
|
||||||
|
- `enable_force_recharge` (boolean) - 是否启用强制充值
|
||||||
|
- `force_recharge_amount` (number, optional) - 强制充值金额(分)
|
||||||
|
- `force_recharge_trigger_type` (number, optional) - 强充触发类型: 1(单次充值) | 2(累计充值)
|
||||||
|
- `status` (number) - 状态 (1:启用, 2:禁用)
|
||||||
|
- `created_at` (string) - 创建时间
|
||||||
|
- `updated_at` (string) - 更新时间
|
||||||
|
|
||||||
|
**创建请求字段** (`CreateShopSeriesAllocationRequest`):
|
||||||
|
- `series_id` (number, required) - 套餐系列ID
|
||||||
|
- `shop_id` (number, required) - 被分配的店铺ID
|
||||||
|
- `one_time_commission_amount` (number, required) - 一次性佣金金额上限(分)
|
||||||
|
- `enable_one_time_commission` (boolean, optional) - 是否启用一次性佣金
|
||||||
|
- `one_time_commission_threshold` (number, optional) - 一次性佣金触发阈值(分)
|
||||||
|
- `one_time_commission_trigger` (string, optional) - 一次性佣金触发类型
|
||||||
|
- `enable_force_recharge` (boolean, optional) - 是否启用强制充值
|
||||||
|
- `force_recharge_amount` (number, optional) - 强制充值金额(分)
|
||||||
|
- `force_recharge_trigger_type` (number, optional) - 强充触发类型
|
||||||
|
|
||||||
|
**更新请求字段** (`UpdateShopSeriesAllocationRequest`):
|
||||||
|
- 所有字段均为可选,允许部分更新
|
||||||
|
- `enable_one_time_commission` (boolean, optional)
|
||||||
|
- `one_time_commission_amount` (number, optional)
|
||||||
|
- `one_time_commission_threshold` (number, optional)
|
||||||
|
- `one_time_commission_trigger` (string, optional)
|
||||||
|
- `enable_force_recharge` (boolean, optional)
|
||||||
|
- `force_recharge_amount` (number, optional)
|
||||||
|
- `force_recharge_trigger_type` (number, optional)
|
||||||
|
- `status` (number, optional)
|
||||||
|
|
||||||
|
#### Scenario: 创建系列分配并启用一次性佣金
|
||||||
|
|
||||||
|
- **GIVEN** 平台管理员登录系统
|
||||||
|
- **AND** 系统中存在ID为1的套餐系列和ID为100的店铺
|
||||||
|
- **WHEN** 创建系列分配时提供以下配置:
|
||||||
|
- `series_id`: 1
|
||||||
|
- `shop_id`: 100
|
||||||
|
- `one_time_commission_amount`: 5000
|
||||||
|
- `enable_one_time_commission`: true
|
||||||
|
- `one_time_commission_threshold`: 10000
|
||||||
|
- `one_time_commission_trigger`: "first_recharge"
|
||||||
|
- **THEN** 系统SHALL创建系列分配记录
|
||||||
|
- **AND** 响应数据中 `allocator_shop_id` 为 0(平台分配)
|
||||||
|
- **AND** 响应数据中包含所有一次性佣金配置字段
|
||||||
|
|
||||||
|
#### Scenario: 创建系列分配并启用强制充值
|
||||||
|
|
||||||
|
- **GIVEN** 代理商A登录系统,shop_id为50
|
||||||
|
- **AND** 系统中存在ID为2的套餐系列和ID为101的下级代理店铺
|
||||||
|
- **WHEN** 创建系列分配时提供以下配置:
|
||||||
|
- `series_id`: 2
|
||||||
|
- `shop_id`: 101
|
||||||
|
- `one_time_commission_amount`: 3000
|
||||||
|
- `enable_force_recharge`: true
|
||||||
|
- `force_recharge_amount`: 15000
|
||||||
|
- `force_recharge_trigger_type`: 1 (单次充值)
|
||||||
|
- **THEN** 系统SHALL创建系列分配记录
|
||||||
|
- **AND** 响应数据中 `allocator_shop_id` 为 50(代理商A)
|
||||||
|
- **AND** 响应数据中包含所有强制充值配置字段
|
||||||
|
|
||||||
|
#### Scenario: 更新系列分配的一次性佣金配置
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为10的系列分配
|
||||||
|
- **WHEN** 更新该分配时提供以下字段:
|
||||||
|
- `one_time_commission_amount`: 8000 (从5000更新到8000)
|
||||||
|
- `one_time_commission_threshold`: 20000
|
||||||
|
- **THEN** 系统SHALL更新指定字段
|
||||||
|
- **AND** 其他字段保持不变
|
||||||
|
- **AND** 响应数据中 `updated_at` 字段更新为当前时间
|
||||||
|
|
||||||
|
#### Scenario: 更新系列分配的强制充值配置
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为10的系列分配
|
||||||
|
- **WHEN** 更新该分配时提供以下字段:
|
||||||
|
- `enable_force_recharge`: true
|
||||||
|
- `force_recharge_amount`: 25000
|
||||||
|
- `force_recharge_trigger_type`: 2 (累计充值)
|
||||||
|
- **THEN** 系统SHALL更新强制充值配置
|
||||||
|
- **AND** 响应数据中包含更新后的强制充值字段
|
||||||
|
|
||||||
|
#### Scenario: 查询系列分配列表并显示分配者信息
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个系列分配,部分由平台创建,部分由代理商创建
|
||||||
|
- **WHEN** 查询系列分配列表 (GET /api/admin/shop-series-allocations)
|
||||||
|
- **THEN** 系统SHALL返回所有分配记录
|
||||||
|
- **AND** 每条记录包含 `allocator_shop_id` 和 `allocator_shop_name`
|
||||||
|
- **AND** 平台创建的分配记录中 `allocator_shop_id` 为 0
|
||||||
|
|
||||||
|
#### Scenario: 按分配者店铺ID筛选系列分配
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在多个系列分配
|
||||||
|
- **WHEN** 使用查询参数 `allocator_shop_id=50`
|
||||||
|
- **THEN** 系统SHALL返回所有由店铺ID为50的代理商创建的分配记录
|
||||||
|
|
||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
### Requirement: Base Commission Configuration
|
||||||
|
|
||||||
|
旧版的 `base_commission` 配置模式已被移除,不再使用 `BaseCommissionConfig` 类型。
|
||||||
|
|
||||||
|
**Reason**: 新的佣金配置模型更加灵活和细粒度,将一次性佣金配置分散到独立字段中,便于查询和管理。
|
||||||
|
|
||||||
|
**Migration**: 现有使用 `base_commission` 字段的代码需要迁移到新的字段结构。如果有历史数据,需要后端提供数据迁移方案。
|
||||||
|
|
||||||
|
### Requirement: Tiered Commission Configuration
|
||||||
|
|
||||||
|
旧版的 `TierCommissionConfig` (周期性梯度返佣) 配置已被移除。
|
||||||
|
|
||||||
|
**Reason**: 系列分配层面不再支持复杂的梯度返佣配置,改为在系列级别统一配置一次性佣金梯度。
|
||||||
|
|
||||||
|
**Migration**: 梯度佣金配置现在在套餐系列级别 (`PackageSeries.one_time_commission_config.tiers`) 进行管理。
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Allocator Information Tracking
|
||||||
|
|
||||||
|
系统SHALL记录系列分配的创建者(分配者)信息,包括分配者店铺ID和名称。
|
||||||
|
|
||||||
|
#### Scenario: 平台创建的分配标记为平台分配
|
||||||
|
|
||||||
|
- **GIVEN** 平台管理员登录系统
|
||||||
|
- **WHEN** 创建系列分配
|
||||||
|
- **THEN** 系统SHALL自动设置 `allocator_shop_id` 为 0
|
||||||
|
- **AND** `allocator_shop_name` 为 "平台" 或相应的平台标识
|
||||||
|
|
||||||
|
#### Scenario: 代理商创建的分配记录代理商信息
|
||||||
|
|
||||||
|
- **GIVEN** 代理商A登录系统,shop_id为50,shop_name为"代理商A"
|
||||||
|
- **WHEN** 创建系列分配给下级代理
|
||||||
|
- **THEN** 系统SHALL自动设置 `allocator_shop_id` 为 50
|
||||||
|
- **AND** `allocator_shop_name` 为 "代理商A"
|
||||||
|
|
||||||
|
### Requirement: Series Code in Allocation Response
|
||||||
|
|
||||||
|
系统SHALL在系列分配响应数据中包含套餐系列编码 (`series_code`)。
|
||||||
|
|
||||||
|
#### Scenario: 获取系列分配详情时包含系列编码
|
||||||
|
|
||||||
|
- **GIVEN** 系统中存在ID为10的系列分配,关联系列编码为"SERIES001"
|
||||||
|
- **WHEN** 请求获取该分配的详情 (GET /api/admin/shop-series-allocations/10)
|
||||||
|
- **THEN** 响应数据中SHALL包含 `series_code` 字段
|
||||||
|
- **AND** 其值为 "SERIES001"
|
||||||
62
openspec/changes/update-package-management-api/tasks.md
Normal file
62
openspec/changes/update-package-management-api/tasks.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Implementation Tasks
|
||||||
|
|
||||||
|
## 1. 类型定义更新
|
||||||
|
|
||||||
|
- [ ] 1.1 更新 `PackageSeriesResponse` - 新增一次性佣金字段
|
||||||
|
- [ ] 1.2 新增 `SeriesOneTimeCommissionConfig` 类型定义
|
||||||
|
- [ ] 1.3 新增 `OneTimeCommissionTier` 类型定义
|
||||||
|
- [ ] 1.4 更新 `CreatePackageSeriesRequest` - 新增一次性佣金配置参数
|
||||||
|
- [ ] 1.5 更新 `UpdatePackageSeriesRequest` - 新增一次性佣金配置参数
|
||||||
|
- [ ] 1.6 更新 `PackageSeriesQueryParams` - 新增 `enable_one_time_commission` 筛选参数
|
||||||
|
- [ ] 1.7 重构 `ShopSeriesAllocationResponse` - 替换为新的字段结构
|
||||||
|
- [ ] 1.8 重构 `CreateShopSeriesAllocationRequest` - 更新为新的请求结构
|
||||||
|
- [ ] 1.9 重构 `UpdateShopSeriesAllocationRequest` - 更新为新的请求结构
|
||||||
|
- [ ] 1.10 更新 `ShopPackageAllocationResponse` - 新增系列关联和分配者字段
|
||||||
|
- [ ] 1.11 更新 `ShopPackageAllocationQueryParams` - 新增筛选参数
|
||||||
|
- [ ] 1.12 移除废弃的类型定义 (`BaseCommissionConfig`, 旧版 `OneTimeCommissionConfig` 等)
|
||||||
|
|
||||||
|
## 2. 套餐系列管理页面适配
|
||||||
|
|
||||||
|
- [ ] 2.1 更新套餐系列列表 - 显示一次性佣金启用状态
|
||||||
|
- [ ] 2.2 更新套餐系列表单 - 新增一次性佣金配置区块
|
||||||
|
- [ ] 2.3 实现一次性佣金配置表单组件 (固定模式)
|
||||||
|
- [ ] 2.4 实现一次性佣金配置表单组件 (梯度模式)
|
||||||
|
- [ ] 2.5 实现强制充值配置表单组件
|
||||||
|
- [ ] 2.6 实现时效配置表单组件 (永久/固定日期/相对时长)
|
||||||
|
- [ ] 2.7 添加一次性佣金配置的表单验证逻辑
|
||||||
|
- [ ] 2.8 更新套餐系列详情展示 - 显示完整的一次性佣金配置
|
||||||
|
|
||||||
|
## 3. 系列分配管理页面适配
|
||||||
|
|
||||||
|
- [ ] 3.1 更新系列分配列表 - 适配新的响应字段结构
|
||||||
|
- [ ] 3.2 移除基础返佣配置表单(旧版)
|
||||||
|
- [ ] 3.3 实现新的系列分配表单 - 强制充值配置
|
||||||
|
- [ ] 3.4 实现新的系列分配表单 - 一次性佣金配置
|
||||||
|
- [ ] 3.5 更新系列分配详情展示 - 显示所有新字段
|
||||||
|
- [ ] 3.6 更新系列分配编辑表单 - 支持修改所有可选字段
|
||||||
|
- [ ] 3.7 添加表单验证逻辑 - 确保必填字段和逻辑一致性
|
||||||
|
|
||||||
|
## 4. 单套餐分配页面适配
|
||||||
|
|
||||||
|
- [ ] 4.1 更新单套餐分配列表 - 显示系列信息和分配者信息
|
||||||
|
- [ ] 4.2 更新列表筛选条件 - 新增系列分配ID和分配者店铺ID筛选
|
||||||
|
- [ ] 4.3 更新单套餐分配详情展示 - 显示所有新字段
|
||||||
|
- [ ] 4.4 移除列表中的"计算成本价"字段显示(已废弃)
|
||||||
|
|
||||||
|
## 5. 测试与验证
|
||||||
|
|
||||||
|
- [ ] 5.1 测试套餐系列的创建 - 包含一次性佣金配置(固定模式)
|
||||||
|
- [ ] 5.2 测试套餐系列的创建 - 包含一次性佣金配置(梯度模式)
|
||||||
|
- [ ] 5.3 测试套餐系列的编辑 - 修改一次性佣金配置
|
||||||
|
- [ ] 5.4 测试系列分配的创建 - 使用新的字段结构
|
||||||
|
- [ ] 5.5 测试系列分配的编辑 - 修改强制充值和一次性佣金参数
|
||||||
|
- [ ] 5.6 测试单套餐分配 - 验证系列关联字段正确显示
|
||||||
|
- [ ] 5.7 测试单套餐分配筛选 - 验证新增筛选参数工作正常
|
||||||
|
- [ ] 5.8 回归测试 - 确保所有现有功能仍然正常工作
|
||||||
|
|
||||||
|
## 6. 文档与清理
|
||||||
|
|
||||||
|
- [ ] 6.1 更新类型定义文件的注释 - 标注新增/修改的字段
|
||||||
|
- [ ] 6.2 删除废弃的类型定义和相关代码
|
||||||
|
- [ ] 6.3 更新相关组件的注释文档
|
||||||
|
- [ ] 6.4 记录API变更影响和迁移说明(如需要)
|
||||||
@@ -611,89 +611,98 @@ export const asyncRoutes: AppRouteRecord[] = [
|
|||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// 物联网卡管理系统模块
|
// 物联网卡管理系统模块
|
||||||
// {
|
{
|
||||||
// path: '/card-management',
|
path: '/card-management',
|
||||||
// name: 'CardManagement',
|
name: 'CardManagement',
|
||||||
// component: RoutesAlias.Home,
|
component: RoutesAlias.Home,
|
||||||
// meta: {
|
meta: {
|
||||||
// title: 'menus.cardManagement.title',
|
title: 'menus.cardManagement.title',
|
||||||
// icon: ''
|
icon: ''
|
||||||
// },
|
},
|
||||||
// children: [
|
children: [
|
||||||
// // {
|
// {
|
||||||
// // path: 'card-detail',
|
// path: 'card-detail',
|
||||||
// // name: 'CardDetail',
|
// name: 'CardDetail',
|
||||||
// // component: RoutesAlias.CardDetail,
|
// component: RoutesAlias.CardDetail,
|
||||||
// // meta: {
|
// meta: {
|
||||||
// // title: 'menus.cardManagement.cardDetail',
|
// title: 'menus.cardManagement.cardDetail',
|
||||||
// // keepAlive: true
|
// keepAlive: true
|
||||||
// // }
|
// }
|
||||||
// // },
|
// },
|
||||||
// {
|
{
|
||||||
// path: 'card-assign',
|
path: 'single-card',
|
||||||
// name: 'CardAssign',
|
name: 'SingleCard',
|
||||||
// component: RoutesAlias.CardAssign,
|
component: RoutesAlias.SingleCard,
|
||||||
// meta: {
|
meta: {
|
||||||
// title: 'menus.cardManagement.cardAssign',
|
title: 'menus.cardManagement.singleCard',
|
||||||
// keepAlive: true
|
keepAlive: true
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
// {
|
||||||
// path: 'card-shutdown',
|
// path: 'card-assign',
|
||||||
// name: 'CardShutdown',
|
// name: 'CardAssign',
|
||||||
// component: RoutesAlias.CardShutdown,
|
// component: RoutesAlias.CardAssign,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.cardShutdown',
|
// title: 'menus.cardManagement.cardAssign',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: 'my-cards',
|
// path: 'card-shutdown',
|
||||||
// name: 'MyCards',
|
// name: 'CardShutdown',
|
||||||
// component: RoutesAlias.MyCards,
|
// component: RoutesAlias.CardShutdown,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.myCards',
|
// title: 'menus.cardManagement.cardShutdown',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: 'card-transfer',
|
// path: 'my-cards',
|
||||||
// name: 'CardTransfer',
|
// name: 'MyCards',
|
||||||
// component: RoutesAlias.CardTransfer,
|
// component: RoutesAlias.MyCards,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.cardTransfer',
|
// title: 'menus.cardManagement.myCards',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: 'card-replacement',
|
// path: 'card-transfer',
|
||||||
// name: 'CardReplacement',
|
// name: 'CardTransfer',
|
||||||
// component: RoutesAlias.CardReplacement,
|
// component: RoutesAlias.CardTransfer,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.cardReplacement',
|
// title: 'menus.cardManagement.cardTransfer',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: 'package-gift',
|
// path: 'card-replacement',
|
||||||
// name: 'PackageGift',
|
// name: 'CardReplacement',
|
||||||
// component: RoutesAlias.PackageGift,
|
// component: RoutesAlias.CardReplacement,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.packageGift',
|
// title: 'menus.cardManagement.cardReplacement',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: 'card-change-card',
|
// path: 'package-gift',
|
||||||
// name: 'CardChangeCard',
|
// name: 'PackageGift',
|
||||||
// component: RoutesAlias.CardChangeCard,
|
// component: RoutesAlias.PackageGift,
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'menus.cardManagement.cardChange',
|
// title: 'menus.cardManagement.packageGift',
|
||||||
// keepAlive: true
|
// keepAlive: true
|
||||||
// }
|
// }
|
||||||
// }
|
// },
|
||||||
// ]
|
// {
|
||||||
// },
|
// path: 'card-change-card',
|
||||||
|
// name: 'CardChangeCard',
|
||||||
|
// component: RoutesAlias.CardChangeCard,
|
||||||
|
// meta: {
|
||||||
|
// title: 'menus.cardManagement.cardChange',
|
||||||
|
// keepAlive: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/package-management',
|
path: '/package-management',
|
||||||
name: 'PackageManagement',
|
name: 'PackageManagement',
|
||||||
|
|||||||
@@ -349,6 +349,7 @@ export interface StandaloneIotCard {
|
|||||||
created_at: string // 创建时间
|
created_at: string // 创建时间
|
||||||
updated_at: string // 更新时间
|
updated_at: string // 更新时间
|
||||||
series_id?: number | null // 套餐系列ID
|
series_id?: number | null // 套餐系列ID
|
||||||
|
series_name?: string // 套餐系列名称
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 单卡批量分配和回收相关 ==========
|
// ========== 单卡批量分配和回收相关 ==========
|
||||||
|
|||||||
@@ -7,6 +7,33 @@ import { PaginationParams } from './common'
|
|||||||
|
|
||||||
// ==================== 套餐系列管理 ====================
|
// ==================== 套餐系列管理 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次性佣金梯度档位配置(套餐系列级别)
|
||||||
|
*/
|
||||||
|
export interface OneTimeCommissionTier {
|
||||||
|
threshold: number // 达标阈值
|
||||||
|
dimension: 'sales_count' | 'sales_amount' // 统计维度:销量或销售额
|
||||||
|
amount: number // 佣金金额(分)
|
||||||
|
stat_scope?: 'self' | 'self_and_sub' // 统计范围:仅自己或自己+下级
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 套餐系列一次性佣金配置
|
||||||
|
*/
|
||||||
|
export interface SeriesOneTimeCommissionConfig {
|
||||||
|
enable: boolean // 是否启用一次性佣金
|
||||||
|
commission_type: 'fixed' | 'tiered' // 佣金类型:固定或梯度
|
||||||
|
commission_amount?: number // 固定佣金金额(分),commission_type=fixed时使用
|
||||||
|
threshold: number // 触发阈值(分)
|
||||||
|
trigger_type: 'first_recharge' | 'accumulated_recharge' // 触发类型:首次充值或累计充值
|
||||||
|
tiers?: OneTimeCommissionTier[] | null // 梯度配置列表,commission_type=tiered时使用
|
||||||
|
enable_force_recharge: boolean // 是否启用强充
|
||||||
|
force_amount?: number // 强充金额(分)
|
||||||
|
force_calc_type?: 'fixed' | 'dynamic' // 强充计算类型:固定或动态
|
||||||
|
validity_type: 'permanent' | 'fixed_date' | 'relative' // 时效类型:永久、固定日期或相对时长
|
||||||
|
validity_value?: string // 时效值(日期字符串或月数)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 套餐系列响应
|
* 套餐系列响应
|
||||||
*/
|
*/
|
||||||
@@ -15,6 +42,8 @@ export interface PackageSeriesResponse {
|
|||||||
series_code: string
|
series_code: string
|
||||||
series_name: string
|
series_name: string
|
||||||
description?: string
|
description?: string
|
||||||
|
enable_one_time_commission: boolean // 是否启用一次性佣金
|
||||||
|
one_time_commission_config?: SeriesOneTimeCommissionConfig // 一次性佣金配置
|
||||||
status: number // 1:启用, 2:禁用
|
status: number // 1:启用, 2:禁用
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_at: string
|
updated_at: string
|
||||||
@@ -26,6 +55,7 @@ export interface PackageSeriesResponse {
|
|||||||
export interface PackageSeriesQueryParams extends PaginationParams {
|
export interface PackageSeriesQueryParams extends PaginationParams {
|
||||||
series_name?: string // 系列名称(模糊搜索)
|
series_name?: string // 系列名称(模糊搜索)
|
||||||
status?: number // 状态筛选
|
status?: number // 状态筛选
|
||||||
|
enable_one_time_commission?: boolean // 是否启用一次性佣金筛选
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,15 +65,16 @@ export interface CreatePackageSeriesRequest {
|
|||||||
series_code: string // 系列编码,必填
|
series_code: string // 系列编码,必填
|
||||||
series_name: string // 系列名称,必填
|
series_name: string // 系列名称,必填
|
||||||
description?: string // 描述,可选
|
description?: string // 描述,可选
|
||||||
|
one_time_commission_config?: SeriesOneTimeCommissionConfig // 一次性佣金配置,可选
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新套餐系列请求
|
* 更新套餐系列请求
|
||||||
*/
|
*/
|
||||||
export interface UpdatePackageSeriesRequest {
|
export interface UpdatePackageSeriesRequest {
|
||||||
series_code?: string
|
|
||||||
series_name?: string
|
series_name?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
one_time_commission_config?: SeriesOneTimeCommissionConfig // 一次性佣金配置,可选
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +101,9 @@ export interface PackageResponse {
|
|||||||
virtual_data_mb: number // 虚流量额度(MB)
|
virtual_data_mb: number // 虚流量额度(MB)
|
||||||
duration_months: number // 有效期(月)
|
duration_months: number // 有效期(月)
|
||||||
price: number // 价格(分)
|
price: number // 价格(分)
|
||||||
|
cost_price: number // 成本价(分)
|
||||||
|
suggested_retail_price: number // 建议零售价(分)
|
||||||
|
enable_virtual_data: boolean // 是否启用虚流量
|
||||||
shelf_status: number // 上架状态 (1:上架, 2:下架)
|
shelf_status: number // 上架状态 (1:上架, 2:下架)
|
||||||
status: number // 状态 (1:启用, 2:禁用)
|
status: number // 状态 (1:启用, 2:禁用)
|
||||||
description?: string
|
description?: string
|
||||||
@@ -154,11 +188,14 @@ export interface ShopPackageAllocationResponse {
|
|||||||
package_id: number
|
package_id: number
|
||||||
package_code: string
|
package_code: string
|
||||||
package_name: string
|
package_name: string
|
||||||
|
series_id: number // 套餐系列ID
|
||||||
|
series_name: string // 套餐系列名称
|
||||||
shop_id: number
|
shop_id: number
|
||||||
shop_name: string
|
shop_name: string
|
||||||
cost_price: number // 覆盖的成本价(分)
|
allocator_shop_id: number // 分配者店铺ID,0表示平台分配
|
||||||
calculated_cost_price: number // 原计算成本价(分,供参考)
|
allocator_shop_name: string // 分配者店铺名称
|
||||||
allocation_id?: number // 关联的系列分配ID
|
series_allocation_id?: number | null // 关联的系列分配ID(可空)
|
||||||
|
cost_price: number // 该代理的成本价(分)
|
||||||
status: number // 1:启用, 2:禁用
|
status: number // 1:启用, 2:禁用
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_at: string
|
updated_at: string
|
||||||
@@ -170,6 +207,8 @@ export interface ShopPackageAllocationResponse {
|
|||||||
export interface ShopPackageAllocationQueryParams extends PaginationParams {
|
export interface ShopPackageAllocationQueryParams extends PaginationParams {
|
||||||
shop_id?: number // 店铺ID筛选
|
shop_id?: number // 店铺ID筛选
|
||||||
package_id?: number // 套餐ID筛选
|
package_id?: number // 套餐ID筛选
|
||||||
|
series_allocation_id?: number // 系列分配ID筛选
|
||||||
|
allocator_shop_id?: number // 分配者店铺ID筛选
|
||||||
status?: number // 状态筛选
|
status?: number // 状态筛选
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,54 +237,6 @@ export interface UpdateShopPackageAllocationStatusRequest {
|
|||||||
|
|
||||||
// ==================== 套餐系列分配 ====================
|
// ==================== 套餐系列分配 ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* 基础返佣配置
|
|
||||||
*/
|
|
||||||
export interface BaseCommissionConfig {
|
|
||||||
mode: 'fixed' | 'percent' // 返佣模式:'fixed':固定金额, 'percent':百分比
|
|
||||||
value: number // 返佣值:固定金额(分)或百分比的千分比(如200表示20%)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 梯度档位配置
|
|
||||||
*/
|
|
||||||
export interface TierEntry {
|
|
||||||
threshold: number // 阈值(销量或销售额)
|
|
||||||
mode: 'fixed' | 'percent' // 返佣模式
|
|
||||||
value: number // 返佣值
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 梯度返佣配置
|
|
||||||
*/
|
|
||||||
export interface TierCommissionConfig {
|
|
||||||
period_type: 'monthly' | 'quarterly' | 'yearly' // 周期类型
|
|
||||||
tier_type: 'sales_count' | 'sales_amount' // 梯度类型:销量或销售额
|
|
||||||
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 // 梯度档位数组(梯度佣金时必填)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 套餐系列分配响应
|
* 套餐系列分配响应
|
||||||
*/
|
*/
|
||||||
@@ -253,13 +244,18 @@ export interface ShopSeriesAllocationResponse {
|
|||||||
id: number
|
id: number
|
||||||
series_id: number
|
series_id: number
|
||||||
series_name: string
|
series_name: string
|
||||||
|
series_code: string // 套餐系列编码
|
||||||
shop_id: number
|
shop_id: number
|
||||||
shop_name: string
|
shop_name: string
|
||||||
allocator_shop_id: number // 分配者店铺ID
|
allocator_shop_id: number // 分配者店铺ID,0表示平台分配
|
||||||
allocator_shop_name: string // 分配者店铺名称
|
allocator_shop_name: string // 分配者店铺名称
|
||||||
base_commission: BaseCommissionConfig // 基础返佣配置
|
|
||||||
enable_one_time_commission: boolean // 是否启用一次性佣金
|
enable_one_time_commission: boolean // 是否启用一次性佣金
|
||||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置(可选)
|
one_time_commission_amount: number // 该代理能拿的一次性佣金金额上限(分)
|
||||||
|
one_time_commission_threshold?: number // 一次性佣金触发阈值(分)
|
||||||
|
one_time_commission_trigger?: 'first_recharge' | 'accumulated_recharge' // 一次性佣金触发类型
|
||||||
|
enable_force_recharge: boolean // 是否启用强制充值
|
||||||
|
force_recharge_amount?: number // 强制充值金额(分)
|
||||||
|
force_recharge_trigger_type?: 1 | 2 // 强充触发类型:1(单次充值)、2(累计充值)
|
||||||
status: number // 1:启用, 2:禁用
|
status: number // 1:启用, 2:禁用
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_at: string
|
updated_at: string
|
||||||
@@ -271,6 +267,7 @@ export interface ShopSeriesAllocationResponse {
|
|||||||
export interface ShopSeriesAllocationQueryParams extends PaginationParams {
|
export interface ShopSeriesAllocationQueryParams extends PaginationParams {
|
||||||
shop_id?: number // 店铺ID筛选
|
shop_id?: number // 店铺ID筛选
|
||||||
series_id?: number // 系列ID筛选
|
series_id?: number // 系列ID筛选
|
||||||
|
allocator_shop_id?: number // 分配者店铺ID筛选
|
||||||
status?: number // 状态筛选
|
status?: number // 状态筛选
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,18 +277,27 @@ export interface ShopSeriesAllocationQueryParams extends PaginationParams {
|
|||||||
export interface CreateShopSeriesAllocationRequest {
|
export interface CreateShopSeriesAllocationRequest {
|
||||||
series_id: number // 套餐系列ID,必填
|
series_id: number // 套餐系列ID,必填
|
||||||
shop_id: number // 店铺ID,必填
|
shop_id: number // 店铺ID,必填
|
||||||
base_commission: BaseCommissionConfig // 基础返佣配置,必填
|
one_time_commission_amount: number // 一次性佣金金额上限(分),必填
|
||||||
enable_one_time_commission?: boolean // 是否启用一次性佣金,可选(默认false)
|
enable_one_time_commission?: boolean // 是否启用一次性佣金,可选
|
||||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置,当enable_one_time_commission为true时必填
|
one_time_commission_threshold?: number // 一次性佣金触发阈值(分),可选
|
||||||
|
one_time_commission_trigger?: 'first_recharge' | 'accumulated_recharge' // 一次性佣金触发类型,可选
|
||||||
|
enable_force_recharge?: boolean // 是否启用强制充值,可选
|
||||||
|
force_recharge_amount?: number // 强制充值金额(分),可选
|
||||||
|
force_recharge_trigger_type?: 1 | 2 // 强充触发类型,可选
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新套餐系列分配请求
|
* 更新套餐系列分配请求
|
||||||
*/
|
*/
|
||||||
export interface UpdateShopSeriesAllocationRequest {
|
export interface UpdateShopSeriesAllocationRequest {
|
||||||
base_commission?: BaseCommissionConfig // 基础返佣配置
|
|
||||||
enable_one_time_commission?: boolean // 是否启用一次性佣金
|
enable_one_time_commission?: boolean // 是否启用一次性佣金
|
||||||
one_time_commission_config?: OneTimeCommissionConfig // 一次性佣金配置
|
one_time_commission_amount?: number // 一次性佣金金额上限(分)
|
||||||
|
one_time_commission_threshold?: number // 一次性佣金触发阈值(分)
|
||||||
|
one_time_commission_trigger?: 'first_recharge' | 'accumulated_recharge' // 一次性佣金触发类型
|
||||||
|
enable_force_recharge?: boolean // 是否启用强制充值
|
||||||
|
force_recharge_amount?: number // 强制充值金额(分)
|
||||||
|
force_recharge_trigger_type?: 1 | 2 // 强充触发类型
|
||||||
|
status?: number // 状态
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -874,6 +874,7 @@
|
|||||||
{ label: '卡业务类型', prop: 'card_category' },
|
{ label: '卡业务类型', prop: 'card_category' },
|
||||||
{ label: '运营商', prop: 'carrier_name' },
|
{ label: '运营商', prop: 'carrier_name' },
|
||||||
{ label: '店铺名称', prop: 'shop_name' },
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
|
{ label: '套餐系列', prop: 'series_name' },
|
||||||
{ label: '成本价', prop: 'cost_price' },
|
{ label: '成本价', prop: 'cost_price' },
|
||||||
{ label: '分销价', prop: 'distribute_price' },
|
{ label: '分销价', prop: 'distribute_price' },
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
@@ -1025,6 +1026,12 @@
|
|||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
formatter: (row: StandaloneIotCard) => row.shop_name || '-'
|
formatter: (row: StandaloneIotCard) => row.shop_name || '-'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'series_name',
|
||||||
|
label: '套餐系列',
|
||||||
|
width: 150,
|
||||||
|
formatter: (row: StandaloneIotCard) => row.series_name || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'cost_price',
|
prop: 'cost_price',
|
||||||
label: '成本价',
|
label: '成本价',
|
||||||
|
|||||||
@@ -58,34 +58,6 @@
|
|||||||
<ElOption :label="t('orderManagement.orderType.device')" value="device" />
|
<ElOption :label="t('orderManagement.orderType.device')" value="device" />
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem :label="t('orderManagement.createForm.packageIds')" prop="package_ids">
|
|
||||||
<ElSelect
|
|
||||||
v-model="createForm.package_ids"
|
|
||||||
:placeholder="t('orderManagement.createForm.packageIdsPlaceholder')"
|
|
||||||
multiple
|
|
||||||
filterable
|
|
||||||
remote
|
|
||||||
reserve-keyword
|
|
||||||
:remote-method="searchPackages"
|
|
||||||
:loading="packageSearchLoading"
|
|
||||||
clearable
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<ElOption
|
|
||||||
v-for="pkg in packageOptions"
|
|
||||||
:key="pkg.id"
|
|
||||||
:label="`${pkg.package_name} (¥${(pkg.price / 100).toFixed(2)})`"
|
|
||||||
:value="pkg.id"
|
|
||||||
>
|
|
||||||
<div style="display: flex; justify-content: space-between">
|
|
||||||
<span>{{ pkg.package_name }}</span>
|
|
||||||
<span style="color: var(--el-text-color-secondary); font-size: 12px">
|
|
||||||
¥{{ (pkg.price / 100).toFixed(2) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</ElOption>
|
|
||||||
</ElSelect>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem
|
<ElFormItem
|
||||||
v-if="createForm.order_type === 'single_card'"
|
v-if="createForm.order_type === 'single_card'"
|
||||||
:label="t('orderManagement.createForm.iotCardId')"
|
:label="t('orderManagement.createForm.iotCardId')"
|
||||||
@@ -148,6 +120,35 @@
|
|||||||
</ElOption>
|
</ElOption>
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
<ElFormItem :label="t('orderManagement.createForm.packageIds')" prop="package_ids">
|
||||||
|
<ElSelect
|
||||||
|
v-model="createForm.package_ids"
|
||||||
|
:placeholder="t('orderManagement.createForm.packageIdsPlaceholder')"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
:remote-method="handlePackageSearch"
|
||||||
|
:loading="packageSearchLoading"
|
||||||
|
:disabled="(!createForm.iot_card_id && !createForm.device_id) || (createForm.order_type === 'single_card' && !createForm.iot_card_id) || (createForm.order_type === 'device' && !createForm.device_id)"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<ElOption
|
||||||
|
v-for="pkg in packageOptions"
|
||||||
|
:key="pkg.id"
|
||||||
|
:label="`${pkg.package_name} (¥${(pkg.cost_price / 100).toFixed(2)})`"
|
||||||
|
:value="pkg.id"
|
||||||
|
>
|
||||||
|
<div style="display: flex; justify-content: space-between">
|
||||||
|
<span>{{ pkg.package_name }}</span>
|
||||||
|
<span style="color: var(--el-text-color-secondary); font-size: 12px">
|
||||||
|
¥{{ (pkg.cost_price / 100).toFixed(2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</ElOption>
|
||||||
|
</ElSelect>
|
||||||
|
</ElFormItem>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
@@ -416,12 +417,13 @@
|
|||||||
const deviceOptions = ref<Device[]>([])
|
const deviceOptions = ref<Device[]>([])
|
||||||
const deviceSearchLoading = ref(false)
|
const deviceSearchLoading = ref(false)
|
||||||
|
|
||||||
// 搜索套餐(根据套餐名称)
|
// 搜索套餐(根据套餐名称,可选按series_id筛选)
|
||||||
const searchPackages = async (query: string) => {
|
const searchPackages = async (query: string, seriesId?: number) => {
|
||||||
packageSearchLoading.value = true
|
packageSearchLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await PackageManageService.getPackages({
|
const res = await PackageManageService.getPackages({
|
||||||
package_name: query || undefined,
|
package_name: query || undefined,
|
||||||
|
series_id: seriesId,
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
status: 1, // 只获取启用的套餐
|
status: 1, // 只获取启用的套餐
|
||||||
@@ -438,6 +440,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 套餐远程搜索方法(自动使用当前选中的IOT卡/设备的series_id)
|
||||||
|
const handlePackageSearch = (query: string) => {
|
||||||
|
let seriesId: number | undefined
|
||||||
|
// 如果是单卡订单并且已选择IOT卡,使用该卡的series_id筛选
|
||||||
|
if (createForm.order_type === 'single_card' && createForm.iot_card_id) {
|
||||||
|
const selectedCard = iotCardOptions.value.find((card) => card.id === createForm.iot_card_id)
|
||||||
|
if (selectedCard && selectedCard.series_id) {
|
||||||
|
seriesId = selectedCard.series_id
|
||||||
|
}
|
||||||
|
} else if (createForm.order_type === 'device' && createForm.device_id) {
|
||||||
|
// 如果是设备订单并且已选择设备,使用设备的series_id筛选
|
||||||
|
const selectedDevice = deviceOptions.value.find((dev) => dev.id === createForm.device_id)
|
||||||
|
if (selectedDevice && selectedDevice.series_id) {
|
||||||
|
seriesId = selectedDevice.series_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchPackages(query, seriesId)
|
||||||
|
}
|
||||||
|
|
||||||
// 搜索IoT卡(根据ICCID)
|
// 搜索IoT卡(根据ICCID)
|
||||||
const searchIotCards = async (query: string) => {
|
const searchIotCards = async (query: string) => {
|
||||||
cardSearchLoading.value = true
|
cardSearchLoading.value = true
|
||||||
@@ -623,6 +644,42 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 当选择IOT卡时,根据series_id筛选套餐
|
||||||
|
watch(
|
||||||
|
() => createForm.iot_card_id,
|
||||||
|
(newCardId) => {
|
||||||
|
if (newCardId && createForm.order_type === 'single_card') {
|
||||||
|
// 找到选中的IOT卡
|
||||||
|
const selectedCard = iotCardOptions.value.find((card) => card.id === newCardId)
|
||||||
|
if (selectedCard && selectedCard.series_id) {
|
||||||
|
// 根据series_id重新加载套餐列表
|
||||||
|
loadDefaultPackages(selectedCard.series_id)
|
||||||
|
} else {
|
||||||
|
// 如果没有series_id,加载所有套餐
|
||||||
|
loadDefaultPackages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 当选择设备时,根据series_id筛选套餐
|
||||||
|
watch(
|
||||||
|
() => createForm.device_id,
|
||||||
|
(newDeviceId) => {
|
||||||
|
if (newDeviceId && createForm.order_type === 'device') {
|
||||||
|
// 找到选中的设备
|
||||||
|
const selectedDevice = deviceOptions.value.find((dev) => dev.id === newDeviceId)
|
||||||
|
if (selectedDevice && selectedDevice.series_id) {
|
||||||
|
// 根据series_id重新加载套餐列表
|
||||||
|
loadDefaultPackages(selectedDevice.series_id)
|
||||||
|
} else {
|
||||||
|
// 如果没有series_id,加载所有套餐
|
||||||
|
loadDefaultPackages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
})
|
})
|
||||||
@@ -642,7 +699,7 @@
|
|||||||
}
|
}
|
||||||
const res = await OrderService.getOrders(params)
|
const res = await OrderService.getOrders(params)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
orderList.value = res.data.items || []
|
orderList.value = res.data.list || []
|
||||||
pagination.total = res.data.total || 0
|
pagination.total = res.data.total || 0
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -692,15 +749,16 @@
|
|||||||
// 显示创建订单对话框
|
// 显示创建订单对话框
|
||||||
const showCreateDialog = async () => {
|
const showCreateDialog = async () => {
|
||||||
createDialogVisible.value = true
|
createDialogVisible.value = true
|
||||||
// 默认加载20条套餐、IoT卡和设备数据
|
// 加载IoT卡和设备列表,套餐列表在选择IoT卡/设备后才加载
|
||||||
await Promise.all([loadDefaultPackages(), loadDefaultIotCards(), loadDefaultDevices()])
|
await Promise.all([loadDefaultIotCards(), loadDefaultDevices()])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载默认套餐列表
|
// 加载默认套餐列表(可选按series_id筛选)
|
||||||
const loadDefaultPackages = async () => {
|
const loadDefaultPackages = async (seriesId?: number) => {
|
||||||
packageSearchLoading.value = true
|
packageSearchLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await PackageManageService.getPackages({
|
const res = await PackageManageService.getPackages({
|
||||||
|
series_id: seriesId,
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
status: 1, // 只获取启用的套餐
|
status: 1, // 只获取启用的套餐
|
||||||
|
|||||||
@@ -283,7 +283,6 @@
|
|||||||
{ label: '套餐名称', prop: 'package_name' },
|
{ label: '套餐名称', prop: 'package_name' },
|
||||||
{ label: '店铺名称', prop: 'shop_name' },
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
{ label: '成本价', prop: 'cost_price' },
|
{ label: '成本价', prop: 'cost_price' },
|
||||||
{ label: '原计算成本价', prop: 'calculated_cost_price' },
|
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '创建时间', prop: 'created_at' },
|
{ label: '创建时间', prop: 'created_at' },
|
||||||
{ label: '操作', prop: 'operation' }
|
{ label: '操作', prop: 'operation' }
|
||||||
@@ -372,13 +371,6 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
prop: 'calculated_cost_price',
|
|
||||||
label: '原计算成本价',
|
|
||||||
width: 120,
|
|
||||||
formatter: (row: ShopPackageAllocationResponse) =>
|
|
||||||
`¥${(row.calculated_cost_price / 100).toFixed(2)}`
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
label: '状态',
|
label: '状态',
|
||||||
|
|||||||
@@ -103,141 +103,69 @@
|
|||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<!-- 基础返佣配置 -->
|
<!-- 一次性佣金配置 -->
|
||||||
<ElDivider content-position="left">基础返佣配置</ElDivider>
|
<ElDivider content-position="left">一次性佣金配置</ElDivider>
|
||||||
<ElFormItem label="返佣模式" prop="base_commission.mode">
|
|
||||||
<ElRadioGroup v-model="form.base_commission.mode">
|
<ElFormItem label="佣金金额上限(分)" prop="one_time_commission_amount">
|
||||||
<ElRadio value="fixed">固定金额</ElRadio>
|
|
||||||
<ElRadio value="percent">百分比</ElRadio>
|
|
||||||
</ElRadioGroup>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem
|
|
||||||
:label="form.base_commission.mode === 'fixed' ? '返佣金额(分)' : '返佣百分比(千分比)'"
|
|
||||||
prop="base_commission.value"
|
|
||||||
>
|
|
||||||
<ElInputNumber
|
<ElInputNumber
|
||||||
v-model="form.base_commission.value"
|
v-model="form.one_time_commission_amount"
|
||||||
:min="0"
|
:min="0"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:placeholder="
|
placeholder="请输入该代理能拿的一次性佣金金额上限(分)"
|
||||||
form.base_commission.mode === 'fixed'
|
|
||||||
? '请输入固定返佣金额(分)'
|
|
||||||
: '请输入返佣百分比的千分比(如200表示20%)'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
<div class="form-tip">
|
<div class="form-tip">该代理在此系列分配下能获得的一次性佣金金额上限(单位:分)</div>
|
||||||
{{
|
|
||||||
form.base_commission.mode === 'fixed'
|
|
||||||
? '每笔交易返佣该固定金额(单位:分)'
|
|
||||||
: '返佣百分比的千分比,如200表示20%,即每笔交易返佣 = 交易金额 × 20%'
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<!-- 一次性佣金配置 -->
|
|
||||||
<ElDivider content-position="left">一次性佣金设置(可选)</ElDivider>
|
|
||||||
<ElFormItem label="启用一次性佣金">
|
<ElFormItem label="启用一次性佣金">
|
||||||
<ElSwitch v-model="form.enable_one_time_commission" />
|
<ElSwitch v-model="form.enable_one_time_commission" />
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<template v-if="form.enable_one_time_commission">
|
<template v-if="form.enable_one_time_commission">
|
||||||
<ElFormItem label="一次性佣金类型" prop="one_time_commission_config.type">
|
<ElFormItem label="触发阈值(分)" prop="one_time_commission_threshold">
|
||||||
<ElRadioGroup v-model="form.one_time_commission_config.type">
|
<ElInputNumber
|
||||||
<ElRadio value="fixed">固定</ElRadio>
|
v-model="form.one_time_commission_threshold"
|
||||||
<ElRadio value="tiered">梯度</ElRadio>
|
:min="0"
|
||||||
</ElRadioGroup>
|
:controls="false"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请输入触发阈值(分)"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">达到此充值金额后触发一次性佣金</div>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem label="触发条件" prop="one_time_commission_config.trigger">
|
<ElFormItem label="触发类型" prop="one_time_commission_trigger">
|
||||||
<ElRadioGroup v-model="form.one_time_commission_config.trigger">
|
<ElRadioGroup v-model="form.one_time_commission_trigger">
|
||||||
<ElRadio value="single_recharge">单次充值</ElRadio>
|
<ElRadio value="first_recharge">首次充值</ElRadio>
|
||||||
<ElRadio value="accumulated_recharge">累计充值</ElRadio>
|
<ElRadio value="accumulated_recharge">累计充值</ElRadio>
|
||||||
</ElRadioGroup>
|
</ElRadioGroup>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
<ElFormItem label="最低阈值(分)" prop="one_time_commission_config.threshold">
|
<!-- 强制充值配置 -->
|
||||||
|
<ElDivider content-position="left">强制充值配置(可选)</ElDivider>
|
||||||
|
|
||||||
|
<ElFormItem label="启用强制充值">
|
||||||
|
<ElSwitch v-model="form.enable_force_recharge" />
|
||||||
|
</ElFormItem>
|
||||||
|
|
||||||
|
<template v-if="form.enable_force_recharge">
|
||||||
|
<ElFormItem label="强充金额(分)" prop="force_recharge_amount">
|
||||||
<ElInputNumber
|
<ElInputNumber
|
||||||
v-model="form.one_time_commission_config.threshold"
|
v-model="form.force_recharge_amount"
|
||||||
:min="1"
|
:min="0"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入最低阈值(分)"
|
placeholder="请输入强制充值金额(分)"
|
||||||
/>
|
/>
|
||||||
|
<div class="form-tip">用户需要达到的强制充值金额</div>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<!-- 固定类型配置 -->
|
<ElFormItem label="强充触发类型" prop="force_recharge_trigger_type">
|
||||||
<template v-if="form.one_time_commission_config.type === 'fixed'">
|
<ElRadioGroup v-model="form.force_recharge_trigger_type">
|
||||||
<ElFormItem label="返佣模式" prop="one_time_commission_config.mode">
|
<ElRadio :value="1">单次充值</ElRadio>
|
||||||
<ElRadioGroup v-model="form.one_time_commission_config.mode">
|
<ElRadio :value="2">累计充值</ElRadio>
|
||||||
<ElRadio value="fixed">固定金额</ElRadio>
|
</ElRadioGroup>
|
||||||
<ElRadio value="percent">百分比</ElRadio>
|
</ElFormItem>
|
||||||
</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>
|
|
||||||
</ElFormItem>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -368,8 +296,9 @@
|
|||||||
{ label: '系列名称', prop: 'series_name' },
|
{ label: '系列名称', prop: 'series_name' },
|
||||||
{ label: '店铺名称', prop: 'shop_name' },
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
{ label: '分配者店铺', prop: 'allocator_shop_name' },
|
{ label: '分配者店铺', prop: 'allocator_shop_name' },
|
||||||
{ label: '基础返佣', prop: 'base_commission' },
|
{ label: '一次性佣金金额', prop: 'one_time_commission_amount' },
|
||||||
{ label: '一次性佣金', prop: 'enable_one_time_commission' },
|
{ label: '一次性佣金状态', prop: 'enable_one_time_commission' },
|
||||||
|
{ label: '强制充值', prop: 'enable_force_recharge' },
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '创建时间', prop: 'created_at' },
|
{ label: '创建时间', prop: 'created_at' },
|
||||||
{ label: '操作', prop: 'operation' }
|
{ label: '操作', prop: 'operation' }
|
||||||
@@ -383,19 +312,13 @@
|
|||||||
series_name: '',
|
series_name: '',
|
||||||
shop_name: '',
|
shop_name: '',
|
||||||
allocator_shop_name: '',
|
allocator_shop_name: '',
|
||||||
base_commission: {
|
|
||||||
mode: 'fixed',
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
enable_one_time_commission: false,
|
enable_one_time_commission: false,
|
||||||
one_time_commission_config: {
|
one_time_commission_amount: 0,
|
||||||
type: 'fixed',
|
one_time_commission_threshold: undefined,
|
||||||
trigger: 'single_recharge',
|
one_time_commission_trigger: 'first_recharge' as 'first_recharge' | 'accumulated_recharge',
|
||||||
threshold: 0,
|
enable_force_recharge: false,
|
||||||
mode: 'fixed',
|
force_recharge_amount: undefined,
|
||||||
value: 0,
|
force_recharge_trigger_type: undefined as 1 | 2 | undefined
|
||||||
tiers: []
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 动态验证规则
|
// 动态验证规则
|
||||||
@@ -403,15 +326,14 @@
|
|||||||
const baseRules: FormRules = {
|
const baseRules: FormRules = {
|
||||||
series_id: [{ required: true, message: '请选择套餐系列', trigger: 'change' }],
|
series_id: [{ required: true, message: '请选择套餐系列', trigger: 'change' }],
|
||||||
shop_id: [{ required: true, message: '请选择店铺', trigger: 'change' }],
|
shop_id: [{ required: true, message: '请选择店铺', trigger: 'change' }],
|
||||||
'base_commission.mode': [{ required: true, message: '请选择返佣模式', trigger: 'change' }],
|
one_time_commission_amount: [
|
||||||
'base_commission.value': [
|
{ required: true, message: '请输入一次性佣金金额上限', trigger: 'blur' },
|
||||||
{ required: true, message: '请输入返佣值', trigger: 'blur' },
|
|
||||||
{
|
{
|
||||||
validator: (rule, value, callback) => {
|
validator: (rule, value, callback) => {
|
||||||
if (value === undefined || value === null || value === '') {
|
if (value === undefined || value === null || value === '') {
|
||||||
callback(new Error('请输入返佣值'))
|
callback(new Error('请输入一次性佣金金额上限'))
|
||||||
} else if (value < 0) {
|
} else if (value < 0) {
|
||||||
callback(new Error('返佣值不能小于0'))
|
callback(new Error('一次性佣金金额不能小于0'))
|
||||||
} else {
|
} else {
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
@@ -421,27 +343,24 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果启用了一次性佣金,添加验证规则
|
// 如果启用了一次性佣金,添加额外验证规则
|
||||||
if (form.enable_one_time_commission) {
|
if (form.enable_one_time_commission) {
|
||||||
baseRules['one_time_commission_config.type'] = [
|
baseRules.one_time_commission_threshold = [
|
||||||
{ required: true, message: '请选择一次性佣金类型', trigger: 'change' }
|
{ required: true, message: '请输入触发阈值', trigger: 'blur' }
|
||||||
]
|
]
|
||||||
baseRules['one_time_commission_config.trigger'] = [
|
baseRules.one_time_commission_trigger = [
|
||||||
{ required: true, message: '请选择触发条件', trigger: 'change' }
|
{ required: true, message: '请选择触发类型', trigger: 'change' }
|
||||||
]
|
|
||||||
baseRules['one_time_commission_config.threshold'] = [
|
|
||||||
{ required: true, message: '请输入最低阈值', trigger: 'blur' }
|
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
// 固定类型验证
|
// 如果启用了强制充值,添加验证规则
|
||||||
if (form.one_time_commission_config.type === 'fixed') {
|
if (form.enable_force_recharge) {
|
||||||
baseRules['one_time_commission_config.mode'] = [
|
baseRules.force_recharge_amount = [
|
||||||
{ required: true, message: '请选择返佣模式', trigger: 'change' }
|
{ required: true, message: '请输入强制充值金额', trigger: 'blur' }
|
||||||
]
|
]
|
||||||
baseRules['one_time_commission_config.value'] = [
|
baseRules.force_recharge_trigger_type = [
|
||||||
{ required: true, message: '请输入佣金值', trigger: 'blur' }
|
{ required: true, message: '请选择强充触发类型', trigger: 'change' }
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseRules
|
return baseRules
|
||||||
@@ -476,23 +395,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'base_commission',
|
prop: 'one_time_commission_amount',
|
||||||
label: '基础返佣',
|
label: '一次性佣金金额',
|
||||||
width: 150,
|
width: 150,
|
||||||
formatter: (row: ShopSeriesAllocationResponse) => {
|
formatter: (row: ShopSeriesAllocationResponse) => {
|
||||||
if (!row.base_commission) return '-'
|
return h(
|
||||||
const { mode, value } = row.base_commission
|
'span',
|
||||||
if (mode === 'fixed') {
|
{ style: 'color: #f56c6c; font-weight: bold' },
|
||||||
return `固定 ¥${(value / 100).toFixed(2)}`
|
`¥${(row.one_time_commission_amount / 100).toFixed(2)}`
|
||||||
} else {
|
)
|
||||||
return `百分比 ${(value / 10).toFixed(1)}%`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'enable_one_time_commission',
|
prop: 'enable_one_time_commission',
|
||||||
label: '一次性佣金',
|
label: '一次性佣金状态',
|
||||||
width: 120,
|
width: 130,
|
||||||
formatter: (row: ShopSeriesAllocationResponse) => {
|
formatter: (row: ShopSeriesAllocationResponse) => {
|
||||||
return h(
|
return h(
|
||||||
ElTag,
|
ElTag,
|
||||||
@@ -501,6 +418,18 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'enable_force_recharge',
|
||||||
|
label: '强制充值',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: ShopSeriesAllocationResponse) => {
|
||||||
|
return h(
|
||||||
|
ElTag,
|
||||||
|
{ type: row.enable_force_recharge ? 'warning' : 'info', size: 'small' },
|
||||||
|
() => (row.enable_force_recharge ? '已启用' : '未启用')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
label: '状态',
|
label: '状态',
|
||||||
@@ -750,21 +679,6 @@
|
|||||||
getTableData()
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加档位
|
|
||||||
const addTier = () => {
|
|
||||||
form.one_time_commission_config.tiers.push({
|
|
||||||
tier_type: 'sales_count',
|
|
||||||
threshold: 0,
|
|
||||||
mode: 'fixed',
|
|
||||||
value: 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除档位
|
|
||||||
const removeTier = (index: number) => {
|
|
||||||
form.one_time_commission_config.tiers.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示新增/编辑对话框
|
// 显示新增/编辑对话框
|
||||||
const showDialog = (type: string, row?: ShopSeriesAllocationResponse) => {
|
const showDialog = (type: string, row?: ShopSeriesAllocationResponse) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
@@ -777,30 +691,13 @@
|
|||||||
form.series_name = row.series_name
|
form.series_name = row.series_name
|
||||||
form.shop_name = row.shop_name
|
form.shop_name = row.shop_name
|
||||||
form.allocator_shop_name = row.allocator_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_one_time_commission = row.enable_one_time_commission
|
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_amount = row.one_time_commission_amount
|
||||||
form.one_time_commission_config = {
|
form.one_time_commission_threshold = row.one_time_commission_threshold
|
||||||
type: row.one_time_commission_config.type,
|
form.one_time_commission_trigger = row.one_time_commission_trigger || 'first_recharge'
|
||||||
trigger: row.one_time_commission_config.trigger,
|
form.enable_force_recharge = row.enable_force_recharge
|
||||||
threshold: row.one_time_commission_config.threshold,
|
form.force_recharge_amount = row.force_recharge_amount
|
||||||
mode: row.one_time_commission_config.mode || 'fixed',
|
form.force_recharge_trigger_type = row.force_recharge_trigger_type
|
||||||
value: row.one_time_commission_config.value || 0,
|
|
||||||
tiers: row.one_time_commission_config.tiers?.map((t) => ({ ...t })) || []
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
form.one_time_commission_config = {
|
|
||||||
type: 'fixed',
|
|
||||||
trigger: 'single_recharge',
|
|
||||||
threshold: 0,
|
|
||||||
mode: 'fixed',
|
|
||||||
value: 0,
|
|
||||||
tiers: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
form.id = 0
|
form.id = 0
|
||||||
form.series_id = undefined
|
form.series_id = undefined
|
||||||
@@ -808,19 +705,13 @@
|
|||||||
form.series_name = ''
|
form.series_name = ''
|
||||||
form.shop_name = ''
|
form.shop_name = ''
|
||||||
form.allocator_shop_name = ''
|
form.allocator_shop_name = ''
|
||||||
form.base_commission = {
|
|
||||||
mode: 'fixed',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
form.enable_one_time_commission = false
|
form.enable_one_time_commission = false
|
||||||
form.one_time_commission_config = {
|
form.one_time_commission_amount = 0
|
||||||
type: 'fixed',
|
form.one_time_commission_threshold = undefined
|
||||||
trigger: 'single_recharge',
|
form.one_time_commission_trigger = 'first_recharge'
|
||||||
threshold: 0,
|
form.enable_force_recharge = false
|
||||||
mode: 'fixed',
|
form.force_recharge_amount = undefined
|
||||||
value: 0,
|
form.force_recharge_trigger_type = undefined
|
||||||
tiers: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置表单验证状态
|
// 重置表单验证状态
|
||||||
@@ -840,19 +731,13 @@
|
|||||||
form.series_name = ''
|
form.series_name = ''
|
||||||
form.shop_name = ''
|
form.shop_name = ''
|
||||||
form.allocator_shop_name = ''
|
form.allocator_shop_name = ''
|
||||||
form.base_commission = {
|
|
||||||
mode: 'fixed',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
form.enable_one_time_commission = false
|
form.enable_one_time_commission = false
|
||||||
form.one_time_commission_config = {
|
form.one_time_commission_amount = 0
|
||||||
type: 'fixed',
|
form.one_time_commission_threshold = undefined
|
||||||
trigger: 'single_recharge',
|
form.one_time_commission_trigger = 'first_recharge'
|
||||||
threshold: 0,
|
form.enable_force_recharge = false
|
||||||
mode: 'fixed',
|
form.force_recharge_amount = undefined
|
||||||
value: 0,
|
form.force_recharge_trigger_type = undefined
|
||||||
tiers: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除分配
|
// 删除分配
|
||||||
@@ -886,59 +771,24 @@
|
|||||||
|
|
||||||
await formEl.validate(async (valid) => {
|
await formEl.validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
// 验证一次性佣金配置
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
submitLoading.value = true
|
submitLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data: any = {
|
const data: any = {
|
||||||
base_commission: {
|
one_time_commission_amount: form.one_time_commission_amount,
|
||||||
mode: form.base_commission.mode,
|
enable_one_time_commission: form.enable_one_time_commission,
|
||||||
value: form.base_commission.value
|
enable_force_recharge: form.enable_force_recharge
|
||||||
},
|
|
||||||
enable_one_time_commission: form.enable_one_time_commission
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果启用了一次性佣金,加入配置
|
// 如果启用了一次性佣金,加入相关字段
|
||||||
if (form.enable_one_time_commission) {
|
if (form.enable_one_time_commission) {
|
||||||
data.one_time_commission_config = {
|
data.one_time_commission_threshold = form.one_time_commission_threshold
|
||||||
type: form.one_time_commission_config.type,
|
data.one_time_commission_trigger = form.one_time_commission_trigger
|
||||||
trigger: form.one_time_commission_config.trigger,
|
}
|
||||||
threshold: form.one_time_commission_config.threshold
|
|
||||||
}
|
|
||||||
|
|
||||||
// 固定类型配置
|
// 如果启用了强制充值,加入相关字段
|
||||||
if (form.one_time_commission_config.type === 'fixed') {
|
if (form.enable_force_recharge) {
|
||||||
data.one_time_commission_config.mode = form.one_time_commission_config.mode
|
data.force_recharge_amount = form.force_recharge_amount
|
||||||
data.one_time_commission_config.value = form.one_time_commission_config.value
|
data.force_recharge_trigger_type = form.force_recharge_trigger_type
|
||||||
}
|
|
||||||
// 梯度类型配置
|
|
||||||
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
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialogType.value === 'add') {
|
if (dialogType.value === 'add') {
|
||||||
|
|||||||
Reference in New Issue
Block a user