修复:统一前后端的订单状态
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
VITE_APP_NAME=后台管理(开发环境)
|
||||
VITE_API_URL=http://127.0.0.1:9200/api
|
||||
#VITE_API_URL=http://127.0.0.1:9200/api
|
||||
#VITE_SERVER_API_URL=http://127.0.0.1:8000/api
|
||||
|
||||
245
docs/ORDER_FILTER_COMPARISON.md
Normal file
245
docs/ORDER_FILTER_COMPARISON.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# 订单筛选逻辑对比
|
||||
|
||||
## 修改前后对比
|
||||
|
||||
### 移动端筛选逻辑
|
||||
|
||||
#### 修改前(复杂的前端筛选)
|
||||
|
||||
```typescript
|
||||
// 原有的复杂筛选逻辑
|
||||
const getOrderStatusParams = (index: string | number) => {
|
||||
let params: ShopOrderParam = {};
|
||||
params.userId = Taro.getStorageSync('UserId');
|
||||
|
||||
const indexStr = String(index);
|
||||
switch (indexStr) {
|
||||
case '1': // 待付款
|
||||
params.payStatus = 0; // 0表示未付款
|
||||
break;
|
||||
case '2': // 待发货
|
||||
params.payStatus = 1; // 1表示已付款
|
||||
params.deliveryStatus = 10; // 10表示未发货
|
||||
break;
|
||||
case '3': // 待收货
|
||||
params.deliveryStatus = 20; // 20表示已发货
|
||||
break;
|
||||
case '4': // 已完成
|
||||
params.orderStatus = 1; // 1表示已完成
|
||||
break;
|
||||
case '5': // 已取消
|
||||
// 对于已取消的订单,获取所有数据然后在前端筛选
|
||||
break;
|
||||
case '0': // 全部
|
||||
default:
|
||||
// 全部订单,不添加额外的筛选条件
|
||||
break;
|
||||
}
|
||||
return params;
|
||||
};
|
||||
|
||||
// 前端二次筛选
|
||||
const filterOrdersByTab = (orders: OrderWithGoods[], tabIndex: number) => {
|
||||
const indexStr = String(tabIndex);
|
||||
return orders.filter(order => {
|
||||
switch (indexStr) {
|
||||
case '1': // 待付款
|
||||
return !order.payStatus && !isCancelledOrder(order);
|
||||
case '2': // 待发货
|
||||
return order.payStatus && order.deliveryStatus === 10 && !isCancelledOrder(order);
|
||||
case '3': // 待收货
|
||||
return order.deliveryStatus === 20 && !isCancelledOrder(order);
|
||||
case '4': // 已完成
|
||||
return order.orderStatus === 1;
|
||||
case '5': // 已取消
|
||||
return isCancelledOrder(order);
|
||||
case '0': // 全部
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 复杂的取消状态判断
|
||||
const isCancelledOrder = (order: ShopOrder) => {
|
||||
const cancelledStatuses = [2, 3, 4, 6, 7];
|
||||
return cancelledStatuses.includes(order.orderStatus || 0);
|
||||
};
|
||||
```
|
||||
|
||||
#### 修改后(统一的后端筛选)
|
||||
|
||||
```typescript
|
||||
// 简化的统一筛选逻辑
|
||||
const tabs = [
|
||||
{
|
||||
index: 0,
|
||||
statusFilter: undefined // 全部
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
statusFilter: 0 // 待付款
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
statusFilter: 1 // 待发货
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
statusFilter: 3 // 待收货
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
statusFilter: 5 // 已完成
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
statusFilter: 6 // 已取消/已退款
|
||||
}
|
||||
];
|
||||
|
||||
const getOrderStatusParams = (index: string | number) => {
|
||||
let params: ShopOrderParam = {};
|
||||
params.userId = Taro.getStorageSync('UserId');
|
||||
|
||||
const currentTab = tabs.find(tab => tab.index === Number(index));
|
||||
if (currentTab && currentTab.statusFilter !== undefined) {
|
||||
params.statusFilter = currentTab.statusFilter;
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
// 不再需要前端二次筛选,直接使用后端返回的数据
|
||||
```
|
||||
|
||||
### 后台管理系统筛选逻辑
|
||||
|
||||
#### 修改前
|
||||
|
||||
```typescript
|
||||
// 注释不够清晰
|
||||
// 根据文档,statusFilter的值对应:
|
||||
// -1全部,0待付款,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除
|
||||
switch (activeKey.value) {
|
||||
case 'all':
|
||||
filterParams.statusFilter = -1; // 使用-1表示全部
|
||||
break;
|
||||
// ... 其他case
|
||||
}
|
||||
```
|
||||
|
||||
#### 修改后
|
||||
|
||||
```typescript
|
||||
// 更清晰的注释和实现
|
||||
// 根据后端 statusFilter 的值对应:
|
||||
// undefined全部,0待付款,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除
|
||||
switch (activeKey.value) {
|
||||
case 'all':
|
||||
// 全部订单:不传statusFilter参数
|
||||
// filterParams.statusFilter = undefined; // 不设置该字段
|
||||
break;
|
||||
// ... 其他case保持一致
|
||||
}
|
||||
```
|
||||
|
||||
## 主要改进点
|
||||
|
||||
### 1. 性能优化
|
||||
|
||||
| 方面 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| 数据筛选位置 | 前端 + 后端 | 纯后端 |
|
||||
| 网络传输 | 传输所有数据再筛选 | 只传输筛选后的数据 |
|
||||
| 前端处理 | 复杂的二次筛选逻辑 | 直接使用后端数据 |
|
||||
| 查询效率 | 低(多次查询+前端筛选) | 高(单次精确查询) |
|
||||
|
||||
### 2. 代码复杂度
|
||||
|
||||
| 方面 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| 筛选逻辑行数 | ~80行 | ~20行 |
|
||||
| 筛选条件数量 | 多个字段组合 | 单个statusFilter |
|
||||
| 维护难度 | 高(前后端都要维护) | 低(只需维护后端) |
|
||||
| 出错概率 | 高(逻辑复杂) | 低(逻辑简单) |
|
||||
|
||||
### 3. 一致性保证
|
||||
|
||||
| 方面 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| 前端移动端 | 自定义筛选逻辑 | 使用statusFilter |
|
||||
| 后台管理系统 | 使用statusFilter | 使用statusFilter |
|
||||
| 数据一致性 | 可能不一致 | 完全一致 |
|
||||
| 维护成本 | 高(两套逻辑) | 低(统一逻辑) |
|
||||
|
||||
## 具体的筛选条件对比
|
||||
|
||||
### 待付款订单
|
||||
|
||||
**修改前(移动端):**
|
||||
```typescript
|
||||
// 后端查询
|
||||
params.payStatus = 0;
|
||||
|
||||
// 前端再筛选
|
||||
return !order.payStatus && !isCancelledOrder(order);
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```typescript
|
||||
// 只需后端查询
|
||||
params.statusFilter = 0; // 对应 pay_status = false
|
||||
```
|
||||
|
||||
### 待发货订单
|
||||
|
||||
**修改前(移动端):**
|
||||
```typescript
|
||||
// 后端查询
|
||||
params.payStatus = 1;
|
||||
params.deliveryStatus = 10;
|
||||
|
||||
// 前端再筛选
|
||||
return order.payStatus && order.deliveryStatus === 10 && !isCancelledOrder(order);
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```typescript
|
||||
// 只需后端查询
|
||||
params.statusFilter = 1; // 对应 pay_status = true AND delivery_status = 10
|
||||
```
|
||||
|
||||
### 已取消订单
|
||||
|
||||
**修改前(移动端):**
|
||||
```typescript
|
||||
// 后端查询所有数据
|
||||
// 无特定筛选条件
|
||||
|
||||
// 前端复杂筛选
|
||||
const isCancelledOrder = (order: ShopOrder) => {
|
||||
const cancelledStatuses = [2, 3, 4, 6, 7];
|
||||
return cancelledStatuses.includes(order.orderStatus || 0);
|
||||
};
|
||||
return isCancelledOrder(order);
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```typescript
|
||||
// 只需后端查询
|
||||
params.statusFilter = 6; // 对应 order_status = 6 (已退款)
|
||||
```
|
||||
|
||||
## 迁移建议
|
||||
|
||||
1. **立即可用**:新的移动端组件已经创建,可以直接使用
|
||||
2. **逐步替换**:可以先在新功能中使用,再逐步替换现有页面
|
||||
3. **测试验证**:建议先在测试环境验证各个筛选条件的正确性
|
||||
4. **性能监控**:关注查询性能的改善情况
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **移动端显示逻辑**:虽然筛选使用statusFilter,但显示的状态文本仍可以根据业务需求自定义
|
||||
2. **特殊状态处理**:某些复杂的状态组合可能需要在显示层面进行适配
|
||||
3. **向后兼容**:确保现有功能不受影响
|
||||
192
docs/ORDER_STATUS_UNIFIED_FILTER.md
Normal file
192
docs/ORDER_STATUS_UNIFIED_FILTER.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# 订单状态筛选统一方案
|
||||
|
||||
## 问题描述
|
||||
|
||||
原先前端移动端和后台管理系统的订单状态筛选逻辑不一致:
|
||||
|
||||
### 移动端原有问题:
|
||||
1. 使用多个字段组合进行筛选(payStatus, orderStatus, deliveryStatus)
|
||||
2. 在前端进行二次过滤,效率低下
|
||||
3. 筛选逻辑复杂,容易出错
|
||||
4. 与后端设计的 statusFilter 字段不一致
|
||||
|
||||
### 后台管理系统:
|
||||
1. 已经使用 statusFilter 字段
|
||||
2. 但注释和实现有些不一致的地方
|
||||
|
||||
## 解决方案
|
||||
|
||||
统一使用后端的 `statusFilter` 字段进行订单状态筛选,所有前端页面都采用相同的筛选逻辑。
|
||||
|
||||
## 后端 statusFilter 字段定义
|
||||
|
||||
根据后端 `ShopOrderParam.java` 和 `ShopOrderMapper.xml` 的定义:
|
||||
|
||||
```java
|
||||
@Schema(description = "订单状态筛选:-1全部,0待支付,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除")
|
||||
private Integer statusFilter;
|
||||
```
|
||||
|
||||
## 统一的状态筛选映射表
|
||||
|
||||
| statusFilter | 标签名称 | 后端筛选条件 | 说明 |
|
||||
|-------------|---------|-------------|------|
|
||||
| undefined | 全部 | 无筛选条件 | 显示所有订单(包括已删除的) |
|
||||
| 0 | 待付款 | pay_status = false | 未付款的订单 |
|
||||
| 1 | 待发货 | pay_status = true AND delivery_status = 10 | 已付款但未发货 |
|
||||
| 2 | 待核销 | pay_status = true AND delivery_status = 10 | 与待发货相同(特定业务场景) |
|
||||
| 3 | 待收货 | pay_status = true AND delivery_status = 20 | 已发货待收货 |
|
||||
| 4 | 待评价 | order_status = 1 | 与已完成相同(特定业务场景) |
|
||||
| 5 | 已完成 | order_status = 1 | 订单已完成 |
|
||||
| 6 | 已退款 | order_status = 6 | 退款成功的订单 |
|
||||
| 7 | 已删除 | deleted = 1 | 已删除的订单 |
|
||||
|
||||
## 前端实现
|
||||
|
||||
### 移动端(React/Taro)
|
||||
|
||||
```typescript
|
||||
// 统一的订单状态标签配置
|
||||
const tabs = [
|
||||
{
|
||||
index: 0,
|
||||
key: '全部',
|
||||
title: '全部',
|
||||
description: '所有订单',
|
||||
statusFilter: undefined // 不传statusFilter,显示所有订单
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
key: '待付款',
|
||||
title: '待付款',
|
||||
description: '等待付款的订单',
|
||||
statusFilter: 0 // 对应后端:pay_status = false
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
key: '待发货',
|
||||
title: '待发货',
|
||||
description: '已付款待发货的订单',
|
||||
statusFilter: 1 // 对应后端:pay_status = true AND delivery_status = 10
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
key: '待收货',
|
||||
title: '待收货',
|
||||
description: '已发货待收货的订单',
|
||||
statusFilter: 3 // 对应后端:pay_status = true AND delivery_status = 20
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
key: '已完成',
|
||||
title: '已完成',
|
||||
description: '已完成的订单',
|
||||
statusFilter: 5 // 对应后端:order_status = 1
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
key: '已取消',
|
||||
title: '已取消',
|
||||
description: '已取消/退款的订单',
|
||||
statusFilter: 6 // 对应后端:order_status = 6 (已退款)
|
||||
}
|
||||
]
|
||||
|
||||
// 筛选参数生成
|
||||
const getOrderStatusParams = (index: string | number) => {
|
||||
let params: ShopOrderParam = {};
|
||||
params.userId = Taro.getStorageSync('UserId');
|
||||
|
||||
const currentTab = tabs.find(tab => tab.index === Number(index));
|
||||
if (currentTab && currentTab.statusFilter !== undefined) {
|
||||
params.statusFilter = currentTab.statusFilter;
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
```
|
||||
|
||||
### 后台管理系统(Vue)
|
||||
|
||||
```typescript
|
||||
const onTabs = () => {
|
||||
const filterParams: Record<string, any> = {};
|
||||
|
||||
switch (activeKey.value) {
|
||||
case 'all':
|
||||
// 全部订单:不传statusFilter参数
|
||||
break;
|
||||
case 'unpaid':
|
||||
filterParams.statusFilter = 0;
|
||||
break;
|
||||
case 'undelivered':
|
||||
filterParams.statusFilter = 1;
|
||||
break;
|
||||
case 'unreceived':
|
||||
filterParams.statusFilter = 3;
|
||||
break;
|
||||
case 'completed':
|
||||
filterParams.statusFilter = 5;
|
||||
break;
|
||||
case 'refunded':
|
||||
filterParams.statusFilter = 6;
|
||||
break;
|
||||
case 'deleted':
|
||||
filterParams.statusFilter = 7;
|
||||
break;
|
||||
}
|
||||
|
||||
reload(filterParams);
|
||||
}
|
||||
```
|
||||
|
||||
## 优化效果
|
||||
|
||||
### 1. 性能提升
|
||||
- 筛选逻辑在后端执行,减少前端数据处理
|
||||
- 减少网络传输的数据量
|
||||
- 提高查询效率
|
||||
|
||||
### 2. 代码简化
|
||||
- 移除前端复杂的筛选逻辑
|
||||
- 统一前后端筛选标准
|
||||
- 减少维护成本
|
||||
|
||||
### 3. 一致性保证
|
||||
- 前端移动端和后台管理系统使用相同的筛选逻辑
|
||||
- 与后端设计保持一致
|
||||
- 避免数据不一致的问题
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 移动端特殊处理
|
||||
移动端可能需要将某些后端状态合并显示:
|
||||
- "已取消" 标签可以包含多种取消状态(orderStatus: 2,3,4,6,7)
|
||||
- 可以在前端显示时进行状态文本的转换,但筛选仍使用 statusFilter
|
||||
|
||||
### 2. 向后兼容
|
||||
- 保持现有API接口不变
|
||||
- 逐步迁移现有代码
|
||||
- 确保不影响现有功能
|
||||
|
||||
### 3. 测试验证
|
||||
- 验证各个状态筛选的正确性
|
||||
- 确认前后端数据一致性
|
||||
- 测试边界情况和异常处理
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
1. **创建新的移动端组件**:使用统一的 statusFilter 逻辑
|
||||
2. **更新后台管理系统**:完善注释和实现细节
|
||||
3. **测试验证**:确保所有筛选功能正常工作
|
||||
4. **逐步替换**:将旧的移动端组件替换为新组件
|
||||
5. **清理代码**:移除不再使用的筛选逻辑
|
||||
|
||||
## 相关文件
|
||||
|
||||
- 移动端新组件:`src/views/shop/shopOrder/mobile/index.tsx`
|
||||
- 后台管理系统:`src/views/shop/shopOrder/index.vue`
|
||||
- API接口:`src/api/shop/shopOrder/index.ts`
|
||||
- 数据模型:`src/api/shop/shopOrder/model/index.ts`
|
||||
- 后端参数:`ShopOrderParam.java`
|
||||
- 后端SQL:`ShopOrderMapper.xml`
|
||||
@@ -1,13 +1,14 @@
|
||||
import request from '@/utils/request';
|
||||
import type { ApiResult, PageResult } from '@/api';
|
||||
import type { ApiResult, PageResult } from '@/api/index';
|
||||
import type { ShopMerchant, ShopMerchantParam } from './model';
|
||||
import { MODULES_API_URL } from '@/config/setting';
|
||||
|
||||
/**
|
||||
* 分页查询商户
|
||||
*/
|
||||
export async function pageShopMerchant(params: ShopMerchantParam) {
|
||||
const res = await request.get<ApiResult<PageResult<ShopMerchant>>>(
|
||||
'/shop/shop-merchant/page',
|
||||
MODULES_API_URL + '/shop/shop-merchant/page',
|
||||
{
|
||||
params
|
||||
}
|
||||
@@ -23,7 +24,7 @@ export async function pageShopMerchant(params: ShopMerchantParam) {
|
||||
*/
|
||||
export async function listShopMerchant(params?: ShopMerchantParam) {
|
||||
const res = await request.get<ApiResult<ShopMerchant[]>>(
|
||||
'/shop/shop-merchant',
|
||||
MODULES_API_URL + '/shop/shop-merchant',
|
||||
{
|
||||
params
|
||||
}
|
||||
@@ -39,7 +40,7 @@ export async function listShopMerchant(params?: ShopMerchantParam) {
|
||||
*/
|
||||
export async function addShopMerchant(data: ShopMerchant) {
|
||||
const res = await request.post<ApiResult<unknown>>(
|
||||
'/shop/shop-merchant',
|
||||
MODULES_API_URL + '/shop/shop-merchant',
|
||||
data
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
@@ -53,7 +54,7 @@ export async function addShopMerchant(data: ShopMerchant) {
|
||||
*/
|
||||
export async function updateShopMerchant(data: ShopMerchant) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
'/shop/shop-merchant',
|
||||
MODULES_API_URL + '/shop/shop-merchant',
|
||||
data
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
@@ -67,7 +68,7 @@ export async function updateShopMerchant(data: ShopMerchant) {
|
||||
*/
|
||||
export async function removeShopMerchant(id?: number) {
|
||||
const res = await request.delete<ApiResult<unknown>>(
|
||||
'/shop/shop-merchant/' + id
|
||||
MODULES_API_URL + '/shop/shop-merchant/' + id
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
return res.data.message;
|
||||
@@ -80,7 +81,7 @@ export async function removeShopMerchant(id?: number) {
|
||||
*/
|
||||
export async function removeBatchShopMerchant(data: (number | undefined)[]) {
|
||||
const res = await request.delete<ApiResult<unknown>>(
|
||||
'/shop/shop-merchant/batch',
|
||||
MODULES_API_URL + '/shop/shop-merchant/batch',
|
||||
{
|
||||
data
|
||||
}
|
||||
@@ -96,7 +97,7 @@ export async function removeBatchShopMerchant(data: (number | undefined)[]) {
|
||||
*/
|
||||
export async function getShopMerchant(id: number) {
|
||||
const res = await request.get<ApiResult<ShopMerchant>>(
|
||||
'/shop/shop-merchant/' + id
|
||||
MODULES_API_URL + '/shop/shop-merchant/' + id
|
||||
);
|
||||
if (res.data.code === 0 && res.data.data) {
|
||||
return res.data.data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PageParam } from '@/api';
|
||||
import type { PageParam } from '@/api/index';
|
||||
|
||||
/**
|
||||
* 商户
|
||||
@@ -56,8 +56,16 @@ export interface ShopMerchant {
|
||||
price?: string;
|
||||
// 是否自营
|
||||
ownStore?: number;
|
||||
// 是否可以快递
|
||||
canExpress?: string;
|
||||
// 是否推荐
|
||||
recommend?: number;
|
||||
// 是否营业
|
||||
isOn?: number;
|
||||
//
|
||||
startTime?: string;
|
||||
//
|
||||
endTime?: string;
|
||||
// 是否需要审核
|
||||
goodsReview?: number;
|
||||
// 管理入口
|
||||
@@ -83,8 +91,5 @@ export interface ShopMerchant {
|
||||
*/
|
||||
export interface ShopMerchantParam extends PageParam {
|
||||
merchantId?: number;
|
||||
phone?: string;
|
||||
userId?: number;
|
||||
shopType?: string;
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
41
src/views/shop/shopMerchant/components/search.vue
Normal file
41
src/views/shop/shopMerchant/components/search.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: any): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
454
src/views/shop/shopMerchant/components/shopMerchantEdit.vue
Normal file
454
src/views/shop/shopMerchant/components/shopMerchantEdit.vue
Normal file
@@ -0,0 +1,454 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑商户' : '添加商户'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="商户名称" name="merchantName">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户名称"
|
||||
v-model:value="form.merchantName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户编号" name="merchantCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户编号"
|
||||
v-model:value="form.merchantCode"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户类型" name="type">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户类型"
|
||||
v-model:value="form.type"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="商户图标"
|
||||
name="image">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseImage"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户手机号" name="phone">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户手机号"
|
||||
v-model:value="form.phone"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户姓名" name="realName">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户姓名"
|
||||
v-model:value="form.realName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="店铺类型" name="shopType">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入店铺类型"
|
||||
v-model:value="form.shopType"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="项目分类" name="itemType">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入项目分类"
|
||||
v-model:value="form.itemType"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户分类" name="category">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户分类"
|
||||
v-model:value="form.category"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户经营分类" name="merchantCategoryId">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户经营分类"
|
||||
v-model:value="form.merchantCategoryId"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商户分类" name="merchantCategoryTitle">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入商户分类"
|
||||
v-model:value="form.merchantCategoryTitle"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="经纬度" name="lngAndLat">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入经纬度"
|
||||
v-model:value="form.lngAndLat"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="" name="lng">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入"
|
||||
v-model:value="form.lng"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="" name="lat">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入"
|
||||
v-model:value="form.lat"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在省份" name="province">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入所在省份"
|
||||
v-model:value="form.province"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在城市" name="city">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入所在城市"
|
||||
v-model:value="form.city"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在辖区" name="region">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入所在辖区"
|
||||
v-model:value="form.region"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="详细地址" name="address">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入详细地址"
|
||||
v-model:value="form.address"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="手续费" name="commission">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入手续费"
|
||||
v-model:value="form.commission"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="关键字" name="keywords">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入关键字"
|
||||
v-model:value="form.keywords"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="资质图片" name="files">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入资质图片"
|
||||
v-model:value="form.files"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="营业时间" name="businessTime">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入营业时间"
|
||||
v-model:value="form.businessTime"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="文章内容" name="content">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入文章内容"
|
||||
v-model:value="form.content"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="每小时价格" name="price">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入每小时价格"
|
||||
v-model:value="form.price"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否自营" name="ownStore">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入是否自营"
|
||||
v-model:value="form.ownStore"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否可以快递" name="canExpress">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入是否可以快递"
|
||||
v-model:value="form.canExpress"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否推荐" name="recommend">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入是否推荐"
|
||||
v-model:value="form.recommend"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否营业" name="isOn">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入是否营业"
|
||||
v-model:value="form.isOn"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="" name="startTime">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入"
|
||||
v-model:value="form.startTime"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="" name="endTime">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入"
|
||||
v-model:value="form.endTime"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否需要审核" name="goodsReview">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入是否需要审核"
|
||||
v-model:value="form.goodsReview"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="管理入口" name="adminUrl">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入管理入口"
|
||||
v-model:value="form.adminUrl"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="comments">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入描述"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所有人" name="userId">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入所有人"
|
||||
v-model:value="form.userId"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态" name="status">
|
||||
<a-radio-group v-model:value="form.status">
|
||||
<a-radio :value="0">显示</a-radio>
|
||||
<a-radio :value="1">隐藏</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addShopMerchant, updateShopMerchant } from '@/api/shop/shopMerchant';
|
||||
import { ShopMerchant } from '@/api/shop/shopMerchant/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: ShopMerchant | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<ShopMerchant>({
|
||||
merchantId: undefined,
|
||||
merchantName: undefined,
|
||||
merchantCode: undefined,
|
||||
type: undefined,
|
||||
image: undefined,
|
||||
phone: undefined,
|
||||
realName: undefined,
|
||||
shopType: undefined,
|
||||
itemType: undefined,
|
||||
category: undefined,
|
||||
merchantCategoryId: undefined,
|
||||
merchantCategoryTitle: undefined,
|
||||
lngAndLat: undefined,
|
||||
lng: undefined,
|
||||
lat: undefined,
|
||||
province: undefined,
|
||||
city: undefined,
|
||||
region: undefined,
|
||||
address: undefined,
|
||||
commission: undefined,
|
||||
keywords: undefined,
|
||||
files: undefined,
|
||||
businessTime: undefined,
|
||||
content: undefined,
|
||||
price: undefined,
|
||||
ownStore: undefined,
|
||||
canExpress: undefined,
|
||||
recommend: undefined,
|
||||
isOn: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
goodsReview: undefined,
|
||||
adminUrl: undefined,
|
||||
comments: undefined,
|
||||
userId: undefined,
|
||||
deleted: undefined,
|
||||
status: undefined,
|
||||
sortNumber: 100,
|
||||
tenantId: undefined,
|
||||
createTime: undefined,
|
||||
shopMerchantId: undefined,
|
||||
shopMerchantName: '',
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
shopMerchantName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写商户名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseImage = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.path,
|
||||
status: 'done'
|
||||
});
|
||||
form.image = data.path;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.image = '';
|
||||
};
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateShopMerchant : addShopMerchant;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if(props.data.image){
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.image,
|
||||
status: 'done'
|
||||
})
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
244
src/views/shop/shopMerchant/index.vue
Normal file
244
src/views/shop/shopMerchant/index.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="merchantId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<Search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'image'">
|
||||
<a-image :src="record.image" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<ShopMerchantEdit v-model:visible="showEdit" :data="current" @done="reload" />
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import {getPageTitle} from '@/utils/common';
|
||||
import ShopMerchantEdit from './components/shopMerchantEdit.vue';
|
||||
import { pageShopMerchant, removeShopMerchant, removeBatchShopMerchant } from '@/api/shop/shopMerchant';
|
||||
import type { ShopMerchant, ShopMerchantParam } from '@/api/shop/shopMerchant/model';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<ShopMerchant[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<ShopMerchant | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
return pageShopMerchant({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: '商户ID',
|
||||
dataIndex: 'merchantId',
|
||||
key: 'merchantId',
|
||||
align: 'center',
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: '商户名称',
|
||||
dataIndex: 'merchantName',
|
||||
key: 'merchantName',
|
||||
align: 'center',
|
||||
},
|
||||
// {
|
||||
// title: '店铺类型',
|
||||
// dataIndex: 'shopType',
|
||||
// key: 'shopType',
|
||||
// align: 'center',
|
||||
// },
|
||||
{
|
||||
title: '商户分类',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '排序号',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
|
||||
}
|
||||
// {
|
||||
// title: '操作',
|
||||
// key: 'action',
|
||||
// width: 180,
|
||||
// fixed: 'right',
|
||||
// align: 'center',
|
||||
// hideInSetting: true
|
||||
// }
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: ShopMerchantParam) => {
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: ShopMerchant) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: ShopMerchant) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeShopMerchant(row.shopMerchantId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchShopMerchant(selection.value.map((d) => d.merchantId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 查询 */
|
||||
const query = () => {
|
||||
loading.value = true;
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: ShopMerchant) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
query();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ShopMerchant'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -12,14 +12,13 @@
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<a-tabs type="card" v-model:activeKey="activeKey" @change="onTabs">
|
||||
<a-tab-pane key="all" tab="全部"/>
|
||||
<a-tab-pane key="unpaid" tab="待支付"/>
|
||||
<a-tab-pane key="unpaid" tab="待付款"/>
|
||||
<a-tab-pane key="undelivered" tab="待发货"/>
|
||||
<a-tab-pane key="unverified" tab="待核销"/>
|
||||
<a-tab-pane key="unreceived" tab="待收货"/>
|
||||
<a-tab-pane key="unevaluated" tab="待评价"/>
|
||||
<a-tab-pane key="completed" tab="已完成"/>
|
||||
<a-tab-pane key="refunded" tab="已退款"/>
|
||||
<a-tab-pane key="deleted" tab="已取消"/>
|
||||
<!-- <a-tab-pane key="unevaluated" tab="待评价"/>-->
|
||||
<!-- <a-tab-pane key="refunded" tab="已退款"/>-->
|
||||
</a-tabs>
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
@@ -268,43 +267,43 @@ const onTabs = () => {
|
||||
// 使用statusFilter进行筛选,这是后端专门为订单状态筛选设计的字段
|
||||
const filterParams: Record<string, any> = {};
|
||||
|
||||
// 根据文档,statusFilter的值对应:
|
||||
// -1全部,0待支付,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除
|
||||
// 根据后端 statusFilter 的值对应:
|
||||
// undefined全部,0待付款,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除
|
||||
switch (activeKey.value) {
|
||||
case 'all':
|
||||
// 全部订单(排除已删除)
|
||||
filterParams.statusFilter = -1;
|
||||
// 全部订单:不传statusFilter参数
|
||||
// filterParams.statusFilter = undefined; // 不设置该字段
|
||||
break;
|
||||
case 'unpaid':
|
||||
// 待支付
|
||||
// 待付款:pay_status = false
|
||||
filterParams.statusFilter = 0;
|
||||
break;
|
||||
case 'undelivered':
|
||||
// 待发货
|
||||
// 待发货:pay_status = true AND delivery_status = 10
|
||||
filterParams.statusFilter = 1;
|
||||
break;
|
||||
case 'unverified':
|
||||
// 待核销
|
||||
// 待核销:pay_status = true AND delivery_status = 10 (与待发货相同)
|
||||
filterParams.statusFilter = 2;
|
||||
break;
|
||||
case 'unreceived':
|
||||
// 待收货
|
||||
// 待收货:pay_status = true AND delivery_status = 20
|
||||
filterParams.statusFilter = 3;
|
||||
break;
|
||||
case 'unevaluated':
|
||||
// 待评价
|
||||
// 待评价:order_status = 1 (与已完成相同)
|
||||
filterParams.statusFilter = 4;
|
||||
break;
|
||||
case 'completed':
|
||||
// 已完成
|
||||
// 已完成:order_status = 1
|
||||
filterParams.statusFilter = 5;
|
||||
break;
|
||||
case 'refunded':
|
||||
// 已退款
|
||||
// 已退款:order_status = 6
|
||||
filterParams.statusFilter = 6;
|
||||
break;
|
||||
case 'deleted':
|
||||
// 已删除/已取消
|
||||
// 已删除:deleted = 1
|
||||
filterParams.statusFilter = 7;
|
||||
break;
|
||||
}
|
||||
|
||||
391
src/views/shop/shopOrder/mobile/index.tsx
Normal file
391
src/views/shop/shopOrder/mobile/index.tsx
Normal file
@@ -0,0 +1,391 @@
|
||||
import {Avatar, Cell, Space, Tabs, Button, TabPane, Image} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {View} from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro';
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import dayjs from "dayjs";
|
||||
import {pageShopOrder, removeShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
|
||||
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
|
||||
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
||||
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
||||
import {copyText} from "@/utils/common";
|
||||
|
||||
const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({
|
||||
marginTop: showSearch ? '65px' : '44px', // 如果显示搜索框,增加更多的上边距
|
||||
height: showSearch ? '75vh' : '82vh', // 相应调整高度
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
|
||||
})
|
||||
|
||||
// 统一的订单状态标签配置,与后端 statusFilter 保持一致
|
||||
const tabs = [
|
||||
{
|
||||
index: 0,
|
||||
key: '全部',
|
||||
title: '全部',
|
||||
description: '所有订单',
|
||||
statusFilter: undefined // 不传statusFilter,显示所有订单
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
key: '待付款',
|
||||
title: '待付款',
|
||||
description: '等待付款的订单',
|
||||
statusFilter: 0 // 对应后端:pay_status = false
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
key: '待发货',
|
||||
title: '待发货',
|
||||
description: '已付款待发货的订单',
|
||||
statusFilter: 1 // 对应后端:pay_status = true AND delivery_status = 10
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
key: '待收货',
|
||||
title: '待收货',
|
||||
description: '已发货待收货的订单',
|
||||
statusFilter: 3 // 对应后端:pay_status = true AND delivery_status = 20
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
key: '已完成',
|
||||
title: '已完成',
|
||||
description: '已完成的订单',
|
||||
statusFilter: 5 // 对应后端:order_status = 1
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
key: '已取消',
|
||||
title: '已取消',
|
||||
description: '已取消/退款的订单',
|
||||
statusFilter: 6 // 对应后端:order_status = 6 (已退款)
|
||||
}
|
||||
]
|
||||
|
||||
// 扩展订单接口,包含商品信息
|
||||
interface OrderWithGoods extends ShopOrder {
|
||||
orderGoods?: ShopOrderGoods[];
|
||||
}
|
||||
|
||||
interface OrderListProps {
|
||||
data: ShopOrder[];
|
||||
onReload?: () => void;
|
||||
searchParams?: ShopOrderParam;
|
||||
showSearch?: boolean;
|
||||
}
|
||||
|
||||
function OrderList(props: OrderListProps) {
|
||||
const [list, setList] = useState<OrderWithGoods[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [tapIndex, setTapIndex] = useState<string | number>(0)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// 获取订单状态文本
|
||||
const getOrderStatusText = (order: ShopOrder) => {
|
||||
console.log(order,'order')
|
||||
|
||||
// 优先检查订单状态
|
||||
if (order.orderStatus === 2) return '已取消';
|
||||
if (order.orderStatus === 4) return '退款申请中';
|
||||
if (order.orderStatus === 5) return '退款被拒绝';
|
||||
if (order.orderStatus === 6) return '退款成功';
|
||||
if (order.orderStatus === 7) return '客户端申请退款';
|
||||
|
||||
// 检查支付状态 (payStatus为boolean类型,false/0表示未付款,true/1表示已付款)
|
||||
if (!order.payStatus) return '等待买家付款';
|
||||
|
||||
// 已付款后检查发货状态
|
||||
if (order.deliveryStatus === 10) return '待发货';
|
||||
if (order.deliveryStatus === 20) return '待收货';
|
||||
if (order.deliveryStatus === 30) return '已收货';
|
||||
|
||||
// 最后检查订单完成状态
|
||||
if (order.orderStatus === 1) return '已完成';
|
||||
if (order.orderStatus === 0) return '未使用';
|
||||
|
||||
return '未知状态';
|
||||
};
|
||||
|
||||
// 获取订单状态颜色
|
||||
const getOrderStatusColor = (order: ShopOrder) => {
|
||||
// 优先检查订单状态
|
||||
if (order.orderStatus === 2) return 'text-gray-500'; // 已取消
|
||||
if (order.orderStatus === 4) return 'text-orange-500'; // 退款申请中
|
||||
if (order.orderStatus === 5) return 'text-red-500'; // 退款被拒绝
|
||||
if (order.orderStatus === 6) return 'text-green-500'; // 退款成功
|
||||
if (order.orderStatus === 7) return 'text-orange-500'; // 客户端申请退款
|
||||
|
||||
// 检查支付状态
|
||||
if (!order.payStatus) return 'text-orange-500'; // 等待买家付款
|
||||
|
||||
// 已付款后检查发货状态
|
||||
if (order.deliveryStatus === 10) return 'text-blue-500'; // 待发货
|
||||
if (order.deliveryStatus === 20) return 'text-purple-500'; // 待收货
|
||||
if (order.deliveryStatus === 30) return 'text-green-500'; // 已收货
|
||||
|
||||
// 最后检查订单完成状态
|
||||
if (order.orderStatus === 1) return 'text-green-600'; // 已完成
|
||||
if (order.orderStatus === 0) return 'text-gray-500'; // 未使用
|
||||
|
||||
return 'text-gray-600'; // 默认颜色
|
||||
};
|
||||
|
||||
// 使用后端统一的 statusFilter 进行筛选
|
||||
const getOrderStatusParams = (index: string | number) => {
|
||||
let params: ShopOrderParam = {};
|
||||
// 添加用户ID过滤
|
||||
params.userId = Taro.getStorageSync('UserId');
|
||||
|
||||
// 获取当前tab的statusFilter配置
|
||||
const currentTab = tabs.find(tab => tab.index === Number(index));
|
||||
if (currentTab && currentTab.statusFilter !== undefined) {
|
||||
params.statusFilter = currentTab.statusFilter;
|
||||
}
|
||||
|
||||
console.log(`Tab ${index} (${currentTab?.title}) 筛选参数:`, params);
|
||||
return params;
|
||||
};
|
||||
|
||||
const reload = async (resetPage = false) => {
|
||||
setLoading(true);
|
||||
const currentPage = resetPage ? 1 : page;
|
||||
const statusParams = getOrderStatusParams(tapIndex);
|
||||
const searchConditions = {
|
||||
page: currentPage,
|
||||
...statusParams,
|
||||
...props.searchParams
|
||||
};
|
||||
console.log('订单筛选条件:', {
|
||||
tapIndex,
|
||||
statusParams,
|
||||
searchConditions
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await pageShopOrder(searchConditions);
|
||||
let newList: OrderWithGoods[] = [];
|
||||
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
// 为每个订单获取商品信息
|
||||
const ordersWithGoods = await Promise.all(
|
||||
res.list.map(async (order) => {
|
||||
try {
|
||||
const orderGoods = await listShopOrderGoods({ orderId: order.orderId });
|
||||
return {
|
||||
...order,
|
||||
orderGoods: orderGoods || []
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取订单商品失败:', error);
|
||||
return {
|
||||
...order,
|
||||
orderGoods: []
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 合并数据
|
||||
newList = resetPage ? ordersWithGoods : list?.concat(ordersWithGoods);
|
||||
setHasMore(true);
|
||||
} else {
|
||||
newList = [];
|
||||
setHasMore(false);
|
||||
}
|
||||
|
||||
setList(newList || []);
|
||||
setPage(currentPage);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error('加载订单失败:', error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1);
|
||||
reload();
|
||||
};
|
||||
|
||||
// 确认收货
|
||||
const confirmReceive = async (order: ShopOrder) => {
|
||||
try {
|
||||
await updateShopOrder({
|
||||
...order,
|
||||
deliveryStatus: 30, // 已收货
|
||||
orderStatus: 1 // 已完成
|
||||
});
|
||||
Taro.showToast({
|
||||
title: '确认收货成功',
|
||||
});
|
||||
reload(true); // 重新加载列表
|
||||
props.onReload?.(); // 通知父组件刷新
|
||||
} catch (error) {
|
||||
Taro.showToast({
|
||||
title: '确认收货失败',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 取消订单
|
||||
const cancelOrder = async (order: ShopOrder) => {
|
||||
try {
|
||||
await removeShopOrder(order.orderId);
|
||||
Taro.showToast({
|
||||
title: '订单已删除',
|
||||
});
|
||||
reload(true); // 重新加载列表
|
||||
props.onReload?.(); // 通知父组件刷新
|
||||
} catch (error) {
|
||||
console.error('取消订单失败:', error);
|
||||
Taro.showToast({
|
||||
title: '取消订单失败',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reload(true); // 首次加载或tab切换时重置页码
|
||||
}, [tapIndex]); // 监听tapIndex变化
|
||||
|
||||
useEffect(() => {
|
||||
reload(true); // 搜索参数变化时重置页码
|
||||
}, [props.searchParams]); // 监听搜索参数变化
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
align={'left'}
|
||||
className={'fixed left-0'}
|
||||
style={{
|
||||
top: '44px',
|
||||
zIndex: 998,
|
||||
borderBottom: '1px solid #e5e5e5'
|
||||
}}
|
||||
tabStyle={{
|
||||
backgroundColor: '#ffffff',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
||||
}}
|
||||
value={tapIndex}
|
||||
onChange={(paneKey) => {
|
||||
console.log('Tab切换到:', paneKey, '对应状态:', tabs[paneKey]?.title);
|
||||
setTapIndex(paneKey)
|
||||
}}
|
||||
>
|
||||
{
|
||||
tabs?.map((item, index) => {
|
||||
return (
|
||||
<TabPane
|
||||
key={index}
|
||||
title={loading && tapIndex === index ? `${item.title}...` : item.title}
|
||||
></TabPane>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Tabs>
|
||||
<div style={getInfiniteUlStyle(props.showSearch)} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
hasMore={hasMore}
|
||||
onLoadMore={reloadMore}
|
||||
onScroll={() => {
|
||||
|
||||
}}
|
||||
onScrollToUpper={() => {
|
||||
|
||||
}}
|
||||
loadingText={
|
||||
<>
|
||||
加载中
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
<>
|
||||
没有更多了
|
||||
</>
|
||||
}
|
||||
>
|
||||
{list?.map((item, index) => {
|
||||
return (
|
||||
<Cell key={index} style={{padding: '16px'}} onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
|
||||
<Space direction={'vertical'} className={'w-full flex flex-col'}>
|
||||
<View className={'order-no flex justify-between'}>
|
||||
<View className={'text-gray-600 font-bold text-sm'}
|
||||
onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</View>
|
||||
<View className={`${getOrderStatusColor(item)} font-medium`}>{getOrderStatusText(item)}</View>
|
||||
</View>
|
||||
<div
|
||||
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
|
||||
|
||||
{/* 商品信息 */}
|
||||
<div className={'goods-info'}>
|
||||
{item.orderGoods && item.orderGoods.length > 0 ? (
|
||||
item.orderGoods.map((goods, goodsIndex) => (
|
||||
<div key={goodsIndex} className={'flex items-center mb-2'}>
|
||||
<Image
|
||||
src={goods.image || '/default-goods.png'}
|
||||
width="50"
|
||||
height="50"
|
||||
lazyLoad={false}
|
||||
className={'rounded'}
|
||||
/>
|
||||
<div className={'ml-2 flex-1'}>
|
||||
<div className={'text-sm font-bold'}>{goods.goodsName}</div>
|
||||
{goods.spec && <div className={'text-gray-500 text-xs'}>规格:{goods.spec}</div>}
|
||||
<div className={'text-gray-500 text-xs'}>数量:{goods.totalNum}</div>
|
||||
</div>
|
||||
<div className={'text-sm'}>¥{goods.price}</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className={'flex items-center'}>
|
||||
<Avatar
|
||||
src='/default-goods.png'
|
||||
size={'50'}
|
||||
shape={'square'}
|
||||
/>
|
||||
<div className={'ml-2'}>
|
||||
<div className={'text-sm'}>{item.title || '订单商品'}</div>
|
||||
<div className={'text-gray-400 text-xs'}>{item.totalNum}件商品</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={'w-full text-right'}>实付金额:¥{item.payPrice}</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<Space className={'btn flex justify-end'}>
|
||||
{/* 待付款状态:显示取消订单和立即支付 */}
|
||||
{(!item.payStatus) && item.orderStatus !== 2 && (
|
||||
<Space>
|
||||
<Button size={'small'} onClick={(e) => {e.stopPropagation(); cancelOrder(item)}}>取消订单</Button>
|
||||
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); console.log('立即支付')}}>立即支付</Button>
|
||||
</Space>
|
||||
)}
|
||||
{/* 待收货状态:显示确认收货 */}
|
||||
{item.deliveryStatus === 20 && (
|
||||
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); confirmReceive(item)}}>确认收货</Button>
|
||||
)}
|
||||
{/* 已完成状态:显示申请退款 */}
|
||||
{item.orderStatus === 1 && (
|
||||
<Button size={'small'} onClick={(e) => {e.stopPropagation(); console.log('申请退款')}}>申请退款</Button>
|
||||
)}
|
||||
{/* 退款相关状态的按钮可以在这里添加 */}
|
||||
</Space>
|
||||
</Space>
|
||||
</Cell>
|
||||
)
|
||||
})}
|
||||
</InfiniteLoading>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrderList
|
||||
Reference in New Issue
Block a user