From 502c236b0db223d77dd1714de58184940f4e435e Mon Sep 17 00:00:00 2001
From: wuxichen <17301714657@163.com>
Date: Wed, 21 Jan 2026 15:07:11 +0800
Subject: [PATCH] feat: new page
---
config/proxy.ts | 2 +-
config/routes.ts | 23 +-
src/components/EditableProTable/index.tsx | 60 ++
src/components/ModalCom/CommonModal.tsx | 36 +
src/components/Upload/UploadImages/index.tsx | 54 +-
src/constants/trade.ts | 20 +-
src/global.less | 9 +-
src/pages/Welcome.tsx | 4 +-
src/pages/prod/list/components/prod-info.tsx | 87 +-
.../list/components/service-rule/index.tsx | 136 ++--
.../service-rule/prodReservationConfig.tsx | 139 ++++
.../service-rule/prodServiceAreasInfo.tsx | 115 +++
.../service-rule/prodWeightConfig.tsx | 278 +++++++
src/pages/prod/list/detail.tsx | 13 +-
src/pages/prod/list/index.tsx | 18 +-
.../order/components/createSales.module.less | 20 +
.../components/createSalesModal copy.tsx | 762 ++++++++++++++++++
.../order/components/createSalesModal.tsx | 609 ++++++++++++++
src/pages/trade/order/detail/index.tsx | 7 +
src/pages/trade/order/detail/order-info.tsx | 4 +-
.../trade/order/detail/src/info/config.tsx | 2 +-
.../trade/order/detail/src/info/prod-info.tsx | 102 +--
.../order/detail/src/info/service-info.tsx | 8 +-
.../trade/order/detail/src/sales-refunds.tsx | 22 +
src/pages/trade/order/list.tsx | 2 +-
src/pages/trade/sales/config.tsx | 157 ++++
src/pages/trade/sales/index.module.less | 13 +
src/pages/trade/sales/index.tsx | 24 +-
src/pages/trade/sales/list.tsx | 133 +++
src/requestErrorConfig.ts | 4 +-
src/utils/menuUtils.tsx | 53 ++
31 files changed, 2730 insertions(+), 186 deletions(-)
create mode 100644 src/components/EditableProTable/index.tsx
create mode 100644 src/components/ModalCom/CommonModal.tsx
create mode 100644 src/pages/prod/list/components/service-rule/prodReservationConfig.tsx
create mode 100644 src/pages/prod/list/components/service-rule/prodServiceAreasInfo.tsx
create mode 100644 src/pages/prod/list/components/service-rule/prodWeightConfig.tsx
create mode 100644 src/pages/trade/order/components/createSales.module.less
create mode 100644 src/pages/trade/order/components/createSalesModal copy.tsx
create mode 100644 src/pages/trade/order/components/createSalesModal.tsx
create mode 100644 src/pages/trade/order/detail/src/sales-refunds.tsx
create mode 100644 src/pages/trade/sales/config.tsx
create mode 100644 src/pages/trade/sales/index.module.less
create mode 100644 src/pages/trade/sales/list.tsx
diff --git a/config/proxy.ts b/config/proxy.ts
index 0494fe5..e51e48a 100644
--- a/config/proxy.ts
+++ b/config/proxy.ts
@@ -16,7 +16,7 @@ export default {
'/admin-api/': {
// http://192.168.1.231:48080 伟强
// https://petshy.tashowz.com/
- target: 'https://petshy.tashowz.com',
+ target: 'https://petshy.tashowz.com/ ',
changeOrigin: true,
},
},
diff --git a/config/routes.ts b/config/routes.ts
index 7059fc2..76acaee 100644
--- a/config/routes.ts
+++ b/config/routes.ts
@@ -46,17 +46,18 @@ export default [
// ],
// },
- {
- path: '/ai1',
- name: 'AI1',
- routes: [
- {
- name: 'ai样本',
- path: '/ai1/tag',
- component: './ai/sample-tag',
- },
- ],
- },
+ // {
+ // path: "/ai1",
+ // name: "AI1",
+ // // redirect: "tag",
+ // routes: [
+ // {
+ // name: "ai样本",
+ // path: "tag",
+ // component: "./prod/list/components/service-rule/index",
+ // },
+ // ],
+ // },
// {
// path: "/system",
// name: "系统管理",
diff --git a/src/components/EditableProTable/index.tsx b/src/components/EditableProTable/index.tsx
new file mode 100644
index 0000000..39c2686
--- /dev/null
+++ b/src/components/EditableProTable/index.tsx
@@ -0,0 +1,60 @@
+import {
+ ActionType,
+ EditableFormInstance,
+ EditableProTable,
+ type EditableProTableProps,
+ type ParamsType,
+ ProColumns,
+} from '@ant-design/pro-components';
+import { Form, Table } from 'antd';
+import React, { forwardRef, useEffect, useRef, useState } from 'react';
+
+interface EditableProTableComProps<
+ T extends Record,
+ U extends ParamsType = ParamsType,
+> extends Omit<
+ EditableProTableProps,
+ 'editableFormRef' | 'recordCreatorProps'
+ > {
+ editableFormRef?: React.Ref;
+ recordCreatorProps?: any;
+}
+
+const EditableProTableCom = forwardRef(
+ >(
+ props: EditableProTableComProps,
+ ref: React.Ref,
+ ) => {
+ // 提取不应该直接传递给 EditableProTable 的属性
+ const { value, columns, ...restProps } = props;
+
+ return (
+
+ );
+ },
+);
+
+// const EditableProTableCom = >(
+// props: EditableProTableComProps
+// ) => {
+// // 提取不应该直接传递给 EditableProTable 的属性
+// const { value, columns, ...restProps } = props;
+
+// return (
+//
+// );
+// };
+export default EditableProTableCom as >(
+ props: EditableProTableComProps & { ref?: React.Ref },
+) => React.ReactElement;
diff --git a/src/components/ModalCom/CommonModal.tsx b/src/components/ModalCom/CommonModal.tsx
new file mode 100644
index 0000000..f29f782
--- /dev/null
+++ b/src/components/ModalCom/CommonModal.tsx
@@ -0,0 +1,36 @@
+import { Modal, type ModalProps } from 'antd';
+import React from 'react';
+
+const CommonModal: React.FC = (props) => {
+ const { children } = props;
+ return (
+
+ {children}
+
+ );
+};
+
+export default React.memo(CommonModal);
diff --git a/src/components/Upload/UploadImages/index.tsx b/src/components/Upload/UploadImages/index.tsx
index 4c269e2..2ba573f 100644
--- a/src/components/Upload/UploadImages/index.tsx
+++ b/src/components/Upload/UploadImages/index.tsx
@@ -120,41 +120,45 @@ const UploadImages: React.FC<{
message.success('上传成功');
} else {
+ setUploading(false);
throw new Error('上传失败');
}
} catch (error) {
- console.error('Upload error:', error);
- onError?.(error as Error);
+ setUploading(false);
message.error(`上传失败: ${(error as Error).message}`);
} finally {
+ console.log('finally');
setUploading(false);
}
};
return (
-
- {fileList.length >= maxCount ? null : uploadButton}
-
- {previewImage && (
- setPreviewOpen(visible),
- afterOpenChange: (visible) => !visible && setPreviewImage(''),
- }}
- src={previewImage}
- />
- )}
+ {uploading}
+ <>
+
+ {fileList.length >= maxCount ? null : uploadButton}
+
+ {previewImage && (
+ setPreviewOpen(visible),
+ afterOpenChange: (visible) => !visible && setPreviewImage(''),
+ }}
+ src={previewImage}
+ />
+ )}
+ >
);
};
diff --git a/src/constants/trade.ts b/src/constants/trade.ts
index 93e5ef5..4e5dc8b 100644
--- a/src/constants/trade.ts
+++ b/src/constants/trade.ts
@@ -1,7 +1,7 @@
import type { BadgeProps } from 'antd';
export enum OrderStatus {
- All = 1,
+ All = 0,
PendingPayment = 10,
PendingConfirmation = 20,
PendingService = 30,
@@ -53,3 +53,21 @@ export const mapOrderStatusToBadgeStatus = (
return statusMap[statusColor] || 'default';
};
+
+export enum SalesStatus {
+ All = 0,
+ Apply = 10,
+ SellerAgree = 20,
+ BuyerDelivery = 30,
+ WaitRefund = 40,
+ Completed = 50,
+}
+
+export const SalesStatusLableMap: Record = {
+ [SalesStatus.All]: '所有售后单',
+ [SalesStatus.Apply]: '等待审核',
+ [SalesStatus.SellerAgree]: '审核通过',
+ [SalesStatus.BuyerDelivery]: '已完成',
+ [SalesStatus.WaitRefund]: '已取消',
+ [SalesStatus.Completed]: '已拒绝',
+};
diff --git a/src/global.less b/src/global.less
index 0d2216c..d6fc3b7 100644
--- a/src/global.less
+++ b/src/global.less
@@ -75,7 +75,9 @@ ul,
ol {
list-style: none;
}
-
+.ant-statistic {
+ text-align: center;
+}
@media (max-width: 768px) {
.ant-table {
width: 100%;
@@ -93,6 +95,11 @@ ol {
}
}
+.ant-table-row {
+ .ant-form-item {
+ margin-block: -16px !important;
+ }
+}
.page-container {
background: #fff;
width: 100%;
diff --git a/src/pages/Welcome.tsx b/src/pages/Welcome.tsx
index 8f3fd98..69cc527 100644
--- a/src/pages/Welcome.tsx
+++ b/src/pages/Welcome.tsx
@@ -131,7 +131,7 @@ const Welcome: React.FC = () => {
百业到家 是一个整合了 umi,Ant Design 和 ProComponents
的脚手架方案。致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。
- {
href="https://procomponents.ant.design"
desc="ProComponents 是一个基于 Ant Design 做了更高抽象的模板组件,以 一个组件就是一个页面为开发理念,为中后台开发带来更好的体验。"
/>
-
+ */}
diff --git a/src/pages/prod/list/components/prod-info.tsx b/src/pages/prod/list/components/prod-info.tsx
index eb5c94b..baa40b5 100644
--- a/src/pages/prod/list/components/prod-info.tsx
+++ b/src/pages/prod/list/components/prod-info.tsx
@@ -27,20 +27,60 @@ const ProdInfo = >(props: ProdInfoProps) => {
name="prodName"
label="商品名字"
rules={[{ required: true }]}
- width="xl"
+ colProps={{
+ span: 24,
+ }}
+ />
+
+
+
+
+
+
-
-
-
-
-
-
e.fileList}
>
@@ -58,7 +98,9 @@ const ProdInfo = >(props: ProdInfoProps) => {
style={{ width: '100%' }}
name="pic"
label="主图"
- width="xl"
+ colProps={{
+ span: 24,
+ }}
extra="仅支持.jpg .png 格式,建议图片比例1:1,限1张"
// rules={[{ required: true }]}
// getValueFromEvent={(e) => e.fileList}
@@ -70,7 +112,9 @@ const ProdInfo = >(props: ProdInfoProps) => {
name="imgs"
label="轮播图"
layout="horizontal"
- width="xl"
+ colProps={{
+ span: 24,
+ }}
extra="仅支持.jpg .png 格式,建议图片比例1:1,限7张"
// getValueFromEvent={(e) => e.fileList}
>
@@ -80,16 +124,21 @@ const ProdInfo = >(props: ProdInfoProps) => {
style={{ width: '100%' }}
name="whiteImg"
label="白底图"
- width="xl"
+ colProps={{
+ span: 24,
+ }}
extra="仅支持.jpg .png 格式,建议图片比例1:1,限1张"
// getValueFromEvent={(e) => e.fileList}
>
e.fileList}
>
@@ -99,7 +148,9 @@ const ProdInfo = >(props: ProdInfoProps) => {
name="content"
label="图文介绍"
rules={[{ required: true }]}
- width="xl"
+ colProps={{
+ span: 24,
+ }}
// getValueFromEvent={(e) => e.fileList}
>
@@ -110,7 +161,7 @@ const ProdInfo = >(props: ProdInfoProps) => {
name="seoShortName"
label="短标题"
colProps={{
- span: 20,
+ span: 24,
}}
/>
@@ -118,7 +169,9 @@ const ProdInfo = >(props: ProdInfoProps) => {
>(props: ProdInfoProps) => {
name="shareContent"
label="分享话术"
placeholder={'请输入分享话术'}
- width={'xl'}
+ colProps={{
+ span: 24,
+ }}
/>
diff --git a/src/pages/prod/list/components/service-rule/index.tsx b/src/pages/prod/list/components/service-rule/index.tsx
index a88d177..9c123a3 100644
--- a/src/pages/prod/list/components/service-rule/index.tsx
+++ b/src/pages/prod/list/components/service-rule/index.tsx
@@ -1,67 +1,85 @@
import { ProForm, ProFormText } from '@ant-design/pro-components';
+import { Anchor, Col, Form, Row } from 'antd';
+import ProdReservationConfig from './prodReservationConfig';
+import ProdServiceAreasInfo from './prodServiceAreasInfo';
+import ProdWeightConfig from './prodWeightConfig';
const ServiceRule: React.FC = () => {
return (
-
- grid
- onFinish={async (values) => {
- // await waitTime(2000);
- console.log(values);
- // message.success('提交成功');
- }}
- initialValues={{
- name: '蚂蚁设计有限公司',
- useMode: 'chapter',
- }}
+ console.log(value)}
+ layout="horizontal"
+ style={{ width: '100%' }}
+ labelCol={{ span: '100px' }}
>
-
-
-
-
-
-
- {/*
- rowKey="id"
- toolBarRender={false}
- columns={columns}
- recordCreatorProps={{
- newRecordType: 'dataSource',
- position: 'top',
- record: () => ({
- id: Date.now(),
- addonBefore: 'ccccccc',
- decs: 'testdesc',
- }),
- }}
- editable={{
- type: 'multiple',
- editableKeys,
- onChange: setEditableRowKeys,
- actionRender: (row, _, dom) => {
- return [dom.delete];
- },
- }} */}
- {/* /> */}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/src/pages/prod/list/components/service-rule/prodReservationConfig.tsx b/src/pages/prod/list/components/service-rule/prodReservationConfig.tsx
new file mode 100644
index 0000000..00f7334
--- /dev/null
+++ b/src/pages/prod/list/components/service-rule/prodReservationConfig.tsx
@@ -0,0 +1,139 @@
+import {
+ ProFormCheckbox,
+ ProFormDigit,
+ ProFormRadio,
+ ProFormSelect,
+ ProFormSwitch,
+ ProFormText,
+} from '@ant-design/pro-components';
+import { Divider, Space, Typography } from 'antd';
+import React from 'react';
+import type { ProdReservationInfoVO } from '@/services/prod/prod-manager/rule';
+
+const { Text, Title } = Typography;
+type ValueConfig = {
+ reservationSwitch?: number;
+ prodReservationConfig?: ProdReservationInfoVO;
+};
+interface ProdReservationConfigProps {
+ value?: ValueConfig;
+ onChange?: (value?: ValueConfig) => void;
+}
+const ProdReservationConfig: React.FC = (props) => {
+ return (
+ <>
+
+
+
服务区域配置
+
+ 默认服务区域为全城,开启此配置,可自定义可服务区域以及超区时的限制规则
+
+
+
+
{
+ props.onChange?.({
+ reservationSwitch: checked ? 1 : 0,
+ });
+ },
+ checkedChildren: '开启',
+ unCheckedChildren: '关闭',
+ }}
+ />
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default React.memo(ProdReservationConfig);
diff --git a/src/pages/prod/list/components/service-rule/prodServiceAreasInfo.tsx b/src/pages/prod/list/components/service-rule/prodServiceAreasInfo.tsx
new file mode 100644
index 0000000..8c4540f
--- /dev/null
+++ b/src/pages/prod/list/components/service-rule/prodServiceAreasInfo.tsx
@@ -0,0 +1,115 @@
+import {
+ ProFormCheckbox,
+ ProFormDigit,
+ ProFormRadio,
+ ProFormSelect,
+ ProFormSwitch,
+ ProFormText,
+} from '@ant-design/pro-components';
+import { Divider, Space, Typography } from 'antd';
+import React from 'react';
+import type { ProdServiceAreasInfoVO } from '@/services/prod/prod-manager/rule';
+
+const { Text, Title } = Typography;
+
+type ValueConfig = {
+ regionSwitch?: number;
+ prodServiceAreasInfo?: ProdServiceAreasInfoVO;
+};
+interface ProdServiceAreasInfoProps {
+ value?: ValueConfig;
+ onChange?: (value?: ValueConfig) => void;
+}
+const ProdServiceAreasInfo: React.FC = (props) => {
+ return (
+ <>
+
+
+
服务区域配置
+
+ 默认服务区域为全城,开启此配置,可自定义可服务区域以及超区时的限制规则
+
+
+
{
+ props.onChange?.({
+ regionSwitch: checked ? 1 : 0,
+ });
+ },
+ checkedChildren: '开启',
+ unCheckedChildren: '关闭',
+ }}
+ />
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default React.memo(ProdServiceAreasInfo);
diff --git a/src/pages/prod/list/components/service-rule/prodWeightConfig.tsx b/src/pages/prod/list/components/service-rule/prodWeightConfig.tsx
new file mode 100644
index 0000000..8e557da
--- /dev/null
+++ b/src/pages/prod/list/components/service-rule/prodWeightConfig.tsx
@@ -0,0 +1,278 @@
+import {
+ DragSortTable,
+ EditableProTable,
+ type ProColumns,
+ ProFormCheckbox,
+ ProFormDigit,
+ ProFormRadio,
+ ProFormSelect,
+ ProFormSwitch,
+ ProFormText,
+} from '@ant-design/pro-components';
+import { Divider, Space, Typography } from 'antd';
+import React from 'react';
+import type {
+ ProdWeightRangePricesDO,
+ ProdWeightRangePricesSaveInfoVO,
+} from '@/services/prod/prod-manager/rule';
+
+const { Text, Title } = Typography;
+type ValueConfig = {
+ weightSwitch?: number;
+ prodWeightConfig?: ProdWeightRangePricesSaveInfoVO;
+};
+interface ProdWeightConfigProps {
+ value?: ValueConfig;
+ onChange?: (value?: ValueConfig) => void;
+}
+
+const columns = [
+ {
+ title: '体型&体重',
+ dataIndex: 'name',
+ key: 'name',
+ width: '20%',
+ render: (text: string, record: any) => {
+ return ;
+ },
+ },
+ {
+ title: '价格',
+ dataIndex: 'price',
+ key: 'price',
+ width: '20%',
+ },
+ {
+ title: '操作',
+ dataIndex: 'price',
+ key: 'price',
+ width: '20%',
+ },
+];
+const prodWeightConfig: React.FC = (props) => {
+ const { value } = props;
+ const columns: ProColumns[] = [
+ {
+ title: '体型&体重 *',
+ dataIndex: 'weightRange',
+ width: '30%',
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ whitespace: true,
+ message: '此项是必填项',
+ },
+ ],
+ },
+ },
+ {
+ title: '价格 *',
+ dataIndex: 'price',
+ width: '30%',
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ whitespace: true,
+ message: '此项是必填项',
+ },
+ ],
+ },
+ },
+
+ {
+ title: '操作',
+ valueType: 'option',
+ width: 250,
+ render: () => {
+ return null;
+ },
+ },
+ ];
+ return (
+ <>
+
+
+
服务区域配置
+
+ 默认服务区域为全城,开启此配置,可自定义可服务区域以及超区时的限制规则
+
+
+
{
+ props.onChange?.({
+ weightSwitch: checked ? 1 : 0,
+ });
+ },
+ checkedChildren: '开启',
+ unCheckedChildren: '关闭',
+ }}
+ />
+
+
+
+
+ {/*
+ rowKey="id"
+ headerTitle="可拖拽编辑表格"
+ maxLength={10}
+ recordCreatorProps={{
+ position: 'bottom',
+ record: () => ({ id: Date.now().toString(), weightRange: '', price: 0, }),
+ }}
+ columns={columns}
+ value={dataSource}
+ onChange={setDataSource}
+ editable={{
+ type: 'multiple',
+ editableKeys,
+ onSave: async (rowKey, data) => {
+ console.log('保存数据:', data);
+ },
+ onChange: setEditableRowKeys,
+ }}
+ // 拖拽排序配置
+ dragSortKey="sort"
+ onDragSortEnd={(newDataSource) => {
+ console.log('排序后:', newDataSource);
+ setDataSource(newDataSource as DataType[]);
+ }}
+ /> */}
+ >
+ );
+};
+
+export default React.memo(prodWeightConfig);
+
+// import { EditableProTable } from '@ant-design/pro-components';
+// import type { ProColumns } from '@ant-design/pro-components';
+// import { useState } from 'react';
+// import { MenuOutlined } from '@ant-design/icons';
+
+// interface DataType {
+// id: string;
+// name: string;
+// age: number;
+// email: string;
+// }
+
+// const DragEditableProTable: React.FC = () => {
+// const [editableKeys, setEditableRowKeys] = useState([]);
+// const [dataSource, setDataSource] = useState([
+// { id: '1', name: '张三', age: 25, email: 'zhangsan@example.com' },
+// { id: '2', name: '李四', age: 30, email: 'lisi@example.com' },
+// { id: '3', name: '王五', age: 28, email: 'wangwu@example.com' },
+// ]);
+
+// const columns: ProColumns[] = [
+// {
+// title: '排序',
+// dataIndex: 'sort',
+// width: 60,
+// render: () => ,
+// },
+// {
+// title: '姓名',
+// dataIndex: 'name',
+// formItemProps: {
+// rules: [{ required: true, message: '请输入姓名' }],
+// },
+// },
+// {
+// title: '年龄',
+// dataIndex: 'age',
+// valueType: 'digit',
+// },
+// {
+// title: '邮箱',
+// dataIndex: 'email',
+// valueType: 'text',
+// },
+// {
+// title: '操作',
+// valueType: 'option',
+// width: 200,
+// render: (text, record, _, action) => [
+// {
+// action?.startEditable?.(record.id);
+// }}
+// >
+// 编辑
+// ,
+// {
+// setDataSource(dataSource.filter((item) => item.id !== record.id));
+// }}
+// >
+// 删除
+// ,
+// ],
+// },
+// ];
+
+// return (
+//
+// rowKey="id"
+// headerTitle="可拖拽编辑表格"
+// maxLength={10}
+// recordCreatorProps={{
+// position: 'bottom',
+// record: () => ({ id: Date.now().toString(), name: '', age: 0, email: '' }),
+// }}
+// columns={columns}
+// value={dataSource}
+// onChange={setDataSource}
+// editable={{
+// type: 'multiple',
+// editableKeys,
+// onSave: async (rowKey, data) => {
+// console.log('保存数据:', data);
+// },
+// onChange: setEditableRowKeys,
+// }}
+// // 拖拽排序配置
+// dragSortKey="sort"
+// onDragSortEnd={(newDataSource) => {
+// console.log('排序后:', newDataSource);
+// setDataSource(newDataSource as DataType[]);
+// }}
+// />
+// );
+// };
+
+// export default DragEditableProTable;
diff --git a/src/pages/prod/list/detail.tsx b/src/pages/prod/list/detail.tsx
index 1d09ab1..cbdc438 100644
--- a/src/pages/prod/list/detail.tsx
+++ b/src/pages/prod/list/detail.tsx
@@ -6,16 +6,9 @@ import ProdInfo from '@/pages/prod/list/components/prod-info';
import Sku from '@/pages/prod/list/components/sku';
import type { Prod, ProdDetail, SkuConfig } from '@/services/prod/prod-manager';
-const waitTime = (time: number = 100) => {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve(true);
- }, time);
- });
-};
-
const ProdDetailPage: React.FC<{
data?: Prod;
+ type?: string;
formRef?: React.RefObject>;
}> = (props) => {
const { data, formRef } = props;
@@ -42,8 +35,8 @@ const ProdDetailPage: React.FC<{
formRef={formRef}
submitter={false}
onFinish={async () => {
- await waitTime(1000);
- message.success('提交成功');
+ // await waitTime(1000);
+ // message.success('提交成功');
}}
formProps={{
validateMessages: {
diff --git a/src/pages/prod/list/index.tsx b/src/pages/prod/list/index.tsx
index 2a02a21..a266e57 100644
--- a/src/pages/prod/list/index.tsx
+++ b/src/pages/prod/list/index.tsx
@@ -1,5 +1,6 @@
import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
+import { useNavigate } from '@umijs/max';
import type { MenuProps, TabsProps } from 'antd';
import { Button, Dropdown, Space, Tabs } from 'antd';
import { useCallback, useRef, useState } from 'react';
@@ -25,6 +26,7 @@ const ProdList = () => {
const [type, setType] = useState<'create' | 'update' | 'test'>('create');
const [status, setStatus] = useState();
const detailRef = useRef(null);
+ const navigator = useNavigate();
// const editRef = useRef(null);
const [id, setId] = useState(0);
const onChange = useCallback(
@@ -89,6 +91,12 @@ const ProdList = () => {
[id, type],
);
+ const handleLink = (key: string, row: Prod) => {
+ if (key === 'sku') {
+ // navigator(`/prod/list/sku/${row.prodId}`);
+ }
+ console.log(key, row);
+ };
// const renderDetailFooter = () => {
// if (type === "update") {
// return (
@@ -118,7 +126,11 @@ const ProdList = () => {
},
{
key: 'sku',
- label: ,
+ label: (
+
+ ),
},
{
key: 'rules-service',
@@ -155,7 +167,9 @@ const ProdList = () => {
-
+
diff --git a/src/pages/trade/order/components/createSales.module.less b/src/pages/trade/order/components/createSales.module.less
new file mode 100644
index 0000000..299fbac
--- /dev/null
+++ b/src/pages/trade/order/components/createSales.module.less
@@ -0,0 +1,20 @@
+.sales {
+ :global {
+ .ant-pro-card-body {
+ padding: 16px !important;
+ }
+ .ant-pro-card-size-small {
+ .ant-pro-card-header {
+ background: rgba(0, 0, 0, 0.02);
+ min-height: 41px;
+ }
+ .ant-form-item {
+ margin-bottom: 0;
+ }
+ .ant-pro-card-col {
+ flex: 1 auto;
+ overflow: auto;
+ }
+ }
+ }
+}
diff --git a/src/pages/trade/order/components/createSalesModal copy.tsx b/src/pages/trade/order/components/createSalesModal copy.tsx
new file mode 100644
index 0000000..4c03152
--- /dev/null
+++ b/src/pages/trade/order/components/createSalesModal copy.tsx
@@ -0,0 +1,762 @@
+//新建售后
+
+import {
+ BetaSchemaForm,
+ type EditableFormInstance,
+ EditableProTable,
+ ProCard,
+ type ProColumns,
+ ProForm,
+ type ProFormColumnsType,
+ ProFormDependency,
+ ProFormDigit,
+ ProFormRadio,
+ ProFormTextArea,
+} from '@ant-design/pro-components';
+import {
+ Badge,
+ Button,
+ Divider,
+ Empty,
+ Form,
+ type FormInstance,
+ Input,
+ InputNumber,
+ Space,
+ Spin,
+ Statistic,
+ Table,
+ Typography,
+} from 'antd';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import EditableProTableCom from '@/components/EditableProTable';
+import CommonModal from '@/components/ModalCom/CommonModal';
+import UploadImages from '@/components/Upload/UploadImages';
+import styles from './createSales.module.less';
+
+const { Title, Text } = Typography;
+
+interface DataType {
+ key: string;
+ name: string;
+ age: number;
+ address: string;
+}
+const originData = Array.from({ length: 100 }).map((_, i) => ({
+ key: i.toString(),
+ name: `Edward ${i}`,
+ age: 32,
+ address: `London Park no. ${i}`,
+}));
+
+const waitTime = (time: number = 100) => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(true);
+ }, time);
+ });
+};
+const CreateSalesModal = () => {
+ const [loading, setLoading] = useState(false);
+ const formRefSales = useRef(null);
+ const formRef = useRef(null);
+
+ const [open, setOpen] = useState(false);
+ const handleOk = useCallback(async () => {
+ try {
+ setLoading(true);
+ const values1 = formRef.current?.getFieldsValue();
+ const values2 = formRefSales.current?.getFieldsValue();
+
+ console.log({ ...values1, values2 });
+ } finally {
+ setLoading(false);
+ }
+ }, [open]);
+
+ const onOpen = useCallback(
+ (flag: boolean) => {
+ setOpen(flag);
+ },
+ [open],
+ );
+ const salesColumns: ProFormColumnsType[] = [
+ {
+ title: '售后类型',
+ dataIndex: 'cancelReason',
+ valueType: 'radioButton',
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ },
+ ],
+ },
+ fieldProps: {
+ options: [{ value: '仅退款', label: '仅退款' }],
+ },
+ },
+ {
+ title: '退款类型',
+ dataIndex: 'cancelRemark',
+ valueType: 'radioButton',
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ },
+ ],
+ },
+ fieldProps: {
+ options: [
+ { value: '整单退款(通用)', label: '整单退款' },
+ { value: '选项二', label: '商品退款' },
+ { value: '选项三', label: '扩展服务退款' },
+ { value: '服务附加费退款', label: '服务附加费退款' },
+ { value: '选项五', label: '退运费' },
+ { value: '其他', label: '其他' },
+ ],
+ },
+ },
+ // {
+ // title: "",
+ // dependencies: ["cancelRemark"],
+ // dataIndex: "cancelRemark1",
+ // valueType: "textarea",
+ // formItemProps: {
+ // rules: [
+ // {
+ // required: true,
+ // },
+ // ],
+ // },
+ // fieldProps: {
+ // placeholder: "请输入",
+ // },
+ // renderFormItem: (schema, config, form) => {
+ // const grade = form.getFieldValue("cancelRemark");
+ // if (grade === "其他") {
+ // return ;
+ // }
+ // if (grade === "服务附加费退款") {
+ // return (
+ //
+ //
+ //
+ // );
+ // }
+ // return <>>;
+ // },
+ // },
+ ];
+ const applyColumns: ProFormColumnsType[] = [
+ {
+ title: '售后类型',
+ dataIndex: 'cancelReason1',
+ valueType: 'radio',
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ },
+ ],
+ },
+ fieldProps: {
+ options: [
+ { value: '用户主动取消', label: '用户主动取消' },
+ { value: '选项二', label: '选项二' },
+ { value: '选项三', label: '选项三' },
+ { value: '选项四', label: '选项四' },
+ { value: '选项五', label: '选项五' },
+ { value: '选项六', label: '选项六' },
+ { value: '选项七', label: '选项七' },
+ ],
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '8px',
+ marginTop: '6px',
+ background: '#f0f0f0',
+ padding: '8px',
+ borderRadius: '8px',
+ },
+ },
+ },
+ {
+ title: '问题描述',
+ dataIndex: 'cancelRemark22',
+ valueType: 'textarea',
+ },
+ {
+ title: '问题凭证',
+ dataIndex: 'cancelRemark23',
+ formItemProps: {
+ extra: '请上传问题凭证,支持.jpg .png 格式,最多 4 张。',
+ },
+ renderFormItem: () => {
+ return ;
+ },
+ },
+
+ {
+ title: '商家备注',
+ dataIndex: 'cancelRemark2311',
+ valueType: 'textarea',
+ },
+ ];
+
+ return (
+ <>
+
+ onOpen(false)}
+ confirmLoading={loading}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 最高退款金额
+ (不含虚拟资产)
+
+
+
+
+
+ 退款金额上限 ¥700
+ ,金额不支持修改,优惠券、积分、余额等虚拟资产不支持修改,虚拟资产将随退款而原路退回,具体金额以实际情况为准。
+
+
+
+ 预估退款项
+
+
+ 商品/服务
+ 合计¥500
+
+ }
+ />
+
+ 增值/扩展服务
+ 合计¥500
+
+ }
+ />
+
+ 运费
+ 合计¥500
+
+ }
+ />
+
+
+
+
+ 预估退款项
+ (退款方式:原支付方式返还)
+
+
+
+
+ 微信支付
+ (建设银行 尾号1626)
+
+ ¥500
+
+ }
+ />
+
+
+ 钱包余额
+ (个人钱包 编号1090)
+
+ ¥500
+
+ }
+ />
+
+ 宠豆豆(积分)
+ ¥500
+
+ }
+ />
+
+ 红包
+ ¥500
+
+ }
+ />
+
+
+ 预估扣除项
+
+ 如果后续有设计退款时扣除比例的设定才会存在此字段。目前暂时不做这个业务逻辑,仅为布局占位。
+
+
+
+
+ 违约金
+ (服务开始前60分钟内退款)
+
+ ¥500
+
+ }
+ />
+
+
+
+
+
+ >
+ );
+};
+type DataSourceType = {
+ id: React.Key;
+ prod?: string;
+ num?: number;
+ state?: string;
+ created_at?: number;
+ children?: DataSourceType[];
+};
+
+const columnsSale: ProColumns[] = [
+ {
+ title: '商品',
+ dataIndex: 'prod',
+ editable: () => false,
+ },
+ {
+ title: '退款信息',
+ key: 'refund',
+ editable: () => false,
+ dataIndex: 'state',
+ },
+ {
+ title: '退款数量',
+ dataIndex: 'num',
+ valueType: 'digit',
+ editable: () => true,
+ formItemProps: {
+ rules: [
+ {
+ required: true,
+ message: '请输入退款数量',
+ },
+ ],
+ },
+ },
+];
+
+const defaultData: DataSourceType[] = new Array(20).fill(1).map((_, index) => {
+ return {
+ id: (Date.now() + index).toString(),
+ prod: `活动名称${index}`,
+ num: index,
+ state: 'open',
+ created_at: 1590486176000,
+ };
+});
+
+const ExtendRefunds = (props: {
+ onChange?: (value: readonly DataSourceType[]) => void;
+}) => {
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const editorFormRef = useRef>(null);
+ const [editableKeys, setEditableRowKeys] = useState([]);
+ const [formformSales] = Form.useForm();
+ const [formExtend] = Form.useForm();
+ const [formServeExtFee] = Form.useForm();
+ const [formShippingFee] = Form.useForm();
+ const fetchData = async () => {
+ await waitTime();
+ setData(defaultData);
+ setLoading(false);
+ };
+
+ useEffect(() => {
+ fetchData();
+ }, []);
+ useEffect(() => {
+ props.onChange?.(data);
+ }, [data, props.onChange]);
+
+ useEffect(() => {
+ setEditableRowKeys(data!.map((item) => item.id));
+ }, [data]);
+ console.log(editableKeys);
+ return (
+ {
+ setData(recordList);
+ },
+ }}
+ />
+ //
+ // columns={columnsSale}
+ // value={defaultData}
+ // rowKey="id"
+ // rowSelection={{
+ // selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+ // }}
+ // editable={{
+ // form: formformSales,
+ // editableKeys: editableKeys,
+ // onChange: setEditableRowKeys,
+ // onValuesChange: (record, recordList) => {
+ // setData(recordList);
+ // },
+ // }}
+ // />
+ );
+};
+// const ExtendRefunds = (props: {
+// onChange?: (value: readonly DataSourceType[]) => void;
+// }) => {
+// const [data, setData] = useState([]);
+// const [loading, setLoading] = useState(true);
+// const editorFormRef = useRef>(null);
+// const [editableKeys, setEditableRowKeys] = useState([]);
+// const [formformSales] = Form.useForm();
+// const [formExtend] = Form.useForm();
+// const [formServeExtFee] = Form.useForm();
+// const [formShippingFee] = Form.useForm();
+
+// const fetchData = async () => {
+// await waitTime();
+// setData(defaultData);
+// setLoading(false);
+// };
+
+// useEffect(() => {
+// fetchData();
+// }, []);
+// useEffect(() => {
+// props.onChange?.(data);
+// }, [data]);
+
+// useEffect(() => {
+// setEditableRowKeys(data!.map((item) => item.id));
+// console.log(data);
+// }, [data]);
+
+// const renderContent = useCallback(() => {
+// // if (data.length === 0 && !loading) {
+// // return (
+// //
+// //
+// //
);
+// // }
+// return (
+//
+//
+// columns={columnsSale}
+// rowKey="id"
+// rowSelection={{
+// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+// }}
+// value={data}
+// editable={{
+// form: formformSales,
+// editableKeys: data.map((item) => item.id),
+// onChange: setEditableRowKeys,
+// onValuesChange: (record, recordList) => {
+// setData(recordList);
+// },
+// }}
+// />
+// {/*
+// columns={columnsSale}
+// rowKey="id"
+// value={data}
+// key={"sale"}
+// // onChange={onTableChange}
+// rowSelection={{
+// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+// }}
+// editable={{
+// form: formformSales,
+// editableKeys: data.map((item) => item.id),
+// onChange: setEditableRowKeys,
+// // onValuesChange: (record, recordList) => {
+// // setData(recordList);
+// // },
+// }}
+// />
+//
+//
+//
+// columns={columnsSale}
+// rowKey="id"
+// value={data}
+// // onChange={onTableChange}
+// rowSelection={{
+// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+// }}
+// key={"sale1"}
+// editable={{
+// form: formExtend,
+// editableKeys,
+// onChange: setEditableRowKeys,
+// // onValuesChange: (record, recordList) => {
+// // setData(recordList);
+// // },
+// }}
+// />
+//
+//
+//
+// columns={columns}
+// rowKey="id"
+// value={data}
+// // onChange={onTableChange}
+// rowSelection={{
+// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+// }}
+// key={"sale2"}
+// editable={{
+// form: formServeExtFee,
+// editableKeys,
+// onChange: setEditableRowKeys,
+// // onValuesChange: (record, recordList) => {
+// // setData(recordList);
+// // },
+// }}
+// />
+//
+//
+//
+// columns={columns}
+// rowKey="id"
+// value={data}
+// key={"sale4"}
+// onChange={onTableChange}
+// rowSelection={{
+// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+// }}
+// editable={{
+// form: formShippingFee,
+// editableKeys,
+// onChange: setEditableRowKeys,
+// // onValuesChange: (record, recordList) => {
+// // setData(recordList);
+// // },
+// }}
+// />
+// */}
+//
+// );
+// }, [loading, data]);
+// return {renderContent()}
;
+// };
+
+export default CreateSalesModal;
+
+const ConditionalFields: React.FC = () => {
+ const cancelRemark = Form.useWatch('cancelRemark', { preserve: true });
+
+ if (cancelRemark === '其他') {
+ return (
+
+ );
+ }
+
+ if (cancelRemark === '服务附加费退款') {
+ return (
+
+
+
+ );
+ }
+
+ return null;
+};
diff --git a/src/pages/trade/order/components/createSalesModal.tsx b/src/pages/trade/order/components/createSalesModal.tsx
new file mode 100644
index 0000000..576e59c
--- /dev/null
+++ b/src/pages/trade/order/components/createSalesModal.tsx
@@ -0,0 +1,609 @@
+import {
+ BetaSchemaForm,
+ ProCard,
+ type ProFormColumnsType,
+} from '@ant-design/pro-components';
+import {
+ Badge,
+ Button,
+ Divider,
+ Form,
+ Input,
+ InputNumber,
+ message,
+ Radio,
+ Space,
+ Statistic,
+ Table,
+ Typography,
+} from 'antd';
+import React, { useCallback, useEffect, useState } from 'react';
+import CommonModal from '@/components/ModalCom/CommonModal';
+import styles from './createSales.module.less';
+
+const { Title, Text } = Typography;
+
+type DataSourceType = {
+ id: React.Key;
+ prod: string;
+ num: number;
+ state: string;
+ availableNum: number; // 新增:可退款数量
+};
+
+const CreateSalesModal = () => {
+ const [loading, setLoading] = useState(false);
+ const [formSales] = Form.useForm();
+ const [formApply] = Form.useForm();
+ const [open, setOpen] = useState(false);
+ const [cancelRemarkType, setCancelRemarkType] = useState('');
+
+ const handleOk = useCallback(async () => {
+ try {
+ setLoading(true);
+ const values1 = await formApply.validateFields();
+ const values2 = await formSales.validateFields();
+
+ console.log('申请信息:', values1);
+ console.log('售后信息:', values2);
+ console.log('表格数据:', values2.extendRefunds);
+
+ message.success('提交成功');
+ onOpen(false);
+ } catch (error) {
+ console.error('表单验证失败:', error);
+ message.error('请完善表单信息');
+ } finally {
+ setLoading(false);
+ }
+ }, [formApply, formSales]);
+
+ const onOpen = useCallback(
+ (flag: boolean) => {
+ setOpen(flag);
+ if (!flag) {
+ setCancelRemarkType('');
+ formSales.resetFields();
+ formApply.resetFields();
+ }
+ },
+ [formSales, formApply],
+ );
+
+ const applyColumns: ProFormColumnsType[] = [
+ {
+ title: '售后类型',
+ dataIndex: 'cancelReason1',
+ valueType: 'radio',
+ formItemProps: {
+ rules: [{ required: true }],
+ },
+ fieldProps: {
+ options: [
+ { value: '用户主动取消', label: '用户主动取消' },
+ { value: '选项二', label: '选项二' },
+ { value: '选项三', label: '选项三' },
+ { value: '选项四', label: '选项四' },
+ { value: '选项五', label: '选项五' },
+ { value: '选项六', label: '选项六' },
+ { value: '选项七', label: '选项七' },
+ ],
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '8px',
+ marginTop: '6px',
+ background: '#f0f0f0',
+ padding: '8px',
+ borderRadius: '8px',
+ },
+ },
+ },
+ {
+ title: '问题描述',
+ dataIndex: 'cancelRemark22',
+ valueType: 'textarea',
+ },
+ {
+ title: '商家备注',
+ dataIndex: 'cancelRemark2311',
+ valueType: 'textarea',
+ },
+ ];
+
+ return (
+ <>
+
+ onOpen(false)}
+ confirmLoading={loading}
+ width={1000}
+ >
+
+ {/* 统计卡片 */}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 售后选项 */}
+
+
+
+ 仅退款
+
+
+
+
+ {
+ setCancelRemarkType(e.target.value);
+ // 切换类型时清空相关字段
+ formSales.setFieldsValue({
+ cancelRemarkText: undefined,
+ extendRefunds: undefined,
+ });
+ }}
+ >
+ 整单退款
+ 商品退款
+ 扩展服务退款
+
+ 服务附加费退款
+
+ 退运费
+ 其他
+
+
+
+ {cancelRemarkType === '其他' && (
+
+
+
+ )}
+
+ {cancelRemarkType === '服务附加费退款' && (
+ {
+ if (!value || value.length === 0) {
+ return Promise.reject(
+ new Error('请至少选择一项服务附加费'),
+ );
+ }
+
+ return Promise.resolve();
+ },
+ },
+ ]}
+ >
+
+
+ )}
+
+
+
+ {/* 申请信息 */}
+
+
+
+
+ {/* 预估退款明细 */}
+
+
+
+
+ 最高退款金额
+ (不含虚拟资产)
+
+
+
+
+
+ 退款金额上限 ¥700
+ ,金额不支持修改,优惠券、积分、余额等虚拟资产不支持修改,虚拟资产将随退款而原路退回,具体金额以实际情况为准。
+
+
+
+ 预估退款项
+
+
+ 商品/服务
+ 合计¥500
+
+ }
+ />
+
+ 增值/扩展服务
+ 合计¥500
+
+ }
+ />
+
+ 运费
+ 合计¥500
+
+ }
+ />
+
+
+
+
+ 预估退款项
+ (退款方式:原支付方式返还)
+
+
+
+
+ 微信支付
+ (建设银行 尾号1626)
+
+ ¥500
+
+ }
+ />
+
+
+ 钱包余额
+ (个人钱包 编号1090)
+
+ ¥500
+
+ }
+ />
+
+ 宠豆豆(积分)
+ ¥500
+
+ }
+ />
+
+ 红包
+ ¥500
+
+ }
+ />
+
+
+ 预估扣除项
+
+ 如果后续有设计退款时扣除比例的设定才会存在此字段。目前暂时不做这个业务逻辑,仅为布局占位。
+
+
+
+
+ 违约金
+ (服务开始前60分钟内退款)
+
+ ¥500
+
+ }
+ />
+
+
+
+
+
+ >
+ );
+};
+
+const ExtendRefundsTable: React.FC<{
+ value?: DataSourceType[];
+ onChange?: (value: DataSourceType[]) => void;
+}> = ({ value, onChange }) => {
+ const [dataSource, setDataSource] = useState([]);
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+
+ // 初始化数据 - 默认值为 1,并设置可退款数量
+ useEffect(() => {
+ const initData: DataSourceType[] = Array.from({ length: 10 }).map(
+ (_, index) => ({
+ id: `service_${index}`,
+ prod: `服务附加费项目${index + 1}`,
+ num: 1,
+ state: '待退款',
+ availableNum: index % 3 === 0 ? 0 : Math.floor(Math.random() * 10) + 1, // 模拟数据,每3个有一个为0
+ }),
+ );
+
+ if (!value) {
+ setDataSource(initData);
+ // 初始化时通知父组件,只选中可退款数量 > 0 的项
+ const validItems = initData.filter((item) => item.availableNum > 0);
+ const validKeys = validItems.map((item) => item.id);
+ notifyChange(initData, validKeys);
+ setSelectedRowKeys(validKeys);
+ }
+ }, []);
+
+ // 当外部 value 变化时更新
+ useEffect(() => {
+ if (value && value.length > 0) {
+ // 更新选中状态,只保留可退款数量 > 0 的项
+ setSelectedRowKeys((prev) =>
+ prev.filter((key) => {
+ const item = value.find((v) => v.id === key);
+ return item && item.availableNum > 0;
+ }),
+ );
+ }
+ }, [value]);
+
+ // 通知父组件数据变化(包含选中信息)
+ const notifyChange = (data: DataSourceType[], selected: React.Key[]) => {
+ // 只返回选中的项
+ const selectedData = data.filter((item) => selected.includes(item.id));
+ onChange?.(selectedData);
+ };
+
+ const handleNumChange = (id: React.Key, num: number | null) => {
+ const record = dataSource.find((item) => item.id === id);
+ if (!record) return;
+
+ // 如果可退款数量为0,不允许修改
+ if (record.availableNum <= 0) return;
+
+ const finalNum = num ?? 1;
+ // 不能超过可退款数量
+ const validNum = Math.min(finalNum, record.availableNum);
+
+ const newData = dataSource.map((item) =>
+ item.id === id ? { ...item, num: validNum } : item,
+ );
+ setDataSource(newData);
+
+ // 如果数量变为 0 或负数,取消选中
+ let newSelectedKeys = selectedRowKeys;
+ if (validNum <= 0) {
+ newSelectedKeys = selectedRowKeys.filter((key) => key !== id);
+ setSelectedRowKeys(newSelectedKeys);
+ }
+
+ notifyChange(newData, newSelectedKeys);
+ };
+
+ // 判断是否有错误
+ const isInvalid = (num: number | undefined | null): boolean => {
+ if (num === undefined || num === null) return true;
+ if (typeof num !== 'number') return true;
+ if (num <= 0) return true;
+ return false;
+ };
+
+ const columns = [
+ {
+ title: '服务项目',
+ dataIndex: 'prod',
+ key: 'prod',
+ width: '30%',
+ ellipsis: true,
+ },
+ {
+ title: '退款信息',
+ dataIndex: 'state',
+ key: 'state',
+ width: '20%',
+ },
+ {
+ title: '可退款数量',
+ dataIndex: 'availableNum',
+ key: 'availableNum',
+ width: '20%',
+ },
+ {
+ title: * 退款数量,
+ dataIndex: 'num',
+ key: 'num',
+ width: '30%',
+ render: (value: number, record: DataSourceType) => {
+ const hasError = isInvalid(value);
+ const isDisabled = record.availableNum <= 0;
+
+ return (
+ handleNumChange(record.id, val)}
+ disabled={isDisabled}
+ style={{
+ width: '100%',
+ }}
+ placeholder={isDisabled ? '不可退款' : '请输入退款数量'}
+ precision={0}
+ />
+ );
+ },
+ },
+ ];
+
+ // 行选择配置
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: (newSelectedRowKeys: React.Key[]) => {
+ setSelectedRowKeys(newSelectedRowKeys);
+ notifyChange(dataSource, newSelectedRowKeys);
+ },
+ getCheckboxProps: (record: DataSourceType) => ({
+ disabled: record.availableNum <= 0, // 可退款数量为 0 时禁用选中
+ name: record.prod,
+ }),
+ };
+
+ return (
+
+
+ 服务附加费退款明细
+
+ 已选择 {selectedRowKeys.length} 项
+
+
+
+
+ );
+};
+export default CreateSalesModal;
diff --git a/src/pages/trade/order/detail/index.tsx b/src/pages/trade/order/detail/index.tsx
index 1e36030..4487120 100644
--- a/src/pages/trade/order/detail/index.tsx
+++ b/src/pages/trade/order/detail/index.tsx
@@ -1,7 +1,9 @@
import { Tabs, type TabsProps } from 'antd';
import React from 'react';
import type { TradeOrderPageRespVO } from '@/services/trade/order';
+import { SalesTable } from '../../sales/list';
import OrderInfo from './order-info';
+import SalesRefunds from './src/sales-refunds';
const DetailCom: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
const items: TabsProps['items'] = [
@@ -20,6 +22,11 @@ const DetailCom: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
label: '商品配送',
children: '商品配送',
},
+ {
+ key: '4',
+ label: '售后与退款',
+ children: ,
+ },
];
return ;
};
diff --git a/src/pages/trade/order/detail/order-info.tsx b/src/pages/trade/order/detail/order-info.tsx
index 6d0aa1d..ab5dcfc 100644
--- a/src/pages/trade/order/detail/order-info.tsx
+++ b/src/pages/trade/order/detail/order-info.tsx
@@ -39,12 +39,12 @@ const OrderDetail: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
return (
-
+
{detais?.tradeServeInfo && (
)}
{detais?.tradeExtendServeInfo && (
diff --git a/src/pages/trade/order/detail/src/info/config.tsx b/src/pages/trade/order/detail/src/info/config.tsx
index b02f20b..f9d6a7a 100644
--- a/src/pages/trade/order/detail/src/info/config.tsx
+++ b/src/pages/trade/order/detail/src/info/config.tsx
@@ -23,7 +23,7 @@ const sharedOnCell = (_: DeptVO, index?: number) => {
export const surchargeInfoColumns: ProColumns[] = [
{
title: '服务附加费',
- dataIndex: 'name',
+ dataIndex: 'serveExtFee',
width: '33.33%',
render: (_, record, index) => {
if (index === 3) {
diff --git a/src/pages/trade/order/detail/src/info/prod-info.tsx b/src/pages/trade/order/detail/src/info/prod-info.tsx
index 913f3c2..9469a45 100644
--- a/src/pages/trade/order/detail/src/info/prod-info.tsx
+++ b/src/pages/trade/order/detail/src/info/prod-info.tsx
@@ -1,5 +1,5 @@
import { ProCard } from '@ant-design/pro-components';
-import { Button, Card, Image, Modal, Space, Tag, Typography } from 'antd';
+import { Button, Card, Image, Modal, Space, Spin, Tag, Typography } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import AdvancedImageGallery from '@/components/AdvancedImageGallery';
import DangerouslySetInnerHTML from '@/components/DangerouslySetInnerHTML';
@@ -42,7 +42,7 @@ const ProdInfo: React.FC> = (props) => {
return (
-
+
{data?.map((item) => (
= (props) => {
const [photo, setPhoto] = useState();
+ const [loading, setLoading] = useState(false);
const { item } = props;
console.log(item, 'item');
const onPhoto = useCallback(async () => {
- const res = await getfastPhoto({
- itemId: item?.id as number,
- spuId: item?.spuId as number,
- });
- setPhoto(res);
+ try {
+ setLoading(true);
+ const res = await getfastPhoto({
+ itemId: item?.id as number,
+ spuId: item?.spuId as number,
+ });
+ setPhoto(res);
+ } finally {
+ setLoading(false);
+ }
}, [item]);
useEffect(() => {
if (props.open && item) {
@@ -198,53 +204,55 @@ const PhotoModal: React.FC<{
footer={null}
width={820}
>
-
-
+
-
{photo?.spuName}
-
- {photo?.brief}
-
-
-
- 当前页面为订单快照,包含订单创建时的商品描述和下单信息,买卖双方和平台在发生交易争议时,将作为判断依据。
-
+
-
已选:{photo?.skuName}(已选的SKU规格)
+
{photo?.spuName}
+
+ {photo?.brief}
+
+
+
+ 当前页面为订单快照,包含订单创建时的商品描述和下单信息,买卖双方和平台在发生交易争议时,将作为判断依据。
+
+
+
+ 已选:{photo?.skuName}(已选的SKU规格)
+
+
-
-
+
);
};
diff --git a/src/pages/trade/order/detail/src/info/service-info.tsx b/src/pages/trade/order/detail/src/info/service-info.tsx
index 1a7fcef..b684e6f 100644
--- a/src/pages/trade/order/detail/src/info/service-info.tsx
+++ b/src/pages/trade/order/detail/src/info/service-info.tsx
@@ -5,7 +5,13 @@ import ServicePetUI from './uis/pets/service';
const ServiceInfo: React.FC
> = (props) => {
const { data = {}, orderCategoryId, id } = props;
- return <>{orderCategoryId === 1 && }>; //宠物服务ui
+ return (
+ <>
+ {orderCategoryId === 1 && (
+
+ )}
+ >
+ );
};
export default React.memo(ServiceInfo);
diff --git a/src/pages/trade/order/detail/src/sales-refunds.tsx b/src/pages/trade/order/detail/src/sales-refunds.tsx
new file mode 100644
index 0000000..ea56e87
--- /dev/null
+++ b/src/pages/trade/order/detail/src/sales-refunds.tsx
@@ -0,0 +1,22 @@
+import { Button, Space, Typography } from 'antd';
+import React from 'react';
+import { SalesTable } from '@/pages/trade/sales/list';
+import CreateSalesModal from '../../components/createSalesModal';
+
+const { Title } = Typography;
+const SalesRefunds: React.FC<{ orderStatus?: number }> = (props) => {
+ return (
+ <>
+
+
+ 售后与退款
+
+
+
+
+
+ >
+ );
+};
+
+export default React.memo(SalesRefunds);
diff --git a/src/pages/trade/order/list.tsx b/src/pages/trade/order/list.tsx
index 33bb684..0e287e0 100644
--- a/src/pages/trade/order/list.tsx
+++ b/src/pages/trade/order/list.tsx
@@ -67,7 +67,7 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
) => {
const data = await getTradeOrderPage({
...params,
- orderStatus,
+ orderStatus: props?.orderStatus ? props?.orderStatus : undefined,
pageNo: params.current,
pageSize: params.pageSize,
});
diff --git a/src/pages/trade/sales/config.tsx b/src/pages/trade/sales/config.tsx
new file mode 100644
index 0000000..dddcc9a
--- /dev/null
+++ b/src/pages/trade/sales/config.tsx
@@ -0,0 +1,157 @@
+import type { ProColumns } from '@ant-design/pro-components';
+import { Badge, Button, Image, Space, Tag, Typography } from 'antd';
+import type { TradeOrderPageRespVO } from '@/services/trade/order';
+
+const { Text, Paragraph } = Typography;
+export const baseOrderColumns: ProColumns[] = [
+ {
+ title: '售后服务单',
+ dataIndex: 'items',
+ hideInSearch: true,
+ ellipsis: true,
+ render: (_, record) => (
+
+
+
+ 售后类型:
+ {record?.payPrice || '-'}
+
+
+ 退款类型:
+ {record.payType || '-'}
+
+
+ 申请原因:
+ {record.financeStatus || '-'}
+
+
+ 申请人员:
+ {record.financeStatus || '-'}
+
+
+ ),
+ },
+
+ {
+ title: '售后商品',
+ dataIndex: 'serveAddress',
+ hideInSearch: true,
+ ellipsis: true,
+ render: (_, record) => {
+ if (!record.items) {
+ return _;
+ }
+ return record.items.map((item, index) => (
+ <>
+
+
+
+
+ {item.spuName}测试商品名称测试商品名称测试商品名称测试商品名称
+
+
{item.skuName}
+
+ 申请数量:
+ {item.count || 0}
+
+
+
+
+ >
+ ));
+ },
+ },
+ {
+ title: '申请信息',
+ dataIndex: 'price',
+ hideInSearch: true,
+ render: (_, record) => (
+
+
+ 售后类型:
+ {record?.payPrice || '-'}
+
+
+ 退款类型:
+ {record.payType || '-'}
+
+
+ 申请原因:
+ {record.financeStatus || '-'}
+
+
+ 申请人员:
+ {record.financeStatus || '-'}
+
+
+ ),
+ },
+ {
+ title: '退款信息',
+ dataIndex: 'merchantName',
+ hideInSearch: true,
+ render: (_, record) => (
+
+
+
+ 退款金额:
+ {record?.payPrice || '-'}
+
+
+ 退款方式:
+ {record.payType || '-'}
+
+
+
+
+
+
+
+
+ ),
+ },
+ {
+ title: '申请人姓名、手机或ID',
+ dataIndex: 'userSearch',
+ hideInTable: true,
+ },
+ {
+ title: '售后类型',
+ dataIndex: 'orderCategoryId',
+ valueType: 'select',
+ hideInTable: true,
+ },
+ {
+ title: '退款类型',
+ dataIndex: 'orderTerminal',
+ valueType: 'select',
+ hideInTable: true,
+ },
+ {
+ title: '退款状态',
+ dataIndex: 'financeStatus',
+ valueType: 'select',
+ hideInTable: true,
+ },
+ {
+ title: '申请时间',
+ dataIndex: 'createTime',
+ valueType: 'dateRange',
+ hideInTable: true,
+ },
+ {
+ title: '退款时间',
+ dataIndex: 'subTime',
+ valueType: 'dateRange',
+ hideInTable: true,
+ },
+];
diff --git a/src/pages/trade/sales/index.module.less b/src/pages/trade/sales/index.module.less
new file mode 100644
index 0000000..5e6312b
--- /dev/null
+++ b/src/pages/trade/sales/index.module.less
@@ -0,0 +1,13 @@
+.sales {
+ :global {
+ .ant-pro-card-header {
+ background: rgba(0, 0, 0, 0.02);
+ min-height: 41px;
+ }
+ .ant-pro-card-col {
+ flex: 1 auto;
+ overflow: auto;
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/src/pages/trade/sales/index.tsx b/src/pages/trade/sales/index.tsx
index c591323..c2a98fe 100644
--- a/src/pages/trade/sales/index.tsx
+++ b/src/pages/trade/sales/index.tsx
@@ -1,6 +1,24 @@
// 售后列表
-const TradeSales: React.FC = () => {
- return 111;
+import type { TabsProps } from 'antd';
+import { Tabs } from 'antd';
+import React from 'react';
+import { SalesStatusLableMap } from '@/constants/trade';
+import SalesListItem from './list';
+
+const TradeSalesList: React.FC = () => {
+ const items: TabsProps['items'] = Object.entries(SalesStatusLableMap).map(
+ ([value, label]) => ({
+ key: value,
+ label: label,
+ children: ,
+ }),
+ );
+
+ return (
+
+
+
+ );
};
-export default TradeSales;
+export default TradeSalesList;
diff --git a/src/pages/trade/sales/list.tsx b/src/pages/trade/sales/list.tsx
new file mode 100644
index 0000000..a185659
--- /dev/null
+++ b/src/pages/trade/sales/list.tsx
@@ -0,0 +1,133 @@
+import { type ActionType, ProCard } from '@ant-design/pro-components';
+import { Button, Input, Space, Statistic } from 'antd';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+
+import EnhancedProTable from '@/components/EnhancedProTable';
+import { baseOrderColumns } from './config';
+
+const { Search } = Input;
+
+import { DownOutlined, UpOutlined, UserOutlined } from '@ant-design/icons';
+
+import {
+ getTradeOrderPage,
+ getTradeSummary,
+ type TradeOrderPageRespVO,
+ type TradeReq,
+ type TradeSummaryRespVO,
+} from '@/services/trade/order';
+
+const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
+ const { orderStatus } = props;
+ const tableRef = useRef(null);
+ const [isShowTotal, setIsShowTotal] = useState(false);
+ const [summary, setSummary] = useState();
+ const fetchSummary = async () => {
+ const res = await getTradeSummary();
+ setSummary(res);
+ };
+ useEffect(() => {
+ fetchSummary();
+ }, []);
+
+ const handleIsTotal = useCallback(() => {
+ setIsShowTotal(!isShowTotal);
+ }, [isShowTotal]);
+
+ const handleSearch = useCallback((value: string) => {
+ console.log('搜索', value);
+ tableRef.current?.reload();
+ }, []);
+ return (
+ <>
+
+
+ : }
+ onClick={handleIsTotal}
+ >
+ 统计
+
+
+ {isShowTotal && (
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ >
+ );
+};
+
+export const SalesTable = (props: {
+ orderStatus?: number;
+ search?: boolean;
+}) => {
+ const tableRef = useRef(null);
+ const onFetch = async (
+ params: TradeReq & {
+ pageSize: number;
+ current: number;
+ },
+ ) => {
+ const data = await getTradeOrderPage({
+ ...params,
+ orderStatus: props?.orderStatus ? props?.orderStatus : undefined,
+ pageNo: params.current,
+ pageSize: params.pageSize,
+ });
+
+ return {
+ data: data.list,
+ success: true,
+ total: data.total,
+ };
+ };
+
+ const tabelConfig = {};
+
+ return (
+
+ ref={tableRef}
+ columns={baseOrderColumns}
+ request={onFetch}
+ headerTitle="售后列表"
+ showIndex={false}
+ showSelection={false}
+ search={props.search ? { defaultCollapsed: true } : false}
+ />
+ );
+};
+
+export default React.memo(OrderListItem);
diff --git a/src/requestErrorConfig.ts b/src/requestErrorConfig.ts
index 42abe9e..d46a46a 100644
--- a/src/requestErrorConfig.ts
+++ b/src/requestErrorConfig.ts
@@ -100,9 +100,6 @@ export const errorConfig: RequestConfig = {
},
// 错误接收及处理
errorHandler: async (error: any, opts: any) => {
- if (opts?.skipErrorHandler) throw error;
- // 我们的 errorThrower 抛出的错误。
- console.log('errorHandler', error);
const errorInfo: ResponseStructure | undefined = error.info;
if (error.name === 'BizError') {
if (errorInfo) {
@@ -118,6 +115,7 @@ export const errorConfig: RequestConfig = {
} else {
message.error(`发送请求时出了点问题:${error.msg}`);
}
+ throw error;
},
},
diff --git a/src/utils/menuUtils.tsx b/src/utils/menuUtils.tsx
index a1745ee..c578e2d 100644
--- a/src/utils/menuUtils.tsx
+++ b/src/utils/menuUtils.tsx
@@ -116,3 +116,56 @@ export const loopMenuItem = (menus: MenuVO[], pId: number | string): any[] => {
// return currentPath;
// }
// }
+// src/utils/route.ts
+import { lazy } from 'react';
+
+export interface RouteItem {
+ id: number;
+ parentId: number;
+ name: string;
+ path: string;
+ component: string | null;
+ componentName: string;
+ icon: string;
+ visible: boolean;
+ keepAlive: boolean;
+ alwaysShow: boolean;
+ children: RouteItem[] | null;
+}
+
+/**
+ * 转换后端路由数据为 Ant Design Pro 路由格式
+ */
+export function transformRoutes(routes: MenuVO[]): any[] {
+ return routes
+ .filter((route) => route.visible) // 只显示可见路由
+ .map((route) => {
+ const routeConfig: any = {
+ path: route.path,
+ name: route.name,
+ icon: route.icon || undefined,
+ };
+
+ // 处理组件
+ if (route.component) {
+ routeConfig.component = lazy(
+ () =>
+ import(`@/pages/${route.component}`).catch(
+ () => import('@/pages/404'),
+ ), // 组件不存在时显示404
+ );
+ }
+
+ // 处理子路由
+ if (route.children && route.children.length > 0) {
+ routeConfig.routes = transformRoutes(route.children);
+ }
+
+ // 隐藏菜单但保留路由
+ if (!route.visible) {
+ routeConfig.hideInMenu = true;
+ }
+
+ return routeConfig;
+ });
+}