采购单修改
This commit is contained in:
@@ -48,7 +48,7 @@ class ProductController extends BaseMiniController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$pageSize = (int) $request->input('pageSize', 10);
|
$pageSize = (int) $request->input('pageSize', 10);
|
||||||
$paginator = $query->orderBy('sort')
|
$paginator = $query->orderBy('sort', 'desc')
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->paginate($pageSize);
|
->paginate($pageSize);
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class ProductController extends BaseController
|
|||||||
$params,
|
$params,
|
||||||
ProductModel::query()->with(['category:id,name', 'supplier:id,name'])
|
ProductModel::query()->with(['category:id,name', 'supplier:id,name'])
|
||||||
)
|
)
|
||||||
->orderBy('sort')
|
->orderBy('sort', 'desc')
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
->paginate($pageSize);
|
->paginate($pageSize);
|
||||||
$data->getCollection()->makeVisible('cost_price');
|
$data->getCollection()->makeVisible('cost_price');
|
||||||
|
|||||||
+54
-284
@@ -5,7 +5,6 @@ import {
|
|||||||
Drawer,
|
Drawer,
|
||||||
Empty,
|
Empty,
|
||||||
Form,
|
Form,
|
||||||
Image,
|
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
message,
|
message,
|
||||||
@@ -32,8 +31,6 @@ import type IPurchaseOrder from '@/domain/iPurchaseOrder.ts';
|
|||||||
import type {
|
import type {
|
||||||
IBill,
|
IBill,
|
||||||
IBillPrepare,
|
IBillPrepare,
|
||||||
IPurchaseCell,
|
|
||||||
IPurchaseCellItem,
|
|
||||||
IPurchaseDetail,
|
IPurchaseDetail,
|
||||||
IPurchaseDetailRow,
|
IPurchaseDetailRow,
|
||||||
IPurchaseStoreItem,
|
IPurchaseStoreItem,
|
||||||
@@ -43,7 +40,6 @@ import type {
|
|||||||
PurchaseStoreItemUpdateParams,
|
PurchaseStoreItemUpdateParams,
|
||||||
} from '@/domain/iPurchaseOrder.ts';
|
} from '@/domain/iPurchaseOrder.ts';
|
||||||
import { PURCHASE_STATUS_MAP, BILL_STATUS_MAP } from '@/domain/iPurchaseOrder.ts';
|
import { PURCHASE_STATUS_MAP, BILL_STATUS_MAP } from '@/domain/iPurchaseOrder.ts';
|
||||||
import { STORE_ORDER_STATUS_MAP } from '@/domain/iStoreOrder.ts';
|
|
||||||
import {
|
import {
|
||||||
addPurchaseStoreItem,
|
addPurchaseStoreItem,
|
||||||
exportPurchase,
|
exportPurchase,
|
||||||
@@ -51,14 +47,11 @@ import {
|
|||||||
exportPurchaseSuppliers,
|
exportPurchaseSuppliers,
|
||||||
generateBill,
|
generateBill,
|
||||||
getBillPrepare,
|
getBillPrepare,
|
||||||
getPurchaseCell,
|
|
||||||
getPurchaseDetail,
|
getPurchaseDetail,
|
||||||
getPurchaseStoreSummary,
|
getPurchaseStoreSummary,
|
||||||
removePurchaseStoreItem,
|
removePurchaseStoreItem,
|
||||||
type BillGenerateStoreParams,
|
type BillGenerateStoreParams,
|
||||||
type PurchaseCellUpdateParams,
|
|
||||||
type PurchaseRowUpdateParams,
|
type PurchaseRowUpdateParams,
|
||||||
updatePurchaseCellItem,
|
|
||||||
updatePurchaseRow,
|
updatePurchaseRow,
|
||||||
updatePurchaseStoreItem,
|
updatePurchaseStoreItem,
|
||||||
} from '@/api/purchase/order.ts';
|
} from '@/api/purchase/order.ts';
|
||||||
@@ -105,22 +98,9 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
const [completing, setCompleting] = useState(false);
|
const [completing, setCompleting] = useState(false);
|
||||||
|
|
||||||
// 行内编辑:正在编辑的单元格(点击 品名/供应商/包规/单位/成本 进入编辑态)
|
// 行内编辑:供应商选项(品名/供应商/包规/单位/成本与各门店订货量直接在表格中编辑)
|
||||||
const [editingCell, setEditingCell] = useState<{ productId: number; dataIndex: InlineEditField } | null>(null);
|
|
||||||
const [suppliers, setSuppliers] = useState<ISupplier[]>([]);
|
const [suppliers, setSuppliers] = useState<ISupplier[]>([]);
|
||||||
|
|
||||||
// 单元格下钻弹窗(门店 × 商品订货明细)
|
|
||||||
const [cellOpen, setCellOpen] = useState(false);
|
|
||||||
const [cellLoading, setCellLoading] = useState(false);
|
|
||||||
const [cellData, setCellData] = useState<IPurchaseCell | null>(null);
|
|
||||||
const [cellQuery, setCellQuery] = useState<{ productId: number; storeId: number } | null>(null);
|
|
||||||
|
|
||||||
// 单元格明细编辑(数量/称重)
|
|
||||||
const [cellItemOpen, setCellItemOpen] = useState(false);
|
|
||||||
const [cellItemTarget, setCellItemTarget] = useState<IPurchaseCellItem | null>(null);
|
|
||||||
const [cellItemSaving, setCellItemSaving] = useState(false);
|
|
||||||
const [cellItemForm] = Form.useForm<PurchaseCellUpdateParams>();
|
|
||||||
|
|
||||||
// 门店购买详情(按商品聚合的门店采购汇总)
|
// 门店购买详情(按商品聚合的门店采购汇总)
|
||||||
const [detailTab, setDetailTab] = useState('items');
|
const [detailTab, setDetailTab] = useState('items');
|
||||||
const [storeId, setStoreId] = useState<number>(0);
|
const [storeId, setStoreId] = useState<number>(0);
|
||||||
@@ -243,13 +223,6 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
getSupplierOptions().then((res) => setSuppliers(res.data.data ?? []));
|
getSupplierOptions().then((res) => setSuppliers(res.data.data ?? []));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 单元格下钻弹窗打开时加载明细
|
|
||||||
useEffect(() => {
|
|
||||||
if (cellOpen && cellQuery) {
|
|
||||||
loadCell();
|
|
||||||
}
|
|
||||||
}, [cellOpen, cellQuery]);
|
|
||||||
|
|
||||||
// 详情加载后默认选中第一个门店(当前选中门店仍在采购单内则保留)
|
// 详情加载后默认选中第一个门店(当前选中门店仍在采购单内则保留)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (detail && detail.stores.length > 0) {
|
if (detail && detail.stores.length > 0) {
|
||||||
@@ -375,75 +348,17 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
await refreshAfterStoreItemChange();
|
await refreshAfterStoreItemChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 打开单元格下钻:门店 + 商品 → 该采购单下全部订货明细 */
|
|
||||||
const openCell = (row: IPurchaseDetailRow, store: { id: number; name: string }) => {
|
|
||||||
setCellQuery({ productId: row.product_id, storeId: store.id });
|
|
||||||
setCellData(null);
|
|
||||||
setCellOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 加载单元格明细 */
|
|
||||||
const loadCell = async () => {
|
|
||||||
if (!detail || !cellQuery) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setCellLoading(true);
|
|
||||||
try {
|
|
||||||
const res = await getPurchaseCell(detail.purchase.id!, cellQuery.productId, cellQuery.storeId);
|
|
||||||
setCellData(res.data.data ?? null);
|
|
||||||
} finally {
|
|
||||||
setCellLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 单元格明细修改/同步后:刷新弹窗、采购单详情与列表 */
|
|
||||||
const refreshAfterCellChange = async () => {
|
|
||||||
await loadCell();
|
|
||||||
if (detail) {
|
|
||||||
await loadDetail(detail.purchase.id!);
|
|
||||||
}
|
|
||||||
await tableRef.current?.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const openCellItemEdit = (item: IPurchaseCellItem) => {
|
|
||||||
setCellItemTarget(item);
|
|
||||||
cellItemForm.setFieldsValue({
|
|
||||||
quantity: item.quantity,
|
|
||||||
price: Number(item.price ?? 0),
|
|
||||||
weight: Number(item.weight ?? 0),
|
|
||||||
});
|
|
||||||
setCellItemOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 提交单元格明细修改:级联重算明细金额、订货单与采购单汇总 */
|
|
||||||
const handleCellItemSave = async (values: PurchaseCellUpdateParams) => {
|
|
||||||
if (!cellItemTarget?.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setCellItemSaving(true);
|
|
||||||
try {
|
|
||||||
await updatePurchaseCellItem(cellItemTarget.id, values);
|
|
||||||
message.success('明细已更新,订货单与采购单汇总已重算');
|
|
||||||
setCellItemOpen(false);
|
|
||||||
setCellItemTarget(null);
|
|
||||||
await refreshAfterCellChange();
|
|
||||||
} finally {
|
|
||||||
setCellItemSaving(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 商品明细是否可行内编辑(进行中采购单 + 修改权限) */
|
/** 商品明细是否可行内编辑(进行中采购单 + 修改权限) */
|
||||||
const canUpdateRow = detail?.purchase.status === 0 && auth('purchase.order.update');
|
const canUpdateRow = detail?.purchase.status === 0 && auth('purchase.order.update');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 行内编辑保存:合并该行当前值整行提交(后端要求全字段);未变更 / 校验失败时不请求。
|
* 行内编辑保存:合并该行当前值整行提交(后端要求全字段);未变更 / 校验失败时不请求。
|
||||||
* Enter 或失焦触发保存,保存后关闭编辑态并刷新详情与列表。
|
* Enter 或失焦触发保存,保存后刷新详情与列表。
|
||||||
*/
|
*/
|
||||||
const handleInlineSave = async (row: IPurchaseDetailRow, dataIndex: InlineEditField, rawValue: string | number) => {
|
const handleInlineSave = async (row: IPurchaseDetailRow, dataIndex: InlineEditField, rawValue: string | number) => {
|
||||||
if (!detail) {
|
if (!detail) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setEditingCell(null);
|
|
||||||
|
|
||||||
const params: PurchaseRowUpdateParams = {
|
const params: PurchaseRowUpdateParams = {
|
||||||
product_name: row.product_name,
|
product_name: row.product_name,
|
||||||
@@ -492,7 +407,29 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
await tableRef.current?.reload();
|
await tableRef.current?.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 可编辑单元格:点击进入编辑态(文本 Input / 供应商 Select / 成本 InputNumber),Esc 取消 */
|
/**
|
||||||
|
* 门店订货量保存:复用门店单品修改接口(仅提交订货量,单价/称重不改动),
|
||||||
|
* 同商品多笔订单明细合并到最早一条并级联重算;未变更 / 校验失败时不请求。
|
||||||
|
*/
|
||||||
|
const handleStoreCellSave = async (row: IPurchaseDetailRow, storeId: number, rawValue: string) => {
|
||||||
|
if (!detail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const quantity = Number(rawValue);
|
||||||
|
if (rawValue.trim() === '' || !Number.isInteger(quantity) || quantity < 0) {
|
||||||
|
message.warning('订货量无效,已取消修改');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (quantity === row.cells[storeId]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await updatePurchaseStoreItem(detail.purchase.id!, storeId, row.product_id, { quantity });
|
||||||
|
message.success('订货量已更新,订货单与采购单汇总已重算');
|
||||||
|
await loadDetail(detail.purchase.id!);
|
||||||
|
await tableRef.current?.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 行内编辑单元格:直接渲染编辑组件(文本 Input / 供应商 Select / 成本 InputNumber),Enter 或失焦保存 */
|
||||||
const renderEditableCell = (
|
const renderEditableCell = (
|
||||||
row: IPurchaseDetailRow,
|
row: IPurchaseDetailRow,
|
||||||
dataIndex: InlineEditField,
|
dataIndex: InlineEditField,
|
||||||
@@ -501,41 +438,17 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
if (!canUpdateRow) {
|
if (!canUpdateRow) {
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
const editing = editingCell?.productId === row.product_id && editingCell.dataIndex === dataIndex;
|
|
||||||
|
|
||||||
if (!editing) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="-mx-1 cursor-pointer rounded px-1 transition-colors hover:bg-blue-50"
|
|
||||||
title="点击修改"
|
|
||||||
onClick={() => setEditingCell({ productId: row.product_id, dataIndex })}
|
|
||||||
>
|
|
||||||
{display}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelOnEscape = {
|
|
||||||
onKeyDown: (e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
setEditingCell(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dataIndex === 'supplier_id') {
|
if (dataIndex === 'supplier_id') {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
size="small"
|
size="small"
|
||||||
autoFocus
|
|
||||||
defaultOpen
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
defaultValue={row.supplier_id > 0 ? row.supplier_id : undefined}
|
value={row.supplier_id > 0 ? row.supplier_id : undefined}
|
||||||
placeholder="选择供应商"
|
placeholder="选择供应商"
|
||||||
showSearch={{ optionFilterProp: 'label' }}
|
showSearch={{ optionFilterProp: 'label' }}
|
||||||
options={suppliers.map((s) => ({ value: s.id, label: s.name }))}
|
options={suppliers.map((s) => ({ value: s.id, label: s.name }))}
|
||||||
onChange={(value) => void handleInlineSave(row, dataIndex, value)}
|
onChange={(value) => void handleInlineSave(row, dataIndex, value)}
|
||||||
onBlur={() => setEditingCell(null)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -543,8 +456,8 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
if (dataIndex === 'cost_price') {
|
if (dataIndex === 'cost_price') {
|
||||||
return (
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
|
key={`${row.product_id}-cost-${row.cost_price}`}
|
||||||
size="small"
|
size="small"
|
||||||
autoFocus
|
|
||||||
min={0}
|
min={0}
|
||||||
precision={2}
|
precision={2}
|
||||||
prefix="¥"
|
prefix="¥"
|
||||||
@@ -552,20 +465,19 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
defaultValue={Number(row.cost_price)}
|
defaultValue={Number(row.cost_price)}
|
||||||
onPressEnter={(e) => void handleInlineSave(row, dataIndex, (e.target as HTMLInputElement).value)}
|
onPressEnter={(e) => void handleInlineSave(row, dataIndex, (e.target as HTMLInputElement).value)}
|
||||||
onBlur={(e) => void handleInlineSave(row, dataIndex, e.target.value)}
|
onBlur={(e) => void handleInlineSave(row, dataIndex, e.target.value)}
|
||||||
{...cancelOnEscape}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
|
key={`${row.product_id}-${dataIndex}-${row[dataIndex]}`}
|
||||||
size="small"
|
size="small"
|
||||||
autoFocus
|
style={{ textAlign: 'center' }}
|
||||||
defaultValue={row[dataIndex]}
|
defaultValue={row[dataIndex]}
|
||||||
maxLength={dataIndex === 'unit' ? 20 : 100}
|
maxLength={dataIndex === 'unit' ? 20 : 100}
|
||||||
onPressEnter={(e) => void handleInlineSave(row, dataIndex, (e.target as HTMLInputElement).value)}
|
onPressEnter={(e) => void handleInlineSave(row, dataIndex, (e.target as HTMLInputElement).value)}
|
||||||
onBlur={(e) => void handleInlineSave(row, dataIndex, e.target.value)}
|
onBlur={(e) => void handleInlineSave(row, dataIndex, e.target.value)}
|
||||||
{...cancelOnEscape}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -653,7 +565,7 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
title: '市场',
|
title: '市场',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
dataIndex: 'market',
|
dataIndex: 'market',
|
||||||
width: 90,
|
width: 110,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
filters: itemMarketOptions.map((m) => ({ text: m, value: m })),
|
filters: itemMarketOptions.map((m) => ({ text: m, value: m })),
|
||||||
filteredValue: itemColumnFilters.market ?? null,
|
filteredValue: itemColumnFilters.market ?? null,
|
||||||
@@ -663,7 +575,7 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
{
|
{
|
||||||
title: '单价',
|
title: '单价',
|
||||||
key: 'retail_price',
|
key: 'retail_price',
|
||||||
width: 100,
|
width: 110,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (_, row) => {
|
render: (_, row) => {
|
||||||
// 加权平均售价 ÷ 包规数值(无订货数量时无参考价)
|
// 加权平均售价 ÷ 包规数值(无订货数量时无参考价)
|
||||||
@@ -677,27 +589,27 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
title: '包规',
|
title: '包规',
|
||||||
dataIndex: 'product_spec',
|
dataIndex: 'product_spec',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 90,
|
width: 110,
|
||||||
render: (v, row) => renderEditableCell(row, 'product_spec', v || '-'),
|
render: (v, row) => renderEditableCell(row, 'product_spec', v || '-'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '单位',
|
title: '单位',
|
||||||
dataIndex: 'unit',
|
dataIndex: 'unit',
|
||||||
width: 70,
|
width: 110,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (v, row) => renderEditableCell(row, 'unit', v || '-'),
|
render: (v, row) => renderEditableCell(row, 'unit', v || '-'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '成本',
|
title: '成本',
|
||||||
dataIndex: 'cost_price',
|
dataIndex: 'cost_price',
|
||||||
width: 100,
|
width: 110,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (v, row) => renderEditableCell(row, 'cost_price', `¥${Number(v).toFixed(2)}`),
|
render: (v, row) => renderEditableCell(row, 'cost_price', `¥${Number(v).toFixed(2)}`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '合计数量',
|
title: '合计数量',
|
||||||
key: 'total_quantity',
|
key: 'total_quantity',
|
||||||
width: 90,
|
width: 110,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (_, row) => <Text strong>{row.quantity}</Text>,
|
render: (_, row) => <Text strong>{row.quantity}</Text>,
|
||||||
},
|
},
|
||||||
@@ -706,14 +618,27 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
const storeColumns = (detail?.stores ?? []).map((store) => ({
|
const storeColumns = (detail?.stores ?? []).map((store) => ({
|
||||||
title: <span className={'text-[red]'}>{store.name}</span>,
|
title: <span className={'text-[red]'}>{store.name}</span>,
|
||||||
key: `store-${store.id}`,
|
key: `store-${store.id}`,
|
||||||
width: 100,
|
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
render: (_: unknown, row: IPurchaseDetailRow) => {
|
render: (_: unknown, row: IPurchaseDetailRow) => {
|
||||||
const quantity = row.cells[store.id];
|
const quantity = row.cells[store.id];
|
||||||
return quantity !== undefined ? (
|
if (quantity === undefined) {
|
||||||
<Typography.Link onClick={() => openCell(row, store)}>{quantity}</Typography.Link>
|
return <Text type="secondary">-</Text>;
|
||||||
) : (
|
}
|
||||||
<Text type="secondary">-</Text>
|
if (!canUpdateRow) {
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
// 订货量直接在单元格编辑:Enter 或失焦保存(单价/称重不改动)
|
||||||
|
return (
|
||||||
|
<InputNumber
|
||||||
|
key={`${row.product_id}-${store.id}-${quantity}`}
|
||||||
|
size="small"
|
||||||
|
min={0}
|
||||||
|
precision={0}
|
||||||
|
className="w-full"
|
||||||
|
defaultValue={quantity}
|
||||||
|
onPressEnter={(e) => void handleStoreCellSave(row, store.id, (e.target as HTMLInputElement).value)}
|
||||||
|
onBlur={(e) => void handleStoreCellSave(row, store.id, e.target.value)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -1352,7 +1277,7 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
</AuthButton>
|
</AuthButton>
|
||||||
{canUpdateRow && (
|
{canUpdateRow && (
|
||||||
<Text type="secondary" className="text-xs">
|
<Text type="secondary" className="text-xs">
|
||||||
点击 品名/供应商/包规/单位/成本 单元格可直接修改;保存将同步该商品全部订货明细与商品档案,成本变化时各门店单价按等级上浮自动重算
|
修改保存将同步该商品全部订货明细与商品档案,成本变化时各门店单价按等级上浮自动重算
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
@@ -1373,161 +1298,6 @@ const PurchaseOrderPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
{/* 单元格下钻 */}
|
|
||||||
<Modal
|
|
||||||
title={cellData ? `${cellData.store?.name ?? ''} · ${cellData.product?.name ?? ''}` : '门店商品明细'}
|
|
||||||
open={cellOpen}
|
|
||||||
onCancel={() => setCellOpen(false)}
|
|
||||||
footer={null}
|
|
||||||
width={1000}
|
|
||||||
destroyOnHidden
|
|
||||||
styles={{ body: {paddingTop: 16} }}
|
|
||||||
>
|
|
||||||
<Spin spinning={cellLoading}>
|
|
||||||
{cellData && (
|
|
||||||
<>
|
|
||||||
{cellData.items.length === 0 ? (
|
|
||||||
<Empty
|
|
||||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
||||||
description="该门店无此商品明细"
|
|
||||||
className="py-8!"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="overflow-hidden rounded border border-gray-200">
|
|
||||||
<div className="flex bg-gray-50 px-4 py-2 text-sm text-gray-500">
|
|
||||||
<div className="flex-1">商品信息</div>
|
|
||||||
<div className="w-30 shrink-0 text-center">单价</div>
|
|
||||||
<div className="w-26 shrink-0 text-center">订货量</div>
|
|
||||||
<div className="w-33 shrink-0 text-center">订货金额</div>
|
|
||||||
<div className="w-33 shrink-0 text-center">重量</div>
|
|
||||||
<div className="w-40 shrink-0 text-center">操作</div>
|
|
||||||
</div>
|
|
||||||
{cellData.items.map((item) => (
|
|
||||||
<div key={item.id} className="flex items-center border-t border-gray-100 px-4 py-3">
|
|
||||||
<div className="flex min-w-0 flex-1 items-center">
|
|
||||||
<Image.PreviewGroup>
|
|
||||||
{item.image ? (
|
|
||||||
<Image
|
|
||||||
src={item.image}
|
|
||||||
width={48}
|
|
||||||
height={48}
|
|
||||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded bg-gray-100 text-xs text-gray-400">
|
|
||||||
暂无图片
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Image.PreviewGroup>
|
|
||||||
<div className="ml-3 min-w-0">
|
|
||||||
<div className="text-sm font-medium">
|
|
||||||
{item.product_name}
|
|
||||||
<Tag
|
|
||||||
className="ml-2!"
|
|
||||||
color={STORE_ORDER_STATUS_MAP[item.order_status]?.color}
|
|
||||||
>
|
|
||||||
{STORE_ORDER_STATUS_MAP[item.order_status]?.text}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
|
||||||
<div className="mt-0.5 text-xs text-gray-500">
|
|
||||||
订单:{item.order_no}
|
|
||||||
</div>
|
|
||||||
{item.remark ? (
|
|
||||||
<div className="mt-0.5 truncate text-xs text-gray-500">
|
|
||||||
备注:{item.remark}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-30 shrink-0 text-center">¥{item.price}</div>
|
|
||||||
<div className="w-26 shrink-0 text-center">{item.quantity}</div>
|
|
||||||
<div className="w-33 shrink-0 text-center">
|
|
||||||
<Text strong>¥{item.amount}</Text>
|
|
||||||
</div>
|
|
||||||
<div className="w-33 shrink-0 text-center">
|
|
||||||
{item.weight ?? '-'} 斤
|
|
||||||
</div>
|
|
||||||
<div className="w-40 shrink-0 text-center">
|
|
||||||
{detail?.purchase.status === 0 ? (
|
|
||||||
<Space size={0}>
|
|
||||||
<AuthButton auth="purchase.order.update">
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
icon={<EditOutlined />}
|
|
||||||
onClick={() => openCellItemEdit(item)}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
</AuthButton>
|
|
||||||
</Space>
|
|
||||||
) : (
|
|
||||||
<Text type="secondary">-</Text>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{cellData.items.length > 0 && (
|
|
||||||
<div className="mt-3! flex justify-end gap-6 text-sm">
|
|
||||||
<Text type="secondary">
|
|
||||||
合计数量:
|
|
||||||
<Text strong>{cellData.items.reduce((sum, item) => sum + item.quantity, 0)}</Text>
|
|
||||||
</Text>
|
|
||||||
<Text type="secondary">
|
|
||||||
合计订货金额:
|
|
||||||
<Text strong type="danger">
|
|
||||||
¥
|
|
||||||
{cellData.items
|
|
||||||
.reduce((sum, item) => sum + Number(item.amount), 0)
|
|
||||||
.toFixed(2)}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Spin>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* 单元格明细编辑 */}
|
|
||||||
<Modal
|
|
||||||
title={cellItemTarget ? `编辑「${cellItemTarget.product_name}」` : '编辑明细'}
|
|
||||||
open={cellItemOpen}
|
|
||||||
onCancel={() => {
|
|
||||||
setCellItemOpen(false);
|
|
||||||
setCellItemTarget(null);
|
|
||||||
}}
|
|
||||||
onOk={() => cellItemForm.submit()}
|
|
||||||
confirmLoading={cellItemSaving}
|
|
||||||
okText="保存"
|
|
||||||
destroyOnHidden
|
|
||||||
>
|
|
||||||
<div className="py-2 text-gray-500">
|
|
||||||
订单 {cellItemTarget?.order_no};修改保存后,系统将自动重算明细金额、订货单与采购单汇总。
|
|
||||||
</div>
|
|
||||||
<Form form={cellItemForm} layout="vertical" onFinish={handleCellItemSave}>
|
|
||||||
<Form.Item
|
|
||||||
label="单价(元)"
|
|
||||||
name="price"
|
|
||||||
rules={[{ required: true, message: '请输入单价' }]}
|
|
||||||
>
|
|
||||||
<InputNumber className="w-full" min={0} precision={2} placeholder="请输入单价" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label="订货量"
|
|
||||||
name="quantity"
|
|
||||||
rules={[{ required: true, message: '请输入订货量' }]}
|
|
||||||
>
|
|
||||||
<InputNumber className="w-full" min={0} precision={0} placeholder="请输入订货量" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="称重" name="weight" rules={[{ required: true, message: '请输入称重' }]}>
|
|
||||||
<InputNumber className="w-full" min={0} precision={3} placeholder="请输入称重" />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* 生成账单:按门店填写配送费/周转筐/托盘数量与售后金额(商品金额只读,由订单汇总) */}
|
{/* 生成账单:按门店填写配送费/周转筐/托盘数量与售后金额(商品金额只读,由订单汇总) */}
|
||||||
<Modal
|
<Modal
|
||||||
title={billPrepare ? `生成账单 · ${billPrepare.purchase.purchase_no}` : '生成账单'}
|
title={billPrepare ? `生成账单 · ${billPrepare.purchase.purchase_no}` : '生成账单'}
|
||||||
|
|||||||
Reference in New Issue
Block a user