feat: 取消订单
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
"browser-id3-writer": "^6.3.1",
|
"browser-id3-writer": "^6.3.1",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"dompurify": "^3.3.0",
|
||||||
"jsencrypt": "^3.5.4",
|
"jsencrypt": "^3.5.4",
|
||||||
"rc-resize-observer": "^1.4.3",
|
"rc-resize-observer": "^1.4.3",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
|||||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -44,6 +44,9 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.18
|
version: 1.11.18
|
||||||
|
dompurify:
|
||||||
|
specifier: ^3.3.0
|
||||||
|
version: 3.3.0
|
||||||
jsencrypt:
|
jsencrypt:
|
||||||
specifier: ^3.5.4
|
specifier: ^3.5.4
|
||||||
version: 3.5.4
|
version: 3.5.4
|
||||||
@@ -2818,6 +2821,9 @@ packages:
|
|||||||
'@types/tough-cookie@4.0.5':
|
'@types/tough-cookie@4.0.5':
|
||||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||||
|
|
||||||
|
'@types/trusted-types@2.0.7':
|
||||||
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
'@types/unist@2.0.11':
|
'@types/unist@2.0.11':
|
||||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||||
|
|
||||||
@@ -4807,6 +4813,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
|
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
dompurify@3.3.0:
|
||||||
|
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
|
||||||
|
|
||||||
domutils@1.7.0:
|
domutils@1.7.0:
|
||||||
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
|
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
|
||||||
|
|
||||||
@@ -15041,6 +15050,9 @@ snapshots:
|
|||||||
|
|
||||||
'@types/tough-cookie@4.0.5': {}
|
'@types/tough-cookie@4.0.5': {}
|
||||||
|
|
||||||
|
'@types/trusted-types@2.0.7':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/unist@2.0.11': {}
|
'@types/unist@2.0.11': {}
|
||||||
|
|
||||||
'@types/use-sync-external-store@0.0.3': {}
|
'@types/use-sync-external-store@0.0.3': {}
|
||||||
@@ -17939,6 +17951,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
|
dompurify@3.3.0:
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
domutils@1.7.0:
|
domutils@1.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
dom-serializer: 0.2.2
|
dom-serializer: 0.2.2
|
||||||
|
|||||||
73
src/components/AdvancedImageGallery/index.module.less
Normal file
73
src/components/AdvancedImageGallery/index.module.less
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* index.less */
|
||||||
|
|
||||||
|
.productImages {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.mainImage {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.largeImage {
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailList {
|
||||||
|
.thumbnail {
|
||||||
|
padding: 2px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.advancedGallery {
|
||||||
|
.mainImageContainer {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.scrollBtn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailWrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailItem {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 2px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/components/AdvancedImageGallery/index.tsx
Normal file
71
src/components/AdvancedImageGallery/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Image } from 'antd';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import styles from './index.module.less';
|
||||||
|
|
||||||
|
const AdvancedImageGallery: React.FC<{ images: string[] }> = ({ images }) => {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const thumbnailRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// 缩略图滚动
|
||||||
|
const scrollThumbnails = (direction: 'left' | 'right') => {
|
||||||
|
if (thumbnailRef.current) {
|
||||||
|
const scrollAmount = direction === 'left' ? -100 : 100;
|
||||||
|
thumbnailRef.current.scrollLeft += scrollAmount;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.advancedGallery}>
|
||||||
|
{/* 大图区域 */}
|
||||||
|
<div className={styles.mainImageContainer}>
|
||||||
|
<Image
|
||||||
|
src={images[currentIndex]}
|
||||||
|
alt="主图"
|
||||||
|
width={'100%'}
|
||||||
|
height={376}
|
||||||
|
style={{ objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 缩略图区域 */}
|
||||||
|
<div className={styles.thumbnailContainer}>
|
||||||
|
<Button
|
||||||
|
icon={<LeftOutlined />}
|
||||||
|
size="small"
|
||||||
|
onClick={() => scrollThumbnails('left')}
|
||||||
|
className={styles.scrollBtn}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div ref={thumbnailRef} className={styles.thumbnailWrapper}>
|
||||||
|
{images.map((img, index) => (
|
||||||
|
<div
|
||||||
|
key={`${index}${Math.random()}`}
|
||||||
|
className={`${styles.thumbnailItem} ${
|
||||||
|
index === currentIndex ? styles.active : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => setCurrentIndex(index)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={img}
|
||||||
|
width={80}
|
||||||
|
height={80}
|
||||||
|
preview={false}
|
||||||
|
alt={`缩略图-${index}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
icon={<RightOutlined />}
|
||||||
|
size="small"
|
||||||
|
onClick={() => scrollThumbnails('right')}
|
||||||
|
className={styles.scrollBtn}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(AdvancedImageGallery);
|
||||||
7
src/components/DangerouslySetInnerHTML/index.module.less
Normal file
7
src/components/DangerouslySetInnerHTML/index.module.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.dangerouslySetInnerHTML {
|
||||||
|
:global {
|
||||||
|
p {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/components/DangerouslySetInnerHTML/index.tsx
Normal file
16
src/components/DangerouslySetInnerHTML/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './index.module.less';
|
||||||
|
|
||||||
|
const DangerouslySetInnerHTML = (props: { content?: string }) => {
|
||||||
|
const { content = '' } = props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.dangerouslySetInnerHTML}
|
||||||
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: 已使用 DOMPurify 进行 HTML 净化
|
||||||
|
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(DangerouslySetInnerHTML);
|
||||||
@@ -22,6 +22,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
toolbarActions,
|
toolbarActions,
|
||||||
rowKey = 'id',
|
rowKey = 'id',
|
||||||
pagination = true,
|
pagination = true,
|
||||||
|
scroll,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
manualRequest={false}
|
manualRequest={false}
|
||||||
showSorterTooltip
|
showSorterTooltip
|
||||||
// scroll={{ x: "max-content" }}
|
// scroll={{ x: "max-content" }}
|
||||||
scroll={{ x: 1200 }}
|
scroll={scroll ? scroll : { x: 1200 }}
|
||||||
components={components}
|
components={components}
|
||||||
search={
|
search={
|
||||||
search
|
search
|
||||||
|
|||||||
@@ -35,7 +35,15 @@ const UploadImages: React.FC<{
|
|||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value) {
|
if (value) {
|
||||||
setFileList([{ uid: '-1', url: value, status: 'done', name: value }]);
|
console.log(value.split(','));
|
||||||
|
const list = value.split(',').map((item, index) => ({
|
||||||
|
uid: index.toString(),
|
||||||
|
url: item,
|
||||||
|
thumbUrl: item,
|
||||||
|
status: 'done',
|
||||||
|
name: item,
|
||||||
|
}));
|
||||||
|
setFileList(list as UploadFile[]);
|
||||||
} else {
|
} else {
|
||||||
setFileList([]);
|
setFileList([]);
|
||||||
}
|
}
|
||||||
@@ -63,7 +71,7 @@ const UploadImages: React.FC<{
|
|||||||
const handleRemove = (file: UploadFile): boolean => {
|
const handleRemove = (file: UploadFile): boolean => {
|
||||||
const newFileList = fileList.filter((item) => item.uid !== file.uid);
|
const newFileList = fileList.filter((item) => item.uid !== file.uid);
|
||||||
const newUrl = newFileList.map((item) => item.url) as string[];
|
const newUrl = newFileList.map((item) => item.url) as string[];
|
||||||
onChange?.(newUrl[0]);
|
onChange?.(newUrl.join(','));
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,8 +109,15 @@ const UploadImages: React.FC<{
|
|||||||
const url = await uploadFile(file as File);
|
const url = await uploadFile(file as File);
|
||||||
onProgress?.({ percent: 100 });
|
onProgress?.({ percent: 100 });
|
||||||
if (url) {
|
if (url) {
|
||||||
onChange?.(url);
|
if (maxCount === 1) {
|
||||||
onSuccess?.({ url });
|
onChange?.(url);
|
||||||
|
onSuccess?.({ url });
|
||||||
|
} else {
|
||||||
|
const res = value?.split(',') || [];
|
||||||
|
console.log([...res, url].join(','));
|
||||||
|
onChange?.([...res, url].join(','));
|
||||||
|
}
|
||||||
|
|
||||||
message.success('上传成功');
|
message.success('上传成功');
|
||||||
} else {
|
} else {
|
||||||
throw new Error('上传失败');
|
throw new Error('上传失败');
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const ProdInfo = <T extends Record<string, any>>(props: ProdInfoProps<T>) => {
|
|||||||
extra="仅支持.jpg .png 格式,建议图片比例1:1,限7张"
|
extra="仅支持.jpg .png 格式,建议图片比例1:1,限7张"
|
||||||
// getValueFromEvent={(e) => e.fileList}
|
// getValueFromEvent={(e) => e.fileList}
|
||||||
>
|
>
|
||||||
<UploadImages />
|
<UploadImages maxCount={7} />
|
||||||
</ProForm.Item>
|
</ProForm.Item>
|
||||||
<ProForm.Item
|
<ProForm.Item
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
|
|||||||
83
src/pages/trade/order/components/cancleOrderModal.tsx
Normal file
83
src/pages/trade/order/components/cancleOrderModal.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
BetaSchemaForm,
|
||||||
|
type ProFormColumnsType,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { type FormInstance, Modal, message } from 'antd';
|
||||||
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
|
import { cancelOrder } from '@/services/trade/order';
|
||||||
|
|
||||||
|
const CancleOrderModal: React.FC<{
|
||||||
|
open?: boolean;
|
||||||
|
id?: number;
|
||||||
|
onClose?: () => void;
|
||||||
|
}> = (props) => {
|
||||||
|
const formRef = useRef<FormInstance>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const columns: ProFormColumnsType[] = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'cancelReason',
|
||||||
|
valueType: 'radio',
|
||||||
|
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: 'cancelRemark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleOk = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const values = formRef.current?.getFieldsValue();
|
||||||
|
console.log(values, props.id, 'ok');
|
||||||
|
await cancelOrder({ id: props.id, ...values });
|
||||||
|
message.success('取消成功');
|
||||||
|
props.onClose?.();
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [open, props.id]);
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="取消订单"
|
||||||
|
open={props.open}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={props?.onClose}
|
||||||
|
confirmLoading={loading}
|
||||||
|
>
|
||||||
|
<span>是否确认取消订单?</span>
|
||||||
|
<BetaSchemaForm
|
||||||
|
layoutType="Form"
|
||||||
|
formRef={formRef}
|
||||||
|
columns={columns || []}
|
||||||
|
layout="horizontal"
|
||||||
|
submitter={false}
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(CancleOrderModal);
|
||||||
@@ -14,10 +14,10 @@ export const baseOrderColumns: ProColumns<TradeOrderPageRespVO>[] = [
|
|||||||
if (!record.items) {
|
if (!record.items) {
|
||||||
return _;
|
return _;
|
||||||
}
|
}
|
||||||
return record.items.map((item) => (
|
return record.items.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
style={{ width: '100%', display: 'flex', gap: '8px' }}
|
style={{ width: '100%', display: 'flex', gap: '8px' }}
|
||||||
key={item.id}
|
key={`${index}${Math.random()}`}
|
||||||
>
|
>
|
||||||
<Image src={item.picUrl} width={64} height={64} />
|
<Image src={item.picUrl} width={64} height={64} />
|
||||||
<div style={{ flex: '1', overflow: 'hidden' }}>
|
<div style={{ flex: '1', overflow: 'hidden' }}>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const DetailCom: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
|
|||||||
children: '商品配送',
|
children: '商品配送',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return <Tabs defaultActiveKey="1" items={items} />;
|
return <Tabs defaultActiveKey="1" items={items} destroyOnHidden />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(DetailCom);
|
export default React.memo(DetailCom);
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ import {
|
|||||||
type TradeOrderDetailRespVO,
|
type TradeOrderDetailRespVO,
|
||||||
type TradeOrderPageRespVO,
|
type TradeOrderPageRespVO,
|
||||||
} from '@/services/trade/order';
|
} from '@/services/trade/order';
|
||||||
import BasicInfo from './component/info/basic-info'; //基本信息(通版)
|
import BasicInfo from './src/info/basic-info'; //基本信息(通版)
|
||||||
import ExtendCostInfo from './component/info/extend-cost'; //服务附加费(殡葬专属字段)
|
import ExtendCostInfo from './src/info/extend-cost'; //服务附加费(殡葬专属字段)
|
||||||
import ExtendService from './component/info/extend-service'; //可选服务(殡葬专属字段)
|
import ExtendService from './src/info/extend-service'; //可选服务(殡葬专属字段)
|
||||||
import ProdInfo from './component/info/prod-info'; //商品信息(通版)
|
import ProdInfo from './src/info/prod-info'; //商品信息(通版)
|
||||||
import ServiceInfo from './component/info/service-info';
|
import ServiceInfo from './src/info/service-info';
|
||||||
|
|
||||||
export interface ItemConfig<T> {
|
export interface ItemConfig<T> {
|
||||||
data?: T;
|
data?: T;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
orderCategoryId?: number;
|
orderCategoryId?: number;
|
||||||
|
id?: number;
|
||||||
}
|
}
|
||||||
const OrderDetail: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
|
const OrderDetail: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
@@ -43,6 +44,7 @@ const OrderDetail: React.FC<{ data?: TradeOrderPageRespVO }> = (props) => {
|
|||||||
<ServiceInfo
|
<ServiceInfo
|
||||||
data={detais.tradeServeInfo}
|
data={detais.tradeServeInfo}
|
||||||
orderCategoryId={detais?.orderCategoryId}
|
orderCategoryId={detais?.orderCategoryId}
|
||||||
|
id={detais?.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{detais?.tradeExtendServeInfo && (
|
{detais?.tradeExtendServeInfo && (
|
||||||
|
|||||||
@@ -73,10 +73,13 @@ export const renderBaseInfoOrder = (
|
|||||||
>
|
>
|
||||||
{data?.orderStatus && orderStatusObj.label}
|
{data?.orderStatus && orderStatusObj.label}
|
||||||
</Title>
|
</Title>
|
||||||
<span>
|
{data.orderStatus === 10 && (
|
||||||
<Text type="secondary">剩余</Text>
|
<span>
|
||||||
<Text>4分30秒</Text>
|
<Text type="secondary">剩余</Text>
|
||||||
</span>
|
<Text>4分30秒</Text>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
{data.orderStatus &&
|
{data.orderStatus &&
|
||||||
Number(data.orderStatus) === OrderStatus.Refunded && (
|
Number(data.orderStatus) === OrderStatus.Refunded && (
|
||||||
<Paragraph>
|
<Paragraph>
|
||||||
@@ -121,18 +124,18 @@ export const renderBaseInfoOrder = (
|
|||||||
{data.orderStatus &&
|
{data.orderStatus &&
|
||||||
Number(data.orderStatus) === OrderStatus.Cancelled && (
|
Number(data.orderStatus) === OrderStatus.Cancelled && (
|
||||||
<>
|
<>
|
||||||
<Paragraph>
|
<div>
|
||||||
<Text type="secondary">取消时间:</Text>
|
<Text type="secondary">取消时间:</Text>
|
||||||
<Text>取消时间</Text>
|
<Text>{data.cancelTime}</Text>
|
||||||
</Paragraph>
|
</div>
|
||||||
<Paragraph>
|
<div>
|
||||||
<Text type="secondary">取消原因:</Text>
|
<Text type="secondary">取消原因:</Text>
|
||||||
<Text>{data.cancelReason}</Text>
|
<Text>{data.cancelReason}</Text>
|
||||||
</Paragraph>
|
</div>
|
||||||
<Paragraph>
|
<div>
|
||||||
<Text type="secondary">备注:</Text>
|
<Text type="secondary">备注:</Text>
|
||||||
<Text>{data?.merchantRemark}</Text>
|
<Text>{data?.cancelRemark}</Text>
|
||||||
</Paragraph>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{data.orderStatus &&
|
{data.orderStatus &&
|
||||||
@@ -1,14 +1,22 @@
|
|||||||
import { ProCard } from '@ant-design/pro-components';
|
import { ProCard } from '@ant-design/pro-components';
|
||||||
import { Button, Card, Image, Space, Tag, Typography } from 'antd';
|
import { Button, Card, Image, Modal, Space, Tag, Typography } from 'antd';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import AdvancedImageGallery from '@/components/AdvancedImageGallery';
|
||||||
|
import DangerouslySetInnerHTML from '@/components/DangerouslySetInnerHTML';
|
||||||
import { fallback } from '@/constants/antd/image';
|
import { fallback } from '@/constants/antd/image';
|
||||||
import type { Item } from '@/services/trade/order/detail';
|
import { getfastPhoto } from '@/services/trade/order';
|
||||||
|
import type {
|
||||||
|
Item,
|
||||||
|
TradeOrderFastPhotoRespVo,
|
||||||
|
} from '@/services/trade/order/detail';
|
||||||
import type { ItemConfig } from '../../order-info';
|
import type { ItemConfig } from '../../order-info';
|
||||||
import styles from './index.module.less';
|
import styles from './index.module.less';
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph, Title } = Typography;
|
||||||
const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
||||||
const { data = [] } = props;
|
const { data = [] } = props;
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [item, setItem] = useState<Item>();
|
||||||
const renderTitle = useCallback((item: Item) => {
|
const renderTitle = useCallback((item: Item) => {
|
||||||
return (
|
return (
|
||||||
<Space style={{ height: '100%' }} size={16}>
|
<Space style={{ height: '100%' }} size={16}>
|
||||||
@@ -24,6 +32,14 @@ const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
|||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
const onPhoto = useCallback(
|
||||||
|
async (item: Item) => {
|
||||||
|
setVisible(true);
|
||||||
|
setItem(item);
|
||||||
|
},
|
||||||
|
[item, visible],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles['order-info']}>
|
<div className={styles['order-info']}>
|
||||||
<Card title="商品信息">
|
<Card title="商品信息">
|
||||||
@@ -35,7 +51,11 @@ const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
|||||||
bordered
|
bordered
|
||||||
headerBordered
|
headerBordered
|
||||||
gutter={8}
|
gutter={8}
|
||||||
extra={<Button size="small">交易快照</Button>}
|
extra={
|
||||||
|
<Button size="small" onClick={() => onPhoto(item)}>
|
||||||
|
交易快照
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<ProCard
|
<ProCard
|
||||||
layout="default"
|
layout="default"
|
||||||
@@ -74,14 +94,12 @@ const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
|||||||
>
|
>
|
||||||
<Text type="secondary">到手价:</Text>
|
<Text type="secondary">到手价:</Text>
|
||||||
<Text>
|
<Text>
|
||||||
{' '}
|
|
||||||
¥{item.handedPrice} {item.unit}
|
¥{item.handedPrice} {item.unit}
|
||||||
</Text>
|
</Text>
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<Paragraph className="order-paragraph">
|
<Paragraph className="order-paragraph">
|
||||||
<Text type="secondary">成本价:</Text>
|
<Text type="secondary">成本价:</Text>
|
||||||
<Text>
|
<Text>
|
||||||
{' '}
|
|
||||||
¥{item.expensePrice} {item.unit}/-
|
¥{item.expensePrice} {item.unit}/-
|
||||||
</Text>
|
</Text>
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
@@ -141,8 +159,94 @@ const ProdInfo: React.FC<ItemConfig<Item[]>> = (props) => {
|
|||||||
</ProCard>
|
</ProCard>
|
||||||
))}
|
))}
|
||||||
</Card>
|
</Card>
|
||||||
|
<PhotoModal
|
||||||
|
open={visible}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 交易快照
|
||||||
|
const PhotoModal: React.FC<{
|
||||||
|
open: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
item?: Item;
|
||||||
|
}> = (props) => {
|
||||||
|
const [photo, setPhoto] = useState<TradeOrderFastPhotoRespVo>();
|
||||||
|
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);
|
||||||
|
}, [item]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.open && item) {
|
||||||
|
onPhoto();
|
||||||
|
}
|
||||||
|
}, [props.open, item]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="交易快照"
|
||||||
|
open={props.open}
|
||||||
|
onCancel={props.onCancel}
|
||||||
|
footer={null}
|
||||||
|
width={820}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '480px',
|
||||||
|
width: '100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: '16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="left" style={{ width: '386px', height: '100%' }}>
|
||||||
|
<AdvancedImageGallery images={photo?.imgs?.split(',') || []} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="right"
|
||||||
|
style={{ height: '100%', overflow: 'auto', width: '386px' }}
|
||||||
|
>
|
||||||
|
<Title level={4}>{photo?.spuName}</Title>
|
||||||
|
<Paragraph>
|
||||||
|
<Text type="secondary">{photo?.brief}</Text>
|
||||||
|
</Paragraph>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginBottom: '16px',
|
||||||
|
background: '#00000005',
|
||||||
|
padding: '8px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
fontSize: '12px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
当前页面为订单快照,包含订单创建时的商品描述和下单信息,买卖双方和平台在发生交易争议时,将作为判断依据。
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginBottom: '16px',
|
||||||
|
background: '#00000005',
|
||||||
|
padding: '8px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<strong>已选:{photo?.skuName}(已选的SKU规格)</strong>
|
||||||
|
</div>
|
||||||
|
<DangerouslySetInnerHTML content={photo?.content} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default React.memo(ProdInfo);
|
export default React.memo(ProdInfo);
|
||||||
@@ -4,8 +4,8 @@ import type { ItemConfig } from '../../order-info';
|
|||||||
import ServicePetUI from './uis/pets/service';
|
import ServicePetUI from './uis/pets/service';
|
||||||
|
|
||||||
const ServiceInfo: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
const ServiceInfo: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
||||||
const { data = {}, orderCategoryId } = props;
|
const { data = {}, orderCategoryId, id } = props;
|
||||||
return <>{orderCategoryId === 1 && <ServicePetUI data={data} />}</>; //宠物服务ui
|
return <>{orderCategoryId === 1 && <ServicePetUI data={data} id={id} />}</>; //宠物服务ui
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(ServiceInfo);
|
export default React.memo(ServiceInfo);
|
||||||
@@ -77,15 +77,23 @@ export const baseOrderColumns: ProColumns<TradeExtendServeInfo>[] = [
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div>{serve.serveName}</div>
|
<div>{serve.serveName}</div>
|
||||||
<Text type="secondary">{serve.serveDesc}</Text>
|
|
||||||
<div>
|
<div>
|
||||||
<Text type="secondary">数量:</Text>
|
<Text type="secondary">{serve.serveDesc}</Text>
|
||||||
<Text>{serve.count}</Text>
|
|
||||||
<Text type="secondary">单价:</Text>
|
|
||||||
<Text>{serve.price}</Text>
|
|
||||||
<Text type="secondary">到手价:</Text>
|
|
||||||
<Text>{serve.handPrice}</Text>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Space>
|
||||||
|
<div>
|
||||||
|
<Text type="secondary">数量:</Text>
|
||||||
|
<Text>{serve.count}</Text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text type="secondary">单价:</Text>
|
||||||
|
<Text>{serve.price}</Text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text type="secondary">到手价:</Text>
|
||||||
|
<Text>{serve.handPrice}</Text>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
))}
|
))}
|
||||||
@@ -1,16 +1,22 @@
|
|||||||
import { ClockCircleOutlined, EnvironmentOutlined } from '@ant-design/icons';
|
import { ClockCircleOutlined, EnvironmentOutlined } from '@ant-design/icons';
|
||||||
import { ProCard } from '@ant-design/pro-components';
|
import { ProCard, type ProColumns } from '@ant-design/pro-components';
|
||||||
import { Button, Card, Image, Space, Timeline, Typography } from 'antd';
|
import { Button, Card, Image, Modal, Space, Timeline, Typography } from 'antd';
|
||||||
import React from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import EnhancedProTable from '@/components/EnhancedProTable';
|
||||||
import { fallback } from '@/constants/antd/image';
|
import { fallback } from '@/constants/antd/image';
|
||||||
import type { TradeServeInfo } from '@/services/trade/order/detail';
|
import { getSubTimeLog } from '@/services/trade/order';
|
||||||
|
import type {
|
||||||
|
TradeOrderSubLogDO,
|
||||||
|
TradeServeInfo,
|
||||||
|
} from '@/services/trade/order/detail';
|
||||||
import type { ItemConfig } from '../../../../order-info';
|
import type { ItemConfig } from '../../../../order-info';
|
||||||
import styles from '../../index.module.less';
|
import styles from '../../index.module.less';
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph } = Typography;
|
||||||
const ServicePetUI: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
const ServicePetUI: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
||||||
const { data = {} } = props;
|
const { data = {}, id } = props;
|
||||||
const { boneInfo, subInfo } = data;
|
const { boneInfo, subInfo } = data;
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
return (
|
return (
|
||||||
<div className={styles['order-info']}>
|
<div className={styles['order-info']}>
|
||||||
<Card title="服务信息">
|
<Card title="服务信息">
|
||||||
@@ -54,7 +60,11 @@ const ServicePetUI: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
|||||||
size="small"
|
size="small"
|
||||||
title="预约信息"
|
title="预约信息"
|
||||||
headerBordered
|
headerBordered
|
||||||
extra={<Button size="small">修改记录</Button>}
|
extra={
|
||||||
|
<Button size="small" onClick={() => setOpen(true)}>
|
||||||
|
修改记录
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Space size={100}>
|
<Space size={100}>
|
||||||
<div>
|
<div>
|
||||||
@@ -99,8 +109,60 @@ const ServicePetUI: React.FC<ItemConfig<TradeServeInfo>> = (props) => {
|
|||||||
</ProCard>
|
</ProCard>
|
||||||
</ProCard>
|
</ProCard>
|
||||||
</Card>
|
</Card>
|
||||||
|
<SubTimeLogModal id={id} open={open} onCancel={() => setOpen(false)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
//修改记录
|
||||||
|
const SubTimeLogModal: React.FC<{
|
||||||
|
id?: number;
|
||||||
|
open?: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
}> = (props) => {
|
||||||
|
const { id, open = false } = props;
|
||||||
|
const [log, setLog] = useState<TradeOrderSubLogDO[]>([]);
|
||||||
|
const fetchSubTimeLog = useCallback(
|
||||||
|
async (id: number) => {
|
||||||
|
const res = await getSubTimeLog(id);
|
||||||
|
console.log(res);
|
||||||
|
setLog(res);
|
||||||
|
},
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
const columns: ProColumns<TradeOrderSubLogDO>[] = [
|
||||||
|
{
|
||||||
|
title: '预约时间',
|
||||||
|
dataIndex: 'subTime',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '修改时间',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && id) fetchSubTimeLog(id);
|
||||||
|
}, [open, id]);
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
width={'60vw'}
|
||||||
|
open={open}
|
||||||
|
onCancel={props.onCancel}
|
||||||
|
title="修改记录"
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<EnhancedProTable<TradeOrderSubLogDO>
|
||||||
|
columns={columns}
|
||||||
|
dataSource={log}
|
||||||
|
showIndex={false}
|
||||||
|
search={false}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
|
showSelection={false}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default React.memo(ServicePetUI);
|
export default React.memo(ServicePetUI);
|
||||||
@@ -20,7 +20,7 @@ const OrderList: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles['trade-order']} "page-container" `}>
|
<div className={`${styles['trade-order']} "page-container" `}>
|
||||||
<Tabs defaultActiveKey="1" items={items} />
|
<Tabs defaultActiveKey="1" items={items} destroyOnHidden />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ import {
|
|||||||
message,
|
message,
|
||||||
Space,
|
Space,
|
||||||
Statistic,
|
Statistic,
|
||||||
Typography,
|
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import React, { useCallback, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import ConfigurableDrawerForm, {
|
import ConfigurableDrawerForm, {
|
||||||
type ConfigurableDrawerFormRef,
|
type ConfigurableDrawerFormRef,
|
||||||
} from '@/components/DrawerForm';
|
} from '@/components/DrawerForm';
|
||||||
@@ -29,14 +28,18 @@ import PopconfirmForm, {
|
|||||||
} from '@/components/PopconfirmForm';
|
} from '@/components/PopconfirmForm';
|
||||||
import {
|
import {
|
||||||
mapOrderStatusToBadgeStatus,
|
mapOrderStatusToBadgeStatus,
|
||||||
type OrderStatus,
|
OrderStatus,
|
||||||
OrderStatusLabels,
|
OrderStatusLabels,
|
||||||
} from '@/constants/trade';
|
} from '@/constants/trade';
|
||||||
import {
|
import {
|
||||||
getTradeOrderPage,
|
getTradeOrderPage,
|
||||||
|
getTradeSummary,
|
||||||
type TradeOrderPageRespVO,
|
type TradeOrderPageRespVO,
|
||||||
type TradeReq,
|
type TradeReq,
|
||||||
|
type TradeSummaryRespVO,
|
||||||
|
updateOrderRemark,
|
||||||
} from '@/services/trade/order';
|
} from '@/services/trade/order';
|
||||||
|
import CancleOrderModal from './components/cancleOrderModal';
|
||||||
import DetailCom from './detail';
|
import DetailCom from './detail';
|
||||||
|
|
||||||
const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
||||||
@@ -46,6 +49,16 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
|||||||
const [modalData, setModalData] = useState<TradeOrderPageRespVO>();
|
const [modalData, setModalData] = useState<TradeOrderPageRespVO>();
|
||||||
const [isShowTotal, setIsShowTotal] = useState<boolean>(false);
|
const [isShowTotal, setIsShowTotal] = useState<boolean>(false);
|
||||||
const popconfirmFormRef = useRef<PopconfirmFormRef>(null);
|
const popconfirmFormRef = useRef<PopconfirmFormRef>(null);
|
||||||
|
const [summary, setSummary] = useState<TradeSummaryRespVO>();
|
||||||
|
const [cancleOrderVisible, setCancleOrderVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const fetchSummary = async () => {
|
||||||
|
const res = await getTradeSummary();
|
||||||
|
setSummary(res);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
fetchSummary();
|
||||||
|
}, []);
|
||||||
const onFetch = async (
|
const onFetch = async (
|
||||||
params: TradeReq & {
|
params: TradeReq & {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
@@ -74,14 +87,24 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
|||||||
[modalData],
|
[modalData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOrder = useCallback((id?: number) => {
|
const handleOrder = useCallback(
|
||||||
console.log(id, '取消订单');
|
(type: string, record?: TradeOrderPageRespVO) => {
|
||||||
// await updateTradeOrder(values.id);
|
setModalData(record);
|
||||||
}, []);
|
if (type === 'order') {
|
||||||
|
setCancleOrderVisible(true);
|
||||||
const handleUpdate = async (_values: TradeOrderPageRespVO) => {
|
}
|
||||||
try {
|
if (type === 'order') {
|
||||||
|
setCancleOrderVisible(true);
|
||||||
|
}
|
||||||
// await updateTradeOrder(values.id);
|
// await updateTradeOrder(values.id);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdate = async (values: TradeOrderPageRespVO, id?: number) => {
|
||||||
|
try {
|
||||||
|
await updateOrderRemark({ remark: values.userRemark || '', id: id });
|
||||||
|
tableRef.current?.reload();
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
@@ -105,37 +128,56 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
|||||||
}}
|
}}
|
||||||
key={record.id}
|
key={record.id}
|
||||||
>
|
>
|
||||||
<a key="cancel" onClick={() => handleOrder(record.id)}>
|
{record.orderStatus !== OrderStatus.Cancelled && (
|
||||||
取消订单
|
<a key="cancel" onClick={() => handleOrder('order', record)}>
|
||||||
</a>
|
取消订单
|
||||||
<a key="order" onClick={() => handleOrder(record.id)}>
|
</a>
|
||||||
开始服务
|
)}
|
||||||
</a>
|
{record.orderStatus === OrderStatus.PendingService && (
|
||||||
<a key="sale" onClick={() => handleOrder(record.id)}>
|
<a key="order" onClick={() => handleOrder('service', record)}>
|
||||||
新建售后
|
开始服务
|
||||||
</a>
|
</a>
|
||||||
|
)}
|
||||||
|
{record.orderStatus === OrderStatus.PendingConfirmation && (
|
||||||
|
<a key="order" onClick={() => handleOrder('service', record)}>
|
||||||
|
接单确定
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{(record.orderStatus as number) > OrderStatus.PendingPayment &&
|
||||||
|
record.orderStatus !== OrderStatus.Cancelled && (
|
||||||
|
<a key="sale" onClick={() => handleOrder('sales', record)}>
|
||||||
|
新建售后
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{record.orderStatus === OrderStatus.PendingAcceptance && (
|
||||||
|
<a key="sale" onClick={() => handleOrder('sales', record)}>
|
||||||
|
服务上报
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
<a key="detail" onClick={() => handleDetail(record)}>
|
<a key="detail" onClick={() => handleDetail(record)}>
|
||||||
订单详情
|
订单详情
|
||||||
</a>
|
</a>
|
||||||
<PopconfirmForm
|
{record.orderStatus === OrderStatus.PendingPayment && (
|
||||||
ref={popconfirmFormRef}
|
<PopconfirmForm
|
||||||
columns={[
|
ref={popconfirmFormRef}
|
||||||
{
|
columns={[
|
||||||
title: '备注',
|
{
|
||||||
name: 'userRemark',
|
title: '备注',
|
||||||
valueType: 'textarea',
|
name: 'merchantRemark',
|
||||||
fieldProps: { rows: 4, autoFocus: false },
|
valueType: 'textarea',
|
||||||
},
|
fieldProps: { rows: 4, autoFocus: false },
|
||||||
]}
|
},
|
||||||
onSubmit={handleUpdate}
|
]}
|
||||||
>
|
onSubmit={(value) => handleUpdate(value, record.id)}
|
||||||
<a
|
|
||||||
key="remark"
|
|
||||||
onClick={() => popconfirmFormRef.current?.open(record)}
|
|
||||||
>
|
>
|
||||||
添加备注
|
<a
|
||||||
</a>
|
key="remark"
|
||||||
</PopconfirmForm>
|
onClick={() => popconfirmFormRef.current?.open(record)}
|
||||||
|
>
|
||||||
|
添加备注
|
||||||
|
</a>
|
||||||
|
</PopconfirmForm>
|
||||||
|
)}
|
||||||
</div>,
|
</div>,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -251,13 +293,25 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
|||||||
{isShowTotal && (
|
{isShowTotal && (
|
||||||
<ProCard.Group direction="row" style={{ marginBottom: 18 }}>
|
<ProCard.Group direction="row" style={{ marginBottom: 18 }}>
|
||||||
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
||||||
<Statistic title="订单数量" value={79.0} precision={2} />
|
<Statistic
|
||||||
|
title="订单数量"
|
||||||
|
value={summary?.orderCount}
|
||||||
|
precision={2}
|
||||||
|
/>
|
||||||
</ProCard>
|
</ProCard>
|
||||||
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
||||||
<Statistic title="实付金额" value={112893.0} precision={2} />
|
<Statistic
|
||||||
|
title="实付金额"
|
||||||
|
value={summary?.payPrice}
|
||||||
|
precision={2}
|
||||||
|
/>
|
||||||
</ProCard>
|
</ProCard>
|
||||||
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
|
||||||
<Statistic title="实收金额" value={93} suffix="/ 100" />
|
<Statistic
|
||||||
|
title="实收金额"
|
||||||
|
value={summary?.livePrice}
|
||||||
|
precision={2}
|
||||||
|
/>
|
||||||
</ProCard>
|
</ProCard>
|
||||||
</ProCard.Group>
|
</ProCard.Group>
|
||||||
)}
|
)}
|
||||||
@@ -286,8 +340,13 @@ const OrderListItem: React.FC<{ orderStatus: number }> = (props) => {
|
|||||||
>
|
>
|
||||||
<DetailCom data={modalData} />
|
<DetailCom data={modalData} />
|
||||||
</ConfigurableDrawerForm>
|
</ConfigurableDrawerForm>
|
||||||
|
<CancleOrderModal
|
||||||
|
open={cancleOrderVisible}
|
||||||
|
onClose={() => setCancleOrderVisible(false)}
|
||||||
|
id={modalData?.id}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderListItem;
|
export default React.memo(OrderListItem);
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export interface TradeExtendServeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TradeOrderDetailRespVO {
|
export interface TradeOrderDetailRespVO {
|
||||||
|
cancelRemark?: string;
|
||||||
/**
|
/**
|
||||||
* 取消原因
|
* 取消原因
|
||||||
*/
|
*/
|
||||||
@@ -346,3 +347,73 @@ export interface TradeOrderStatusRespVo {
|
|||||||
*/
|
*/
|
||||||
orderId?: number;
|
orderId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TradeOrderFastPhotoRespVo {
|
||||||
|
/**
|
||||||
|
* 商品概述
|
||||||
|
*/
|
||||||
|
brief?: string;
|
||||||
|
/**
|
||||||
|
* 商品详细描述
|
||||||
|
*/
|
||||||
|
content?: string;
|
||||||
|
/**
|
||||||
|
* 产品轮播图
|
||||||
|
*/
|
||||||
|
imgs?: string;
|
||||||
|
/**
|
||||||
|
* 产品主图
|
||||||
|
*/
|
||||||
|
pic?: string;
|
||||||
|
/**
|
||||||
|
* sku
|
||||||
|
*/
|
||||||
|
skuName?: string;
|
||||||
|
/**
|
||||||
|
* 商品名称
|
||||||
|
*/
|
||||||
|
spuName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TradeOrderSubLogDO {
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
createTime?: string;
|
||||||
|
/**
|
||||||
|
* 创建者,目前使用 SysUser 的 id 编号
|
||||||
|
*
|
||||||
|
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||||
|
*/
|
||||||
|
creator?: string;
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
deleted?: number;
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
|
*/
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
* 订单号
|
||||||
|
*/
|
||||||
|
orderId?: number;
|
||||||
|
/**
|
||||||
|
* 预约时间
|
||||||
|
*/
|
||||||
|
subTime?: string;
|
||||||
|
/**
|
||||||
|
* 更新者,目前使用 SysUser 的 id 编号
|
||||||
|
*
|
||||||
|
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||||
|
*/
|
||||||
|
updater?: string;
|
||||||
|
/**
|
||||||
|
* 最后更新时间
|
||||||
|
*/
|
||||||
|
updateTime?: string;
|
||||||
|
/**
|
||||||
|
* 用户编号
|
||||||
|
*/
|
||||||
|
userId?: number;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { request } from "@umijs/max";
|
import { request } from "@umijs/max";
|
||||||
import { TradeOrderPageRespVO, TradeReq } from "./list";
|
import { TradeOrderPageRespVO, TradeReq, TradeSummaryRespVO } from "./list";
|
||||||
import { TradeOrderDetailRespVO } from "./detail";
|
import {
|
||||||
|
TradeOrderDetailRespVO,
|
||||||
|
TradeOrderFastPhotoRespVo,
|
||||||
|
TradeOrderSubLogDO,
|
||||||
|
} from "./detail";
|
||||||
|
|
||||||
export const getTradeOrderPage = async (params: TradeReq) => {
|
export const getTradeOrderPage = async (params: TradeReq) => {
|
||||||
return request<PageResult<TradeOrderPageRespVO[]>>("/trade/order/page", {
|
return request<PageResult<TradeOrderPageRespVO[]>>("/trade/order/page", {
|
||||||
@@ -16,4 +20,76 @@ export const getTradeOrderDetail = async (id: number) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { TradeOrderPageRespVO, TradeOrderDetailRespVO, TradeReq };
|
//获得交易订单统计
|
||||||
|
export const getTradeSummary = async () => {
|
||||||
|
return request<TradeSummaryRespVO>("/trade/order/summary", {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//交易快照
|
||||||
|
|
||||||
|
export const getfastPhoto = async (params: {
|
||||||
|
itemId: number;
|
||||||
|
spuId: number;
|
||||||
|
}) => {
|
||||||
|
return request<TradeOrderFastPhotoRespVo>("/trade/order/fastPhoto", {
|
||||||
|
method: "GET",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//服务信息修改记录
|
||||||
|
|
||||||
|
export const getSubTimeLog = async (id: number) => {
|
||||||
|
return request<TradeOrderSubLogDO[]>(`/trade/order/subTimeLog/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//取消订单
|
||||||
|
export const cancelOrder = async (data: {
|
||||||
|
id: number;
|
||||||
|
cancelReason: string;
|
||||||
|
cancelRemark: string;
|
||||||
|
}) => {
|
||||||
|
return request<boolean>(`/trade/order/cancel`, {
|
||||||
|
method: "PUT",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
//接单确认
|
||||||
|
export const acceptConfirm = async (id: number) => {
|
||||||
|
return request<boolean>(`/trade/order/acceptConfirm`, {
|
||||||
|
method: "PUT",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//服务上报
|
||||||
|
export const reportServe = async (id: number) => {
|
||||||
|
return request<boolean>(`/trade/order/reportServe`, {
|
||||||
|
method: "PUT",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//商家备注
|
||||||
|
export const updateOrderRemark = async (params: {
|
||||||
|
id?: number;
|
||||||
|
remark?: string;
|
||||||
|
}) => {
|
||||||
|
return request<boolean>(`/trade/order/update-remark`, {
|
||||||
|
method: "PUT",
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
TradeOrderPageRespVO,
|
||||||
|
TradeOrderDetailRespVO,
|
||||||
|
TradeReq,
|
||||||
|
TradeSummaryRespVO,
|
||||||
|
TradeOrderFastPhotoRespVo,
|
||||||
|
TradeOrderSubLogDO,
|
||||||
|
};
|
||||||
|
|||||||
@@ -192,3 +192,9 @@ export interface TradeReq {
|
|||||||
*/
|
*/
|
||||||
userSearch?: string;
|
userSearch?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TradeSummaryRespVO {
|
||||||
|
orderCount?: number;
|
||||||
|
livePrice?: number;
|
||||||
|
payPrice?: number;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user