2376 lines
74 KiB
TypeScript
2376 lines
74 KiB
TypeScript
import styles from '../../../style/order-list.css';
|
||
|
||
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
|
||
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||
import { ORDER_STATUS_ENUM } from '@/constants';
|
||
import {
|
||
logisticscontrollerCreateshipment,
|
||
logisticscontrollerGetshipmentfee,
|
||
logisticscontrollerDelshipment,
|
||
logisticscontrollerGetpaymentmethods,
|
||
logisticscontrollerGetratelist,
|
||
logisticscontrollerGetshippingaddresslist,
|
||
// logisticscontrollerGetshipmentlabel,
|
||
} from '@/servers/api/logistics';
|
||
import {
|
||
ordercontrollerCancelorder,
|
||
ordercontrollerChangestatus,
|
||
ordercontrollerCompletedorder,
|
||
ordercontrollerCreatenote,
|
||
ordercontrollerCreateorder,
|
||
ordercontrollerGetorderbynumber,
|
||
ordercontrollerGetorderdetail,
|
||
ordercontrollerGetorders,
|
||
ordercontrollerRefundorder,
|
||
ordercontrollerSyncorder,
|
||
ordercontrollerSyncorderbyid,
|
||
ordercontrollerUpdateorderitems,
|
||
} from '@/servers/api/order';
|
||
import { productcontrollerSearchproducts } from '@/servers/api/product';
|
||
import { wpproductcontrollerSearchproducts } from '@/servers/api/wpProduct';
|
||
import { sitecontrollerAll } from '@/servers/api/site';
|
||
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
||
import { formatShipmentState, formatSource } from '@/utils/format';
|
||
import {
|
||
CodeSandboxOutlined,
|
||
CopyOutlined,
|
||
DeleteFilled,
|
||
DownOutlined,
|
||
FileDoneOutlined,
|
||
SyncOutlined,
|
||
TagsOutlined,
|
||
} from '@ant-design/icons';
|
||
import {
|
||
ActionType,
|
||
DrawerForm,
|
||
ModalForm,
|
||
PageContainer,
|
||
ProColumns,
|
||
ProDescriptions,
|
||
ProForm,
|
||
ProFormDatePicker,
|
||
ProFormDependency,
|
||
ProFormDigit,
|
||
ProFormInstance,
|
||
ProFormItem,
|
||
ProFormList,
|
||
ProFormRadio,
|
||
ProFormSelect,
|
||
ProFormText,
|
||
ProFormTextArea,
|
||
ProTable,
|
||
} from '@ant-design/pro-components';
|
||
import {
|
||
App,
|
||
Button,
|
||
Card,
|
||
Col,
|
||
Descriptions,
|
||
Divider,
|
||
Drawer,
|
||
Dropdown,
|
||
Empty,
|
||
message,
|
||
Popconfirm,
|
||
Radio,
|
||
Row,
|
||
Space,
|
||
Tabs,
|
||
TabsProps,
|
||
} from 'antd';
|
||
import Item from 'antd/es/list/Item';
|
||
import dayjs from 'dayjs';
|
||
import React, { useMemo, useRef, useState } from 'react';
|
||
import { printPDF } from '@/utils/util';
|
||
|
||
const ListPage: React.FC = () => {
|
||
const actionRef = useRef<ActionType>();
|
||
const [activeKey, setActiveKey] = useState<string>('all');
|
||
const [count, setCount] = useState<any[]>([]);
|
||
const [activeLine, setActiveLine] = useState<number>(-1);
|
||
const tabs: TabsProps['items'] = useMemo(() => {
|
||
const total = count.reduce((acc, cur) => acc + Number(cur.count), 0);
|
||
const tabs = [
|
||
{
|
||
key: 'pending',
|
||
label: '待确认',
|
||
},
|
||
{
|
||
key: 'processing',
|
||
label: '待发货',
|
||
},
|
||
{
|
||
key: 'completed',
|
||
label: '已完成',
|
||
},
|
||
{
|
||
key: 'cancelled',
|
||
label: '已取消',
|
||
},
|
||
{
|
||
key: 'refunded',
|
||
label: '已退款',
|
||
},
|
||
{
|
||
key: 'failed',
|
||
label: '失败',
|
||
},
|
||
{
|
||
key: 'after_sale_pending',
|
||
label: '售后处理中',
|
||
},
|
||
{
|
||
key: 'pending_reshipment',
|
||
label: '待补发',
|
||
},
|
||
{
|
||
key: 'refund_requested',
|
||
label: '已申请退款',
|
||
},
|
||
{
|
||
key: 'refund_approved',
|
||
label: '退款申请已通过',
|
||
},
|
||
{
|
||
key: 'refund_cancelled',
|
||
label: '已取消退款',
|
||
},
|
||
// {
|
||
// key: 'pending_refund',
|
||
// label: '待退款',
|
||
// },
|
||
].map((v) => {
|
||
const number = count.find((el) => el.status === v.key)?.count || '0';
|
||
return {
|
||
label: `${v.label}(${number})`,
|
||
key: v.key,
|
||
};
|
||
});
|
||
|
||
return [
|
||
{
|
||
key: 'all',
|
||
label: `全部(${total})`,
|
||
},
|
||
...tabs,
|
||
];
|
||
}, [count]);
|
||
const { message } = App.useApp();
|
||
|
||
const columns: ProColumns<API.Order>[] = [
|
||
{
|
||
title: 'ID',
|
||
dataIndex: 'id',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '日期',
|
||
dataIndex: 'date',
|
||
hideInTable: true,
|
||
valueType: 'dateRange',
|
||
},
|
||
{
|
||
title: '订单号',
|
||
dataIndex: 'externalOrderId',
|
||
},
|
||
{
|
||
title: '站点',
|
||
dataIndex: 'siteId',
|
||
valueType: 'select',
|
||
request: async () => {
|
||
const { data = [] } = await sitecontrollerAll();
|
||
return data.map((item) => ({
|
||
label: item.siteName,
|
||
value: item.id,
|
||
}));
|
||
},
|
||
},
|
||
{
|
||
title: '订单包含',
|
||
dataIndex: 'keyword',
|
||
hideInTable: true,
|
||
},
|
||
{
|
||
title: '订单日期',
|
||
dataIndex: 'date_created',
|
||
hideInSearch: true,
|
||
valueType: 'dateTime',
|
||
},
|
||
{
|
||
title: '金额',
|
||
dataIndex: 'total',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '支付方式',
|
||
dataIndex: 'payment_method',
|
||
},
|
||
{
|
||
title: '总订单数',
|
||
dataIndex: 'order_count',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '总订单金额',
|
||
dataIndex: 'total_spent',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '客户邮箱',
|
||
dataIndex: 'customer_email',
|
||
},
|
||
{
|
||
title: '联系电话',
|
||
dataIndex: 'billing_phone',
|
||
render: (_, record) => record.shipping?.phone || record.billing?.phone,
|
||
},
|
||
{
|
||
title: '换货次数',
|
||
dataIndex: 'exchange_frequency',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '州',
|
||
hideInSearch: true,
|
||
render: (_, record) => record.shipping?.state || record.billing?.state,
|
||
},
|
||
{
|
||
title: '状态',
|
||
dataIndex: 'orderStatus',
|
||
hideInSearch: true,
|
||
valueType: 'select',
|
||
valueEnum: ORDER_STATUS_ENUM,
|
||
},
|
||
{
|
||
title: '物流',
|
||
dataIndex: 'shipmentList',
|
||
hideInSearch: true,
|
||
render: (_, record) => {
|
||
return (
|
||
<div>
|
||
{record?.shipmentList?.map((item) => {
|
||
if (!item) return;
|
||
return (
|
||
<div>
|
||
{item.tracking_provider}:{item.primary_tracking_number} (
|
||
{formatShipmentState(item.state)})
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
);
|
||
},
|
||
},
|
||
{
|
||
title: 'IP',
|
||
dataIndex: 'customer_ip_address',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '设备',
|
||
dataIndex: 'device_type',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '来源',
|
||
hideInSearch: true,
|
||
render: (_, record) =>
|
||
formatSource(record.source_type, record.utm_source),
|
||
},
|
||
{
|
||
title: '客户备注',
|
||
dataIndex: 'customer_note',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '操作',
|
||
dataIndex: 'option',
|
||
valueType: 'option',
|
||
fixed: 'right',
|
||
width: '200',
|
||
render: (_, record) => {
|
||
return (
|
||
<>
|
||
{['processing', 'pending_reshipment'].includes(
|
||
record.orderStatus,
|
||
) ? (
|
||
<>
|
||
<Shipping id={record.id as number} tableRef={actionRef} setActiveLine={setActiveLine}/>
|
||
<Divider type="vertical" />
|
||
</>
|
||
) : (
|
||
<></>
|
||
)}
|
||
<Detail
|
||
key={record.id}
|
||
record={record}
|
||
tableRef={actionRef}
|
||
orderId={record.id as number}
|
||
setActiveLine={setActiveLine}
|
||
/>
|
||
<Divider type="vertical" />
|
||
<Dropdown
|
||
menu={{
|
||
items: [
|
||
{
|
||
key: 'sync',
|
||
label: (
|
||
<Button
|
||
type="primary"
|
||
onClick={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerSyncorderbyid({
|
||
siteId: record.siteId as string,
|
||
orderId: record.externalOrderId as string,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
message.success('同步成功');
|
||
actionRef.current?.reload();
|
||
} catch (error) {
|
||
message.error(error?.message || '同步失败');
|
||
}
|
||
}}
|
||
>
|
||
同步订单
|
||
</Button>
|
||
),
|
||
style: {
|
||
display: [
|
||
'after_sale_pending',
|
||
'pending_reshipment',
|
||
].includes(record.orderStatus)
|
||
? 'none'
|
||
: 'block',
|
||
},
|
||
},
|
||
{
|
||
key: 'history',
|
||
label:
|
||
<HistoryOrder
|
||
email={record.customer_email}
|
||
tableRef={actionRef}
|
||
/>,
|
||
},
|
||
{
|
||
key: 'note',
|
||
label: <OrderNote id={record.id as number} />,
|
||
},
|
||
{
|
||
key: 'cancel',
|
||
label: (
|
||
<Popconfirm
|
||
title="转至售后"
|
||
description="确认转至售后?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerChangestatus(
|
||
{
|
||
id: record.id,
|
||
},
|
||
{
|
||
status: 'after_sale_pending',
|
||
},
|
||
);
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
actionRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至售后
|
||
</Button>
|
||
</Popconfirm>
|
||
),
|
||
style: {
|
||
display: [
|
||
'processing',
|
||
'pending_reshipment',
|
||
'completed',
|
||
'pending_refund',
|
||
].includes(record.orderStatus)
|
||
? 'block'
|
||
: 'none',
|
||
},
|
||
},
|
||
],
|
||
}}
|
||
>
|
||
<a onClick={(e) => e.preventDefault()}>
|
||
<Space>
|
||
更多
|
||
<DownOutlined />
|
||
</Space>
|
||
</a>
|
||
</Dropdown>
|
||
</>
|
||
);
|
||
},
|
||
},
|
||
];
|
||
return (
|
||
<PageContainer ghost>
|
||
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
|
||
<ProTable
|
||
params={{ status: activeKey }}
|
||
headerTitle="查询表格"
|
||
scroll={{ x: 'max-content' }}
|
||
actionRef={actionRef}
|
||
rowKey="id"
|
||
rowClassName={(record) => {
|
||
return record.id === activeLine ? styles['selected-line-order-protable']: '';
|
||
}}
|
||
toolBarRender={() => [
|
||
<CreateOrder tableRef={actionRef} />,
|
||
<SyncForm tableRef={actionRef} />,
|
||
]}
|
||
request={async ({ date, ...param }) => {
|
||
if (param.status === 'all') {
|
||
delete param.status;
|
||
}
|
||
if (date) {
|
||
const [startDate, endDate] = date;
|
||
param.startDate = `${startDate} 00:00:00`;
|
||
param.endDate = `${endDate} 23:59:59`;
|
||
}
|
||
const { data, success } = await ordercontrollerGetorders(param);
|
||
if (success) {
|
||
setCount(data?.count || []);
|
||
return {
|
||
total: data?.total || 0,
|
||
data: data?.items || [],
|
||
};
|
||
}
|
||
return {
|
||
data: [],
|
||
};
|
||
}}
|
||
columns={columns}
|
||
/>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
const SyncForm: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ tableRef }) => {
|
||
const { message } = App.useApp();
|
||
return (
|
||
<DrawerForm<API.ordercontrollerSyncorderParams>
|
||
title="同步订单"
|
||
trigger={
|
||
<Button key="syncSite" type="primary">
|
||
<SyncOutlined />
|
||
同步订单
|
||
</Button>
|
||
}
|
||
autoFocusFirstInput
|
||
drawerProps={{
|
||
destroyOnHidden: true,
|
||
}}
|
||
onFinish={async (values) => {
|
||
try {
|
||
const { success, message: errMsg } = await ordercontrollerSyncorder(
|
||
values,
|
||
);
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
message.success('同步成功');
|
||
tableRef.current?.reload();
|
||
return true;
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<ProForm.Group>
|
||
<ProFormSelect
|
||
name="siteId"
|
||
width="lg"
|
||
label="站点"
|
||
placeholder="请选择站点"
|
||
request={async () => {
|
||
const { data = [] } = await sitecontrollerAll();
|
||
return data.map((item) => ({
|
||
label: item.siteName,
|
||
value: item.id,
|
||
}));
|
||
}}
|
||
/>
|
||
</ProForm.Group>
|
||
</DrawerForm>
|
||
);
|
||
};
|
||
|
||
const Detail: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
orderId: number;
|
||
record: API.Order;
|
||
setActiveLine: Function
|
||
}> = ({ tableRef, orderId, record, setActiveLine }) => {
|
||
const [visiable, setVisiable] = useState(false);
|
||
const { message } = App.useApp();
|
||
const ref = useRef<ActionType>();
|
||
|
||
const initRequest = async () => {
|
||
const { data, success }: API.OrderDetailRes =
|
||
await ordercontrollerGetorderdetail({
|
||
orderId,
|
||
});
|
||
if (!success || !data) return { data: {} };
|
||
// 合并订单中相同的sku,只显示一次记录总数
|
||
data.sales = data.sales?.reduce(
|
||
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
||
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||
if (idx === -1) {
|
||
acc.push(cur);
|
||
} else {
|
||
acc[idx].quantity += cur.quantity;
|
||
}
|
||
return acc;
|
||
},
|
||
[],
|
||
);
|
||
return {
|
||
data,
|
||
};
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<Button key="detail" type="primary" onClick={() => {
|
||
setVisiable(true);
|
||
setActiveLine(record.id);
|
||
}}>
|
||
<FileDoneOutlined />
|
||
详情
|
||
</Button>
|
||
<Drawer
|
||
title="订单详情"
|
||
open={visiable}
|
||
destroyOnHidden
|
||
size="large"
|
||
onClose={() => setVisiable(false)}
|
||
footer={[
|
||
<OrderNote id={orderId} descRef={ref} />,
|
||
...(['after_sale_pending', 'pending_reshipment'].includes(
|
||
record.orderStatus,
|
||
)
|
||
? []
|
||
: [
|
||
<Divider type="vertical" />,
|
||
<Button
|
||
type="primary"
|
||
onClick={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerSyncorderbyid({
|
||
siteId: record.siteId as string,
|
||
orderId: record.externalOrderId as string,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
message.success('同步成功');
|
||
tableRef.current?.reload();
|
||
} catch (error) {
|
||
message.error(error?.message || '同步失败');
|
||
}
|
||
}}
|
||
>
|
||
同步订单
|
||
</Button>,
|
||
]),
|
||
// ...(['processing', 'pending_reshipment'].includes(record.orderStatus)
|
||
// ? [
|
||
// <Divider type="vertical" />,
|
||
// <Shipping
|
||
// id={record.id as number}
|
||
// tableRef={tableRef}
|
||
// descRef={ref}
|
||
// reShipping={true}
|
||
// />,
|
||
// ]
|
||
// : []),
|
||
...([
|
||
'processing',
|
||
'pending_reshipment',
|
||
'completed',
|
||
'pending_refund',
|
||
].includes(record.orderStatus)
|
||
? [
|
||
<Divider type="vertical" />,
|
||
<Popconfirm
|
||
title="转至售后"
|
||
description="确认转至售后?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerChangestatus(
|
||
{
|
||
id: record.id,
|
||
},
|
||
{
|
||
status: 'after_sale_pending',
|
||
},
|
||
);
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至售后
|
||
</Button>
|
||
</Popconfirm>,
|
||
]
|
||
: []),
|
||
...(record.orderStatus === 'after_sale_pending'
|
||
? [
|
||
<Divider type="vertical" />,
|
||
<Popconfirm
|
||
title="转至取消"
|
||
description="确认转至取消?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerCancelorder({
|
||
id: record.id,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至取消
|
||
</Button>
|
||
</Popconfirm>,
|
||
<Divider type="vertical" />,
|
||
<Popconfirm
|
||
title="转至退款"
|
||
description="确认转至退款?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerRefundorder({
|
||
id: record.id,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至退款
|
||
</Button>
|
||
</Popconfirm>,
|
||
<Divider type="vertical" />,
|
||
<Popconfirm
|
||
title="转至完成"
|
||
description="确认转至完成?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerCompletedorder({
|
||
id: record.id,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至完成
|
||
</Button>
|
||
</Popconfirm>,
|
||
<Divider type="vertical" />,
|
||
<Popconfirm
|
||
title="转至待补发"
|
||
description="确认转至待补发?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await ordercontrollerChangestatus(
|
||
{
|
||
id: record.id,
|
||
},
|
||
{
|
||
status: 'pending_reshipment',
|
||
},
|
||
);
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" ghost>
|
||
转至待补发
|
||
</Button>
|
||
</Popconfirm>,
|
||
]
|
||
: []),
|
||
]}
|
||
>
|
||
<ProDescriptions
|
||
labelStyle={{ width: '100px' }}
|
||
actionRef={ref}
|
||
request={initRequest}
|
||
>
|
||
<ProDescriptions.Item
|
||
label="站点"
|
||
dataIndex="siteId"
|
||
valueType="select"
|
||
request={async () => {
|
||
const { data = [] } = await sitecontrollerAll();
|
||
return data.map((item) => ({
|
||
label: item.siteName,
|
||
value: item.id,
|
||
}));
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="订单日期"
|
||
dataIndex="date_created"
|
||
valueType="dateTime"
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="订单状态"
|
||
dataIndex="orderStatus"
|
||
valueType="select"
|
||
valueEnum={ORDER_STATUS_ENUM}
|
||
/>
|
||
<ProDescriptions.Item label="金额" dataIndex="total" />
|
||
<ProDescriptions.Item label="客户邮箱" dataIndex="customer_email" />
|
||
<ProDescriptions.Item label="联系电话" span={3}
|
||
render={(_, record) => { return (
|
||
<div>
|
||
<span>
|
||
{record?.shipping?.phone || record?.billing?.phone || '-'}
|
||
</span>
|
||
</div>
|
||
);
|
||
}} />
|
||
<ProDescriptions.Item label="交易Id" dataIndex="transaction_id" />
|
||
<ProDescriptions.Item label="IP" dataIndex="customer_id_address" />
|
||
<ProDescriptions.Item label="设备" dataIndex="device_type" />
|
||
<ProDescriptions.Item
|
||
label="来源"
|
||
render={(_, record) =>
|
||
formatSource(record.source_type, record.utm_source)
|
||
}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="原订单状态"
|
||
dataIndex="status"
|
||
valueType="select"
|
||
valueEnum={ORDER_STATUS_ENUM}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="支付链接"
|
||
dataIndex="payment_url"
|
||
span={3}
|
||
copyable
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="客户备注"
|
||
dataIndex="customer_note"
|
||
span={3}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="发货信息"
|
||
span={3}
|
||
render={(_, record) => {
|
||
return (
|
||
<div>
|
||
<div>
|
||
company:
|
||
<span>
|
||
{record?.shipping?.company ||
|
||
record?.billing?.company ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
first_name:
|
||
<span>
|
||
{record?.shipping?.first_name ||
|
||
record?.billing?.first_name ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
last_name:
|
||
<span>
|
||
{record?.shipping?.last_name ||
|
||
record?.billing?.last_name ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
country:
|
||
<span>
|
||
{record?.shipping?.country ||
|
||
record?.billing?.country ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
state:
|
||
<span>
|
||
{record?.shipping?.state || record?.billing?.state || '-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
city:
|
||
<span>
|
||
{record?.shipping?.city || record?.billing?.city || '-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
postcode:
|
||
<span>
|
||
{record?.shipping?.postcode ||
|
||
record?.billing?.postcode ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
phone:
|
||
<span>
|
||
{record?.shipping?.phone || record?.billing?.phone || '-'}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
address_1:
|
||
<span>
|
||
{record?.shipping?.address_1 ||
|
||
record?.billing?.address_1 ||
|
||
'-'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="原始订单"
|
||
span={3}
|
||
render={(_, record) => {
|
||
return (
|
||
<ul>
|
||
{record?.items?.map((item: any) => (
|
||
<li key={item.id}>
|
||
{item.name}:{item.quantity}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="订单内容"
|
||
span={3}
|
||
render={(_, record) => {
|
||
return (
|
||
<ul>
|
||
{record?.sales?.map((item: any) => (
|
||
<li key={item.id}>
|
||
{item.name}:{item.quantity}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="换货"
|
||
span={3}
|
||
render={(_, record) => {
|
||
return (
|
||
<SalesChange
|
||
detailRef={ref}
|
||
id={record.id as number}
|
||
/>
|
||
)
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="备注"
|
||
span={3}
|
||
render={(_, record) => {
|
||
if (!record.notes || record.notes.length === 0)
|
||
return <Empty description="暂无备注" />;
|
||
return (
|
||
<div style={{ width: '100%' }}>
|
||
{record.notes.map((note: any) => (
|
||
<div style={{ marginBottom: 10 }} key={note.id}>
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
}}
|
||
>
|
||
<span>{note.username}</span>
|
||
<span>{note.createdAt}</span>
|
||
</div>
|
||
<div>{note.content}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
label="物流信息"
|
||
span={3}
|
||
render={(_, record) => {
|
||
console.log('record', record);
|
||
if (!record.shipment || record.shipment.length === 0) {
|
||
return <Empty description="暂无物流信息" />;
|
||
}
|
||
return (
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
width: '100%',
|
||
}}
|
||
>
|
||
{record.shipment.map((v) => (
|
||
<Card
|
||
style={{ marginBottom: '10px' }}
|
||
extra={formatShipmentState(v.state)}
|
||
title={
|
||
<>
|
||
{v.tracking_provider}
|
||
{v.primary_tracking_number}
|
||
<CopyOutlined
|
||
onClick={async () => {
|
||
try {
|
||
await navigator.clipboard.writeText(
|
||
v.tracking_url,
|
||
);
|
||
message.success('复制成功!');
|
||
} catch (err) {
|
||
message.error('复制失败!');
|
||
}
|
||
}}
|
||
/>
|
||
</>
|
||
}
|
||
actions={
|
||
v.state === 'waiting-for-scheduling' ||
|
||
v.state === 'waiting-for-transit'
|
||
? [
|
||
<Popconfirm
|
||
title="取消运单"
|
||
description="确认取消运单?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await logisticscontrollerDelshipment({
|
||
id: v.id,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
tableRef.current?.reload();
|
||
initRequest();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<DeleteFilled />
|
||
取消运单
|
||
</Popconfirm>,
|
||
]
|
||
: []
|
||
}
|
||
>
|
||
<div>订单号: {v?.orderIds?.join(',')}</div>
|
||
{v?.items?.map((item) => (
|
||
<div>
|
||
{item.name}: {item.quantity}
|
||
</div>
|
||
))}
|
||
</Card>
|
||
))}
|
||
</div>
|
||
);
|
||
}}
|
||
/>
|
||
</ProDescriptions>
|
||
</Drawer>
|
||
</>
|
||
);
|
||
};
|
||
|
||
const OrderNote: React.FC<{
|
||
id: number;
|
||
descRef?: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ id, descRef }) => {
|
||
const { message } = App.useApp();
|
||
return (
|
||
<ModalForm
|
||
title="添加备注"
|
||
trigger={
|
||
<Button type="primary" ghost>
|
||
<TagsOutlined />
|
||
备注
|
||
</Button>
|
||
}
|
||
onFinish={async (values: any) => {
|
||
try {
|
||
const { success, message: errMsg } = await ordercontrollerCreatenote({
|
||
...values,
|
||
orderId: id,
|
||
});
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
descRef?.current?.reload();
|
||
message.success('提交成功');
|
||
return true;
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<ProFormTextArea
|
||
name="content"
|
||
label="内容"
|
||
width="lg"
|
||
placeholder="请输入备注"
|
||
rules={[{ required: true, message: '请输入备注' }]}
|
||
/>
|
||
</ModalForm>
|
||
);
|
||
};
|
||
|
||
const region = {
|
||
AB: 'Alberta',
|
||
BC: 'British',
|
||
MB: 'Manitoba',
|
||
NB: 'New',
|
||
NL: 'Newfoundland',
|
||
NS: 'Nova',
|
||
ON: 'Ontario',
|
||
PE: 'Prince',
|
||
QC: 'Quebec',
|
||
SK: 'Saskatchewan',
|
||
NT: 'Northwest',
|
||
NU: 'Nunavut',
|
||
YT: 'Yukon',
|
||
|
||
// 美国州名
|
||
// AL: 'Alabama',
|
||
// AK: 'Alaska',
|
||
// AZ: 'Arizona',
|
||
// AR: 'Arkansas',
|
||
// CA: 'California',
|
||
// CO: 'Colorado',
|
||
// CT: 'Connecticut',
|
||
// DE: 'Delaware',
|
||
// FL: 'Florida',
|
||
// GA: 'Georgia',
|
||
// HI: 'Hawaii',
|
||
// ID: 'Idaho',
|
||
// IL: 'Illinois',
|
||
// IN: 'Indiana',
|
||
// IA: 'Iowa',
|
||
// KS: 'Kansas',
|
||
// KY: 'Kentucky',
|
||
// LA: 'Louisiana',
|
||
// ME: 'Maine',
|
||
// MD: 'Maryland',
|
||
// MA: 'Massachusetts',
|
||
// MI: 'Michigan',
|
||
// MN: 'Minnesota',
|
||
// MS: 'Mississippi',
|
||
// MO: 'Missouri',
|
||
// MT: 'Montana',
|
||
// NE: 'Nebraska',
|
||
// NV: 'Nevada',
|
||
// NH: 'New Hampshire',
|
||
// NJ: 'New Jersey',
|
||
// NM: 'New Mexico',
|
||
// NY: 'New York',
|
||
// NC: 'North Carolina',
|
||
// ND: 'North Dakota',
|
||
// OH: 'Ohio',
|
||
// OK: 'Oklahoma',
|
||
// OR: 'Oregon',
|
||
// PA: 'Pennsylvania',
|
||
// RI: 'Rhode Island',
|
||
// SC: 'South Carolina',
|
||
// SD: 'South Dakota',
|
||
// TN: 'Tennessee',
|
||
// TX: 'Texas',
|
||
// UT: 'Utah',
|
||
// VT: 'Vermont',
|
||
// VA: 'Virginia',
|
||
// WA: 'Washington',
|
||
// WV: 'West Virginia',
|
||
// WI: 'Wisconsin',
|
||
// WY: 'Wyoming',
|
||
};
|
||
const Shipping: React.FC<{
|
||
id: number;
|
||
tableRef?: React.MutableRefObject<ActionType | undefined>;
|
||
descRef?: React.MutableRefObject<ActionType | undefined>;
|
||
reShipping?: boolean;
|
||
setActiveLine: Function;
|
||
}> = ({ id, tableRef, descRef, reShipping = false, setActiveLine }) => {
|
||
const [options, setOptions] = useState<any[]>([]);
|
||
const formRef = useRef<ProFormInstance>();
|
||
|
||
const [shipmentFee, setShipmentFee] = useState<number>(0);
|
||
const [rates, setRates] = useState<API.RateDTO[]>([]);
|
||
const [ratesLoading, setRatesLoading] = useState(false);
|
||
const { message } = App.useApp();
|
||
|
||
return (
|
||
<ModalForm
|
||
formRef={formRef}
|
||
title="创建运单"
|
||
size="large"
|
||
width="80vw"
|
||
modalProps={{
|
||
destroyOnHidden: true,
|
||
styles: {
|
||
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
||
},
|
||
}}
|
||
trigger={
|
||
<Button type="primary"
|
||
onClick={() => {
|
||
setActiveLine(id);
|
||
}}
|
||
>
|
||
<CodeSandboxOutlined />
|
||
创建运单
|
||
</Button>
|
||
}
|
||
request={async () => {
|
||
const { data, success }: API.OrderDetailRes =
|
||
await ordercontrollerGetorderdetail({
|
||
orderId: id,
|
||
});
|
||
if (!success || !data) return {};
|
||
data.sales = data.sales?.reduce(
|
||
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
||
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||
if (idx === -1) {
|
||
acc.push(cur);
|
||
} else {
|
||
acc[idx].quantity += cur.quantity;
|
||
}
|
||
return acc;
|
||
},
|
||
[],
|
||
);
|
||
setOptions(
|
||
data.sales?.map((item) => ({
|
||
label: item.name,
|
||
value: item.sku,
|
||
})) || [],
|
||
);
|
||
if (reShipping) data.sales = [{}];
|
||
let shipmentInfo = localStorage.getItem('shipmentInfo');
|
||
if (shipmentInfo) shipmentInfo = JSON.parse(shipmentInfo);
|
||
return {
|
||
...data,
|
||
// payment_method_id: shipmentInfo?.payment_method_id,
|
||
stockPointId: shipmentInfo?.stockPointId,
|
||
details: {
|
||
destination: {
|
||
name: data?.shipping?.company || data?.billing?.company || ' ',
|
||
address: {
|
||
address_line_1:
|
||
data?.shipping?.address_1 || data?.billing?.address_1,
|
||
city: data?.shipping?.city || data?.billing?.city,
|
||
region: data?.shipping?.state || data?.billing?.state,
|
||
postal_code:
|
||
data?.shipping?.postcode || data?.billing?.postcode,
|
||
},
|
||
contact_name:
|
||
data?.shipping?.first_name || data?.shipping?.last_name
|
||
? `${data?.shipping?.first_name} ${data?.shipping?.last_name}`
|
||
: `${data?.billing?.first_name} ${data?.billing?.last_name}`,
|
||
phone_number: {
|
||
phone: data?.shipping?.phone || data?.billing?.phone,
|
||
},
|
||
email_addresses: data?.shipping?.email || data?.billing?.email,
|
||
signature_requirement: 'not-required',
|
||
},
|
||
origin: {
|
||
name: data?.siteName,
|
||
email_addresses: data?.email,
|
||
contact_name: data?.siteName,
|
||
phone_number: shipmentInfo?.phone_number,
|
||
address: {
|
||
region: shipmentInfo?.region,
|
||
city: shipmentInfo?.city,
|
||
postal_code: shipmentInfo?.postal_code,
|
||
address_line_1: shipmentInfo?.address_line_1,
|
||
},
|
||
},
|
||
packaging_type: 'package',
|
||
expected_ship_date: dayjs(),
|
||
packaging_properties: {
|
||
packages: [
|
||
{
|
||
measurements: {
|
||
weight: {
|
||
unit: 'LBS',
|
||
value: 1,
|
||
},
|
||
cuboid: {
|
||
unit: 'IN',
|
||
l: 6,
|
||
w: 4,
|
||
h: 4,
|
||
},
|
||
},
|
||
description: 'food',
|
||
},
|
||
],
|
||
},
|
||
},
|
||
|
||
};
|
||
}}
|
||
onFinish={async ({ customer_note, notes, items, details, externalOrderId, ...data }) => {
|
||
details.origin.email_addresses =
|
||
details.origin.email_addresses.split(',');
|
||
details.destination.email_addresses =
|
||
details.destination.email_addresses.split(',');
|
||
details.destination.phone_number.number =
|
||
details.destination.phone_number.phone;
|
||
details.origin.phone_number.number = details.origin.phone_number.phone;
|
||
try {
|
||
const { success, message: errMsg, ...resShipment } =
|
||
await logisticscontrollerCreateshipment(
|
||
{ orderId: id },
|
||
{
|
||
details,
|
||
...data,
|
||
},
|
||
);
|
||
if (!success) throw new Error(errMsg);
|
||
message.success('创建成功');
|
||
tableRef?.current?.reload();
|
||
descRef?.current?.reload();
|
||
|
||
localStorage.setItem(
|
||
'shipmentInfo',
|
||
JSON.stringify({
|
||
// payment_method_id: data.payment_method_id,
|
||
stockPointId: data.stockPointId,
|
||
region: details.origin.address.region,
|
||
city: details.origin.address.city,
|
||
postal_code: details.origin.address.postal_code,
|
||
address_line_1: details.origin.address.address_line_1,
|
||
phone_number: details.origin.phone_number,
|
||
}),
|
||
);
|
||
|
||
// todo, 直接打印label
|
||
// const { resLabel } = await logisticscontrollerGetShipmentLabel(resShipment.data.shipmentId);
|
||
// console.log('res', resShipment.data.shipmentId, resLabel);
|
||
// const labelContent = resLabel.content;
|
||
// printPDF([labelContent]);
|
||
return true;
|
||
} catch (error) {
|
||
message.error(error?.message || '创建失败');
|
||
}
|
||
}}
|
||
onFinishFailed={() => {
|
||
const element = document.querySelector('.ant-form-item-explain-error');
|
||
if (element) {
|
||
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
}}
|
||
>
|
||
<ProFormText
|
||
label="订单号"
|
||
readonly
|
||
name={"externalOrderId"}
|
||
/>
|
||
<ProFormText label="客户备注" readonly name="customer_note" />
|
||
<ProFormList
|
||
label="后台备注"
|
||
name="notes"
|
||
actionRender={() => []}
|
||
readonly
|
||
>
|
||
<ProFormText readonly name="content" />
|
||
</ProFormList>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<ProFormSelect
|
||
label="合并发货订单号"
|
||
name="orderIds"
|
||
showSearch
|
||
mode="multiple"
|
||
request={async ({ keyWords: number }) => {
|
||
if (!number) return [];
|
||
const { data, success } = await ordercontrollerGetorderbynumber({
|
||
number,
|
||
});
|
||
if (success) {
|
||
return data.map((v) => ({
|
||
label: `${v.siteName} ${v.externalOrderId}`,
|
||
value: v.id,
|
||
}));
|
||
}
|
||
return [];
|
||
}}
|
||
/>
|
||
</Col>
|
||
{/* <Col span={12}>
|
||
<ProFormSelect
|
||
label="付款方式"
|
||
name="payment_method_id"
|
||
rules={[{ required: true, message: '请选择付款方式' }]}
|
||
request={async () => {
|
||
const { data, success } =
|
||
await logisticscontrollerGetpaymentmethods();
|
||
if (success) {
|
||
return data.map((v) => ({
|
||
label: v.label,
|
||
value: v.id,
|
||
}));
|
||
}
|
||
return [];
|
||
}}
|
||
/>
|
||
</Col> */}
|
||
</Row>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<ProFormList
|
||
label="原始订单"
|
||
name="items"
|
||
readonly
|
||
actionRender={() => []}
|
||
>
|
||
<ProForm.Group>
|
||
<ProFormText name="name" readonly />
|
||
<ProFormDigit name="quantity" readonly />
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
</Col>
|
||
<Col span={12}>
|
||
<ProFormList
|
||
label="发货产品"
|
||
name="sales"
|
||
// rules={[
|
||
// {
|
||
// required: true,
|
||
// message: '至少需要一个商品',
|
||
// validator: (_, value) =>
|
||
// value && value.length > 0
|
||
// ? Promise.resolve()
|
||
// : Promise.reject('至少需要一个商品'),
|
||
// },
|
||
// ]}
|
||
>
|
||
<ProForm.Group>
|
||
<ProFormSelect
|
||
params={{ options }}
|
||
request={async ({ keyWords, options }) => {
|
||
if (!keyWords || keyWords.length < 2) return options;
|
||
try {
|
||
const { data } = await productcontrollerSearchproducts({
|
||
name: keyWords,
|
||
});
|
||
return (
|
||
data?.map((item) => {
|
||
return {
|
||
label: `${item.name} - ${item.nameCn}`,
|
||
value: item?.sku,
|
||
};
|
||
}) || options
|
||
);
|
||
} catch (error) {
|
||
return options;
|
||
}
|
||
}}
|
||
name="sku"
|
||
label="产品"
|
||
placeholder="请选择产品"
|
||
tooltip="至少输入3个字符"
|
||
fieldProps={{
|
||
showSearch: true,
|
||
filterOption: false,
|
||
}}
|
||
debounceTime={300} // 防抖,减少请求频率
|
||
rules={[{ required: true, message: '请选择产品' }]}
|
||
/>
|
||
<ProFormDigit
|
||
name="quantity"
|
||
colProps={{ span: 12 }}
|
||
label="数量"
|
||
placeholder="请输入数量"
|
||
rules={[{ required: true, message: '请输入数量' }]}
|
||
fieldProps={{
|
||
precision: 0,
|
||
}}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
</Col>
|
||
</Row>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<ProForm.Group
|
||
title="发货信息"
|
||
extra={
|
||
<AddressPicker
|
||
onChange={({
|
||
address,
|
||
phone_number,
|
||
phone_number_extension,
|
||
stockPointId,
|
||
}) => {
|
||
formRef?.current?.setFieldsValue({
|
||
stockPointId,
|
||
details: {
|
||
origin: {
|
||
address,
|
||
phone_number: {
|
||
phone: phone_number,
|
||
extension: phone_number_extension,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
}}
|
||
/>
|
||
}
|
||
>
|
||
<ProFormSelect
|
||
name="stockPointId"
|
||
width="md"
|
||
label="发货仓库点"
|
||
placeholder="请选择仓库点"
|
||
rules={[{ required: true, message: '请选择发货仓库点' }]}
|
||
request={async () => {
|
||
const { data = [] } = await stockcontrollerGetallstockpoints();
|
||
return data.map((item) => ({
|
||
label: item.name,
|
||
value: item.id,
|
||
}));
|
||
}}
|
||
/>
|
||
<ProFormText
|
||
label="公司名称"
|
||
name={['details', 'origin', 'name']}
|
||
rules={[{ required: true, message: '请输入公司名称' }]}
|
||
/>
|
||
<ProFormItem
|
||
name={['details', 'origin', 'address', 'country']}
|
||
label="国家"
|
||
initialValue={'CA'}
|
||
hidden
|
||
/>
|
||
<ProFormSelect
|
||
name={['details', 'origin', 'address', 'region']}
|
||
label="地区"
|
||
placeholder={'请选择地区'}
|
||
showSearch
|
||
required
|
||
valueEnum={region}
|
||
rules={[{ required: true, message: '请选择地区' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'origin', 'address', 'city']}
|
||
label="城市"
|
||
placeholder="请输入城市"
|
||
required
|
||
rules={[{ required: true, message: '请输入城市' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'origin', 'address', 'postal_code']}
|
||
label="邮编"
|
||
placeholder="请输入邮编"
|
||
required
|
||
rules={[{ required: true, message: '请输入邮编' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'origin', 'address', 'address_line_1']}
|
||
label="详细地址"
|
||
placeholder="请输入详细地址"
|
||
width="lg"
|
||
required
|
||
rules={[{ required: true, message: '请输入详细地址' }]}
|
||
/>
|
||
<ProFormText
|
||
label="联系人"
|
||
name={['details', 'origin', 'contact_name']}
|
||
rules={[{ required: true, message: '请输入联系人' }]}
|
||
/>
|
||
<ProFormItem
|
||
name={['details', 'origin', 'phone_number']}
|
||
label="联系电话"
|
||
required
|
||
rules={[
|
||
{
|
||
required: true,
|
||
async validator(_, value) {
|
||
if (!value?.phone) {
|
||
return Promise.reject('请输入联系电话');
|
||
}
|
||
return Promise.resolve();
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<InternationalPhoneInput />
|
||
</ProFormItem>
|
||
<ProFormText
|
||
label="邮箱"
|
||
tooltip="多个邮箱用英文逗号隔开"
|
||
name={['details', 'origin', 'email_addresses']}
|
||
rules={[{ required: true, message: '请输入邮箱' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
</Col>
|
||
<Col span={12}>
|
||
<ProForm.Group title="收货信息">
|
||
<ProFormText
|
||
label="公司名称"
|
||
name={['details', 'destination', 'name']}
|
||
rules={[{ required: true, message: '请输入公司名称' }]}
|
||
/>
|
||
<ProFormItem
|
||
name={['details', 'destination', 'address', 'country']}
|
||
label="国家"
|
||
initialValue={'CA'}
|
||
hidden
|
||
/>
|
||
<ProFormSelect
|
||
name={['details', 'destination', 'address', 'region']}
|
||
label="地区"
|
||
placeholder={'请选择地区'}
|
||
showSearch
|
||
required
|
||
valueEnum={region}
|
||
rules={[{ required: true, message: '请选择地区' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'destination', 'address', 'city']}
|
||
label="城市"
|
||
placeholder="请输入城市"
|
||
required
|
||
rules={[{ required: true, message: '请输入城市' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'destination', 'address', 'postal_code']}
|
||
label="邮编"
|
||
placeholder="请输入邮编"
|
||
required
|
||
rules={[{ required: true, message: '请输入邮编' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'destination', 'address', 'address_line_1']}
|
||
label="详细地址1"
|
||
placeholder="请输入详细地址"
|
||
width="lg"
|
||
required
|
||
rules={[{ required: true, message: '请输入详细地址' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['details', 'destination', 'address', 'address_line_2']}
|
||
label="详细地址2"
|
||
placeholder="请输入详细地址"
|
||
width="lg"
|
||
/>
|
||
<ProFormText
|
||
label="联系人"
|
||
name={['details', 'destination', 'contact_name']}
|
||
rules={[{ required: true, message: '请输入联系人' }]}
|
||
/>
|
||
<ProFormItem
|
||
name={['details', 'destination', 'phone_number']}
|
||
label="联系电话"
|
||
required
|
||
rules={[
|
||
{
|
||
required: true,
|
||
async validator(_, value) {
|
||
if (!value?.phone) {
|
||
return Promise.reject('请输入联系电话');
|
||
}
|
||
return Promise.resolve();
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<InternationalPhoneInput />
|
||
</ProFormItem>
|
||
<ProFormText
|
||
label="邮箱"
|
||
tooltip="多个邮箱用英文逗号隔开"
|
||
name={['details', 'destination', 'email_addresses']}
|
||
rules={[{ required: true, message: '请输入邮箱' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
<ProFormRadio.Group
|
||
name={['details', 'destination', 'signature_requirement']}
|
||
label="签收"
|
||
options={[
|
||
{ label: '否', value: 'not-required' },
|
||
{ label: '是', value: 'required' },
|
||
{ label: '成人签收', value: 'adult-required' },
|
||
]}
|
||
/>
|
||
</Col>
|
||
</Row>
|
||
<ProFormDatePicker
|
||
name={['details', 'expected_ship_date']}
|
||
label="预计发货日期"
|
||
rules={[{ required: true, message: '请选择预计发货日期' }]}
|
||
fieldProps={{
|
||
disabledDate: (current) => {
|
||
return current && current < dayjs().startOf('day');
|
||
},
|
||
}}
|
||
transform={(value) => {
|
||
return {
|
||
details: {
|
||
expected_ship_date: {
|
||
year: dayjs(value).year(),
|
||
month: dayjs(value).month() + 1,
|
||
day: dayjs(value).date(),
|
||
},
|
||
},
|
||
};
|
||
}}
|
||
/>
|
||
<ProFormRadio.Group
|
||
name={['details', 'packaging_type']}
|
||
label="包装类型"
|
||
options={[
|
||
{ label: '木托', value: 'pallet', disabled: true },
|
||
{ label: '纸箱', value: 'package' },
|
||
{ label: '文件袋', value: 'courier-pak', disabled: true },
|
||
{ label: '信封', value: 'envelope', disabled: true },
|
||
]}
|
||
/>
|
||
<ProFormDependency name={[['details', 'packaging_type']]}>
|
||
{({ details: { packaging_type } }) => {
|
||
if (packaging_type === 'package') {
|
||
return (
|
||
<ProFormList
|
||
min={1}
|
||
max={1}
|
||
label="纸箱尺寸"
|
||
name={['details', 'packaging_properties', 'packages']}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
validator: (_, value) =>
|
||
value && value.length > 0
|
||
? Promise.resolve()
|
||
: Promise.reject('至少选择一个包裹'),
|
||
},
|
||
]}
|
||
>
|
||
{(f, idx, action) => {
|
||
return (
|
||
<ProForm.Item label={`纸箱${idx + 1}`}>
|
||
<Radio.Group>
|
||
<Radio.Button
|
||
onClick={() => {
|
||
action.setCurrentRowData({
|
||
measurements: {
|
||
weight: {
|
||
unit: 'lb',
|
||
value: 1,
|
||
},
|
||
cuboid: {
|
||
unit: 'in',
|
||
l: 6,
|
||
w: 4,
|
||
h: 4,
|
||
},
|
||
},
|
||
});
|
||
}}
|
||
>
|
||
尺寸1
|
||
</Radio.Button>
|
||
<Radio.Button
|
||
onClick={() => {
|
||
action.setCurrentRowData({
|
||
measurements: {
|
||
weight: {
|
||
unit: 'lb',
|
||
value: 5,
|
||
},
|
||
cuboid: {
|
||
unit: 'in',
|
||
l: 8,
|
||
w: 6,
|
||
h: 6,
|
||
},
|
||
},
|
||
});
|
||
}}
|
||
>
|
||
尺寸2
|
||
</Radio.Button>
|
||
<Radio.Button
|
||
onClick={() => {
|
||
action.setCurrentRowData({
|
||
measurements: {
|
||
weight: {
|
||
unit: 'lb',
|
||
value: 5,
|
||
},
|
||
cuboid: {
|
||
unit: 'in',
|
||
l: 12,
|
||
w: 8,
|
||
h: 6,
|
||
},
|
||
},
|
||
});
|
||
}}
|
||
>
|
||
尺寸3
|
||
</Radio.Button>
|
||
</Radio.Group>
|
||
</ProForm.Item>
|
||
);
|
||
}}
|
||
<ProForm.Group>
|
||
<ProFormDigit
|
||
label="长"
|
||
name={['measurements', 'cuboid', 'l']}
|
||
placeholder="请输入长"
|
||
rules={[{ required: true, message: '请输入长' }]}
|
||
/>
|
||
<ProFormDigit
|
||
label="宽"
|
||
name={['measurements', 'cuboid', 'w']}
|
||
placeholder="请输入宽"
|
||
rules={[{ required: true, message: '请输入宽' }]}
|
||
/>
|
||
<ProFormDigit
|
||
label="高"
|
||
name={['measurements', 'cuboid', 'h']}
|
||
placeholder="请输入高"
|
||
rules={[{ required: true, message: '请输入高' }]}
|
||
/>
|
||
<ProFormSelect
|
||
label="单位"
|
||
name={['measurements', 'cuboid', 'unit']}
|
||
valueEnum={{
|
||
CM: '厘米',
|
||
IN: '英寸',
|
||
}}
|
||
placeholder="请输入单位"
|
||
rules={[{ required: true, message: '请输入单位' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
<ProForm.Group>
|
||
<ProFormDigit
|
||
label="重量"
|
||
name={['measurements', 'weight', 'value']}
|
||
placeholder="请输入重量"
|
||
rules={[{ required: true, message: '请输入重量' }]}
|
||
/>
|
||
<ProFormSelect
|
||
label="单位"
|
||
name={['measurements', 'weight', 'unit']}
|
||
valueEnum={{ KGS: '千克', LBS: '磅' }}
|
||
// valueEnum={{ KGS: '千克', LBS: '磅', OZS: '盎司' }}
|
||
placeholder="请输入单位"
|
||
rules={[{ required: true, message: '请输入单位' }]}
|
||
/>
|
||
<ProFormText
|
||
label="描述"
|
||
name="description"
|
||
placeholder="请输入描述"
|
||
width="lg"
|
||
// rules={[{ required: true, message: '请输入描述' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
);
|
||
}
|
||
if (packaging_type === 'courier-pak') {
|
||
return (
|
||
<ProFormList
|
||
label="文件袋"
|
||
name={['details', 'packaging_properties', 'courier_paks']}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
validator: (_, value) =>
|
||
value && value.length > 0
|
||
? Promise.resolve()
|
||
: Promise.reject('至少选择一个'),
|
||
},
|
||
]}
|
||
>
|
||
<ProForm.Group>
|
||
<ProFormDigit
|
||
label="重量"
|
||
name={['weight', 'value']}
|
||
placeholder="请输入重量"
|
||
rules={[{ required: true, message: '请输入重量' }]}
|
||
/>
|
||
<ProFormSelect
|
||
label="单位"
|
||
name={['weight', 'unit']}
|
||
valueEnum={{ kg: '千克', lb: '磅' }}
|
||
// valueEnum={{ kg: '千克', lb: '磅', oz: '盎司' }}
|
||
placeholder="请输入单位"
|
||
rules={[{ required: true, message: '请输入单位' }]}
|
||
/>
|
||
<ProFormText
|
||
label="描述"
|
||
name="description"
|
||
placeholder="请输入描述"
|
||
width="lg"
|
||
rules={[{ required: true, message: '请输入描述' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
);
|
||
}
|
||
}}
|
||
</ProFormDependency>
|
||
<Button
|
||
loading={ratesLoading}
|
||
onClick={async () => {
|
||
try {
|
||
const { customer_note, notes, items, details, externalOrderId, ...data } = formRef.current?.getFieldsValue();
|
||
const originEmail = details.origin.email_addresses;
|
||
const destinationEmail = details.destination.email_addresses;
|
||
details.origin.email_addresses =
|
||
details.origin.email_addresses.split(',');
|
||
details.destination.email_addresses =
|
||
details.destination.email_addresses.split(',');
|
||
details.destination.phone_number.number =
|
||
details.destination.phone_number.phone;
|
||
details.origin.phone_number.number = details.origin.phone_number.phone;
|
||
const res =
|
||
await logisticscontrollerGetshipmentfee(
|
||
{
|
||
stockPointId: data.stockPointId,
|
||
|
||
sender: details.origin.contact_name,
|
||
startPhone: details.origin.phone_number,
|
||
startPostalCode: details.origin.address.postal_code.replace(/\s/g, ''),
|
||
pickupAddress: details.origin.address.address_line_1,
|
||
shipperCountryCode: details.origin.address.country,
|
||
receiver: details.destination.contact_name,
|
||
city: details.destination.address.city,
|
||
province: details.destination.address.region,
|
||
country: details.destination.address.country,
|
||
postalCode: details.destination.address.postal_code.replace(/\s/g, ''),
|
||
deliveryAddress: details.destination.address.address_line_1,
|
||
receiverPhone: details.destination.phone_number.number,
|
||
receiverEmail: details.destination.email_addresses,
|
||
length: details.packaging_properties.packages[0].measurements.cuboid.l,
|
||
width: details.packaging_properties.packages[0].measurements.cuboid.w,
|
||
height: details.packaging_properties.packages[0].measurements.cuboid.h,
|
||
dimensionUom: details.packaging_properties.packages[0].measurements.cuboid.unit,
|
||
weight: details.packaging_properties.packages[0].measurements.weight.value,
|
||
weightUom: details.packaging_properties.packages[0].measurements.weight.unit,
|
||
},
|
||
);
|
||
if (!res?.success) throw new Error(res?.message);
|
||
const fee = res.data;
|
||
setShipmentFee(fee);
|
||
details.origin.email_addresses = originEmail;
|
||
details.destination.email_addresses = destinationEmail;
|
||
formRef.current?.setFieldValue("details", {
|
||
...details,
|
||
shipmentFee: fee
|
||
});
|
||
message.success('获取运费成功');
|
||
} catch (error) {
|
||
message.error(error?.message || '获取运费失败');
|
||
}
|
||
}}
|
||
>
|
||
获取运费
|
||
</Button>
|
||
<ProFormText
|
||
readonly
|
||
name={["details", "shipmentFee"]}
|
||
fieldProps={{
|
||
value: (shipmentFee / 100.0).toFixed(2)
|
||
}}
|
||
/>
|
||
</ModalForm>
|
||
);
|
||
};
|
||
|
||
const SalesChange: React.FC<{
|
||
id: number;
|
||
detailRef?: React.MutableRefObject<ActionType | undefined>;
|
||
reShipping?: boolean;
|
||
}> = ({ id, detailRef }) => {
|
||
const formRef = useRef<ProFormInstance>();
|
||
|
||
|
||
return (
|
||
<ModalForm
|
||
formRef={formRef}
|
||
title="换货"
|
||
size="large"
|
||
width="80vw"
|
||
modalProps={{
|
||
destroyOnHidden: true,
|
||
styles: {
|
||
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
||
},
|
||
}}
|
||
trigger={
|
||
<Button type="primary">
|
||
<CodeSandboxOutlined />
|
||
换货
|
||
</Button>
|
||
}
|
||
request={async () => {
|
||
const { data, success }: API.OrderDetailRes =
|
||
await ordercontrollerGetorderdetail({
|
||
orderId: id,
|
||
});
|
||
if (!success || !data) return {};
|
||
data.sales = data.sales?.reduce((acc: API.OrderSale[], cur: API.OrderSale) => {
|
||
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||
if (idx === -1) {
|
||
acc.push(cur);
|
||
} else {
|
||
acc[idx].quantity += cur.quantity;
|
||
}
|
||
return acc;
|
||
},
|
||
[],
|
||
);
|
||
|
||
|
||
// setOptions(
|
||
// data.sales?.map((item) => ({
|
||
// label: item.name,
|
||
// value: item.sku,
|
||
// })) || [],
|
||
// );
|
||
return { ...data};
|
||
}}
|
||
onFinish={async (formData: any) => {
|
||
const { sales,items } = formData;
|
||
const data ={sales,items }
|
||
const res = await ordercontrollerUpdateorderitems({orderId:id}, data);
|
||
if (!res.success) {
|
||
message.error(`更新货物信息失败: ${res.message}`);
|
||
return false;
|
||
}
|
||
message.success('更新成功')
|
||
detailRef?.current?.reload();
|
||
return true;
|
||
}}
|
||
>
|
||
<ProFormList
|
||
label="换货订单"
|
||
name="items"
|
||
>
|
||
<ProForm.Group>
|
||
|
||
<ProFormSelect
|
||
params={{ }}
|
||
request={async ({ keyWords }) => {
|
||
try {
|
||
const { data } = await wpproductcontrollerSearchproducts({
|
||
name: keyWords,
|
||
});
|
||
return (
|
||
data?.map((item) => {
|
||
return {
|
||
label: `${item.name}`,
|
||
value: item?.sku,
|
||
};
|
||
})
|
||
);
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}}
|
||
name="sku"
|
||
label="订单"
|
||
placeholder="请选择订单"
|
||
tooltip="至少输入3个字符"
|
||
fieldProps={{
|
||
showSearch: true,
|
||
filterOption: false,
|
||
}}
|
||
debounceTime={300} // 防抖,减少请求频率
|
||
rules={[{ required: true, message: '请选择订单' }]}
|
||
/>
|
||
<ProFormDigit
|
||
name="quantity"
|
||
colProps={{ span: 12 }}
|
||
label="订单数量"
|
||
placeholder="请输入数量"
|
||
rules={[{ required: true, message: '请输入数量' }]}
|
||
fieldProps={{
|
||
precision: 0,
|
||
}}
|
||
/>
|
||
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
|
||
<ProFormList
|
||
label="换货产品"
|
||
name="sales"
|
||
>
|
||
<ProForm.Group>
|
||
|
||
|
||
<ProFormSelect
|
||
params={{ }}
|
||
request={async ({ keyWords }) => {
|
||
try {
|
||
const { data } = await productcontrollerSearchproducts({
|
||
name: keyWords,
|
||
});
|
||
return (
|
||
data?.map((item) => {
|
||
return {
|
||
label: `${item.name} - ${item.nameCn}`,
|
||
value: item?.sku,
|
||
};
|
||
})
|
||
);
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}}
|
||
name="sku"
|
||
label="产品"
|
||
placeholder="请选择产品"
|
||
tooltip="至少输入3个字符"
|
||
fieldProps={{
|
||
showSearch: true,
|
||
filterOption: false,
|
||
}}
|
||
debounceTime={300} // 防抖,减少请求频率
|
||
rules={[{ required: true, message: '请选择产品' }]}
|
||
/>
|
||
<ProFormDigit
|
||
name="quantity"
|
||
colProps={{ span: 12 }}
|
||
label="数量"
|
||
placeholder="请输入数量"
|
||
rules={[{ required: true, message: '请输入数量' }]}
|
||
fieldProps={{
|
||
precision: 0,
|
||
}}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
</ModalForm>
|
||
);
|
||
}
|
||
|
||
const CreateOrder: React.FC<{
|
||
tableRef?: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ tableRef }) => {
|
||
const formRef = useRef<ProFormInstance>();
|
||
const { message } = App.useApp();
|
||
|
||
return (
|
||
<ModalForm
|
||
formRef={formRef}
|
||
title="创建订单"
|
||
size="large"
|
||
width="80vw"
|
||
modalProps={{
|
||
destroyOnHidden: true,
|
||
styles: {
|
||
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
||
},
|
||
}}
|
||
trigger={
|
||
<Button type="primary">
|
||
<CodeSandboxOutlined />
|
||
创建订单
|
||
</Button>
|
||
}
|
||
params={{
|
||
source_type: 'admin',
|
||
}}
|
||
onFinish={async ({ items, details, ...data }) => {
|
||
try {
|
||
const { success, message: errMsg } = await ordercontrollerCreateorder(
|
||
{
|
||
...data,
|
||
customer_email: data?.billing?.email,
|
||
billing_phone: data?.billing?.phone,
|
||
},
|
||
);
|
||
if (!success) throw new Error(errMsg);
|
||
message.success('创建成功');
|
||
tableRef?.current?.reload();
|
||
|
||
return true;
|
||
} catch (error) {
|
||
message.error(error?.message || '创建失败');
|
||
}
|
||
}}
|
||
onFinishFailed={() => {
|
||
const element = document.querySelector('.ant-form-item-explain-error');
|
||
if (element) {
|
||
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
}}
|
||
>
|
||
<ProFormList label="订单内容" name="sales">
|
||
<ProForm.Group>
|
||
<ProFormSelect
|
||
request={async ({ keyWords, options }) => {
|
||
if (!keyWords || keyWords.length < 2) return options;
|
||
try {
|
||
const { data } = await productcontrollerSearchproducts({
|
||
name: keyWords,
|
||
});
|
||
return (
|
||
data?.map((item) => {
|
||
return {
|
||
label: `${item.name} - ${item.nameCn}`,
|
||
value: item?.sku,
|
||
};
|
||
}) || options
|
||
);
|
||
} catch (error) {
|
||
return options;
|
||
}
|
||
}}
|
||
name="sku"
|
||
label="产品"
|
||
placeholder="请选择产品"
|
||
tooltip="至少输入3个字符"
|
||
fieldProps={{
|
||
showSearch: true,
|
||
filterOption: false,
|
||
}}
|
||
debounceTime={300} // 防抖,减少请求频率
|
||
rules={[{ required: true, message: '请选择产品' }]}
|
||
/>
|
||
<ProFormDigit
|
||
name="quantity"
|
||
colProps={{ span: 12 }}
|
||
label="数量"
|
||
placeholder="请输入数量"
|
||
rules={[{ required: true, message: '请输入数量' }]}
|
||
fieldProps={{
|
||
precision: 0,
|
||
}}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
<ProFormDigit
|
||
label="金额"
|
||
name="total"
|
||
rules={[{ required: true, message: '请输入金额' }]}
|
||
/>
|
||
<ProForm.Group title="收货信息">
|
||
<ProFormText
|
||
label="公司名称"
|
||
name={['billing', 'company']}
|
||
rules={[{ required: true, message: '请输入公司名称' }]}
|
||
/>
|
||
<ProFormItem
|
||
name={['billing', 'country']}
|
||
label="国家"
|
||
initialValue={'CA'}
|
||
hidden
|
||
/>
|
||
<ProFormSelect
|
||
name={['billing', 'state']}
|
||
label="地区"
|
||
placeholder={'请选择地区'}
|
||
showSearch
|
||
required
|
||
valueEnum={region}
|
||
rules={[{ required: true, message: '请选择地区' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['billing', 'city']}
|
||
label="城市"
|
||
placeholder="请输入城市"
|
||
required
|
||
rules={[{ required: true, message: '请输入城市' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['billing', 'postcode']}
|
||
label="邮编"
|
||
placeholder="请输入邮编"
|
||
required
|
||
rules={[{ required: true, message: '请输入邮编' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['billing', 'address_1']}
|
||
label="详细地址1"
|
||
placeholder="请输入详细地址"
|
||
width="lg"
|
||
required
|
||
rules={[{ required: true, message: '请输入详细地址' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['billing', 'address_2']}
|
||
label="详细地址2"
|
||
placeholder="请输入详细地址"
|
||
width="lg"
|
||
/>
|
||
<ProFormText
|
||
label="联系人"
|
||
name={['billing', 'first_name']}
|
||
rules={[{ required: true, message: '请输入联系人' }]}
|
||
/>
|
||
<ProFormText
|
||
name={['billing', 'phone']}
|
||
label="联系电话"
|
||
required
|
||
rules={[{ required: true, message: '请输入联系电话' }]}
|
||
/>
|
||
<ProFormText
|
||
label="邮箱"
|
||
name={['billing', 'email']}
|
||
rules={[{ required: true, message: '请输入邮箱' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
{/* <ProFormText label="客户邮箱" name="customer_email" /> */}
|
||
</ModalForm>
|
||
);
|
||
};
|
||
|
||
const AddressPicker: React.FC<{
|
||
value?: any;
|
||
onChange?: (value: any) => void;
|
||
}> = ({ onChange, value }) => {
|
||
const [selectedRow, setSelectedRow] = useState(null);
|
||
const { message } = App.useApp();
|
||
const columns: ProColumns<API.ShippingAddress>[] = [
|
||
{
|
||
title: '仓库点',
|
||
dataIndex: 'stockPointId',
|
||
hideInSearch: true,
|
||
valueType: 'select',
|
||
request: async () => {
|
||
const { data = [] } = await stockcontrollerGetallstockpoints();
|
||
return data.map((item) => ({
|
||
label: item.name,
|
||
value: item.id,
|
||
}));
|
||
},
|
||
},
|
||
{
|
||
title: '地区',
|
||
dataIndex: ['address', 'region'],
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '城市',
|
||
dataIndex: ['address', 'city'],
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '邮编',
|
||
dataIndex: ['address', 'postal_code'],
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '详细地址',
|
||
dataIndex: ['address', 'address_line_1'],
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '联系电话',
|
||
render: (_, record) =>
|
||
`+${record.phone_number_extension} ${record.phone_number}`,
|
||
hideInSearch: true,
|
||
},
|
||
];
|
||
return (
|
||
<ModalForm
|
||
title="选择地址"
|
||
trigger={<Button type="primary">选择地址</Button>}
|
||
modalProps={{ destroyOnHidden: true }}
|
||
onFinish={async () => {
|
||
if (!selectedRow) {
|
||
message.error('请选择地址');
|
||
return false;
|
||
}
|
||
if (onChange) onChange(selectedRow);
|
||
return true;
|
||
}}
|
||
>
|
||
<ProTable
|
||
rowKey="id"
|
||
request={async () => {
|
||
const { data, success } =
|
||
await logisticscontrollerGetshippingaddresslist();
|
||
if (success) {
|
||
return {
|
||
data: data,
|
||
};
|
||
}
|
||
return {
|
||
data: [],
|
||
};
|
||
}}
|
||
columns={columns}
|
||
search={false}
|
||
rowSelection={{
|
||
type: 'radio',
|
||
onChange: (_, selectedRows) => {
|
||
setSelectedRow(selectedRows[0]);
|
||
},
|
||
}}
|
||
/>
|
||
</ModalForm>
|
||
);
|
||
};
|
||
|
||
export default ListPage;
|