feat: 订单列表

This commit is contained in:
2025-10-16 16:32:59 +08:00
parent a5b7207f44
commit 3c7473f8d1
48 changed files with 1917 additions and 624 deletions

View File

@@ -193,10 +193,10 @@ export const request: RequestConfig = {
return { url, options: { ...options, headers } };
},
],
// 添加参数序列化配置
paramsSerializer: (params) => {
const searchParams = new URLSearchParams();
const appendParams = (key: string, value: any) => {
if (Array.isArray(value)) {
// 特殊处理 createTime 数组,转换为 createTime[0] 和 createTime[1] 格式

View File

@@ -1,17 +1,20 @@
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Button, type ColProps, Drawer, Space, Typography } from 'antd';
import { Button, type ColProps, Drawer, Space } from 'antd';
import type { FormInstance } from 'antd/lib';
import React, { forwardRef, useImperativeHandle } from 'react';
interface ConfigurableDrawerFormProps {
title?: string;
columns: ProFormColumnsType[];
columns?: ProFormColumnsType[];
onSubmit?: (values: any) => Promise<boolean>;
initialValues?: Record<string, any>;
width?: number | string;
labelCol?: ColProps;
wrapperCol?: ColProps;
footer: React.ReactNode;
children?: React.ReactNode;
bodyStyle: React.CSSProperties;
}
export interface ConfigurableDrawerFormRef {
@@ -32,6 +35,9 @@ const ConfigurableDrawerForm = forwardRef<
onSubmit,
initialValues,
width = 600,
footer,
children,
bodyStyle = {},
},
ref,
) => {
@@ -69,31 +75,7 @@ const ConfigurableDrawerForm = forwardRef<
setLoading(false);
}
};
// const customHeader = (
// <div
// style={{
// display: "flex",
// justifyContent: "space-between",
// alignItems: "center",
// padding: "0 0px 16px 0px",
// borderBottom: "1px solid #f0f0f0",
// marginBottom: "16px",
// }}
// >
// <Title level={4} style={{ margin: 0 }}>
// {title}
// </Title>
// <Button
// type="text"
// icon={<CloseOutlined />}
// onClick={() => setOpen(false)}
// style={{
// border: "none",
// boxShadow: "none",
// }}
// />
// </div>
// );
console.log(footer);
return (
<Drawer
title={title}
@@ -102,6 +84,11 @@ const ConfigurableDrawerForm = forwardRef<
textAlign: 'left',
position: 'relative',
},
body: {
background: 'var(--ant-background-color)',
padding: 'var(--ant-padding-lg)',
...bodyStyle,
},
}}
destroyOnHidden
closable={true} // 隐藏默认关闭按钮
@@ -110,24 +97,33 @@ const ConfigurableDrawerForm = forwardRef<
width={width}
footer={
<Space style={{ width: '100%', justifyContent: 'end' }}>
<Button onClick={() => setOpen(false)}></Button>
<Button loading={loading} type="primary" onClick={handleSubmit}>
</Button>
{footer ? (
footer
) : (
<>
<Button onClick={() => setOpen(false)}></Button>
<Button loading={loading} type="primary" onClick={handleSubmit}>
</Button>
</>
)}
</Space>
}
>
{/* {customHeader} */}
<BetaSchemaForm
initialValues={formData}
layoutType="Form"
formRef={formRef}
columns={columns}
layout="horizontal"
labelCol={labelCol}
wrapperCol={wrapperCol}
submitter={false}
/>
{columns ? (
<BetaSchemaForm
initialValues={formData}
layoutType="Form"
formRef={formRef}
columns={columns}
layout="horizontal"
labelCol={labelCol}
wrapperCol={wrapperCol}
submitter={false}
/>
) : (
children
)}
</Drawer>
);
},

View File

@@ -17,25 +17,11 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
const {
columns,
request,
// actions = [],
// permissions = [],
// checkPermission = () => true,
components,
search = {},
toolbarActions,
// showIndex = true,
// showActions = true,
// maxActionCount = 2,
// onAdd,
// onEdit,
// onDelete,
// onView,
// onExport,
// customToolbarRender,
// showSelection = true,
rowKey = 'id',
// onRow,
// rowClassName,
// enableRowClick = false,
// clickableRowClassName = "clickable-row", // 添加可点击样式
pagination = true,
...restProps
} = props;
@@ -120,25 +106,37 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
manualRequest={false}
showSorterTooltip
scroll={{ x: 'max-content' }}
search={{
labelWidth: 'auto',
defaultCollapsed: false,
...restProps.search,
}}
options={{
fullScreen: true,
reload: true,
setting: true,
density: true,
...restProps.options,
}}
pagination={{
showSizeChanger: true,
showQuickJumper: true,
pageSize: 10,
showTotal: formatPaginationTotal,
...restProps.pagination,
}}
components={components}
search={
search
? {
labelWidth: 'auto',
defaultCollapsed: false,
...search,
}
: false
}
options={
search
? {
fullScreen: true,
reload: true,
setting: true,
density: true,
...restProps.options,
}
: false
}
pagination={
pagination
? {
showSizeChanger: true,
showQuickJumper: true,
pageSize: 10,
showTotal: formatPaginationTotal,
}
: false
}
/>
);
}

