feat(trade): 仓配可调配履约与三分支推单
CI / verify (pull_request) Has been cancelled

同城有仓按仓库绑定承运商自动推单或自管填单,无仓/跨城走总部快递;新增仓配注册表、FulfillmentService 及三端运单追踪。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-16 17:51:20 +08:00
parent f1dc23c54c
commit f6d97b4ee8
30 changed files with 1615 additions and 57 deletions
+66 -2
View File
@@ -51,6 +51,8 @@ type OrderDetail = AdminOrderRow & {
productAmount?: number;
freightAmount?: number;
benefitAmount?: number;
deliveryType?: string;
fulfillmentWarehouseId?: string | null;
paidAt?: string | null;
payExpireAt?: string | null;
items?: Array<Record<string, unknown>>;
@@ -63,6 +65,7 @@ type OrderDetail = AdminOrderRow & {
export default function OrdersPage() {
const [form] = Form.useForm();
const [shipForm] = Form.useForm();
const [logisticsForm] = Form.useForm();
const [data, setData] = useState<Paginated<AdminOrderRow> | null>(null);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
@@ -74,6 +77,7 @@ export default function OrdersPage() {
const [batchDeleting, setBatchDeleting] = useState(false);
const [shipDefaults, setShipDefaults] = useState<ShipDefaults | null>(null);
const [shipping, setShipping] = useState(false);
const [logisticsShipping, setLogisticsShipping] = useState(false);
const selectedOrders = useMemo(
() => (data?.items ?? []).filter((row) => selectedRowKeys.includes(row.id)),
@@ -143,6 +147,25 @@ export default function OrdersPage() {
}
}
async function submitLogisticsShip() {
if (!detail) return;
const values = await logisticsForm.validateFields();
setLogisticsShipping(true);
try {
const res = await request<OrderDetail>(`/admin/orders/${detail.id}/logistics-ship`, {
method: 'POST',
body: JSON.stringify(values),
});
message.success('快递单已录入');
setDetail(res);
void load();
} catch (e) {
message.error(e instanceof Error ? e.message : '填单失败');
} finally {
setLogisticsShipping(false);
}
}
async function confirmBatchDelete() {
if (!selectedRowKeys.length) return;
setBatchDeleting(true);
@@ -324,16 +347,55 @@ export default function OrdersPage() {
{detail.delivery && (
<Descriptions column={1} bordered size="small" title="配送" style={{ marginTop: 16 }}>
<Descriptions.Item label="快递公司">
{DELIVERY_PROVIDER_LABELS[detail.delivery.provider] || detail.delivery.provider}
{detail.delivery.logisticsCompany ||
DELIVERY_PROVIDER_LABELS[detail.delivery.provider] ||
detail.delivery.provider}
</Descriptions.Item>
<Descriptions.Item label="运单号">{detail.delivery.trackingNo || '—'}</Descriptions.Item>
<Descriptions.Item label="三方单号">{detail.delivery.providerOrderNo || '—'}</Descriptions.Item>
{detail.delivery.manualQueryUrl && (
<Descriptions.Item label="查询链接">
<a href={detail.delivery.manualQueryUrl} target="_blank" rel="noreferrer">
</a>
</Descriptions.Item>
)}
</Descriptions>
)}
{['PENDING_SHIP', 'OUT_WAREHOUSE'].includes(detail.status) && !detail.delivery?.trackingNo && (
<div style={{ marginTop: 16 }}>
<Typography.Title level={5}></Typography.Title>
{(detail.deliveryType === 'CROSS_CITY' || !detail.fulfillmentWarehouseId) && (
<>
<Typography.Title level={5}></Typography.Title>
<Typography.Paragraph type="secondary" style={{ fontSize: 12 }}>
</Typography.Paragraph>
<Form form={logisticsForm} layout="vertical" size="small">
<Form.Item name="logisticsCompany" label="快递公司" rules={[{ required: true }]}>
<Input placeholder="如 顺丰速运、京东物流" />
</Form.Item>
<Form.Item name="trackingNo" label="运单号" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item name="manualQueryUrl" label="物流查询链接(可选)">
<Input placeholder="https://..." />
</Form.Item>
<Button type="primary" loading={logisticsShipping} onClick={() => void submitLogisticsShip()}>
</Button>
</Form>
</>
)}
{detail.fulfillmentWarehouseId && (
<>
<Typography.Title level={5} style={{ marginTop: 16 }}>
</Typography.Title>
<Typography.Paragraph type="secondary" style={{ fontSize: 12 }}>
</Typography.Paragraph>
<Form form={shipForm} layout="vertical" size="small">
<Form.Item name="provider" label="快递公司" rules={[{ required: true }]}>
<Select
@@ -389,6 +451,8 @@ export default function OrdersPage() {
</Button>
</Form>
</>
)}
</div>
)}