10fa361983
Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
5.8 KiB
TypeScript
141 lines
5.8 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
Button, Descriptions, Drawer, Form, Input, Space, Table, Typography, message,
|
|
} from 'antd';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import OrderTrackDrawer from '../components/OrderTrackDrawer';
|
|
import { request } from '../lib/api';
|
|
import { ORDER_STATUS_LABELS, fmtTime } from '../lib/constants';
|
|
import { useAdminList } from '../lib/useAdminList';
|
|
|
|
type Row = {
|
|
id: string;
|
|
orderId: string;
|
|
provider: string;
|
|
trackingNo: string | null;
|
|
providerOrderNo: string | null;
|
|
updatedAt: string;
|
|
order?: {
|
|
id: string;
|
|
orderNo: string;
|
|
status: string;
|
|
receiverName: string;
|
|
receiverPhone: string;
|
|
deliveryType: string;
|
|
};
|
|
};
|
|
|
|
export default function DeliveriesPage() {
|
|
const [form] = Form.useForm();
|
|
const [editForm] = Form.useForm();
|
|
const [filters, setFilters] = useState<Record<string, string>>({});
|
|
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
|
'/admin/deliveries',
|
|
() => {
|
|
const qs = new URLSearchParams();
|
|
if (filters.provider) qs.set('provider', filters.provider);
|
|
if (filters.trackingNo) qs.set('trackingNo', filters.trackingNo);
|
|
if (filters.orderNo) qs.set('orderNo', filters.orderNo);
|
|
return qs;
|
|
},
|
|
[filters],
|
|
);
|
|
const [detail, setDetail] = useState<Row | null>(null);
|
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
const [trackOpen, setTrackOpen] = useState(false);
|
|
const [trackOrderId, setTrackOrderId] = useState<string | null>(null);
|
|
const [trackOrderNo, setTrackOrderNo] = useState<string | null>(null);
|
|
const [trackOrderStatus, setTrackOrderStatus] = useState<string | null>(null);
|
|
|
|
function openTrack(row: Row) {
|
|
const orderId = row.orderId || row.order?.id;
|
|
if (!orderId) {
|
|
message.warning('缺少关联订单,无法查询路由');
|
|
return;
|
|
}
|
|
setTrackOrderId(orderId);
|
|
setTrackOrderNo(row.order?.orderNo ?? null);
|
|
setTrackOrderStatus(row.order?.status ?? null);
|
|
setTrackOpen(true);
|
|
}
|
|
|
|
const columns: ColumnsType<Row> = [
|
|
{ title: '订单号', dataIndex: ['order', 'orderNo'], width: 170 },
|
|
{ title: 'provider', dataIndex: 'provider', width: 90 },
|
|
{ title: '运单号', dataIndex: 'trackingNo', width: 140, render: (v) => v || '—' },
|
|
{ title: '第三方单号', dataIndex: 'providerOrderNo', width: 140, render: (v) => v || '—' },
|
|
{ title: '订单状态', dataIndex: ['order', 'status'], width: 100, render: (s) => ORDER_STATUS_LABELS[s] || s },
|
|
{ title: '收货人', dataIndex: ['order', 'receiverName'], width: 90 },
|
|
{ title: '更新', dataIndex: 'updatedAt', width: 160, render: fmtTime },
|
|
{
|
|
title: '操作', width: 140,
|
|
render: (_, row) => (
|
|
<Space size={0}>
|
|
<Button type="link" size="small" onClick={() => openTrack(row)}>路由</Button>
|
|
<Button type="link" size="small" onClick={async () => {
|
|
const d = await request<Row>(`/admin/deliveries/${row.id}`);
|
|
setDetail(d);
|
|
editForm.setFieldsValue({ provider: d.provider, trackingNo: d.trackingNo, providerOrderNo: d.providerOrderNo });
|
|
setDrawerOpen(true);
|
|
}}>编辑</Button>
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div>
|
|
<Typography.Title level={4}>快递/配送单</Typography.Title>
|
|
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
|
<Form.Item name="orderNo" label="订单号"><Input allowClear /></Form.Item>
|
|
<Form.Item name="provider" label="provider"><Input allowClear placeholder="MOCK" /></Form.Item>
|
|
<Form.Item name="trackingNo" label="运单号"><Input allowClear /></Form.Item>
|
|
<Form.Item><Button type="primary" htmlType="submit">查询</Button></Form.Item>
|
|
</Form>
|
|
<Table rowKey="id" loading={loading} columns={columns} dataSource={data?.items ?? []} scroll={{ x: 1100 }}
|
|
pagination={{ current: page, pageSize, total: data?.total ?? 0, showSizeChanger: true, onChange: (p, ps) => { setPage(p); setPageSize(ps); } }} />
|
|
<Drawer title="配送单编辑" width={480} open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
|
extra={
|
|
<Space>
|
|
<Button onClick={() => setDrawerOpen(false)}>取消</Button>
|
|
<Button type="primary" onClick={async () => {
|
|
if (!detail) return;
|
|
const v = await editForm.validateFields();
|
|
await request(`/admin/deliveries/${detail.id}`, { method: 'PUT', body: JSON.stringify(v) });
|
|
message.success('已保存');
|
|
setDrawerOpen(false);
|
|
void reload();
|
|
}}>保存</Button>
|
|
</Space>
|
|
}>
|
|
{detail && (
|
|
<>
|
|
<Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
|
|
<Descriptions.Item label="订单">{detail.order?.orderNo}</Descriptions.Item>
|
|
<Descriptions.Item label="收货">{detail.order?.receiverName} {detail.order?.receiverPhone}</Descriptions.Item>
|
|
</Descriptions>
|
|
<Form form={editForm} layout="vertical">
|
|
<Form.Item name="provider" label="provider" rules={[{ required: true }]}><Input /></Form.Item>
|
|
<Form.Item name="providerOrderNo" label="第三方单号"><Input /></Form.Item>
|
|
<Form.Item name="trackingNo" label="运单号"><Input /></Form.Item>
|
|
</Form>
|
|
<Button
|
|
style={{ marginTop: 8 }}
|
|
onClick={() => openTrack(detail)}
|
|
>
|
|
查看路由
|
|
</Button>
|
|
</>
|
|
)}
|
|
</Drawer>
|
|
<OrderTrackDrawer
|
|
open={trackOpen}
|
|
orderId={trackOrderId}
|
|
orderNo={trackOrderNo}
|
|
orderStatus={trackOrderStatus}
|
|
onClose={() => setTrackOpen(false)}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|