@@ -45,6 +45,8 @@ type CityOption = { id: string; name: string; code: string };
|
||||
type WarehouseOption = {
|
||||
id: string;
|
||||
name: string;
|
||||
cityId?: string;
|
||||
cityName?: string;
|
||||
status?: string;
|
||||
contactName: string;
|
||||
contactPhone: string;
|
||||
@@ -192,16 +194,13 @@ export default function OrdersPage() {
|
||||
});
|
||||
}
|
||||
|
||||
async function loadWarehousesForOrder(order: OrderDetail) {
|
||||
const cityId = order.city?.id || order.cityId;
|
||||
if (!cityId) {
|
||||
setWarehouses([]);
|
||||
return [] as WarehouseOption[];
|
||||
}
|
||||
const rows = await request<WarehouseOption[]>(`/admin/cities/${cityId}/warehouses`).catch(() => []);
|
||||
const active = rows.filter((w) => !w.status || w.status === 'ACTIVE');
|
||||
setWarehouses(active);
|
||||
return active;
|
||||
async function loadWarehouses() {
|
||||
const res = await request<Paginated<WarehouseOption>>(
|
||||
`/admin/city-warehouses?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}&status=ACTIVE`,
|
||||
).catch(() => null);
|
||||
const rows = (res?.items ?? []).filter((w) => !w.status || w.status === 'ACTIVE');
|
||||
setWarehouses(rows);
|
||||
return rows;
|
||||
}
|
||||
|
||||
async function openDetail(id: string) {
|
||||
@@ -214,16 +213,25 @@ export default function OrdersPage() {
|
||||
const res = await request<OrderDetail>(`/admin/orders/${id}`);
|
||||
setShipTarget(res);
|
||||
setDetail(res);
|
||||
logisticsForm.resetFields();
|
||||
const rows = await loadWarehousesForOrder(res);
|
||||
const preferredWarehouseId = res.fulfillmentWarehouseId || res.fulfillmentWarehouse?.id || rows[0]?.id;
|
||||
const preferred = rows.find((w) => w.id === preferredWarehouseId) || rows[0];
|
||||
logisticsForm.setFieldsValue({
|
||||
logisticsCompany: '',
|
||||
trackingNo: '',
|
||||
manualQueryUrl: '',
|
||||
});
|
||||
const rows = await loadWarehouses();
|
||||
const preferredWarehouseId = res.fulfillmentWarehouseId || res.fulfillmentWarehouse?.id;
|
||||
const preferred =
|
||||
rows.find((w) => w.id === preferredWarehouseId) ||
|
||||
rows.find((w) => w.cityId === (res.city?.id || res.cityId)) ||
|
||||
rows[0];
|
||||
const defaults = preferred
|
||||
? warehouseToDefaults(preferred, shipDefaults)
|
||||
: (shipDefaults ?? await request<ShipDefaults>('/admin/orders/ship-defaults').catch(() => null));
|
||||
if (defaults) {
|
||||
setShipDefaults(defaults);
|
||||
applyShipDefaults(defaults, preferred?.id);
|
||||
} else {
|
||||
shipForm.setFieldsValue({ warehouseId: preferred?.id, provider: 'XFX' });
|
||||
}
|
||||
setShipMode(preferred ? 'WAREHOUSE' : 'EXPRESS');
|
||||
setShipModalOpen(true);
|
||||
@@ -242,9 +250,21 @@ export default function OrdersPage() {
|
||||
try {
|
||||
const res = await request<OrderDetail>(`/admin/orders/${shipTarget.id}/ship`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(values),
|
||||
body: JSON.stringify({
|
||||
provider: values.provider || 'XFX',
|
||||
warehouseId: values.warehouseId,
|
||||
weight: values.weight,
|
||||
payMode: values.payMode,
|
||||
remark: values.remark,
|
||||
fromName: values.fromName,
|
||||
fromMobile: values.fromMobile,
|
||||
fromAddress: values.fromAddress,
|
||||
fromAddressDetail: values.fromAddressDetail,
|
||||
fromLng: values.fromLng,
|
||||
fromLat: values.fromLat,
|
||||
}),
|
||||
});
|
||||
message.success('仓配发货成功');
|
||||
message.success('选仓发货成功');
|
||||
setShipTarget(res);
|
||||
setDetail(res);
|
||||
setShipModalOpen(false);
|
||||
@@ -258,12 +278,22 @@ export default function OrdersPage() {
|
||||
|
||||
async function submitLogisticsShip() {
|
||||
if (!shipTarget) return;
|
||||
const values = await logisticsForm.validateFields();
|
||||
const values = await logisticsForm.validateFields(['logisticsCompany', 'trackingNo', 'manualQueryUrl']);
|
||||
const logisticsCompany = String(values.logisticsCompany ?? '').trim();
|
||||
const trackingNo = String(values.trackingNo ?? '').trim();
|
||||
if (!logisticsCompany || !trackingNo) {
|
||||
message.error('请填写快递公司与运单号');
|
||||
return;
|
||||
}
|
||||
setLogisticsShipping(true);
|
||||
try {
|
||||
const res = await request<OrderDetail>(`/admin/orders/${shipTarget.id}/logistics-ship`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(values),
|
||||
body: JSON.stringify({
|
||||
logisticsCompany,
|
||||
trackingNo,
|
||||
manualQueryUrl: String(values.manualQueryUrl ?? '').trim() || undefined,
|
||||
}),
|
||||
});
|
||||
message.success('快递单已录入');
|
||||
setShipTarget(res);
|
||||
@@ -593,13 +623,14 @@ export default function OrdersPage() {
|
||||
open={shipModalOpen}
|
||||
onCancel={() => setShipModalOpen(false)}
|
||||
width={560}
|
||||
destroyOnClose
|
||||
destroyOnClose={false}
|
||||
forceRender
|
||||
footer={
|
||||
<Space>
|
||||
<Button onClick={() => setShipModalOpen(false)}>取消</Button>
|
||||
{shipMode === 'WAREHOUSE' ? (
|
||||
<Button type="primary" loading={shipping} onClick={() => void submitShip()}>
|
||||
确认仓配发货
|
||||
确认选仓发货
|
||||
</Button>
|
||||
) : (
|
||||
<Button type="primary" loading={logisticsShipping} onClick={() => void submitLogisticsShip()}>
|
||||
@@ -629,21 +660,26 @@ export default function OrdersPage() {
|
||||
buttonStyle="solid"
|
||||
style={{ marginBottom: 16 }}
|
||||
options={[
|
||||
{ value: 'WAREHOUSE', label: '仓库配送(小飞侠)' },
|
||||
{ value: 'WAREHOUSE', label: '选仓配送' },
|
||||
{ value: 'EXPRESS', label: '填写快递单号' },
|
||||
]}
|
||||
/>
|
||||
|
||||
{shipMode === 'WAREHOUSE' ? (
|
||||
<Form form={shipForm} layout="vertical" size="small">
|
||||
<div style={{ display: shipMode === 'WAREHOUSE' ? 'block' : 'none' }}>
|
||||
<Form form={shipForm} layout="vertical" size="small" preserve>
|
||||
<Form.Item
|
||||
name="warehouseId"
|
||||
label="履约仓库"
|
||||
label="选择仓库"
|
||||
rules={[{ required: true, message: '请选择仓库' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder={warehouses.length ? '选择仓库' : '该城市暂无仓库'}
|
||||
options={warehouses.map((w) => ({ value: w.id, label: w.name }))}
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={warehouses.length ? '选择仓库' : '暂无可用仓库'}
|
||||
options={warehouses.map((w) => ({
|
||||
value: w.id,
|
||||
label: w.cityName ? `${w.cityName} · ${w.name}` : w.name,
|
||||
}))}
|
||||
onChange={(id) => void onWarehouseChange(id)}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -696,22 +732,32 @@ export default function OrdersPage() {
|
||||
}]}
|
||||
/>
|
||||
</Form>
|
||||
) : (
|
||||
<Form form={logisticsForm} layout="vertical" size="small">
|
||||
</div>
|
||||
|
||||
<div style={{ display: shipMode === 'EXPRESS' ? 'block' : 'none' }}>
|
||||
<Form form={logisticsForm} layout="vertical" size="small" preserve>
|
||||
<Typography.Paragraph type="secondary" style={{ fontSize: 12, marginTop: 0 }}>
|
||||
与仓库端手动发货一致:填写快递公司与运单号即可完成发货
|
||||
填写快递公司与运单号即可完成发货
|
||||
</Typography.Paragraph>
|
||||
<Form.Item name="logisticsCompany" label="快递公司" rules={[{ required: true, message: '请填写快递公司' }]}>
|
||||
<Form.Item
|
||||
name="logisticsCompany"
|
||||
label="快递公司"
|
||||
rules={[{ required: true, message: '请填写快递公司' }]}
|
||||
>
|
||||
<Input placeholder="如 顺丰速运、京东物流" />
|
||||
</Form.Item>
|
||||
<Form.Item name="trackingNo" label="运单号" rules={[{ required: true, message: '请填写运单号' }]}>
|
||||
<Input />
|
||||
<Form.Item
|
||||
name="trackingNo"
|
||||
label="运单号"
|
||||
rules={[{ required: true, message: '请填写运单号' }]}
|
||||
>
|
||||
<Input placeholder="请输入运单号" />
|
||||
</Form.Item>
|
||||
<Form.Item name="manualQueryUrl" label="物流查询链接(可选)">
|
||||
<Input placeholder="https://..." />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user