WEB/src/pages/Order/List/index.tsx

2488 lines
78 KiB
TypeScript

import styles from '../../../style/order-list.css';
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
import SyncForm from '@/components/SyncForm';
import { showSyncResult, SyncResultData } from '@/components/SyncResultMessage';
import { ORDER_STATUS_ENUM } from '@/constants';
import { HistoryOrder } from '@/pages/Statistics/Order';
import {
logisticscontrollerCreateshipment,
logisticscontrollerDelshipment,
logisticscontrollerGetshipmentfee,
logisticscontrollerGetshippingaddresslist,
} from '@/servers/api/logistics';
import {
ordercontrollerCancelorder,
ordercontrollerChangestatus,
ordercontrollerCompletedorder,
ordercontrollerCreatenote,
ordercontrollerCreateorder,
ordercontrollerGetorderbynumber,
ordercontrollerGetorderdetail,
ordercontrollerGetorders,
ordercontrollerRefundorder,
ordercontrollerSyncorderbyid,
ordercontrollerSyncorders,
ordercontrollerUpdateorderitems,
} from '@/servers/api/order';
import { productcontrollerSearchproducts } from '@/servers/api/product';
import { sitecontrollerAll } from '@/servers/api/site';
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
import { formatShipmentState, formatSource } from '@/utils/format';
import {
CodeSandboxOutlined,
CopyOutlined,
DeleteFilled,
DownOutlined,
FileDoneOutlined,
TagsOutlined,
} from '@ant-design/icons';
import {
ActionType,
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,
Divider,
Drawer,
Dropdown,
Empty,
message,
Popconfirm,
Radio,
Row,
Space,
Tabs,
TabsProps,
Tag,
} from 'antd';
import React, { useMemo, useRef, useState } from 'react';
import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
import { request } from '@umijs/max';
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: '已退款',
// label: '退款申请已通过',
},
{
key: 'refund_cancelled',
label: '已完成',
// 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: 'isSubscription',
hideInSearch: true,
render: (_, record) => {
const related = Array.isArray((record as any)?.related)
? (record as any).related
: [];
const isSub = related.some(
(it: any) =>
it?.externalSubscriptionId || it?.billing_period || it?.line_items,
);
return (
<Tag color={isSub ? 'green' : 'default'}>{isSub ? '是' : '否'}</Tag>
);
},
},
{
title: '站点',
dataIndex: 'siteId',
valueType: 'select',
request: async () => {
try {
const result = await sitecontrollerAll();
const {success, data}= result
if (success && data) {
return data.map((site: any) => ({
label: site.name,
value: site.id,
}));
}
return [];
} catch (error) {
console.error('获取站点列表失败:', error);
return [];
}
},
},
{
title: '订单包含',
dataIndex: 'keyword',
hideInTable: true,
},
{
title: '订单ID',
dataIndex: 'externalOrderId',
},
{
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: 'fulfillments',
hideInSearch: true,
render: (_, record) => {
return (
<div>
{(record as any)?.fulfillments?.map((item: any) => {
if (!item) return;
return (
<div style={{ display:"flex", alignItems:"center" }}>
{item.tracking_provider}
{item.tracking_number}
</div>
);
})}
</div>
);
},
},
{
title: 'IP',
dataIndex: 'customer_ip_address',
},
{
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 {
if (!record.siteId || !record.externalOrderId) {
message.error('站点ID或外部订单ID不存在');
return;
}
const {
success,
message: errMsg,
data,
} = await ordercontrollerSyncorderbyid({
siteId: record.siteId,
orderId: record.externalOrderId,
});
if (!success) {
throw new Error(errMsg);
}
showSyncResult(data as SyncResultData, '订单');
actionRef.current?.reload();
} catch (error: any) {
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 {
if (!record.id) {
message.error('订单ID不存在');
return;
}
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>
</>
);
},
},
];
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
return (
<PageContainer ghost>
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
<ProTable
params={{ status: activeKey }}
headerTitle="查询表格"
scroll={{ x: 'max-content' }}
actionRef={actionRef}
rowKey="id"
columns={columns}
rowSelection={{
selectedRowKeys,
onChange: (keys) => setSelectedRowKeys(keys),
}}
rowClassName={(record) => {
return record.id === activeLine
? styles['selected-line-order-protable']
: '';
}}
pagination={{
pageSizeOptions: ['10', '20', '50', '100', '1000'],
showSizeChanger: true,
showQuickJumper: true,
defaultPageSize: 10,
}}
toolBarRender={() => [
// <CreateOrder tableRef={actionRef} />,
<SyncForm
onFinish={async (values: any) => {
try {
const {
success,
message: errMsg,
data,
} = await ordercontrollerSyncorders(values);
if (!success) {
throw new Error(errMsg);
}
// 使用 showSyncResult 函数显示详细的同步结果
showSyncResult(data as SyncResultData, '订单');
actionRef.current?.reload();
} catch (error: any) {
message.error(error?.message || '同步失败');
}
}}
tableRef={actionRef}
/>,
<Popconfirm
title="批量导出"
description="确认导出选中的订单吗?"
onConfirm={async () => {
console.log(selectedRowKeys);
try {
const res = await request('/order/export', {
method: 'POST',
data: {
ids: selectedRowKeys,
}
});
if (res?.success && res.data) {
const blob = new Blob([res.data], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'orders.csv';
a.click();
URL.revokeObjectURL(url);
} else {
message.error(res.message || '导出失败');
}
actionRef.current?.reload();
setSelectedRowKeys([]);
} catch (error: any) {
message.error(error?.message || '导出失败');
}
}}
>
<Button
type="primary"
disabled={selectedRowKeys.length === 0}
ghost
>
</Button>
</Popconfirm>,
]}
request={async ({ date, ...param }: any) => {
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 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 {
if (!record.siteId || !record.externalOrderId) {
message.error('站点ID或外部订单ID不存在');
return;
}
const {
success,
message: errMsg,
data,
} = await ordercontrollerSyncorderbyid({
siteId: record.siteId,
orderId: record.externalOrderId,
});
if (!success) {
throw new Error(errMsg);
}
showSyncResult(data as SyncResultData, '订单');
tableRef.current?.reload();
} catch (error: any) {
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 {
if (!record.id) {
message.error('订单ID不存在');
return;
}
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 {
if (!record.id) {
message.error('订单ID不存在');
return;
}
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 {
if (!record.id) {
message.error('订单ID不存在');
return;
}
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 {
if (!record.id) {
message.error('订单ID不存在');
return;
}
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.name,
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>
);
}}
/>
{/* 显示 related order */}
<ProDescriptions.Item
label="关联"
span={3}
render={(_, record) => {
return <RelatedOrders data={record?.related} />;
}}
/>
{/* 订单内容 */}
<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?.name,
email_addresses: data?.email,
contact_name: data?.name,
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: any) {
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.name} ${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: any) {
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 }) => {
// 根据包装类型决定渲染内容
const selectedPackagingType = details?.packaging_type;
// 判断是否为纸箱
if (selectedPackagingType === '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 (selectedPackagingType === '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: any) {
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 } = formData;
const res = await ordercontrollerUpdateorderitems(
{ orderId: id },
sales,
);
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 productcontrollerSearchproducts({
name: keyWords,
});
return data?.map((item) => {
return {
label: `${item.name} - ${item.nameCn}`,
value: item?.sku,
};
});
} catch (error: any) {
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: any) {
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;