feat: add detail pages for stock checks and adjusts
Both list pages had 查看 links with no onClick. Added detail pages showing header info and line items, with action buttons (确认开始盘点 / 审核通过+驳回). Also: extended StockCheck/StockAdjust types to include details and extra fields; added /inventory/checks/:id and /inventory/adjusts/:id routes; moved columns definitions inside components (module-level columns with history.push were silently ignored by the browser due to MFSU caching); switched to Link component for SPA navigation.
This commit is contained in:
parent
c08a9a4854
commit
7545799fdc
@ -63,7 +63,9 @@ export default defineConfig({
|
||||
{ name: '库存查询', path: '/inventory/stocks', component: './Inventory/Stocks' },
|
||||
{ name: '库存统计', path: '/inventory/stats', component: './Inventory/Stats' },
|
||||
{ name: '库存盘点', path: '/inventory/checks', component: './Inventory/Checks' },
|
||||
{ name: '盘点单详情', path: '/inventory/checks/:id', component: './Inventory/Checks/detail', hideInMenu: true },
|
||||
{ name: '库存调整', path: '/inventory/adjusts', component: './Inventory/Adjusts' },
|
||||
{ name: '调整单详情', path: '/inventory/adjusts/:id', component: './Inventory/Adjusts/detail', hideInMenu: true },
|
||||
{ name: 'Excel导入', path: '/inventory/import', component: './Inventory/Import' },
|
||||
{ name: '库存变动记录', path: '/inventory/logs', component: './Inventory/Logs' },
|
||||
],
|
||||
|
||||
98
src/pages/Inventory/Adjusts/detail.tsx
Normal file
98
src/pages/Inventory/Adjusts/detail.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Button, Card, Descriptions, Modal, Space, Tag, Typography, message } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { history, useParams } from '@umijs/max';
|
||||
import { approveStockAdjust, getStockAdjust } from '@/services/api';
|
||||
import type { StockAdjust } from '@/types/api';
|
||||
|
||||
const STATUS_MAP: Record<number, { text: string; color: string }> = {
|
||||
0: { text: '待审核', color: 'warning' },
|
||||
1: { text: '已审核', color: 'success' },
|
||||
2: { text: '已驳回', color: 'error' },
|
||||
};
|
||||
|
||||
const AdjustDetailPage: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const [adjust, setAdjust] = useState<StockAdjust | null>(null);
|
||||
|
||||
const load = async () => {
|
||||
if (!id) return;
|
||||
const res = await getStockAdjust(id);
|
||||
if (res?.code === 0) setAdjust(res.data as StockAdjust);
|
||||
};
|
||||
|
||||
useEffect(() => { load(); }, [id]);
|
||||
|
||||
const handleApprove = (action: number) => {
|
||||
Modal.confirm({
|
||||
title: action === 1 ? '确认审核通过?此操作将实际调整库存。' : '确认驳回此调整单?',
|
||||
okType: action === 2 ? 'danger' : 'primary',
|
||||
onOk: async () => {
|
||||
await approveStockAdjust(id!, action);
|
||||
message.success(action === 1 ? '已审核通过' : '已驳回');
|
||||
load();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (!adjust) return null;
|
||||
|
||||
const status = STATUS_MAP[adjust.status] ?? STATUS_MAP[0];
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
<Space>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => history.back()}>返回</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>调整单详情 — {adjust.adjustNo}</Typography.Title>
|
||||
</Space>
|
||||
|
||||
<Card>
|
||||
<Descriptions column={3} bordered size="small">
|
||||
<Descriptions.Item label="调整单号">{adjust.adjustNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="调整日期">{adjust.adjustDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="调整原因">{adjust.adjustReason || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="操作人">{adjust.operator || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="审核人">{adjust.approver || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">
|
||||
<Tag color={status.color}>{status.text}</Tag>
|
||||
</Descriptions.Item>
|
||||
{adjust.remark && <Descriptions.Item label="备注" span={3}>{adjust.remark}</Descriptions.Item>}
|
||||
</Descriptions>
|
||||
{adjust.status === 0 && (
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Button type="primary" onClick={() => handleApprove(1)}>审核通过</Button>
|
||||
<Button danger onClick={() => handleApprove(2)}>驳回</Button>
|
||||
</Space>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="调整明细">
|
||||
<ProTable
|
||||
headerTitle={false}
|
||||
rowKey="detailId"
|
||||
search={false}
|
||||
pagination={false}
|
||||
dataSource={adjust.details || []}
|
||||
columns={[
|
||||
{ title: '产品', dataIndex: 'productName' },
|
||||
{ title: '调整前数量(米)', dataIndex: 'beforeQuantity' },
|
||||
{
|
||||
title: '调整数量(米)',
|
||||
dataIndex: 'adjustQuantity',
|
||||
render: (v: string) => {
|
||||
const n = parseFloat(v);
|
||||
const color = n > 0 ? '#52c41a' : n < 0 ? '#ff4d4f' : undefined;
|
||||
return <span style={{ color }}>{n > 0 ? `+${v}` : v}</span>;
|
||||
},
|
||||
},
|
||||
{ title: '调整后数量(米)', dataIndex: 'afterQuantity' },
|
||||
{ title: '备注', dataIndex: 'remark', render: (v: string) => v || '-' },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdjustDetailPage;
|
||||
@ -2,6 +2,7 @@ import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { Button, Col, Form, Input, Modal, Row, Select, Table, Tag, message } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Link } from '@umijs/max';
|
||||
import { createStockAdjust, listProducts, listStockAdjusts } from '@/services/api';
|
||||
import type { Product, StockAdjust } from '@/types/api';
|
||||
|
||||
@ -14,7 +15,8 @@ const STATUS_MAP: Record<number, { text: string; color: string }> = {
|
||||
interface DetailRow { key: number; productId?: string; adjustQuantity?: string }
|
||||
let rowKey = 0;
|
||||
|
||||
const columns: ProColumns<StockAdjust>[] = [
|
||||
const AdjustsPage: React.FC = () => {
|
||||
const columns: ProColumns<StockAdjust>[] = [
|
||||
{ title: '调整单号', dataIndex: 'adjustNo' },
|
||||
{ title: '调整日期', dataIndex: 'adjustDate', valueType: 'date', search: false },
|
||||
{ title: '调整原因', dataIndex: 'adjustReason', search: false },
|
||||
@ -29,10 +31,8 @@ const columns: ProColumns<StockAdjust>[] = [
|
||||
},
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
|
||||
{ title: '操作', valueType: 'option', render: () => [<a key="view">查看</a>] },
|
||||
];
|
||||
|
||||
const AdjustsPage: React.FC = () => {
|
||||
{ title: '操作', valueType: 'option', render: (_, record) => [<Link key="view" to={`/inventory/adjusts/${record.adjustId}`}>查看</Link>] },
|
||||
];
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [form] = Form.useForm();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
94
src/pages/Inventory/Checks/detail.tsx
Normal file
94
src/pages/Inventory/Checks/detail.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Button, Card, Descriptions, Modal, Space, Tag, Typography, message } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { history, useParams } from '@umijs/max';
|
||||
import { confirmStockCheck, getStockCheck } from '@/services/api';
|
||||
import type { StockCheck } from '@/types/api';
|
||||
|
||||
const STATUS_MAP: Record<number, { text: string; color: string }> = {
|
||||
0: { text: '草稿', color: 'default' },
|
||||
1: { text: '进行中', color: 'processing' },
|
||||
2: { text: '已完成', color: 'success' },
|
||||
};
|
||||
|
||||
const CheckDetailPage: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const [check, setCheck] = useState<StockCheck | null>(null);
|
||||
|
||||
const load = async () => {
|
||||
if (!id) return;
|
||||
const res = await getStockCheck(id);
|
||||
if (res?.code === 0) setCheck(res.data as StockCheck);
|
||||
};
|
||||
|
||||
useEffect(() => { load(); }, [id]);
|
||||
|
||||
const handleConfirm = () => {
|
||||
Modal.confirm({
|
||||
title: '确认开始盘点?',
|
||||
onOk: async () => {
|
||||
await confirmStockCheck(id!);
|
||||
message.success('已确认');
|
||||
load();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (!check) return null;
|
||||
|
||||
const status = STATUS_MAP[check.status] ?? STATUS_MAP[0];
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
<Space>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => history.back()}>返回</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>盘点单详情 — {check.checkNo}</Typography.Title>
|
||||
</Space>
|
||||
|
||||
<Card>
|
||||
<Descriptions column={3} bordered size="small">
|
||||
<Descriptions.Item label="盘点单号">{check.checkNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="盘点日期">{check.checkDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="盘点人">{check.checker || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">
|
||||
<Tag color={status.color}>{status.text}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间">{check.createdAt}</Descriptions.Item>
|
||||
{check.remark && <Descriptions.Item label="备注" span={3}>{check.remark}</Descriptions.Item>}
|
||||
</Descriptions>
|
||||
{check.status === 0 && (
|
||||
<Button type="primary" style={{ marginTop: 16 }} onClick={handleConfirm}>确认开始盘点</Button>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="盘点明细">
|
||||
<ProTable
|
||||
headerTitle={false}
|
||||
rowKey="detailId"
|
||||
search={false}
|
||||
pagination={false}
|
||||
dataSource={check.details || []}
|
||||
columns={[
|
||||
{ title: '产品', dataIndex: 'productName' },
|
||||
{ title: '账面数量(米)', dataIndex: 'systemQuantity' },
|
||||
{ title: '实盘数量(米)', dataIndex: 'actualQuantity' },
|
||||
{
|
||||
title: '差异数量(米)',
|
||||
dataIndex: 'diffQuantity',
|
||||
render: (v: string) => {
|
||||
const n = parseFloat(v);
|
||||
const color = n > 0 ? '#52c41a' : n < 0 ? '#ff4d4f' : undefined;
|
||||
return <span style={{ color }}>{v}</span>;
|
||||
},
|
||||
},
|
||||
{ title: '差异金额', dataIndex: 'diffAmount', render: (v: string) => `¥ ${v}` },
|
||||
{ title: '备注', dataIndex: 'remark', render: (v: string) => v || '-' },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckDetailPage;
|
||||
@ -2,6 +2,7 @@ import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { Button, Col, Form, Input, Modal, Row, Select, Table, Tag, message } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Link } from '@umijs/max';
|
||||
import { createStockCheck, listProducts, listStockChecks } from '@/services/api';
|
||||
import type { Product, StockCheck } from '@/types/api';
|
||||
|
||||
@ -14,7 +15,8 @@ const STATUS_MAP: Record<number, { text: string; color: string }> = {
|
||||
interface DetailRow { key: number; productId?: string; actualQuantity?: string }
|
||||
let rowKey = 0;
|
||||
|
||||
const columns: ProColumns<StockCheck>[] = [
|
||||
const ChecksPage: React.FC = () => {
|
||||
const columns: ProColumns<StockCheck>[] = [
|
||||
{ title: '盘点单号', dataIndex: 'checkNo' },
|
||||
{ title: '盘点日期', dataIndex: 'checkDate', valueType: 'date', search: false },
|
||||
{ title: '盘点人', dataIndex: 'checker', search: false },
|
||||
@ -28,10 +30,8 @@ const columns: ProColumns<StockCheck>[] = [
|
||||
},
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
|
||||
{ title: '操作', valueType: 'option', render: () => [<a key="view">查看</a>] },
|
||||
];
|
||||
|
||||
const ChecksPage: React.FC = () => {
|
||||
{ title: '操作', valueType: 'option', render: (_, record) => [<Link key="view" to={`/inventory/checks/${record.checkId}`}>查看</Link>] },
|
||||
];
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [form] = Form.useForm();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@ -144,13 +144,39 @@ export interface StockGroup {
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
export interface StockCheckDetail {
|
||||
detailId: string;
|
||||
checkId: string;
|
||||
productId: string;
|
||||
productName: string;
|
||||
systemQuantity: string;
|
||||
actualQuantity: string;
|
||||
diffQuantity: string;
|
||||
diffAmount: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface StockCheck {
|
||||
checkId: string;
|
||||
checkNo: string;
|
||||
checkDate: string;
|
||||
checker: string;
|
||||
status: number;
|
||||
remark?: string;
|
||||
createdAt: string;
|
||||
updatedAt?: string;
|
||||
details: StockCheckDetail[];
|
||||
}
|
||||
|
||||
export interface StockAdjustDetail {
|
||||
detailId: string;
|
||||
adjustId: string;
|
||||
productId: string;
|
||||
productName: string;
|
||||
beforeQuantity: string;
|
||||
adjustQuantity: string;
|
||||
afterQuantity: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface StockAdjust {
|
||||
@ -159,8 +185,12 @@ export interface StockAdjust {
|
||||
adjustDate: string;
|
||||
adjustReason: string;
|
||||
operator: string;
|
||||
approver?: string;
|
||||
status: number;
|
||||
remark?: string;
|
||||
createdAt: string;
|
||||
updatedAt?: string;
|
||||
details: StockAdjustDetail[];
|
||||
}
|
||||
|
||||
// ── System ────────────────────────────────────────
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user