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 + + } + /> + +
+ + + 预估退款项 + <Text type="secondary">(退款方式:原支付方式返还)</Text> + + + + + 微信支付 + (建设银行 尾号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 + + } + /> + +
+ + + 预估退款项 + <Text type="secondary">(退款方式:原支付方式返还)</Text> + + + + + 微信支付 + (建设银行 尾号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 ( + <> + + + + + {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; + }); +}