forked from yoone/WEB
2502 lines
79 KiB
TypeScript
2502 lines
79 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 { request } from '@umijs/max';
|
|
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';
|
|
|
|
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: 'date_paid',
|
|
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',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<span>物流供应商: {item.shipping_provider}</span>
|
|
<span>物流单号: {item.tracking_number}</span>
|
|
</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, {
|
|
after: values.dateRange?.[0] + 'T00:00:00Z',
|
|
before: values.dateRange?.[1] + 'T23:59:59Z',
|
|
});
|
|
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" 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.sku}):{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.sku}):{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
|
|
//</Col> ? 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;
|