仓库自动配送开关
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-07-17 08:50:18 +08:00
parent c643b0b979
commit c204fb0a92
3 changed files with 85 additions and 74 deletions
+80 -69
View File
@@ -11,6 +11,7 @@ import {
Popconfirm, Popconfirm,
Select, Select,
Space, Space,
Switch,
Table, Table,
Tag, Tag,
Typography, Typography,
@@ -59,29 +60,54 @@ type PartnerOption = { id: string; companyName: string };
const MANAGER_OPTIONS = Object.entries(WAREHOUSE_MANAGER_LABELS).map(([value, label]) => ({ value, label })); const MANAGER_OPTIONS = Object.entries(WAREHOUSE_MANAGER_LABELS).map(([value, label]) => ({ value, label }));
const STATUS_OPTIONS = Object.entries(WAREHOUSE_STATUS_LABELS).map(([value, label]) => ({ value, label })); const STATUS_OPTIONS = Object.entries(WAREHOUSE_STATUS_LABELS).map(([value, label]) => ({ value, label }));
const FULFILLMENT_MODE_OPTIONS = Object.entries(WAREHOUSE_FULFILLMENT_MODE_LABELS).map(([value, label]) => ({
value,
label,
}));
function FulfillmentFields({ function FulfillmentFields({
mode, mode,
providerOptions, providerOptions,
onModeChange,
}: { }: {
mode: WarehouseFulfillmentMode; mode: WarehouseFulfillmentMode;
providerOptions: FulfillmentProviderDto[]; providerOptions: FulfillmentProviderDto[];
onModeChange: (mode: WarehouseFulfillmentMode) => void;
}) { }) {
const autoShip = mode === WarehouseFulfillmentMode.API_AUTO;
return ( return (
<> <>
{mode === WarehouseFulfillmentMode.API_AUTO && ( <Form.Item
<Form.Item name="fulfillmentProviderId" label="仓配承运商" rules={[{ required: true }]}> name="fulfillmentMode"
label="支付后自动发货"
extra={
autoShip
? '同城订单支付成功后,自动推送仓配承运商(如小飞侠)并进入配送中'
: '支付后仅分配仓库,保持待发货,由仓管或总部手工填运单'
}
getValueProps={(v) => ({ checked: v === WarehouseFulfillmentMode.API_AUTO })}
getValueFromEvent={(checked: boolean) =>
checked ? WarehouseFulfillmentMode.API_AUTO : WarehouseFulfillmentMode.MANUAL
}
>
<Switch
checkedChildren="开"
unCheckedChildren="关"
onChange={(checked) => {
onModeChange(
checked ? WarehouseFulfillmentMode.API_AUTO : WarehouseFulfillmentMode.MANUAL,
);
}}
/>
</Form.Item>
{autoShip && (
<Form.Item
name="fulfillmentProviderId"
label="仓配承运商"
rules={[{ required: true, message: '自动发货须选择承运商' }]}
>
<Select <Select
placeholder={providerOptions.length ? '选择已注册承运商' : '请先在仓配管理注册'} placeholder={providerOptions.length ? '选择已注册承运商' : '请先在仓配管理注册'}
options={providerOptions.map((p) => ({ value: p.id, label: `${p.name} (${p.code})` }))} options={providerOptions.map((p) => ({ value: p.id, label: `${p.name} (${p.code})` }))}
/> />
</Form.Item> </Form.Item>
)} )}
{mode === WarehouseFulfillmentMode.MANUAL && ( {!autoShip && (
<> <>
<Form.Item name="manualCarrierLabel" label="默认承运商名称"> <Form.Item name="manualCarrierLabel" label="默认承运商名称">
<Input placeholder="如 顺丰速运" /> <Input placeholder="如 顺丰速运" />
@@ -183,18 +209,17 @@ export default function CityWarehousesPage() {
} }
function onManagerTypeChange( function onManagerTypeChange(
type: WarehouseManagerType, v: WarehouseManagerType,
form: typeof createForm | typeof editForm, form: typeof createForm,
setType: (v: WarehouseManagerType) => void, setType: (t: WarehouseManagerType) => void,
) { ) {
setType(type); setType(v);
if (type !== WarehouseManagerType.PARTNER) { if (v !== WarehouseManagerType.PARTNER) {
form.setFieldValue('partnerAccountId', undefined); form.setFieldValue('partnerAccountId', undefined);
} }
} }
const columns: ColumnsType<Row> = [ const columns: ColumnsType<Row> = [
{ title: '仓库名', dataIndex: 'name', ellipsis: true, width: 140 },
{ {
title: '城市', title: '城市',
width: 100, width: 100,
@@ -204,17 +229,17 @@ export default function CityWarehousesPage() {
</span> </span>
), ),
}, },
{ title: '地址', dataIndex: 'address', ellipsis: true }, { title: '仓库', dataIndex: 'name', width: 140, ellipsis: true },
{ title: '地址', dataIndex: 'address', width: 180, ellipsis: true },
{ title: '联系人', dataIndex: 'contactName', width: 90 }, { title: '联系人', dataIndex: 'contactName', width: 90 },
{ title: '电话', dataIndex: 'contactPhone', width: 120 }, { title: '电话', dataIndex: 'contactPhone', width: 120 },
{ {
title: '管仓类型', title: '管仓',
dataIndex: 'managerType', width: 90,
width: 100, render: (_, row) => WAREHOUSE_MANAGER_LABELS[row.managerType] || row.managerType,
render: (v) => WAREHOUSE_MANAGER_LABELS[v as WarehouseManagerType] || v,
}, },
{ {
title: '管仓合伙人', title: '合伙人',
dataIndex: 'partnerCompanyName', dataIndex: 'partnerCompanyName',
width: 120, width: 120,
ellipsis: true, ellipsis: true,
@@ -226,12 +251,14 @@ export default function CityWarehousesPage() {
), ),
}, },
{ {
title: '履约', title: '自动发货',
width: 120, width: 130,
render: (_, row) => render: (_, row) =>
row.fulfillmentMode === 'API_AUTO' row.fulfillmentMode === 'API_AUTO' ? (
? row.fulfillmentProviderName || 'API' <Tag color="blue">{row.fulfillmentProviderName || '自动'}</Tag>
: WAREHOUSE_FULFILLMENT_MODE_LABELS[WarehouseFulfillmentMode.MANUAL], ) : (
<Tag>{WAREHOUSE_FULFILLMENT_MODE_LABELS[WarehouseFulfillmentMode.MANUAL]}</Tag>
),
}, },
{ {
title: '状态', title: '状态',
@@ -274,21 +301,23 @@ export default function CityWarehousesPage() {
return ( return (
<div> <div>
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}> <Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
<Typography.Title level={4} style={{ margin: 0 }}> <Typography.Title level={4} style={{ margin: 0 }}></Typography.Title>
</Typography.Title>
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
createForm.resetFields(); createForm.resetFields();
createForm.setFieldsValue({ managerType: WarehouseManagerType.HQ, status: WarehouseStatus.ACTIVE });
setCreateManagerType(WarehouseManagerType.HQ); setCreateManagerType(WarehouseManagerType.HQ);
setCreateCityId(undefined); setCreateCityId(undefined);
setPartners([]); setCreateFulfillmentMode(WarehouseFulfillmentMode.MANUAL);
createForm.setFieldsValue({
managerType: WarehouseManagerType.HQ,
status: WarehouseStatus.ACTIVE,
fulfillmentMode: WarehouseFulfillmentMode.MANUAL,
});
setCreateOpen(true); setCreateOpen(true);
}} }}
> >
</Button> </Button>
</Space> </Space>
@@ -296,7 +325,7 @@ export default function CityWarehousesPage() {
type="info" type="info"
showIcon showIcon
style={{ marginBottom: 16 }} style={{ marginBottom: 16 }}
message="管仓合伙人仅可在此页面分配;城市合伙人详情中的管仓仓库为只读展示。" message="同城有仓订单:支付后按「自动发货」开关决定是否推仓配 API;关闭则待人工填单。跨城/无仓仍走总部快递填单。"
/> />
<Form <Form
@@ -309,7 +338,7 @@ export default function CityWarehousesPage() {
}} }}
> >
<Form.Item name="name" label="仓库名"> <Form.Item name="name" label="仓库名">
<Input allowClear placeholder="仓库名" /> <Input allowClear />
</Form.Item> </Form.Item>
<Form.Item name="cityId" label="城市"> <Form.Item name="cityId" label="城市">
<Select <Select
@@ -317,47 +346,33 @@ export default function CityWarehousesPage() {
showSearch showSearch
optionFilterProp="label" optionFilterProp="label"
style={{ width: 140 }} style={{ width: 140 }}
placeholder="全部城市"
options={cities.map((c) => ({ value: c.id, label: c.name }))} options={cities.map((c) => ({ value: c.id, label: c.name }))}
/> />
</Form.Item> </Form.Item>
<Form.Item name="managerType" label="管仓类型"> <Form.Item name="managerType" label="管仓">
<Select allowClear style={{ width: 120 }} placeholder="全部" options={MANAGER_OPTIONS} /> <Select allowClear style={{ width: 120 }} options={MANAGER_OPTIONS} />
</Form.Item> </Form.Item>
<Form.Item name="status" label="状态"> <Form.Item name="status" label="状态">
<Select allowClear style={{ width: 100 }} placeholder="全部" options={STATUS_OPTIONS} /> <Select allowClear style={{ width: 100 }} options={STATUS_OPTIONS} />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Space> <Button type="primary" htmlType="submit">
<Button type="primary" htmlType="submit">
</Button>
</Button>
<Button
onClick={() => {
filterForm.resetFields();
setFilters({});
setPage(1);
}}
>
</Button>
</Space>
</Form.Item> </Form.Item>
</Form> </Form>
<Table <Table
rowKey="id" rowKey="id"
className="admin-table-nowrap"
loading={loading} loading={loading}
columns={columns} columns={columns}
dataSource={data?.items ?? []} dataSource={data?.items ?? []}
scroll={{ x: 1100 }} scroll={{ x: 1400 }}
pagination={{ pagination={{
current: page, current: page,
pageSize, pageSize,
total: data?.total ?? 0, total: data?.total ?? 0,
showSizeChanger: true, showSizeChanger: true,
showTotal: (t) => `${t}`,
onChange: (p, ps) => { onChange: (p, ps) => {
setPage(p); setPage(p);
setPageSize(ps); setPageSize(ps);
@@ -366,7 +381,7 @@ export default function CityWarehousesPage() {
/> />
<Modal <Modal
title="新仓库" title="新仓库"
open={createOpen} open={createOpen}
width={520} width={520}
onCancel={() => setCreateOpen(false)} onCancel={() => setCreateOpen(false)}
@@ -425,13 +440,11 @@ export default function CityWarehousesPage() {
<Form.Item name="status" label="状态" initialValue={WarehouseStatus.ACTIVE}> <Form.Item name="status" label="状态" initialValue={WarehouseStatus.ACTIVE}>
<Select options={STATUS_OPTIONS} /> <Select options={STATUS_OPTIONS} />
</Form.Item> </Form.Item>
<Form.Item name="fulfillmentMode" label="履约方式" initialValue={WarehouseFulfillmentMode.MANUAL}> <FulfillmentFields
<Select mode={createFulfillmentMode}
options={FULFILLMENT_MODE_OPTIONS} providerOptions={providerOptions}
onChange={(v) => setCreateFulfillmentMode(v)} onModeChange={setCreateFulfillmentMode}
/> />
</Form.Item>
<FulfillmentFields mode={createFulfillmentMode} providerOptions={providerOptions} />
</Form> </Form>
</Modal> </Modal>
@@ -490,13 +503,11 @@ export default function CityWarehousesPage() {
<Form.Item name="status" label="状态"> <Form.Item name="status" label="状态">
<Select options={STATUS_OPTIONS} /> <Select options={STATUS_OPTIONS} />
</Form.Item> </Form.Item>
<Form.Item name="fulfillmentMode" label="履约方式"> <FulfillmentFields
<Select mode={editFulfillmentMode}
options={FULFILLMENT_MODE_OPTIONS} providerOptions={providerOptions}
onChange={(v) => setEditFulfillmentMode(v)} onModeChange={setEditFulfillmentMode}
/> />
</Form.Item>
<FulfillmentFields mode={editFulfillmentMode} providerOptions={providerOptions} />
</Form> </Form>
</Modal> </Modal>
</div> </div>
+2 -2
View File
@@ -194,8 +194,8 @@ export const FULFILLMENT_PROVIDER_STATUS_LABELS: Record<FulfillmentProviderStatu
}; };
export const WAREHOUSE_FULFILLMENT_MODE_LABELS: Record<WarehouseFulfillmentMode, string> = { export const WAREHOUSE_FULFILLMENT_MODE_LABELS: Record<WarehouseFulfillmentMode, string> = {
[WarehouseFulfillmentMode.API_AUTO]: 'API 自动推单', [WarehouseFulfillmentMode.API_AUTO]: '自动发货',
[WarehouseFulfillmentMode.MANUAL]: '自管手工填单', [WarehouseFulfillmentMode.MANUAL]: '关闭(手工填单',
}; };
export enum AccountStatus { export enum AccountStatus {
+3 -3
View File
@@ -177,7 +177,7 @@ async function main() {
const xfxProvider = await prisma.fulfillmentProvider.create({ await prisma.fulfillmentProvider.create({
data: { data: {
code: 'XFX', code: 'XFX',
name: '小飞侠', name: '小飞侠',
@@ -218,9 +218,9 @@ async function main() {
status: 'ACTIVE', status: 'ACTIVE',
fulfillmentMode: 'API_AUTO', fulfillmentMode: 'MANUAL',
fulfillmentProviderId: xfxProvider.id, fulfillmentProviderId: null,
lng: 113.665, lng: 113.665,