城市合伙人端的修改(后台)
This commit is contained in:
@@ -0,0 +1,406 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Descriptions,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import {
|
||||
WAREHOUSE_MANAGER_LABELS,
|
||||
WAREHOUSE_STATUS_LABELS,
|
||||
WarehouseManagerType,
|
||||
WarehouseStatus,
|
||||
} from '@dukang/shared-types';
|
||||
import { request, type Paginated } from '../lib/api';
|
||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
|
||||
type Row = {
|
||||
id: string;
|
||||
cityId: string;
|
||||
cityName: string;
|
||||
cityCode: string;
|
||||
name: string;
|
||||
address: string;
|
||||
contactName: string;
|
||||
contactPhone: string;
|
||||
managerType: WarehouseManagerType;
|
||||
partnerAccountId: string | null;
|
||||
partnerCompanyName?: string | null;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type CityOption = { id: string; name: string; code: string };
|
||||
type PartnerOption = { id: string; companyName: string };
|
||||
|
||||
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 }));
|
||||
|
||||
export default function CityWarehousesPage() {
|
||||
const [filterForm] = Form.useForm();
|
||||
const [createForm] = Form.useForm();
|
||||
const [editForm] = Form.useForm();
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
||||
'/admin/city-warehouses',
|
||||
() => {
|
||||
const qs = new URLSearchParams();
|
||||
if (filters.name) qs.set('name', filters.name);
|
||||
if (filters.cityId) qs.set('cityId', filters.cityId);
|
||||
if (filters.managerType) qs.set('managerType', filters.managerType);
|
||||
if (filters.status) qs.set('status', filters.status);
|
||||
return qs;
|
||||
},
|
||||
[filters],
|
||||
);
|
||||
const [cities, setCities] = useState<CityOption[]>([]);
|
||||
const [partners, setPartners] = useState<PartnerOption[]>([]);
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [editRow, setEditRow] = useState<Row | null>(null);
|
||||
const [createManagerType, setCreateManagerType] = useState<WarehouseManagerType>(WarehouseManagerType.HQ);
|
||||
const [editManagerType, setEditManagerType] = useState<WarehouseManagerType>(WarehouseManagerType.HQ);
|
||||
const [createCityId, setCreateCityId] = useState<string | undefined>();
|
||||
|
||||
const loadCities = useCallback(async () => {
|
||||
const res = await request<Paginated<CityOption>>(`/admin/cities?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`);
|
||||
setCities(res.items);
|
||||
}, []);
|
||||
|
||||
const loadPartners = useCallback(async (cityId: string) => {
|
||||
const res = await request<Paginated<PartnerOption>>(
|
||||
`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}&cityId=${cityId}`,
|
||||
);
|
||||
setPartners(res.items);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void loadCities();
|
||||
}, [loadCities]);
|
||||
|
||||
async function openEdit(row: Row) {
|
||||
setEditRow(row);
|
||||
setEditManagerType(row.managerType);
|
||||
await loadPartners(row.cityId);
|
||||
editForm.setFieldsValue({
|
||||
name: row.name,
|
||||
address: row.address,
|
||||
contactName: row.contactName,
|
||||
contactPhone: row.contactPhone,
|
||||
managerType: row.managerType,
|
||||
partnerAccountId: row.partnerAccountId,
|
||||
status: row.status,
|
||||
});
|
||||
setEditOpen(true);
|
||||
}
|
||||
|
||||
function onManagerTypeChange(
|
||||
type: WarehouseManagerType,
|
||||
form: typeof createForm | typeof editForm,
|
||||
setType: (v: WarehouseManagerType) => void,
|
||||
) {
|
||||
setType(type);
|
||||
if (type !== WarehouseManagerType.PARTNER) {
|
||||
form.setFieldValue('partnerAccountId', undefined);
|
||||
}
|
||||
}
|
||||
|
||||
const columns: ColumnsType<Row> = [
|
||||
{ title: '仓库名', dataIndex: 'name', ellipsis: true, width: 140 },
|
||||
{
|
||||
title: '城市',
|
||||
width: 100,
|
||||
render: (_, row) => (
|
||||
<span title={row.cityCode}>
|
||||
{row.cityName}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{ title: '地址', dataIndex: 'address', ellipsis: true },
|
||||
{ title: '联系人', dataIndex: 'contactName', width: 90 },
|
||||
{ title: '电话', dataIndex: 'contactPhone', width: 120 },
|
||||
{
|
||||
title: '管仓类型',
|
||||
dataIndex: 'managerType',
|
||||
width: 100,
|
||||
render: (v) => WAREHOUSE_MANAGER_LABELS[v as WarehouseManagerType] || v,
|
||||
},
|
||||
{
|
||||
title: '管仓合伙人',
|
||||
dataIndex: 'partnerCompanyName',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
render: (v, row) =>
|
||||
v && row.partnerAccountId ? (
|
||||
<Link to="/city-partners">{v}</Link>
|
||||
) : (
|
||||
'—'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
render: (s) => (
|
||||
<Tag color={s === WarehouseStatus.ACTIVE ? 'green' : 'default'}>
|
||||
{WAREHOUSE_STATUS_LABELS[s as WarehouseStatus] || s}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{ title: '创建', dataIndex: 'createdAt', width: 150, render: fmtTime },
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
render: (_, row) => (
|
||||
<Space size={0}>
|
||||
<Button type="link" size="small" onClick={() => void openEdit(row)}>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定删除该仓库?"
|
||||
description="删除后将解除关联合伙人的管仓绑定"
|
||||
onConfirm={async () => {
|
||||
await request(`/admin/city-warehouses/${row.id}`, { method: 'DELETE' });
|
||||
message.success('已删除');
|
||||
void reload();
|
||||
}}
|
||||
>
|
||||
<Button type="link" size="small" danger>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
仓库管理
|
||||
</Typography.Title>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
createForm.resetFields();
|
||||
createForm.setFieldsValue({ managerType: WarehouseManagerType.HQ, status: WarehouseStatus.ACTIVE });
|
||||
setCreateManagerType(WarehouseManagerType.HQ);
|
||||
setCreateCityId(undefined);
|
||||
setPartners([]);
|
||||
setCreateOpen(true);
|
||||
}}
|
||||
>
|
||||
新建仓库
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="管仓合伙人仅可在此页面分配;城市合伙人详情中的管仓仓库为只读展示。"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={filterForm}
|
||||
layout="inline"
|
||||
style={{ marginBottom: 16 }}
|
||||
onFinish={(v) => {
|
||||
setFilters(v);
|
||||
setPage(1);
|
||||
}}
|
||||
>
|
||||
<Form.Item name="name" label="仓库名">
|
||||
<Input allowClear placeholder="仓库名" />
|
||||
</Form.Item>
|
||||
<Form.Item name="cityId" label="城市">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
style={{ width: 140 }}
|
||||
placeholder="全部城市"
|
||||
options={cities.map((c) => ({ value: c.id, label: c.name }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="managerType" label="管仓类型">
|
||||
<Select allowClear style={{ width: 120 }} placeholder="全部" options={MANAGER_OPTIONS} />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select allowClear style={{ width: 100 }} placeholder="全部" options={STATUS_OPTIONS} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
查询
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
filterForm.resetFields();
|
||||
setFilters({});
|
||||
setPage(1);
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
className="admin-table-nowrap"
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={data?.items ?? []}
|
||||
scroll={{ x: 1100 }}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total: data?.total ?? 0,
|
||||
showSizeChanger: true,
|
||||
showTotal: (t) => `共 ${t} 条`,
|
||||
onChange: (p, ps) => {
|
||||
setPage(p);
|
||||
setPageSize(ps);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="新建仓库"
|
||||
open={createOpen}
|
||||
width={520}
|
||||
onCancel={() => setCreateOpen(false)}
|
||||
onOk={async () => {
|
||||
const v = await createForm.validateFields();
|
||||
await request(`/admin/cities/${v.cityId}/warehouses`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(v),
|
||||
});
|
||||
message.success('已创建');
|
||||
setCreateOpen(false);
|
||||
void reload();
|
||||
}}
|
||||
>
|
||||
<Form form={createForm} layout="vertical">
|
||||
<Form.Item name="cityId" label="开城城市" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={cities.map((c) => ({ value: c.id, label: `${c.name} (${c.code})` }))}
|
||||
onChange={(id) => {
|
||||
setCreateCityId(id);
|
||||
createForm.setFieldValue('partnerAccountId', undefined);
|
||||
void loadPartners(id);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="name" label="仓库名" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="address" label="地址" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="contactName" label="联系人" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="contactPhone" label="联系电话" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="managerType" label="管仓类型" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={MANAGER_OPTIONS}
|
||||
onChange={(v) => onManagerTypeChange(v, createForm, setCreateManagerType)}
|
||||
/>
|
||||
</Form.Item>
|
||||
{createManagerType === WarehouseManagerType.PARTNER && createCityId && (
|
||||
<Form.Item name="partnerAccountId" label="管仓合伙人" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={partners.length ? '选择合伙人' : '该城市暂无合伙人'}
|
||||
options={partners.map((p) => ({ value: p.id, label: p.companyName }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item name="status" label="状态" initialValue={WarehouseStatus.ACTIVE}>
|
||||
<Select options={STATUS_OPTIONS} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="编辑仓库"
|
||||
open={editOpen}
|
||||
width={520}
|
||||
onCancel={() => setEditOpen(false)}
|
||||
onOk={async () => {
|
||||
if (!editRow) return;
|
||||
const v = await editForm.validateFields();
|
||||
await request(`/admin/city-warehouses/${editRow.id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(v),
|
||||
});
|
||||
message.success('已更新');
|
||||
setEditOpen(false);
|
||||
void reload();
|
||||
}}
|
||||
>
|
||||
{editRow && (
|
||||
<Descriptions column={1} size="small" bordered style={{ marginBottom: 16 }}>
|
||||
<Descriptions.Item label="所属城市">
|
||||
{editRow.cityName}({editRow.cityCode})
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
<Form form={editForm} layout="vertical">
|
||||
<Form.Item name="name" label="仓库名" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="address" label="地址" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="contactName" label="联系人" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="contactPhone" label="联系电话" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="managerType" label="管仓类型" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={MANAGER_OPTIONS}
|
||||
onChange={(v) => onManagerTypeChange(v, editForm, setEditManagerType)}
|
||||
/>
|
||||
</Form.Item>
|
||||
{editManagerType === WarehouseManagerType.PARTNER && editRow && (
|
||||
<Form.Item name="partnerAccountId" label="管仓合伙人" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={partners.map((p) => ({ value: p.id, label: p.companyName }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select options={STATUS_OPTIONS} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user