同城有仓按仓库绑定承运商自动推单或自管填单,无仓/跨城走总部快递;新增仓配注册表、FulfillmentService 及三端运单追踪。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import {
|
||||
FULFILLMENT_PROVIDER_STATUS_LABELS,
|
||||
FULFILLMENT_PROVIDER_TYPE_LABELS,
|
||||
FulfillmentProviderStatus,
|
||||
FulfillmentProviderType,
|
||||
type FulfillmentProviderDto,
|
||||
} from '@dukang/shared-types';
|
||||
import { request } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
|
||||
const TYPE_OPTIONS = Object.entries(FULFILLMENT_PROVIDER_TYPE_LABELS).map(([value, label]) => ({
|
||||
value,
|
||||
label,
|
||||
}));
|
||||
const STATUS_OPTIONS = Object.entries(FULFILLMENT_PROVIDER_STATUS_LABELS).map(([value, label]) => ({
|
||||
value,
|
||||
label,
|
||||
}));
|
||||
|
||||
export default function FulfillmentProvidersPage() {
|
||||
const [rows, setRows] = useState<FulfillmentProviderDto[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [editRow, setEditRow] = useState<FulfillmentProviderDto | null>(null);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await request<FulfillmentProviderDto[]>('/admin/fulfillment-providers');
|
||||
setRows(res);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
function openCreate() {
|
||||
setEditRow(null);
|
||||
form.resetFields();
|
||||
form.setFieldsValue({
|
||||
type: FulfillmentProviderType.API,
|
||||
status: FulfillmentProviderStatus.ACTIVE,
|
||||
capabilitiesJson: JSON.stringify(
|
||||
{ createShipment: true, getTrack: true, callback: true },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
});
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
function openEdit(row: FulfillmentProviderDto) {
|
||||
setEditRow(row);
|
||||
form.setFieldsValue({
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
type: row.type,
|
||||
status: row.status,
|
||||
capabilitiesJson: row.capabilities ? JSON.stringify(row.capabilities, null, 2) : '',
|
||||
configJson: '',
|
||||
});
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
const v = await form.validateFields();
|
||||
if (editRow) {
|
||||
await request(`/admin/fulfillment-providers/${editRow.id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
name: v.name,
|
||||
type: v.type,
|
||||
status: v.status,
|
||||
configJson: v.configJson || undefined,
|
||||
capabilitiesJson: v.capabilitiesJson || undefined,
|
||||
}),
|
||||
});
|
||||
message.success('已更新');
|
||||
} else {
|
||||
await request('/admin/fulfillment-providers', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(v),
|
||||
});
|
||||
message.success('已创建');
|
||||
}
|
||||
setOpen(false);
|
||||
void load();
|
||||
}
|
||||
|
||||
const columns: ColumnsType<FulfillmentProviderDto> = [
|
||||
{ title: '编码', dataIndex: 'code', width: 100 },
|
||||
{ title: '名称', dataIndex: 'name' },
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
render: (v) => FULFILLMENT_PROVIDER_TYPE_LABELS[v as FulfillmentProviderType] || v,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
render: (v) => (
|
||||
<Tag color={v === 'ACTIVE' ? 'green' : 'default'}>
|
||||
{FULFILLMENT_PROVIDER_STATUS_LABELS[v as FulfillmentProviderStatus] || v}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '能力',
|
||||
render: (_, row) => {
|
||||
const caps = row.capabilities;
|
||||
if (!caps) return '—';
|
||||
return Object.entries(caps)
|
||||
.filter(([, on]) => on)
|
||||
.map(([k]) => k)
|
||||
.join('、') || '—';
|
||||
},
|
||||
},
|
||||
{ title: '更新时间', dataIndex: 'updatedAt', width: 170, render: fmtTime },
|
||||
{
|
||||
title: '操作',
|
||||
width: 80,
|
||||
render: (_, row) => (
|
||||
<Button type="link" size="small" onClick={() => openEdit(row)}>
|
||||
编辑
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
仓配管理
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
注册第三方履约接口后,仓库设置中方可选择对应承运商
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
注册承运商
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<Table rowKey="id" loading={loading} columns={columns} dataSource={rows} pagination={false} />
|
||||
|
||||
<Modal
|
||||
title={editRow ? '编辑承运商' : '注册承运商'}
|
||||
open={open}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={() => void submit()}
|
||||
width={560}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item name="code" label="编码" rules={[{ required: true }]}>
|
||||
<Input disabled={Boolean(editRow)} placeholder="如 XFX、JD、SF" />
|
||||
</Form.Item>
|
||||
<Form.Item name="name" label="名称" rules={[{ required: true }]}>
|
||||
<Input placeholder="如 小飞侠、京东物流" />
|
||||
</Form.Item>
|
||||
<Form.Item name="type" label="类型" rules={[{ required: true }]}>
|
||||
<Select options={TYPE_OPTIONS} />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="状态" rules={[{ required: true }]}>
|
||||
<Select options={STATUS_OPTIONS} />
|
||||
</Form.Item>
|
||||
<Form.Item name="configJson" label="凭证配置 JSON(可选)">
|
||||
<Input.TextArea rows={3} placeholder="API 密钥等,仅存服务端" />
|
||||
</Form.Item>
|
||||
<Form.Item name="capabilitiesJson" label="能力配置 JSON">
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user