This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# Design: 订单管理响应语义升级
|
||||
|
||||
## Context
|
||||
|
||||
订单管理功能已经有初步页面和类型定义,但其实现建立在较早的接口语义上。最新后端返回已经把以下概念拆分清楚:
|
||||
|
||||
- 操作者是谁
|
||||
- 订单业务归属到哪个销售店铺
|
||||
- 佣金流程走到哪一步
|
||||
- 佣金业务结果是什么
|
||||
|
||||
当前页面仍然把这些概念部分混用,导致 UI 语义不稳定,也容易让后续实现继续复制错误判断。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals:
|
||||
- 让订单模块的类型定义、列表页、详情页统一按新语义工作
|
||||
- 消除对 `commission_status` 的错误业务推断
|
||||
- 在页面上明确区分“操作者”和“销售店铺”
|
||||
- Non-Goals:
|
||||
- 不改动 `purchase-check` 和 `cancel` 接口流程
|
||||
- 不改造后端返回结构
|
||||
- 不新增全新的订单业务流程
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: 在前端类型层面显式补齐新增字段,而不是通过 `any` 或临时属性兼容
|
||||
- Rationale: 这次变化的核心是语义纠偏。如果类型层继续缺字段,页面很容易回退到旧假设。
|
||||
|
||||
- Decision: 列表页必须同时呈现佣金流程状态和佣金业务结果,但不强制唯一 UI 形式
|
||||
- Rationale: `commission_status_name` 仅代表流程状态,`commission_result_name` 代表业务结果。无论是两列展示,还是“主状态 + 副标签”,都必须让用户能明确区分这两个维度。
|
||||
|
||||
- Decision: 详情页将“操作者信息”和“业务归属信息”拆成两组展示项
|
||||
- Rationale: 同一信息块内混放会继续强化旧认知,不利于用户理解订单责任归属。
|
||||
|
||||
- Decision: 所有“是否有佣金”的判断必须同时参考 `commission_status` 与 `commission_result`
|
||||
- Rationale: 后端已经把流程状态和业务结果拆开,单独读取 `commission_status` 会产生错误结论。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 风险: 订单模块以外可能也有复用订单类型或字段语义的代码。
|
||||
- Mitigation: 实施前使用全局搜索盘点 `operator_name`、`commission_status`、`commission_result` 的相关用法。
|
||||
|
||||
- 风险: 列表新增列后表格宽度会增加。
|
||||
- Mitigation: 优先保留关键列,可通过列宽、固定列或列配置能力控制首屏拥挤度。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 先调整订单类型定义和展示 helper,建立统一语义基础。
|
||||
2. 再修改订单列表页,确保两类状态与店铺信息展示正确。
|
||||
3. 最后修改详情页,把操作者信息和业务归属信息拆开。
|
||||
4. 通过接口联调样例验证典型场景:
|
||||
- 平台订单返回 `operator_name = admin`
|
||||
- 已完成且有佣金
|
||||
- 已完成但无佣金
|
||||
- 链路异常待人工处理
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 订单列表中的佣金信息最终采用哪种视觉结构更合适:
|
||||
- 两列展示
|
||||
- 主状态 + 副标签
|
||||
- 只要能同时表达“流程状态”和“业务结果”,两种方案都符合本次提案范围
|
||||
@@ -0,0 +1,42 @@
|
||||
# Change: 更新订单管理响应语义与展示规则
|
||||
|
||||
## Why
|
||||
|
||||
订单管理页面已经接入 `/api/admin/orders` 相关接口,但当前前端实现仍带有旧语义假设:
|
||||
|
||||
- `operator_*` 在部分场景被当成代理店铺信息理解和展示。
|
||||
- `commission_status` 仍被当成“是否有佣金”的单一结果使用。
|
||||
- 订单列表和详情页没有显式表达“真实操作者”和“业务归属店铺”的区别。
|
||||
|
||||
后端最新 `OrderResponse` 已经明确拆分了这些概念,并新增了 `seller_shop_*` 与 `commission_result*` 字段。如果前端不跟进,用户会在订单列表和详情页看到混淆信息,且“已完成但无佣金”和“已完成且有佣金”无法被正确区分。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 更新订单管理前端对 `OrderResponse` 的类型定义和字段语义解释:
|
||||
- `operator_*` 统一表示真实操作者
|
||||
- `seller_shop_*` 统一表示业务归属店铺
|
||||
- `commission_status` 统一表示佣金流程状态
|
||||
- `commission_result` 统一表示佣金业务结果
|
||||
- 更新订单列表页展示规则:
|
||||
- 增加“销售店铺/归属店铺”展示
|
||||
- 佣金信息必须同时呈现“流程状态”和“业务结果”两个维度
|
||||
- 具体呈现形式可以是两列,或“主状态 + 副标签”
|
||||
- 更新订单详情页信息结构:
|
||||
- 单独展示操作者信息
|
||||
- 单独展示业务归属信息
|
||||
- 对齐创建订单成功后的响应处理,避免继续使用旧语义做前端判断
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `order-management`
|
||||
- Affected code:
|
||||
- `src/types/api/order.ts`
|
||||
- `src/api/modules/order.ts`
|
||||
- `src/views/order-management/order-list/index.vue`
|
||||
- `src/views/order-management/order-list/detail.vue`
|
||||
- Dependencies:
|
||||
- 依赖后端已提供最新 `OrderResponse` 字段和语义
|
||||
- 与活跃变更 `add-order-management`、`update-order-upload-and-device-switch-mode` 属于同一能力域,落地时需要一起核对订单模块现状
|
||||
- Breaking changes:
|
||||
- 前端内部对订单字段的解释逻辑会变化;任何把 `operator_name` 当店铺名、把 `commission_status` 当佣金结果的代码都需要调整
|
||||
@@ -0,0 +1,66 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Updated Order Response Semantics
|
||||
|
||||
The system SHALL interpret the latest `OrderResponse` fields consistently across order creation, order list, and order detail experiences.
|
||||
|
||||
#### Scenario: Order response fields are typed and understood by new semantics
|
||||
|
||||
- **GIVEN** 前端接收到 `POST /api/admin/orders`、`GET /api/admin/orders` 或 `GET /api/admin/orders/:id` 返回的 `OrderResponse`
|
||||
- **WHEN** 前端解析订单对象
|
||||
- **THEN** `operator_*` MUST 被解释为真实操作者信息
|
||||
- **AND** `seller_shop_*` MUST 被解释为订单业务归属店铺信息
|
||||
- **AND** `commission_status` MUST 被解释为佣金流程状态
|
||||
- **AND** `commission_result` MUST 被解释为佣金业务结果
|
||||
|
||||
#### Scenario: Created order response follows the same semantics
|
||||
|
||||
- **GIVEN** 用户成功调用 `POST /api/admin/orders` 创建订单
|
||||
- **WHEN** 前端处理创建成功后返回的订单对象
|
||||
- **THEN** 系统 MUST 按最新 `OrderResponse` 语义解析新增字段和语义变更字段
|
||||
- **AND** 系统 MUST NOT 仅根据 `commission_status` 推断该订单“有佣金”
|
||||
|
||||
### Requirement: Order List Must Distinguish Operator, Shop, and Commission Result
|
||||
|
||||
The system SHALL display order list data in a way that distinguishes real operator identity, seller shop ownership, commission workflow status, and commission business result.
|
||||
|
||||
#### Scenario: Order list shows operator and seller shop separately
|
||||
|
||||
- **GIVEN** 用户正在查看后台订单列表
|
||||
- **WHEN** 某条订单同时存在 `operator_name` 和 `seller_shop_name`
|
||||
- **THEN** 系统 MUST 将“操作者”展示为 `operator_name`
|
||||
- **AND** 系统 MUST 将“销售店铺”或“归属店铺”展示为 `seller_shop_name`
|
||||
- **AND** 系统 MUST NOT 把 `operator_name` 当成店铺名称展示
|
||||
|
||||
#### Scenario: Order list distinguishes workflow status from business result
|
||||
|
||||
- **GIVEN** 用户正在查看后台订单列表
|
||||
- **WHEN** 某条订单返回 `commission_status_name` 与 `commission_result_name`
|
||||
- **THEN** 系统 MUST 让用户同时看到佣金流程状态和佣金业务结果
|
||||
- **AND** 系统 MAY 通过两列展示,或通过“主状态 + 副标签”展示
|
||||
- **AND** “已完成且有佣金”与“已完成但无佣金” MUST 可被明确区分
|
||||
- **AND** “链路异常待人工处理” MUST 可被明确区分
|
||||
|
||||
### Requirement: Order Detail Must Separate Operator Info from Seller Shop Info
|
||||
|
||||
The system SHALL structure order detail views so that operator identity and seller shop ownership are displayed as separate concepts.
|
||||
|
||||
#### Scenario: Order detail splits operator and ownership information
|
||||
|
||||
- **GIVEN** 用户正在查看后台订单详情
|
||||
- **WHEN** 订单详情返回 `operator_id`、`operator_type`、`operator_name`、`seller_shop_id` 与 `seller_shop_name`
|
||||
- **THEN** 系统 MUST 单独展示操作者信息
|
||||
- **AND** 系统 MUST 单独展示业务归属信息
|
||||
- **AND** 页面 MUST 让用户明确区分“谁发起下单”和“订单归属哪个销售店铺”
|
||||
|
||||
### Requirement: Commission Presence Must Not Be Inferred from Workflow Status Alone
|
||||
|
||||
The system SHALL avoid inferring commission presence from `commission_status` alone.
|
||||
|
||||
#### Scenario: Completed order still requires commission result to determine outcome
|
||||
|
||||
- **GIVEN** 某条订单的 `commission_status = 2`
|
||||
- **WHEN** 前端需要判断该订单是否“有佣金”
|
||||
- **THEN** 系统 MUST 继续读取 `commission_result`
|
||||
- **AND** 仅当 `commission_status = 2` 且 `commission_result = 1` 时,系统才可判定为“有佣金”
|
||||
- **AND** 当 `commission_status = 2` 且 `commission_result = 2` 时,系统 MUST 判定为“无佣金”
|
||||
@@ -0,0 +1,28 @@
|
||||
## 1. 类型与语义对齐
|
||||
|
||||
- [x] 1.1 更新 `src/types/api/order.ts`,补齐 `seller_shop_*`、`commission_result*`、`operator_id`、`operator_type` 等字段定义。
|
||||
- [x] 1.2 更新订单相关枚举和注释,明确 `commission_status` 是流程状态,`commission_result` 是业务结果。
|
||||
- [x] 1.3 盘点订单模块中仍按旧语义使用 `operator_*` 或 `commission_status` 的逻辑。
|
||||
|
||||
## 2. 列表页改造
|
||||
|
||||
- [x] 2.1 更新订单列表列配置,单独展示“操作者”和“销售店铺/归属店铺”。
|
||||
- [x] 2.2 调整列表中的佣金展示,确保用户能同时看到“流程状态”和“业务结果”两个维度。
|
||||
- [x] 2.3 修正任何基于 `commission_status === 2` 推导“有佣金”的旧判断。
|
||||
|
||||
## 3. 详情页改造
|
||||
|
||||
- [x] 3.1 调整订单详情信息结构,新增“操作者信息”展示块。
|
||||
- [x] 3.2 调整订单详情信息结构,新增“业务归属信息”展示块。
|
||||
- [x] 3.3 在详情页显式展示佣金流程状态和佣金业务结果。
|
||||
|
||||
## 4. 创建订单响应处理
|
||||
|
||||
- [x] 4.1 对齐 `POST /api/admin/orders` 返回的 `OrderResponse` 类型。
|
||||
- [x] 4.2 检查创建成功后的前端处理,确保不再基于旧佣金语义做推断。
|
||||
|
||||
## 5. 验证
|
||||
|
||||
- [x] 5.1 验证订单列表能够区分“已完成且有佣金”和“已完成但无佣金”。
|
||||
- [x] 5.2 验证订单详情能够区分“操作者”和“销售店铺”。
|
||||
- [x] 5.3 运行 `openspec.cmd validate update-order-management-response-semantics --strict`。
|
||||
@@ -19,11 +19,22 @@ export type BuyerType = 'personal' | 'agent'
|
||||
// 订单支付方式
|
||||
export type OrderPaymentMethod = 'wallet' | 'wechat' | 'alipay' | 'offline'
|
||||
|
||||
// 订单佣金状态
|
||||
// 订单操作者类型
|
||||
export type OrderOperatorType = 'platform' | 'agent' | 'enterprise' | 'personal_customer'
|
||||
|
||||
// 订单佣金流程状态
|
||||
export enum OrderCommissionStatus {
|
||||
NOT_APPLICABLE = 0, // 不适用
|
||||
PENDING = 1, // 待结算
|
||||
SETTLED = 2 // 已结算
|
||||
PENDING = 1, // 待计算
|
||||
COMPLETED = 2, // 已完成
|
||||
MANUAL_REVIEW = 3 // 待人工处理
|
||||
}
|
||||
|
||||
// 订单佣金业务结果
|
||||
export enum OrderCommissionResult {
|
||||
UNKNOWN = 0, // 未知
|
||||
HAS_COMMISSION = 1, // 有佣金
|
||||
NO_COMMISSION = 2, // 无佣金
|
||||
LINK_EXCEPTION = 3 // 链路异常
|
||||
}
|
||||
|
||||
// 订单明细
|
||||
@@ -52,8 +63,10 @@ export interface Order {
|
||||
total_amount: number // 订单总金额(分)
|
||||
actual_paid_amount?: number // 实付金额(分)
|
||||
paid_at: string | null
|
||||
commission_status: OrderCommissionStatus
|
||||
commission_status_name?: string // 佣金状态名称(中文)
|
||||
commission_status: OrderCommissionStatus // 佣金流程状态
|
||||
commission_status_name?: string // 佣金流程状态名称(中文)
|
||||
commission_result?: OrderCommissionResult // 佣金业务结果
|
||||
commission_result_name?: string // 佣金业务结果名称(中文)
|
||||
commission_config_version: number
|
||||
device_id: number | null
|
||||
iot_card_id: number | null
|
||||
@@ -61,7 +74,14 @@ export interface Order {
|
||||
asset_type?: string // 资产类型:single_card 或 device
|
||||
purchase_role?: string // 订单渠道
|
||||
purchase_remark?: string // 购买备注
|
||||
operator_name?: string // 操作者
|
||||
is_purchased_by_parent?: boolean // 是否由上级代购
|
||||
is_expired?: boolean // 是否已过期
|
||||
expires_at?: string | null // 订单超时时间
|
||||
operator_id?: number | null // 真实操作者ID
|
||||
operator_type?: OrderOperatorType // 真实操作者类型
|
||||
operator_name?: string // 真实操作者名称
|
||||
seller_shop_id?: number | null // 销售店铺ID
|
||||
seller_shop_name?: string // 销售店铺名称
|
||||
items: OrderItem[] | null
|
||||
payment_voucher_key?: string // 支付凭证(线下支付时才有值)
|
||||
created_at: string
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@focus="handleOwnerShopFocus"
|
||||
@visible-change="handleOwnerShopVisibleChange"
|
||||
@change="handleShopChange"
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -530,10 +532,6 @@
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
// 只有非代理账号才需要加载店铺列表
|
||||
if (!isAgentAccount.value) {
|
||||
loadShopList()
|
||||
}
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
@@ -542,6 +540,8 @@
|
||||
|
||||
// 加载店铺列表 - 改用级联查询
|
||||
const loadShopList = async () => {
|
||||
if (shopLoading.value) return
|
||||
|
||||
shopLoading.value = true
|
||||
try {
|
||||
const res = await ShopService.getShopsCascade({ parent_id: undefined })
|
||||
@@ -559,6 +559,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
const handleOwnerShopFocus = async () => {
|
||||
if (isAgentAccount.value) return
|
||||
await loadShopList()
|
||||
}
|
||||
|
||||
const handleOwnerShopVisibleChange = async (visible: boolean) => {
|
||||
if (!visible) return
|
||||
await handleOwnerShopFocus()
|
||||
}
|
||||
|
||||
// 处理店铺选择变化
|
||||
const handleShopChange = (value: any) => {
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
|
||||
@@ -178,7 +178,6 @@
|
||||
<ElDivider content-position="left">已授权设备</ElDivider>
|
||||
<ElTable :data="operationResult.authorized_devices" border max-height="200">
|
||||
<ElTableColumn prop="virtual_no" label="设备号" width="180" />
|
||||
<ElTableColumn prop="device_id" label="设备ID" width="100" />
|
||||
<ElTableColumn label="绑定卡数">
|
||||
<template #default="{ row }">
|
||||
{{ row.card_count || 0 }}
|
||||
|
||||
@@ -60,14 +60,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElCard, ElButton, ElIcon, ElMessage, ElTable, ElTableColumn, ElTag } from 'element-plus'
|
||||
import { ElCard, ElButton, ElIcon, ElMessage, ElTable, ElTableColumn } from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
import DetailPage from '@/components/common/DetailPage.vue'
|
||||
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||
import { OrderService } from '@/api/modules'
|
||||
import type { Order } from '@/types/api'
|
||||
import type {
|
||||
Order,
|
||||
OrderCommissionResult,
|
||||
OrderCommissionStatus,
|
||||
OrderOperatorType,
|
||||
OrderPaymentMethod
|
||||
} from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
|
||||
defineOptions({ name: 'OrderDetail' })
|
||||
@@ -94,25 +100,61 @@
|
||||
}
|
||||
|
||||
// 获取支付方式文本
|
||||
const getPaymentMethodText = (method: string): string => {
|
||||
const methodMap: Record<string, string> = {
|
||||
const getPaymentMethodText = (method: OrderPaymentMethod): string => {
|
||||
const methodMap: Record<OrderPaymentMethod, string> = {
|
||||
wallet: '钱包支付',
|
||||
wechat: '微信支付',
|
||||
alipay: '支付宝支付'
|
||||
alipay: '支付宝支付',
|
||||
offline: '线下支付'
|
||||
}
|
||||
return methodMap[method] || method
|
||||
}
|
||||
|
||||
// 获取佣金状态文本
|
||||
const getCommissionStatusText = (status: number): string => {
|
||||
const statusMap: Record<number, string> = {
|
||||
0: '不适用',
|
||||
1: '待结算',
|
||||
2: '已结算'
|
||||
// 获取操作者类型文本
|
||||
const getOperatorTypeText = (type?: OrderOperatorType): string => {
|
||||
if (!type) return '-'
|
||||
const typeMap: Record<OrderOperatorType, string> = {
|
||||
platform: '平台',
|
||||
agent: '代理',
|
||||
enterprise: '企业',
|
||||
personal_customer: '个人客户'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 获取佣金流程状态文本
|
||||
const getCommissionStatusText = (
|
||||
status?: OrderCommissionStatus,
|
||||
statusName?: string
|
||||
): string => {
|
||||
if (statusName) return statusName
|
||||
if (status === undefined) return '-'
|
||||
|
||||
const statusMap: Record<OrderCommissionStatus, string> = {
|
||||
1: '待计算',
|
||||
2: '已完成',
|
||||
3: '待人工处理'
|
||||
}
|
||||
return statusMap[status] || '-'
|
||||
}
|
||||
|
||||
// 获取佣金业务结果文本
|
||||
const getCommissionResultText = (
|
||||
result?: OrderCommissionResult,
|
||||
resultName?: string
|
||||
): string => {
|
||||
if (resultName) return resultName
|
||||
if (result === undefined) return '-'
|
||||
|
||||
const resultMap: Record<OrderCommissionResult, string> = {
|
||||
0: '未知',
|
||||
1: '有佣金',
|
||||
2: '无佣金',
|
||||
3: '链路异常'
|
||||
}
|
||||
return resultMap[result] || '-'
|
||||
}
|
||||
|
||||
// 获取订单角色文本
|
||||
const getPurchaseRoleText = (role: string): string => {
|
||||
const roleMap: Record<string, string> = {
|
||||
@@ -173,6 +215,11 @@
|
||||
prop: 'purchase_remark',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '支付时间',
|
||||
prop: 'paid_at',
|
||||
formatter: (value) => (value ? formatDateTime(value) : '-')
|
||||
},
|
||||
{
|
||||
label: '是否上级代购',
|
||||
formatter: (_, data) => (data.is_purchased_by_parent ? '是' : '否')
|
||||
@@ -182,16 +229,6 @@
|
||||
prop: 'asset_identifier',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '操作者类型',
|
||||
prop: 'operator_type',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '操作者名称',
|
||||
prop: 'operator_name',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '是否超时',
|
||||
formatter: (_, data) => {
|
||||
@@ -204,14 +241,6 @@
|
||||
prop: 'expires_at',
|
||||
formatter: (value) => (value ? formatDateTime(value) : '-')
|
||||
},
|
||||
{
|
||||
label: '佣金状态',
|
||||
formatter: (_, data) => getCommissionStatusText(data.commission_status)
|
||||
},
|
||||
{
|
||||
label: '佣金配置版本',
|
||||
prop: 'commission_config_version'
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'created_at',
|
||||
@@ -223,6 +252,50 @@
|
||||
formatter: (value) => formatDateTime(value)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '操作者信息',
|
||||
fields: [
|
||||
{
|
||||
label: '操作者类型',
|
||||
prop: 'operator_type',
|
||||
formatter: (value) => getOperatorTypeText(value)
|
||||
},
|
||||
{
|
||||
label: '操作者名称',
|
||||
prop: 'operator_name',
|
||||
formatter: (value) => value || '-'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '业务归属信息',
|
||||
fields: [
|
||||
{
|
||||
label: '销售店铺名称',
|
||||
prop: 'seller_shop_name',
|
||||
formatter: (value) => value || '-'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '佣金信息',
|
||||
fields: [
|
||||
{
|
||||
label: '流程状态',
|
||||
formatter: (_, data) =>
|
||||
getCommissionStatusText(data.commission_status, data.commission_status_name)
|
||||
},
|
||||
{
|
||||
label: '业务结果',
|
||||
formatter: (_, data) =>
|
||||
getCommissionResultText(data.commission_result, data.commission_result_name)
|
||||
},
|
||||
{
|
||||
label: '佣金配置版本',
|
||||
prop: 'commission_config_version'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:actions="getActions"
|
||||
:actionsWidth="120"
|
||||
:actionsWidth="180"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
@@ -227,90 +227,6 @@
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 订单详情对话框 -->
|
||||
<ElDialog v-model="detailDialogVisible" :title="'订单详情'" width="800px">
|
||||
<div v-if="currentOrder" class="order-detail">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem :label="'订单编号'">
|
||||
{{ currentOrder.order_no }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'订单类型'">
|
||||
{{ getOrderTypeText(currentOrder.order_type) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'支付状态'">
|
||||
<ElTag :type="getPaymentStatusType(currentOrder.payment_status)">
|
||||
{{ currentOrder.payment_status_text }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'订单金额'">
|
||||
{{ formatCurrency(currentOrder.total_amount) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'支付方式'">
|
||||
{{ getPaymentMethodText(currentOrder.payment_method) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'买家类型'">
|
||||
{{ getBuyerTypeText(currentOrder.buyer_type) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'买家手机号'">
|
||||
{{ currentOrder.buyer_phone || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'买家昵称'">
|
||||
{{ currentOrder.buyer_nickname || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'佣金状态'">
|
||||
{{ getCommissionStatusText(currentOrder.commission_status) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'支付时间'">
|
||||
{{ currentOrder.paid_at ? formatDateTime(currentOrder.paid_at) : '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'创建时间'">
|
||||
{{ formatDateTime(currentOrder.created_at) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="'更新时间'">
|
||||
{{ formatDateTime(currentOrder.updated_at) }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<!-- 订单项列表 -->
|
||||
<div
|
||||
v-if="currentOrder.items && currentOrder.items.length > 0"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<h4>{{ '订单项' }}</h4>
|
||||
<ElTable :data="currentOrder.items" border style="margin-top: 10px">
|
||||
<ElTableColumn prop="package_name" :label="'套餐名称'" min-width="150" />
|
||||
<ElTableColumn prop="package_type" :label="'套餐类型'" width="120">
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
row.package_type === 'formal'
|
||||
? '正式套餐'
|
||||
: row.package_type === 'addon'
|
||||
? '加油包'
|
||||
: '-'
|
||||
}}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="quantity" :label="'数量'" width="100" />
|
||||
<ElTableColumn prop="unit_price" :label="'单价'" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ formatCurrency(row.unit_price) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="amount" :label="'金额'" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ formatCurrency(row.amount) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="detailDialogVisible = false">{{ '关闭' }}</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 支付凭证预览 -->
|
||||
<PaymentVoucherDialog
|
||||
:file-key="paymentVoucherFileKey"
|
||||
@@ -344,6 +260,7 @@
|
||||
BuyerType,
|
||||
OrderPaymentMethod,
|
||||
OrderCommissionStatus,
|
||||
OrderCommissionResult,
|
||||
StandaloneIotCard,
|
||||
Device,
|
||||
PackageResponse
|
||||
@@ -358,6 +275,10 @@
|
||||
|
||||
defineOptions({ name: 'OrderList' })
|
||||
|
||||
type OrderSearchFormState = Omit<OrderQueryParams, 'is_expired'> & {
|
||||
is_expired?: number
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
const { hasAuth } = useAuth()
|
||||
const userStore = useUserStore()
|
||||
@@ -367,13 +288,11 @@
|
||||
const voucherUploading = ref(false)
|
||||
const tableRef = ref()
|
||||
const createDialogVisible = ref(false)
|
||||
const detailDialogVisible = ref(false)
|
||||
const currentOrder = ref<Order | null>(null)
|
||||
const paymentVoucherFileKey = ref('')
|
||||
let activeVoucherUploadId = 0
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState: OrderQueryParams = {
|
||||
const initialSearchState: OrderSearchFormState = {
|
||||
order_no: '',
|
||||
buyer_phone: '',
|
||||
payment_status: undefined,
|
||||
@@ -388,7 +307,7 @@
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive<OrderQueryParams>({ ...initialSearchState })
|
||||
const searchForm = reactive<OrderSearchFormState>({ ...initialSearchState })
|
||||
|
||||
// 店铺列表(用于代理商搜索)
|
||||
const shopOptions = ref<any[]>([])
|
||||
@@ -508,8 +427,8 @@
|
||||
type: 'select',
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ label: '已过期', value: true },
|
||||
{ label: '未过期', value: false }
|
||||
{ label: '已过期', value: 1 },
|
||||
{ label: '未过期', value: 0 }
|
||||
],
|
||||
config: {
|
||||
clearable: true
|
||||
@@ -576,8 +495,10 @@
|
||||
{ label: '购买备注', prop: 'purchase_remark' },
|
||||
{ label: '下单时间', prop: 'created_at' },
|
||||
{ label: '操作者', prop: 'operator_name' },
|
||||
{ label: '销售店铺', prop: 'seller_shop_name' },
|
||||
{ label: '支付状态', prop: 'payment_status' },
|
||||
{ label: '佣金状态', prop: 'commission_status_name' },
|
||||
{ label: '佣金流程状态', prop: 'commission_status_name' },
|
||||
{ label: '佣金业务结果', prop: 'commission_result_name' },
|
||||
{ label: '订单金额', prop: 'total_amount' },
|
||||
{ label: '实付金额', prop: 'actual_paid_amount' },
|
||||
{ label: '支付方式', prop: 'payment_method' },
|
||||
@@ -876,22 +797,70 @@
|
||||
return methodMap[method] || method
|
||||
}
|
||||
|
||||
// 获取佣金状态文本
|
||||
const getCommissionStatusText = (status: OrderCommissionStatus): string => {
|
||||
// 获取佣金流程状态文本
|
||||
const getCommissionStatusText = (status?: OrderCommissionStatus, statusName?: string): string => {
|
||||
if (statusName) return statusName
|
||||
if (status === undefined) return '-'
|
||||
|
||||
const statusMap: Record<OrderCommissionStatus, string> = {
|
||||
0: '不适用',
|
||||
1: '待结算',
|
||||
2: '已结算'
|
||||
1: '待计算',
|
||||
2: '已完成',
|
||||
3: '待人工处理'
|
||||
}
|
||||
return statusMap[status] || '-'
|
||||
}
|
||||
|
||||
// 获取佣金业务结果文本
|
||||
const getCommissionResultText = (result?: OrderCommissionResult, resultName?: string): string => {
|
||||
if (resultName) return resultName
|
||||
if (result === undefined) return '-'
|
||||
|
||||
const resultMap: Record<OrderCommissionResult, string> = {
|
||||
0: '未知',
|
||||
1: '有佣金',
|
||||
2: '无佣金',
|
||||
3: '链路异常'
|
||||
}
|
||||
return resultMap[result] || '-'
|
||||
}
|
||||
|
||||
const getCommissionStatusType = (
|
||||
status?: OrderCommissionStatus
|
||||
): 'success' | 'info' | 'warning' | 'danger' => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return 'warning'
|
||||
case 2:
|
||||
return 'success'
|
||||
case 3:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
const getCommissionResultType = (
|
||||
result?: OrderCommissionResult
|
||||
): 'success' | 'info' | 'warning' | 'danger' => {
|
||||
switch (result) {
|
||||
case 1:
|
||||
return 'success'
|
||||
case 2:
|
||||
return 'info'
|
||||
case 3:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'warning'
|
||||
}
|
||||
}
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'order_no',
|
||||
label: '订单编号',
|
||||
minWidth: 220,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => {
|
||||
return h(
|
||||
'span',
|
||||
@@ -992,14 +961,33 @@
|
||||
{
|
||||
prop: 'operator_name',
|
||||
label: '操作者',
|
||||
width: 120,
|
||||
width: 140,
|
||||
formatter: (row: Order) => row.operator_name || '-'
|
||||
},
|
||||
{
|
||||
prop: 'seller_shop_name',
|
||||
label: '销售店铺',
|
||||
width: 160,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => row.seller_shop_name || '-'
|
||||
},
|
||||
{
|
||||
prop: 'commission_status_name',
|
||||
label: '佣金状态',
|
||||
width: 120,
|
||||
formatter: (row: Order) => row.commission_status_name || '-'
|
||||
label: '佣金流程状态',
|
||||
width: 140,
|
||||
formatter: (row: Order) =>
|
||||
h(ElTag, { type: getCommissionStatusType(row.commission_status), size: 'small' }, () =>
|
||||
getCommissionStatusText(row.commission_status, row.commission_status_name)
|
||||
)
|
||||
},
|
||||
{
|
||||
prop: 'commission_result_name',
|
||||
label: '佣金业务结果',
|
||||
width: 140,
|
||||
formatter: (row: Order) =>
|
||||
h(ElTag, { type: getCommissionResultType(row.commission_result), size: 'small' }, () =>
|
||||
getCommissionResultText(row.commission_result, row.commission_result_name)
|
||||
)
|
||||
},
|
||||
{
|
||||
prop: 'total_amount',
|
||||
@@ -1070,7 +1058,8 @@
|
||||
order_type: searchForm.order_type,
|
||||
purchase_role: searchForm.purchase_role,
|
||||
identifier: searchForm.identifier || undefined,
|
||||
is_expired: searchForm.is_expired,
|
||||
is_expired:
|
||||
searchForm.is_expired === undefined ? undefined : Boolean(searchForm.is_expired),
|
||||
seller_shop_id: searchForm.seller_shop_id,
|
||||
start_time: searchForm.start_time || undefined,
|
||||
end_time: searchForm.end_time || undefined
|
||||
@@ -1229,12 +1218,6 @@
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否是赠送套餐
|
||||
const selectedPackage = packageOptions.value.find(
|
||||
(pkg) => pkg.id === createForm.package_id
|
||||
)
|
||||
const isGiftPackage = selectedPackage?.is_gift || false
|
||||
|
||||
// 线下支付时,支付凭证为必填
|
||||
if (createForm.payment_method === 'offline' && !createForm.payment_voucher_key) {
|
||||
ElMessage.error('线下支付必须上传支付凭证')
|
||||
@@ -1253,7 +1236,8 @@
|
||||
data.payment_voucher_key = createForm.payment_voucher_key
|
||||
}
|
||||
|
||||
await OrderService.createOrder(data)
|
||||
const res = await OrderService.createOrder(data)
|
||||
if (res.code !== 0) return
|
||||
|
||||
// 根据支付方式显示不同的成功消息
|
||||
if (createForm.payment_method === 'wallet') {
|
||||
@@ -1362,12 +1346,6 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.order-detail {
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table__row.table-row-with-context-menu) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-tip"
|
||||
>设置每个套餐的成本价(单位:元),不能低于原价,不能高于原价的50%溢价</div
|
||||
>设置每个套餐的成本价(单位:元),不能低于成本价,不能高于建议售价的50%溢价</div
|
||||
>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user