订单详情

This commit is contained in:
liu
2026-08-12 09:20:36 +08:00
parent ce958d690f
commit fa4bf6fe61
3 changed files with 117 additions and 85 deletions
+1 -1
View File
@@ -138,7 +138,7 @@ export default function XinTable<T extends Record<string, any> = any>(props: Xin
// 暴露方法到 tableRef // 暴露方法到 tableRef
useImperativeHandle(tableRef, (): XinTableInstance<T> => ({ useImperativeHandle(tableRef, (): XinTableInstance<T> => ({
reload: async () => { await handleRequest(); }, reload: async () => { await handleRequest(requestParams); },
reset: async () => { reset: async () => {
searchRef.resetFields(); searchRef.resetFields();
setRequestParams({ page: 1, pageSize: 10 }); setRequestParams({ page: 1, pageSize: 10 });
+1
View File
@@ -35,6 +35,7 @@ export interface IStoreOrderItemUpdate {
cost_price: number; cost_price: number;
quantity: number; quantity: number;
weight: number; weight: number;
remark: string;
} }
/** 门店订单 */ /** 门店订单 */
+109 -78
View File
@@ -42,7 +42,7 @@ import { getSupplierOptions } from '@/api/customer/supplier.ts';
import type IStore from '@/domain/iStore.ts'; import type IStore from '@/domain/iStore.ts';
import type ISupplier from '@/domain/iSupplier.ts'; import type ISupplier from '@/domain/iSupplier.ts';
import AuthButton from '@/components/AuthButton'; import AuthButton from '@/components/AuthButton';
import { EditOutlined, SyncOutlined, UnorderedListOutlined } from '@ant-design/icons'; import { EditOutlined, SettingOutlined, SyncOutlined, UnorderedListOutlined } from '@ant-design/icons';
import TextArea from "antd/es/input/TextArea"; import TextArea from "antd/es/input/TextArea";
const { Title, Text } = Typography; const { Title, Text } = Typography;
@@ -57,16 +57,10 @@ const ITEM_EDITABLE_STATUS = [0, 1, 2, 3];
* 状态流转合法路径:待接单→已接单/已取消;采购中→配送中/已完成;配送中→已完成 * 状态流转合法路径:待接单→已接单/已取消;采购中→配送中/已完成;配送中→已完成
* (已接单→采购中 通过「生成采购单」完成,不在流转按钮内) * (已接单→采购中 通过「生成采购单」完成,不在流转按钮内)
*/ */
const NEXT_STATUS: Record<number, { status: number; label: string; danger?: boolean }[]> = { const NEXT_STATUS: Record<number, { status: number; label: string; }> = {
0: [ 0: { status: 1, label: '接单' },
{ status: 1, label: '接单' }, 2: { status: 3, label: '开始配送' },
{ status: 9, label: '取消订单', danger: true }, 3: { status: 4, label: '完成订单' },
],
2: [
{ status: 3, label: '开始配送' },
{ status: 4, label: '完成订单' },
],
3: [{ status: 4, label: '完成订单' }],
}; };
/** 批量流转可选目标状态(弹窗单选,后端全量校验,任一不允许则整批中止) */ /** 批量流转可选目标状态(弹窗单选,后端全量校验,任一不允许则整批中止) */
@@ -201,6 +195,7 @@ const StoreOrderPage: React.FC = () => {
cost_price: Number(item.cost_price ?? 0), cost_price: Number(item.cost_price ?? 0),
quantity: Number(item.quantity ?? 0), quantity: Number(item.quantity ?? 0),
weight: Number(item.weight ?? 0), weight: Number(item.weight ?? 0),
remark: item.remark ?? '',
}); });
setItemEditOpen(true); setItemEditOpen(true);
}; };
@@ -254,28 +249,6 @@ const StoreOrderPage: React.FC = () => {
valueType: 'text', valueType: 'text',
hideInForm: true hideInForm: true
}, },
{
title: '商品信息',
dataIndex: 'items',
hideInForm: true,
hideInSearch: true,
width: 300,
render: (_, record) => {
return (
<Space orientation={'vertical'} className={'max-h-30 w-60 overflow-y-auto pr-6'}>
{ record.items && record.items.length > 0 && record.items.map(item => (
<div className={'flex'}>
<Image src={item.image} width={40} height={40} ></Image>
<div className={'ml-2.5'}>
<div>{item.product_name}</div>
<div>{ item.price } × { item.quantity }</div>
</div>
</div>
))}
</Space>
)
}
},
{ {
title: '基本信息', title: '基本信息',
dataIndex: 'order_no', dataIndex: 'order_no',
@@ -285,15 +258,42 @@ const StoreOrderPage: React.FC = () => {
render: (_, record) => { render: (_, record) => {
return ( return (
<Space orientation={'vertical'}> <Space orientation={'vertical'}>
<div>
<Text type={'secondary'}></Text>
<Tag color="blue">{record.store?.name ?? `门店#${record.store_id}`}</Tag>
</div>
<div> <div>
<Text type={'secondary'}></Text> <Text type={'secondary'}></Text>
<Text copyable={{ text: record.order_no }}>{record.order_no}</Text> <Text copyable={{ text: record.order_no }}>{record.order_no}</Text>
</div> </div>
<div><Text type={'secondary'}></Text>{record.created_at}</div> <div><Text type={'secondary'}></Text>{record.created_at}</div>
<div>
<Text type={'secondary'}></Text>
<Tag color={STORE_ORDER_STATUS_MAP[record.status ?? 0]?.color}>
{STORE_ORDER_STATUS_MAP[record.status ?? 0]?.text}
</Tag>
</div>
</Space>
)
}
},
{
title: '商品信息',
dataIndex: 'items',
hideInForm: true,
hideInSearch: true,
width: 550,
render: (_, record) => {
return (
<Space wrap size={20}>
{ record.items && record.items.length > 0 && record.items.map(item => (
<div className={'flex'}>
<Image src={item.image} width={60} height={60} ></Image>
<div className={'ml-2.5'}>
<div>{item.product_name}</div>
<div className={'text-[12px] text-[#999]'}>{item.product_spec} {item.unit}</div>
<div className={'text-[12px] text-[#999]'}>
<span className={'text-[red]'}>{ item.price } </span> × { item.quantity }
</div>
</div>
</div>
))}
</Space> </Space>
) )
} }
@@ -319,6 +319,10 @@ const StoreOrderPage: React.FC = () => {
width: 300, width: 300,
render: (_, record) => ( render: (_, record) => (
<Space orientation={'vertical'}> <Space orientation={'vertical'}>
<div>
<Text type={'secondary'}></Text>
<Tag color="blue">{record.store?.name ?? `门店#${record.store_id}`}</Tag>
</div>
<div><Text type={'secondary'}></Text>{ record.store?.contact ?? '-' }</div> <div><Text type={'secondary'}></Text>{ record.store?.contact ?? '-' }</div>
<div><Text type={'secondary'}></Text>{ record.store?.phone ?? '-' }</div> <div><Text type={'secondary'}></Text>{ record.store?.phone ?? '-' }</div>
<div><Text type={'secondary'}></Text>{ record.store?.address ?? '-' }</div> <div><Text type={'secondary'}></Text>{ record.store?.address ?? '-' }</div>
@@ -365,16 +369,16 @@ const StoreOrderPage: React.FC = () => {
render: (_, record) => ( render: (_, record) => (
<Space orientation={'vertical'}> <Space orientation={'vertical'}>
<div> <div>
<Text type={'secondary'}></Text> <Text type={'secondary'}></Text>
{record.added_amount} <span className={'text-[red]'}>{record.product_amount} </span>
</div> </div>
<div> <div>
<Text type={'secondary'}></Text> <Text type={'secondary'}></Text>
{record.product_amount} <span className={'text-[red]'}>{record.added_amount} </span>
</div> </div>
<div> <div>
<Text type={'secondary'}></Text> <Text type={'secondary'}></Text>
{record.total_amount} <span className={'text-[red] text-[16px]'}>{record.total_amount} </span>
</div> </div>
</Space> </Space>
) )
@@ -385,17 +389,13 @@ const StoreOrderPage: React.FC = () => {
valueType: 'select', valueType: 'select',
hideInForm: true, hideInForm: true,
align: 'center', align: 'center',
hideInTable: true,
fieldProps: { fieldProps: {
options: Object.entries(STORE_ORDER_STATUS_MAP).map(([value, item]) => ({ options: Object.entries(STORE_ORDER_STATUS_MAP).map(([value, item]) => ({
value: Number(value), value: Number(value),
label: item.text, label: item.text,
})), })),
}, }
render: (_, record) => (
<Tag color={STORE_ORDER_STATUS_MAP[record.status ?? 0]?.color}>
{STORE_ORDER_STATUS_MAP[record.status ?? 0]?.text}
</Tag>
)
}, },
{ {
title: '采购单ID', title: '采购单ID',
@@ -412,37 +412,49 @@ const StoreOrderPage: React.FC = () => {
{ {
title: '操作栏', title: '操作栏',
dataIndex: 'operate', dataIndex: 'operate',
align: 'center',
hideInForm: true, hideInForm: true,
hideInSearch: true, hideInSearch: true,
width: 180,
render: (_, record) => ( render: (_, record) => (
<Space orientation={'vertical'}> <Space size={5} wrap>
<Button key="detail" type={'link'} icon={<UnorderedListOutlined />} onClick={() => openDetail(record.id!)}> <Button
type={'primary'}
</Button> icon={<UnorderedListOutlined />}
{ onClick={() => openDetail(record.id!)}
(NEXT_STATUS[record.status ?? -1] ?? []).map((action) => ( />
<AuthButton key={action.status} auth="order.store.update"> { CONTAINER_EDITABLE_STATUS.includes(record.status ?? -1) && (
<AuthButton key="container" auth="order.store.update">
<Button
icon={<SettingOutlined />}
type={'primary'}
onClick={() => openContainer(record)}
/>
</AuthButton>
)}
{ NEXT_STATUS[record.status!] && (
<AuthButton auth="order.store.update">
<Popconfirm <Popconfirm
title={`确认将订单状态更新为「${action.label}」?`} title={`确认将订单状态更新为「${NEXT_STATUS[record.status!].label}」?`}
onConfirm={() => handleStatusChange(record.id!, action.status)} onConfirm={() => handleStatusChange(record.id!, NEXT_STATUS[record.status!].status)}
> >
<Button type={action.danger ? undefined : 'link'} danger={action.danger}> <Button type={'primary'} color='green' variant="solid">
{action.label} {NEXT_STATUS[record.status!].label}
</Button> </Button>
</Popconfirm> </Popconfirm>
</AuthButton> </AuthButton>
)) )}
} { record.status === 0 && (
{ <AuthButton auth="order.store.update">
CONTAINER_EDITABLE_STATUS.includes(record.status ?? -1) ? ( <Popconfirm
<AuthButton key="container" auth="order.store.update"> title={`确认取消该订单吗?`}
<Button type={'link'} onClick={() => openContainer(record)}> onConfirm={() => handleStatusChange(record.id!, 9)}
>
<Button type={'primary'} color='danger' variant="solid">
</Button> </Button>
</Popconfirm>
</AuthButton> </AuthButton>
) : null )}
}
</Space> </Space>
) )
} }
@@ -497,20 +509,39 @@ const StoreOrderPage: React.FC = () => {
size={1000} size={1000}
loading={detailLoading} loading={detailLoading}
footer={ footer={
detail && NEXT_STATUS[detail.status ?? -1] ? ( detail ? (
<Space className="flex justify-end"> <Space className="flex justify-end" wrap>
{NEXT_STATUS[detail.status!].map((action) => ( {CONTAINER_EDITABLE_STATUS.includes(detail.status ?? -1) && (
<AuthButton key={action.status} auth="order.store.update"> <AuthButton auth="order.store.update">
<Button icon={<SettingOutlined />} onClick={() => openContainer(detail)}>
</Button>
</AuthButton>
)}
{ NEXT_STATUS[detail.status!] && (
<AuthButton auth="order.store.update">
<Popconfirm <Popconfirm
title={`确认将订单状态更新为「${action.label}」?`} title={`确认将订单状态更新为「${NEXT_STATUS[detail.status!].label}」?`}
onConfirm={() => handleStatusChange(detail.id!, action.status)} onConfirm={() => handleStatusChange(detail.id!, NEXT_STATUS[detail.status!].status)}
> >
<Button type={action.danger ? undefined : 'primary'} danger={action.danger}> <Button type={'primary'} color='green' variant="solid">
{action.label} {NEXT_STATUS[detail.status!].label}
</Button> </Button>
</Popconfirm> </Popconfirm>
</AuthButton> </AuthButton>
))} )}
{ detail.status === 0 && (
<AuthButton auth="order.store.update">
<Popconfirm
title={`确认取消该订单吗?`}
onConfirm={() => handleStatusChange(detail.id!, 9)}
>
<Button type={'primary'} color='danger' variant="solid">
</Button>
</Popconfirm>
</AuthButton>
)}
</Space> </Space>
) : null ) : null
} }