View File

@@ -1,31 +1,31 @@
import { GithubOutlined } from "@ant-design/icons";
import { DefaultFooter } from "@ant-design/pro-components";
import React from "react";
import { GithubOutlined } from '@ant-design/icons';
import { DefaultFooter } from '@ant-design/pro-components';
import React from 'react';
const Footer: React.FC = () => {
return (
<DefaultFooter
style={{
background: "none",
background: 'none',
}}
copyright="20250923"
links={[
{
key: "by",
title: "百业到家",
href: "/welcome",
key: 'by',
title: '百业到家',
href: '/welcome',
blankTarget: true,
},
{
key: "github",
key: 'github',
title: <GithubOutlined />,
href: "https://github.com/ant-design/ant-design-pro",
href: 'https://github.com/ant-design/ant-design-pro',
blankTarget: true,
},
{
key: "Ant Design",
title: "Ant Design",
href: "https://ant.design",
key: 'bydj',
title: '宠悦',
href: 'https://ant.design',
blankTarget: true,
},
]}

View File

@@ -0,0 +1,96 @@
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { type ColProps, Popconfirm } from 'antd';
import type { FormInstance } from 'antd/lib';
import React, { forwardRef, useImperativeHandle } from 'react';
interface PopconfirmFormProps {
title?: string;
columns?: ProFormColumnsType[];
onSubmit?: (values: any) => Promise<boolean>;
initialValues?: Record<string, any>;
labelCol?: ColProps;
wrapperCol?: ColProps;
children?: React.ReactNode;
}
export interface PopconfirmFormRef {
open: (data?: Record<string, any>) => void;
close: () => void;
}
const PopconfirmForm = forwardRef<PopconfirmFormRef, PopconfirmFormProps>(
(
{
labelCol = { span: 4 },
wrapperCol = { span: 20 },
columns,
onSubmit,
initialValues,
children,
},
ref,
) => {
const [open, setOpen] = React.useState(false);
const [formData, setFormData] = React.useState(initialValues || {});
// const [loading, setLoading] = React.useState<boolean>(false);
// 添加表单实例引用
const formRef = React.useRef<FormInstance>(null);
useImperativeHandle(ref, () => ({
open: (data) => {
if (data) {
setFormData(data);
}
setOpen(true);
},
close: () => setOpen(false),
}));
const handleSubmit = async () => {
try {
if (onSubmit) {
await formRef.current?.validateFields();
// setLoading(true);
const values = formRef.current?.getFieldsValue();
const success = await onSubmit(values);
if (success) {
setOpen(false);
return true;
}
return false;
}
return true;
} finally {
// setLoading(false);
}
};
return (
<Popconfirm
title={null}
destroyOnHidden
icon={null}
open={open}
okText="确定"
cancelText="取消"
description={
<BetaSchemaForm
initialValues={formData}
layoutType="Form"
formRef={formRef}
columns={columns || []}
layout="horizontal"
labelCol={labelCol}
wrapperCol={wrapperCol}
submitter={false}
/>
}
onConfirm={handleSubmit}
onCancel={() => setOpen(false)}
>
{children}
</Popconfirm>
);
},
);
export default React.memo(PopconfirmForm);

View File

@@ -2,6 +2,7 @@ export const formStatusType: { [key: string]: string } = {
create: '创建',
update: '编辑',
test: '测试',
detail: '详情',
};
export const tenantStatus = [

0
src/constants/order.ts Normal file
View File

View File

@@ -92,3 +92,25 @@ ol {
}
}
}
.page-container {
background: #fff;
width: 100%;
height: 100%;
padding: 16px;
border-radius: 8px;
.ant-tabs {
.ant-pro-query-filter-container {
form {
padding-left: 0;
padding-right: 0;
padding-bottom: 0;
}
}
.ant-pro-table {
.ant-pro-card-body {
padding: 0px;
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "Ant Design Pro",
"short_name": "Ant Design Pro",
"name": "百业到家",
"short_name": "百业到家",
"display": "standalone",
"start_url": "./?utm_source=homescreen",
"theme_color": "#002140",

View File

@@ -116,7 +116,7 @@ const Welcome: React.FC = () => {
color: token.colorTextHeading,
}}
>
使 Ant Design Pro
使
</div>
<p
style={{
@@ -128,7 +128,7 @@ const Welcome: React.FC = () => {
width: '65%',
}}
>
Ant Design Pro umiAnt Design ProComponents
umiAnt Design ProComponents
//
</p>
<div

View File

@@ -25,7 +25,7 @@ export const baseTenantColumns: ProColumns<AiSampleRespVO>[] = [
{
title: '标签',
hideInTable: true,
dataIndex: 'tag_name',
dataIndex: 'tagIds',
valueType: 'select',
search: {
transform: (value) => {

View File

@@ -252,10 +252,6 @@ const SampleTagDetail = <T extends Record<string, any>>(
<span>: </span>
<span>{value.sampleMineType}</span>
</Space>
<Space size={10} style={{ width: '100%', marginBottom: 12 }}>
<span>: </span>
<span>{value.sampleTime}</span>
</Space>
{type === 'checkbox' && (
<>

View File

@@ -0,0 +1,140 @@
import type { ProColumns } from '@ant-design/pro-components';
import { Space, Typography } from 'antd';
import type { TradeOrderPageRespVO } from '@/services/trade/order';
const { Text } = Typography;
export const baseOrderColumns: ProColumns<TradeOrderPageRespVO>[] = [
{
title: '商品',
dataIndex: 'id',
width: '20%',
hideInSearch: true,
ellipsis: true,
render: (_, record) =>
// <Space style={{ width: "100%" }}>
// <Image
// src={record.picUrl}
// width={64}
// fallback="data:image/png;base64,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"
// />
record.spuName,
// <Paragraph ellipsis>
// <Paragraph ellipsis>{record.spuName}</Paragraph>
// <div>{record.skuName}</div>
// <Space>
// <div>
// <Text type="secondary">数量:</Text>
// <Text>{record.count}</Text>
// </div>
// <div>
// <Text type="secondary">单价:</Text>
// <Text>
// {record.price}/{record.unit}
// </Text>
// </div>
// <div>
// <Text type="secondary">到手价:</Text>
// <Text>
// {record.handedPrice}/{record.unit}
// </Text>
// </div>
// </Space>
// </Paragraph>
// </Space>
},
{
title: '服务',
dataIndex: 'serveAddress',
hideInSearch: true,
render: (_, record) => (
<Space direction="vertical">
<div>
<Text type="secondary"></Text>
<Text>{record.subTime}</Text>
</div>
<div>
<Text type="secondary"></Text>
<Text>{record.serveAddress}</Text>
</div>
<div>
<Text type="secondary"></Text>
<Text>{record.userRemark}</Text>
</div>
</Space>
),
},
{
title: '财务',
dataIndex: 'price',
hideInSearch: true,
render: (_, record) => (
<Space direction="vertical">
<div>
<Text type="secondary"></Text>
<Text>{record.payPrice}</Text>
</div>
<div>
<Text type="secondary"></Text>
<Text>{record.payType}</Text>
</div>
<div>
<Text type="secondary"></Text>
<Text>{record.financeStatus}</Text>
</div>
</Space>
),
},
{
title: '卖家名称',
dataIndex: 'merchantName',
hideInTable: true,
},
{
title: '买家昵称/手机号',
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: 'afterSaleStatus',
valueType: 'select',
hideInTable: true,
},
{
title: '预约状态',
dataIndex: 'subType',
valueType: 'select',
hideInTable: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
valueType: 'dateRange',
hideInTable: true,
},
{
title: '预约时间',
dataIndex: 'subTime',
valueType: 'dateRange',
hideInTable: true,
},
];

View File

@@ -0,0 +1,90 @@
import { ProCard } from '@ant-design/pro-components';
import { Button, Card, Steps, Typography } from 'antd';
import RcResizeObserver from 'rc-resize-observer';
import React, { useCallback, useState } from 'react';
import styles from './index.module.less';
const { Title, Text } = Typography;
const BasicInfo: React.FC = () => {
const [responsive, setResponsive] = useState(false);
const renderTitle = useCallback(() => {
return <div></div>;
}, []);
return (
<div className={styles['order-info']}>
<Card title="基本信息">
<ProCard
size="small"
title={renderTitle()}
bordered
headerBordered
gutter={8}
className="order-info-card"
>
<ProCard colSpan="200px">
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 10,
alignItems: 'center',
}}
>
<Title type="danger" level={4} style={{ margin: 0 }}>
</Title>
<span>
<Text type="secondary"></Text>
<Text>430</Text>
</span>
<Button type="primary" block>
</Button>
<Button block></Button>
</div>
</ProCard>
<ProCard layout="center">
<RcResizeObserver
key="resize-observer"
onResize={(offset) => {
setResponsive(offset.width < 460);
}}
>
<Steps
size="small"
progressDot
style={{ width: '100%' }}
current={1}
direction={responsive ? 'vertical' : 'horizontal'}
items={[
{
title: '创建订单',
description: 'This is a description.',
},
{
title: '等待付款',
},
{
title: '等待确定',
},
{
title: '等待服务',
},
{
title: '等待验收',
},
{
title: '完成',
},
]}
/>
</RcResizeObserver>
</ProCard>
</ProCard>
</Card>
</div>
);
};
export default React.memo(BasicInfo);

View File

@@ -0,0 +1,64 @@
import type { ProColumns } from '@ant-design/pro-components';
import { Tag } from 'antd';
import type { DeptVO } from '@/services/system/dept';
export const baseOrderColumns: ProColumns<DeptVO>[] = [
{
title: '商品',
dataIndex: 'name',
width: '33.33%',
render: (_, record) => (
<Tag color={record.status === 1 ? 'green' : 'red'}>{record.name}</Tag>
),
},
{
title: '服务',
dataIndex: 'leaderUserId',
hideInSearch: true,
},
{
title: '财务',
dataIndex: 'sort',
hideInSearch: true,
},
];
const sharedOnCell = (_: DeptVO, index?: number) => {
if (index === 3) {
return { colSpan: 0 };
}
return {};
};
export const surchargeInfoColumns: ProColumns<DeptVO>[] = [
{
title: '服务附加费',
dataIndex: 'name',
width: '33.33%',
render: (_, record, index) => {
if (index === 3) {
return 33333;
}
return (
<Tag color={record.status === 1 ? 'green' : 'red'}>{record.name}</Tag>
);
},
onCell: (_, index) => ({
colSpan: index === 3 ? 4 : 1,
}),
},
{
title: '费用详情',
dataIndex: 'leaderUserId',
onCell: sharedOnCell,
},
{
title: '付款信息',
dataIndex: 'sort',
onCell: sharedOnCell,
},
{
title: '退款信息',
dataIndex: 'leaderUserId1',
onCell: sharedOnCell,
},
];

View File

@@ -0,0 +1,25 @@
.order-info {
:global {
.ant-pro-card-header {
background: rgba(0, 0, 0, 0.02);
}
.ant-pro-card-col {
flex: 1 auto;
overflow: auto;
}
.order-info-card {
.ant-pro-card-col {
flex: 1 auto;
overflow: auto;
display: flex;
align-items: center;
}
}
.order-paragraph {
margin-bottom: 0;
}
.ant-pro-card-body {
padding: 0px;
}
}
}

View File

@@ -0,0 +1,134 @@
import { ProCard } from '@ant-design/pro-components';
import { Button, Card, Image, Space, Tag, Typography } from 'antd';
import React, { useCallback } from 'react';
import styles from './index.module.less';
const { Text, Paragraph } = Typography;
const ProdInfo: React.FC = () => {
const renderTitle = useCallback(() => {
return (
<Space style={{ height: '100%' }} size={16}>
<Paragraph className="order-paragraph"></Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary"> ID</Text>
<Text copyable>8877777</Text>
</Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary">SKU ID</Text>
<Text copyable>8877777</Text>
</Paragraph>
</Space>
);
}, []);
return (
<div className={styles['order-info']}>
<Card title="商品信息">
<ProCard
size="small"
title={renderTitle()}
bordered
headerBordered
gutter={8}
extra={<Button size="small"></Button>}
>
<ProCard
layout="default"
colSpan={'50%'}
style={{ borderRight: '1px solid rgba(5,5,5,0.06)' }}
>
<Space>
<Image
width={64}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<div>
<Text></Text>
<Paragraph className="order-paragraph">
<Text></Text>
<Text type="secondary"> ,,</Text>
</Paragraph>
<Space wrap size={0} direction="horizontal">
<Paragraph
className="order-paragraph"
style={{ marginRight: 16 }}
>
<Text type="secondary"></Text>
<Text> 1</Text>
</Paragraph>
<Paragraph
className="order-paragraph"
style={{ marginRight: 16 }}
>
<Text type="secondary"></Text>
<Text> ¥100 </Text>
</Paragraph>
<Paragraph
className="order-paragraph"
style={{ marginRight: 16 }}
>
<Text type="secondary"></Text>
<Text> ¥80 </Text>
</Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary"></Text>
<Text> ¥50 /-</Text>
</Paragraph>
</Space>
</div>
</Space>
<Paragraph style={{ marginTop: 16 }}>
<Tag></Tag>
<Tag></Tag>
<Tag></Tag>
<Tag></Tag>
<Tag></Tag>
</Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary"></Text>
<Paragraph className="order-paragraph">
</Paragraph>
</Paragraph>
</ProCard>
<ProCard
style={{
borderRight: '1px solid rgba(5,5,5,0.06)',
height: '100%',
}}
>
<Space direction="vertical">
<Text></Text>
<Paragraph className="order-paragraph">
<Text type="secondary"></Text>
<Text>¥500</Text>
</Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary"></Text>
<Text>¥500</Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text>¥500</Text>
</Paragraph>
</Space>
</ProCard>
<ProCard>
<Space direction="vertical">
<Text>退</Text>
<Paragraph className="order-paragraph">
<Text type="secondary">退</Text>
<Text>¥500</Text>
</Paragraph>
<Paragraph className="order-paragraph">
<Text type="secondary">退</Text>
<Text>1</Text>
</Paragraph>
</Space>
</ProCard>
</ProCard>
</Card>
</div>
);
};
export default React.memo(ProdInfo);

View File

@@ -0,0 +1,76 @@
import { ProCard } from '@ant-design/pro-components';
import { Card, Space } from 'antd';
import React from 'react';
import EnhancedProTable from '@/components/EnhancedProTable';
import {
type DeptReqVO,
type DeptVO,
getDeptPage,
} from '@/services/system/dept';
import { baseOrderColumns } from '../../../config';
import styles from './index.module.less';
const SelectInfo: React.FC = () => {
const onFetch = async (
params: DeptReqVO & {
pageSize?: number;
current?: number;
},
) => {
const data = await getDeptPage({
...params,
pageNo: params.current,
pageSize: params.pageSize,
});
return {
data: data,
success: true,
total: data.total,
};
};
return (
<div className={styles['order-info']}>
<Card title="可选服务">
<Space direction="vertical" style={{ width: '100%' }} size={16}>
<ProCard split="vertical" bordered>
<ProCard size="small" title="服务" headerBordered>
1111
</ProCard>
<ProCard size="small" title="付款信息" headerBordered>
1111
</ProCard>
<ProCard size="small" title="退款信息" headerBordered>
1111
</ProCard>
</ProCard>
<EnhancedProTable<DeptVO>
columns={baseOrderColumns}
request={onFetch}
showIndex={false}
size="small"
search={false}
showActions={false}
showSelection={false}
bordered
pagination={false}
/>
<ProCard split="vertical" bordered>
<ProCard size="small" title="纪念品" headerBordered>
1111
</ProCard>
<ProCard size="small" title="付款信息" headerBordered>
1111
</ProCard>
<ProCard size="small" title="退款摘要" headerBordered>
1111
</ProCard>
</ProCard>
</Space>
</Card>
</div>
);
};
export default React.memo(SelectInfo);

View File

@@ -0,0 +1,98 @@
import { ProCard } from '@ant-design/pro-components';
import { Button, Card, Image, Space, Typography } from 'antd';
import React from 'react';
import styles from './index.module.less';
const { Text, Paragraph } = Typography;
const ServiceInfo: React.FC = () => {
return (
<div className={styles['order-info']}>
<Card title="服务信息">
<ProCard split="vertical">
<ProCard
title="遗体信息"
extra={<Button size="small"></Button>}
size="small"
colSpan="30%"
headerBordered
>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary">/</Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Space wrap>
<Image
width={48}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<Image
width={48}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<Image
width={48}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<Image
width={48}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<Image
width={48}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</Space>
</Paragraph>
</ProCard>
<ProCard
size="small"
title="预约信息"
headerBordered
extra={<Button size="small"></Button>}
>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
<Paragraph>
<Text type="secondary"></Text>
<Text></Text>
</Paragraph>
</ProCard>
</ProCard>
</Card>
</div>
);
};
export default React.memo(ServiceInfo);

View File

@@ -0,0 +1,49 @@
import { Card } from 'antd';
import React from 'react';
import EnhancedProTable from '@/components/EnhancedProTable';
import {
type DeptReqVO,
type DeptVO,
getDeptPage,
} from '@/services/system/dept';
import { surchargeInfoColumns } from './config';
import styles from './index.module.less';
const SurchargeInfo: React.FC = () => {
const onFetch = async (
params: DeptReqVO & {
pageSize?: number;
current?: number;
},
) => {
const data = await getDeptPage({
...params,
pageNo: params.current,
pageSize: params.pageSize,
});
return {
data: data,
success: true,
total: data.total,
};
};
return (
<div className={styles['order-info']}>
<Card title="服务信息">
<EnhancedProTable<DeptVO>
columns={surchargeInfoColumns}
request={onFetch}
showIndex={false}
size="small"
search={false}
showActions={false}
showSelection={false}
bordered
pagination={false}
/>
</Card>
</div>
);
};
export default React.memo(SurchargeInfo);

View File

@@ -0,0 +1,26 @@
import { Tabs, type TabsProps } from 'antd';
import React from 'react';
import OrderInfo from './order-info';
const DetailCom: React.FC = () => {
const items: TabsProps['items'] = [
{
key: '1',
label: '订单信息 ',
children: <OrderInfo />,
},
{
key: '2',
label: '服务履约',
children: '服务履约',
},
{
key: '3',
label: '商品配送',
children: '商品配送',
},
];
return <Tabs defaultActiveKey="1" items={items} />;
};
export default React.memo(DetailCom);

View File

@@ -0,0 +1,21 @@
import { Space } from 'antd';
import React from 'react';
import BasicInfo from './component/info/basic-info'; //基本信息(通版)
import ProdInfo from './component/info/prod-info'; //商品信息(通版)
import SelectInfo from './component/info/select-info'; //可选服务(殡葬专属字段)
import ServiceInfo from './component/info/service-info';
import SurchargeInfo from './component/info/surcharge-info'; //服务附加费(殡葬专属字段)
const OrderDetail: React.FC = () => {
return (
<Space direction="vertical" size={24} style={{ width: '100%' }}>
<BasicInfo />
<ProdInfo />
<ServiceInfo />
<SelectInfo />
<SurchargeInfo />
</Space>
);
};
export default React.memo(OrderDetail);

View File

@@ -0,0 +1,9 @@
.trade-order {
:global {
.expanded-row-top {
order: -1; /* 在 Flex 布局中将元素移到前面 */
position: relative;
margin: 0 -16px 16px -16px; /* 调整边距以填满整行 */
}
}
}

View File

@@ -0,0 +1,37 @@
import type { TabsProps } from 'antd';
import { Tabs } from 'antd';
import React from 'react';
import styles from './index.module.less';
import OrderListItem from './list';
const onChange = (key: string) => {
console.log(key);
};
const status: string[] = [
'所有订单',
'等待付款',
'等待确定',
'等待服务',
'等待验收',
'等待评价',
'已完成',
'已取消',
'已退款',
];
const OrderList: React.FC = () => {
const items: TabsProps['items'] = status.map((item, index) => ({
key: `${index + 1}`,
label: item,
children: <OrderListItem />,
}));
return (
<div className={`${styles['trade-order']} "page-container" `}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange} />
</div>
);
};
export default OrderList;

View File

@@ -0,0 +1,259 @@
import {
type ActionType,
ProCard,
type ProColumns,
} from '@ant-design/pro-components';
import {
Avatar,
Badge,
Button,
Divider,
Input,
message,
Space,
Statistic,
} from 'antd';
import React, { useCallback, useRef, useState } from 'react';
import ConfigurableDrawerForm, {
type ConfigurableDrawerFormRef,
} from '@/components/DrawerForm';
import EnhancedProTable from '@/components/EnhancedProTable';
import { baseOrderColumns } from './config';
const { Search } = Input;
import { DownOutlined, UpOutlined, UserOutlined } from '@ant-design/icons';
import PopconfirmForm, {
type PopconfirmFormRef,
} from '@/components/PopconfirmForm';
import {
getTradeOrderPage,
type TradeOrderPageRespVO,
type TradeReq,
} from '@/services/trade/order';
import DetailCom from './detail';
const OrderListItem: React.FC = () => {
const tableRef = useRef<ActionType>(null);
const configurableDrawerRef = useRef<ConfigurableDrawerFormRef>(null);
const [modalTitle, setModalTitle] = useState<string>('订单BZ000548787');
const [isShowTotal, setIsShowTotal] = useState<boolean>(false);
const popconfirmFormRef = useRef<PopconfirmFormRef>(null);
const onFetch = async (
params: TradeReq & {
pageSize?: number;
current?: number;
},
) => {
const data = await getTradeOrderPage({
...params,
pageNo: params.current,
pageSize: params.pageSize,
});
return {
data: data.list,
success: true,
total: data.total,
};
};
const handleDetail = useCallback((record: TradeOrderPageRespVO) => {
setModalTitle(`订单:${record.orderNum}`);
configurableDrawerRef.current?.open();
}, []);
const handleOrder = useCallback((id?: number) => {
console.log(id, '取消订单');
// await updateTradeOrder(values.id);
}, []);
const handleUpdate = async (_values: TradeOrderPageRespVO) => {
try {
// await updateTradeOrder(values.id);
return true;
} finally {
message.success('更新成功');
}
// await updateTradeOrder(values.id);
};
const actionColumns: ProColumns<TradeOrderPageRespVO> = {
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
width: 100,
render: (_text: React.ReactNode, record: TradeOrderPageRespVO) => [
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 4,
justifyContent: 'center',
}}
key={record.id}
>
<a key="cancel" onClick={() => handleOrder(record.id)}>
</a>
<a key="detail" onClick={() => handleDetail(record)}>
</a>
<PopconfirmForm
ref={popconfirmFormRef}
columns={[
{
title: '备注',
name: 'userRemark',
valueType: 'textarea',
fieldProps: { rows: 4, autoFocus: false },
},
]}
onSubmit={handleUpdate}
>
<a
key="remark"
onClick={() => popconfirmFormRef.current?.open(record)}
>
</a>
</PopconfirmForm>
</div>,
],
};
const onClose = useCallback(() => {
configurableDrawerRef.current?.close();
}, []);
const handleRow = (record: TradeOrderPageRespVO) => {
return {
'data-record': JSON.stringify(record),
className: 'order-row-with-info',
};
};
const components = {
body: {
row: (props: any) => {
const { children, ...restProps } = props;
const record = props['data-record']
? JSON.parse(props['data-record'])
: {};
return (
<>
{record && (
<tr style={{ background: 'rgba(0, 0, 0, 0.06)' }}>
<td
colSpan={columns.length}
style={{
padding: '0px 8px',
borderBottom: '1px solid #f0f0f0',
fontSize: '12px',
color: '#666',
}}
>
<Space>
<span>
<Badge
status="success"
text={record.orderStatus}
size="small"
/>
</span>
<Divider />
<span> {record.orderCategoryName}</span>
<span> {record.createTime}</span>
<span>{record.orderNum}</span>
<span> {record.payType}</span>
<Space>
<Avatar icon={<UserOutlined />} />
{/* <Image src={record.picUrl} width={64} /> */}
</Space>
<Space>
{record.userAvatar ? (
<Avatar src={record.userAvatar} />
) : (
<Avatar icon={<UserOutlined />} />
)}
{record.userNickName || record.userName}
<span>{record.userMobile} </span>
</Space>
</Space>
</td>
</tr>
)}
<tr {...restProps}>{children}</tr>
</>
);
},
},
};
const columns = [...baseOrderColumns, actionColumns];
const handleIsTotal = useCallback(() => {
setIsShowTotal(!isShowTotal);
}, [isShowTotal]);
const handleSearch = useCallback((value: string) => {
console.log('搜索', value);
tableRef.current?.reload();
}, []);
return (
<>
<Space>
<Search
placeholder="商品名称/商品ID/订单号"
enterButton
onSearch={handleSearch}
/>
<Button
icon={isShowTotal ? <DownOutlined /> : <UpOutlined />}
onClick={handleIsTotal}
>
</Button>
</Space>
{isShowTotal && (
<ProCard.Group direction="row" bordered style={{ marginTop: 16 }}>
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
<Statistic title="订单数量" value={79.0} precision={2} />
</ProCard>
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
<Statistic title="实付金额" value={112893.0} precision={2} />
</ProCard>
<ProCard layout="center" style={{ background: '#f5f5f5' }}>
<Statistic title="实收金额" value={93} suffix="/ 100" />
</ProCard>
</ProCard.Group>
)}
<EnhancedProTable<TradeOrderPageRespVO>
ref={tableRef}
columns={columns}
request={onFetch}
headerTitle="销售管理"
showIndex={false}
showSelection={false}
search={{ defaultCollapsed: true }}
onRow={handleRow}
components={components}
/>
<ConfigurableDrawerForm
ref={configurableDrawerRef}
width="80vw"
title={modalTitle}
bodyStyle={{
background: '#f5f5f5',
paddingTop: 8,
}}
footer={<Button onClick={onClose}></Button>}
>
<DetailCom />
</ConfigurableDrawerForm>
</>
);
};
export default OrderListItem;

View File

@@ -0,0 +1,473 @@
import { request } from "@umijs/max";
export interface TradeReq {
/**
* 售后状态示例1
*/
afterSaleStatus?: number;
/**
* 创建时间
*/
createTime?: string[];
/**
* 财务状态示例1
*/
financeStatus?: number;
/**
* 卖家名称
*/
merchantName?: string;
/**
* 订单类目id示例1
*/
orderCategoryId?: number;
/**
* 订单状态示例1
*/
orderStatus?: number;
/**
* 订单来源示例1
*/
orderTerminal?: number;
/**
* 页码,从 1 开始", example = "1
*/
pageNo?: number;
/**
* 每页条数,最大值为 100"
*/
pageSize?: number;
/**
* 聚合检索字段商品名称商品id订单号
*/
prodSearch?: string;
/**
* 创建时间
*/
subTime?: string[];
/**
* 预约类型示例1
*/
subType?: number;
/**
* 聚合检索字段 买家昵称/手机号
*/
userSearch?: string;
}
export interface TradeOrderPageRespVO {
/**
* 购买的商品数量
*/
count?: number;
/**
* 下单时间
*/
createTime?: string;
/**
* 财务状态
*/
financeStatus?: string;
/**
* 到手价
*/
handedPrice?: number;
/**
* 订单编号
*/
id?: number;
/**
* 订单类目
*/
orderCategoryName?: string;
/**
* 订单流水号
*/
orderNum?: string;
/**
* 订单状态
*/
orderStatus?: number;
/**
* 订单来源
*/
orderTerminal?: number;
/**
* 预约时间
*/
subTime?: string;
/**
* 到手价
*/
payPrice?: number;
/**
* 支付方式
*/
payType?: string;
/**
* 商品图片
*/
picUrl?: string;
/**
* 单价
*/
price?: number;
/**
* 服务地址
*/
serveAddress?: string;
/**
* 商品规格
*/
skuName?: string;
/**
* 商品名称
*/
spuName?: string;
/**
* 单位
*/
unit?: string;
/**
* 用户头像
*/
userAvatar?: string;
/**
* 用户编号
*/
userId?: number;
/**
* 用户手机号
*/
userMobile?: string;
/**
* 用户姓名
*/
userName?: string;
/**
* 用户昵称
*/
userNickName?: string;
/**
* 用户备注 - 必填,示例:你猜
*/
userRemark?: string;
}
/**
* 返回数据
*
* TradeOrderDetailRespVO
*/
export interface TradeOrderDetailRespVO {
/**
* 购买的商品数量
*/
count?: number;
/**
* 下单时间 - 必填
*/
createTime?: string;
/**
* 财务状态
*/
financeStatus?: string;
/**
* 到手价
*/
handedPrice?: number;
/**
* 订单编号 - 必填示例1024
*/
id?: number;
/**
* 订单类目
*/
orderCategoryName?: string;
/**
* 订单流水号 - 必填示例1146347329394184195
*/
orderNum?: string;
/**
* 订单状态
*/
orderStatus?: number;
/**
* 订单来源
*/
orderTerminal?: number;
/**
* 预约时间
*/
orderTime?: string;
/**
* 应付金额(总) - 必填示例1000
*/
payPrice?: number;
/**
* 支付方式
*/
payType?: string;
/**
* 商品图片
*/
picUrl?: string;
/**
* 单价
*/
price?: number;
/**
* 服务地址
*/
serveAddress?: string;
/**
* 商品规格
*/
skuName?: string;
/**
* 商品名称
*/
spuName?: string;
/**
* 配送信息
*/
tradeDeliveryInfo?: TradeDeliveryInfo;
/**
* 附加费信息
*/
tradeExtendCostInfo?: string;
/**
* 扩展服务信息
*/
tradeExtendServeInfo?: string;
/**
* 基本信息
*/
tradeOrderInfoBase?: TradeOrderBaseInfo;
/**
* 商品信息
*/
tradeProductInfo?: string;
/**
* 单位
*/
unit?: string;
/**
* 用户头像
*/
userAvatar?: string;
/**
* 用户编号 - 必填示例2048
*/
userId?: number;
/**
* 用户手机号
*/
userMobile?: string;
/**
* 用户姓名
*/
userName?: string;
/**
* 用户昵称
*/
userNickName?: string;
/**
* 用户备注 - 必填,示例:你猜
*/
userRemark?: string;
}
/**
* 配送信息
*
* TradeDeliveryInfo
*/
export interface TradeDeliveryInfo {
/**
* 快递详情
*/
deliveryDetail?: string;
/**
* 承运方
*/
logisticsName?: string;
/**
* 送货上门
*/
logisticsNum?: string;
/**
* 送货方式
*/
logisticsType?: string;
/**
* 收货地址
*/
receiverDetailAddress?: string;
/**
* 收货手机
*/
receiverMobile?: string;
/**
* 收货人
*/
receiverName?: string;
}
/**
* 基本信息
*
* TradeOrderBaseInfo
*/
export interface TradeOrderBaseInfo {
/**
* 实收金额
*/
actualPrice?: string;
/**
* 售后编号 - 必填示例450878
*/
afterSaleCode?: string;
/**
* 售后类型 - 必填,示例:仅退款/退货退款/可扩展增加使用
*/
afterSaleType?: string;
/**
* 申请人 - 必填,示例:用户/商家/平台+ID
*/
applicant?: string;
/**
* 审核人 - 必填,示例:姓名+ID
*/
auditor?: string;
/**
* 审核时间 - 必填示例2025-07-01 12:00:00
*/
auditTime?: string;
/**
* 取消时间
*/
cancelTime?: string;
/**
* 关闭时间 - 必填示例2025-07-01 12:00:00
*/
closeTime?: string;
/**
* 创建时间
*/
createTime?: string;
/**
* 优惠金额
*/
discountPrice?: string;
/**
* 完成时间
*/
finishTime?: string;
/**
* 订单id
*/
id?: string;
/**
* 订单编号
*/
orderNo?: string;
/**
* 订单状态
*/
orderStatus?: string;
/**
* 订单来源
*/
orderTerminal?: string;
/**
* 订单类型
*/
orderType?: string;
/**
* 支付方式
*/
payChannel?: string;
/**
* 交易流水号
*/
payOrderId?: string;
/**
* 订单金额
*/
payPrice?: string;
/**
* 支付时间
*/
payTime?: string;
/**
* 退款金额 - 必填
*/
refundAmount?: number;
/**
* 退款方式 - 必填,示例:系统自动退款
*/
refundMethod?: string;
/**
* 退款金额
*/
refundPrice?: string;
/**
* 退款原因 - 必填,示例:不想要了
*/
refundReason?: string;
/**
* 退款说明 - 必填,示例:这里是用户填写的退款描述
*/
refundRemark?: string;
/**
* 退款状态 - 必填,示例:待审核/待退款/已退款/已拒绝
*/
refundStatus?: string;
/**
* 退款至 - 必填,示例:原支付方式返还
*/
refundTo?: string;
/**
* 退款类型 - 必填,示例:订单退款/差价退款/运费退款/可扩展增加
*/
refundType?: string;
/**
* 用户ID 示例666
*/
userId?: number;
/**
* 用户昵称 示例:钱多多
*/
userNickname?: string;
}
// /**
// * CommonResultTradeOrderDetailRespVO
// */
// export interface ApifoxModel {
// /**
// * 错误码
// */
// code?: number;
// /**
// * 返回数据
// */
// data?: TradeOrderDetailRespVO;
// /**
// * 错误提示,用户可阅读
// */
// msg?: string;
// }
export const getTradeOrderPage = async (params: TradeReq) => {
return request<PageResult<TradeOrderPageRespVO[]>>("/trade/order/page", {
method: "GET",
params,
});
};
export const getTradeOrderDetail = async (id: number) => {
return request<IResponse<TradeOrderDetailRespVO>>("/trade/order/get-detail", {
method: "GET",
params: { id },
});
};