import React, { useEffect, useRef, useState } from 'react'; import { Button, DatePicker, Descriptions, Drawer, Empty, Form, InputNumber, message, Modal, Popconfirm, Radio, Space, Tag, Typography, Image, } from 'antd'; import dayjs from 'dayjs'; import XinTable from '@/components/XinTable'; import type { XinTableColumn, XinTableInstance, XinTableProps, } from '@/components/XinTable/typings.ts'; import type IStoreOrder from '@/domain/iStoreOrder.ts'; import { STORE_ORDER_STATUS_MAP } from '@/domain/iStoreOrder.ts'; import { getStoreOrder, updateOrderContainer, updateOrderStatus, batchUpdateOrderStatus, deleteStoreOrder, } from '@/api/order/store.ts'; import { generatePurchase } from '@/api/purchase/order.ts'; import { getStoreOptions } from '@/api/customer/store.ts'; import type IStore from '@/domain/iStore.ts'; import AuthButton from '@/components/AuthButton'; import { DeleteOutlined, SettingOutlined, UnorderedListOutlined } from '@ant-design/icons'; const { Title, Text } = Typography; /** 允许修改周转框/托盘数量的订单状态:已接单、采购中、配送中 */ const CONTAINER_EDITABLE_STATUS = [1, 2, 3]; /** * 状态流转合法路径:待接单→已接单/已取消;采购中→配送中/已完成;配送中→已完成 * (已接单→采购中 通过「生成采购单」完成,不在流转按钮内) */ const NEXT_STATUS: Record = { 0: { status: 1, label: '接单' }, 2: { status: 3, label: '开始配送' }, 3: { status: 4, label: '完成订单' }, }; /** 批量流转可选目标状态(弹窗单选,后端全量校验,任一不允许则整批中止) */ const BATCH_TARGET_OPTIONS = [ { value: 1, label: '接单(待接单 → 已接单)' }, { value: 3, label: '开始配送(采购中 → 配送中)' }, { value: 4, label: '完成订单(采购中/配送中 → 已完成)' }, { value: 9, label: '取消订单(待接单 → 已取消)' }, ]; /** * 门店订单管理(只读 + 状态流转,订单由小程序端创建) */ const StoreOrderPage: React.FC = () => { const tableRef = useRef>(null); const [stores, setStores] = useState([]); const [detailOpen, setDetailOpen] = useState(false); const [detail, setDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const [containerOpen, setContainerOpen] = useState(false); const [containerOrder, setContainerOrder] = useState(null); const [containerSaving, setContainerSaving] = useState(false); const [containerForm] = Form.useForm<{ box_num: number; tray_num: number }>(); /** 勾选行(批量流转/生成采购单用) */ const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [batchOpen, setBatchOpen] = useState(false); const [batchTarget, setBatchTarget] = useState(null); const [batchLoading, setBatchLoading] = useState(false); /** 生成采购单(合并已接单订单,生成后源订单转采购中) */ const [generateOpen, setGenerateOpen] = useState(false); const [generateLoading, setGenerateLoading] = useState(false); const [generateForm] = Form.useForm<{ purchase_date: dayjs.Dayjs }>(); useEffect(() => { getStoreOptions().then((res) => setStores(res.data.data ?? [])); }, []); const openDetail = async (id: number) => { setDetailOpen(true); setDetailLoading(true); try { const res = await getStoreOrder(id); setDetail(res.data.data ?? null); } finally { setDetailLoading(false); } }; const handleStatusChange = async (id: number, status: number) => { await updateOrderStatus(id, status); message.success('状态已更新'); setDetailOpen(false); await tableRef.current?.reload(); }; /** 批量状态流转:后端全量校验,任一订单不允许流转则整批中止 */ const handleBatchStatus = async () => { if (batchTarget === null) { message.warning('请选择要流转的目标状态'); return; } setBatchLoading(true); try { const res = await batchUpdateOrderStatus(selectedRowKeys as number[], batchTarget); message.success(`已批量流转 ${res.data.data?.success ?? 0} 单`); setBatchOpen(false); setBatchTarget(null); setSelectedRowKeys([]); await tableRef.current?.reload(); } finally { setBatchLoading(false); } }; /** 生成采购单:勾选时合并所选订单(须均为已接单),未勾选时合并全部已接单订单 */ const handleGeneratePurchase = async (values: { purchase_date: dayjs.Dayjs }) => { setGenerateLoading(true); try { const orderIds = selectedRowKeys.length > 0 ? selectedRowKeys.map(Number) : undefined; const res = await generatePurchase(values.purchase_date.format('YYYY-MM-DD'), orderIds); message.success(`采购单 ${res.data.data?.purchase_no} 已生成,源订单已转为采购中`); setGenerateOpen(false); setSelectedRowKeys([]); await tableRef.current?.reload(); } finally { setGenerateLoading(false); } }; const openContainer = (record: IStoreOrder) => { setContainerOrder(record); containerForm.setFieldsValue({ box_num: record.box_num, tray_num: record.tray_num }); setContainerOpen(true); }; const handleContainerSave = async (values: { box_num: number; tray_num: number }) => { if (!containerOrder?.id) return; setContainerSaving(true); try { await updateOrderContainer(containerOrder.id, values); message.success('周转框/托盘数量已更新'); setContainerOpen(false); await tableRef.current?.reload(); } finally { setContainerSaving(false); } }; /** 删除订单(软删除,仅已取消订单可删;删除后后台/小程序端均不可见) */ const handleDelete = async (id: number) => { await deleteStoreOrder(id); message.success('订单已删除'); setDetailOpen(false); await tableRef.current?.reload(); }; // 弹窗内实时预览:附加金额 = 框×单价 + 托盘×单价;订单总金额 = 商品金额 + 附加金额 const watchBoxNum = Number(Form.useWatch('box_num', containerForm) ?? 0); const watchTrayNum = Number(Form.useWatch('tray_num', containerForm) ?? 0); const previewAdded = watchBoxNum * Number(containerOrder?.box_price ?? 0) + watchTrayNum * Number(containerOrder?.tray_price ?? 0); const previewTotal = Number(containerOrder?.product_amount ?? 0) + previewAdded; const columns: XinTableColumn[] = [ { title: '订单号', hideInTable: true, dataIndex: 'order_no', valueType: 'text', hideInForm: true }, { title: '商品名称', hideInTable: true, dataIndex: 'product_name', valueType: 'text', hideInForm: true }, { title: '基本信息', dataIndex: 'order_no', hideInForm: true, hideInSearch: true, width: 300, render: (_, record) => { return (
订单号: {record.order_no}
订货时间:{record.created_at}
订单状态: {STORE_ORDER_STATUS_MAP[record.status ?? 0]?.text}
) } }, { title: '商品信息', dataIndex: 'items', hideInForm: true, hideInSearch: true, width: 550, render: (_, record) => { return ( { record.items && record.items.length > 0 && record.items.map(item => (
{item.product_name}
{item.product_spec} {item.unit}
{ item.price } ¥ × { item.quantity }
))}
) } }, { title: '门店', dataIndex: 'store_id', valueType: 'select', hideInForm: true, hideInTable: true, fieldProps: { options: stores.map((s) => ({ label: s.name, value: s.id })), showSearch: true, optionFilterProp: 'label', }, render: (_, record) => record.store?.name ?? '-', }, { title: '门店信息', dataIndex: 'store_id', hideInForm: true, hideInSearch: true, width: 300, render: (_, record) => (
订货门店: {record.store?.name ?? `门店#${record.store_id}`}
联系人:{ record.store?.contact ?? '-' }
联系电话:{ record.store?.phone ?? '-' }
门店地址:{ record.store?.address ?? '-' }
), }, { title: '订货日期', dataIndex: 'order_date', valueType: 'dateRange', hideInForm: true, hideInTable: true, align: 'center', }, { title: '附加信息', hideInForm: true, dataIndex: 'status', hideInSearch: true, width: 200, render: (_, record) => (
订货数量: {record.total_quantity}
周转框数量: {record.box_num}
周转托盘数量: {record.tray_num}
) }, { title: '订单金额', hideInForm: true, dataIndex: 'box_num', hideInSearch: true, width: 200, render: (_, record) => (
商品总金额: {record.product_amount} ¥
附加总金额: {record.added_amount} ¥
订单总金额: {record.total_amount} ¥
) }, { title: '订单状态', dataIndex: 'status', valueType: 'select', hideInForm: true, align: 'center', hideInTable: true, fieldProps: { options: Object.entries(STORE_ORDER_STATUS_MAP).map(([value, item]) => ({ value: Number(value), label: item.text, })), } }, { title: '采购单ID', dataIndex: 'purchase_id', valueType: 'digit', hideInForm: true, }, { title: '账单ID', dataIndex: 'statement_id', valueType: 'digit', hideInForm: true, }, { title: '操作栏', dataIndex: 'operate', hideInForm: true, hideInSearch: true, width: 180, render: (_, record) => ( )} { record.status === 0 && ( handleStatusChange(record.id!, 9)} > )} { record.status === 9 && ( handleDelete(record.id!)} > , , dom.keywordSearch, ], }; return ( <>
门店订单 小程序下单为待接单;接单后通过「生成采购单」合并为采购单并转采购中,采购中可批量流转为配送中或已完成。
{...tableProps} /> setDetailOpen(false)} size={1000} loading={detailLoading} footer={ detail ? ( {CONTAINER_EDITABLE_STATUS.includes(detail.status ?? -1) && ( )} { NEXT_STATUS[detail.status!] && ( handleStatusChange(detail.id!, NEXT_STATUS[detail.status!].status)} > )} { detail.status === 0 && ( handleStatusChange(detail.id!, 9)} > )} { detail.status === 9 && ( handleDelete(detail.id!)} > )} ) : null } > {detail ? ( <> {/* 门店信息 */} {detail.store?.name ?? `门店#${detail.store_id}`} {detail.store?.contact ?? '-'} {detail.store?.phone ?? '-'} {detail.store?.address ?? '-'} {/* 订单信息 */} {detail.order_no} {detail.order_date} {STORE_ORDER_STATUS_MAP[detail.status ?? 0]?.text} {detail.total_quantity} {detail.box_num} {detail.tray_num} {detail.remark ? ( {detail.remark} ) : null} {/* 商品明细(商城模式:首图 + 单价 × 订货量 + 金额) */} 商品明细
商品信息
供应商
单价
订货量
订货金额
重量
{(detail.items ?? []).map((item) => (
{item.image ? ( ) : (
暂无图片
)}
{item.product_name}
{item.product_spec || '-'} {item.unit || '-'}
成本:¥{item.cost_price ?? '0.00'}
{item.remark ? (
备注:{item.remark}
) : null}
{item.supplier?.name ?? '-'}
¥{item.price}
{item.quantity}
¥{item.amount}
{item.weight ?? '-'} 斤
))} {(detail.items ?? []).length === 0 && ( )}
{/* 附加信息 */}
周转框金额({detail.box_num} × ¥{detail.box_price ?? '0.00'}) ¥{detail.box_amount ?? '0.00'}
周转托盘金额({detail.tray_num} × ¥{detail.tray_price ?? '0.00'}) ¥{detail.tray_amount ?? '0.00'}
商品总金额 ¥{detail.product_amount}
订单总金额 ¥{detail.total_amount}
) : null}
{/* 生成采购单:合并已接单订单 */} setGenerateOpen(false)} onOk={() => generateForm.submit()} confirmLoading={generateLoading} okText="确认生成" destroyOnHidden >
{selectedRowKeys.length > 0 ? `将合并选中的 ${selectedRowKeys.length} 笔订单生成一张采购单(须均为已接单状态,否则整批中止)。` : '未勾选订单时,将合并全部「已接单」订单生成一张采购单。'} 生成后源订单自动转为「采购中」。
{/* 批量状态流转 */} setBatchOpen(false)} onOk={handleBatchStatus} confirmLoading={batchLoading} okText="确认流转" destroyOnHidden >
已勾选 {selectedRowKeys.length} 笔订单;任一订单不满足流转条件时,本次批量操作将整批中止,不做任何修改。
setBatchTarget(e.target.value as number)} > {BATCH_TARGET_OPTIONS.map((opt) => ( {opt.label} ))}
{/* 修改周转框/托盘数量 */} setContainerOpen(false)} onOk={() => containerForm.submit()} confirmLoading={containerSaving} okText="保存" destroyOnHidden >
订单 {containerOrder?.order_no},保存后将按单价自动重算附加金额与订单总金额。
商品总金额:¥{containerOrder?.product_amount ?? '0.00'}
附加总金额: ¥{previewAdded.toFixed(2)}
订单总金额: ¥{previewTotal.toFixed(2)}
); }; export default StoreOrderPage;