182 lines
6.3 KiB
TypeScript
Raw Normal View History

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 { createStockCheck, listProducts, listStockChecks } from '@/services/api';
import type { Product, 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' },
};
interface DetailRow { key: number; productId?: string; actualQuantity?: string }
let rowKey = 0;
const columns: ProColumns<StockCheck>[] = [
{ title: '盘点单号', dataIndex: 'checkNo' },
{ title: '盘点日期', dataIndex: 'checkDate', valueType: 'date', search: false },
{ title: '盘点人', dataIndex: 'checker', search: false },
{
title: '状态',
dataIndex: 'status',
search: false,
render: (_, record) => {
const s = STATUS_MAP[record.status] ?? STATUS_MAP[0];
return <Tag color={s.color}>{s.text}</Tag>;
},
},
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
{ title: '操作', valueType: 'option', render: () => [<a key="view"></a>] },
];
const ChecksPage: React.FC = () => {
const actionRef = useRef<ActionType>();
const [form] = Form.useForm();
const [open, setOpen] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [products, setProducts] = useState<Product[]>([]);
const [details, setDetails] = useState<DetailRow[]>([]);
useEffect(() => {
listProducts({ page: 1, pageSize: 500 }).then((res) => {
if (res?.code === 0) setProducts(res.data?.list || []);
});
}, []);
const handleOpen = () => {
setDetails([]);
setOpen(true);
};
const handleSubmit = async () => {
let values: { checkDate: string; checker?: string; remark?: string };
try {
values = await form.validateFields();
} catch {
return;
}
setSubmitting(true);
try {
const res = await createStockCheck({
...values,
details: details
.filter((r) => r.productId && r.actualQuantity)
.map((r) => ({ productId: r.productId!, actualQuantity: r.actualQuantity! })),
});
if (res?.code !== 0) { message.error(res?.msg || '创建失败'); return; }
setOpen(false);
actionRef.current?.reload();
message.success('盘点单已创建');
} catch {
message.error('创建失败');
} finally {
setSubmitting(false);
}
};
const addRow = () => setDetails((prev) => [...prev, { key: rowKey++ }]);
const removeRow = (idx: number) => setDetails((prev) => prev.filter((_, i) => i !== idx));
const updateRow = (idx: number, field: keyof DetailRow, value: string) =>
setDetails((prev) => prev.map((r, i) => (i === idx ? { ...r, [field]: value } : r)));
const today = new Date().toISOString().slice(0, 10);
return (
<>
<ProTable<StockCheck>
actionRef={actionRef}
headerTitle="库存盘点"
rowKey="checkId"
columns={columns}
search={false}
request={async (params) => {
const res = await listStockChecks({ page: params.current, pageSize: params.pageSize });
return { data: res?.data?.list || [], total: res?.data?.total || 0, success: res?.code === 0 };
}}
toolBarRender={() => [
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={handleOpen}></Button>,
]}
/>
<Modal
title="创建盘点单"
open={open}
onOk={handleSubmit}
onCancel={() => setOpen(false)}
confirmLoading={submitting}
width={680}
afterOpenChange={(o) => { if (!o) form.resetFields(); }}
>
<Form form={form} layout="vertical" initialValues={{ checkDate: today, checker: '', remark: '' }}>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="checkDate" label="盘点日期" rules={[{ required: true, message: '请选择日期' }]}>
<Input type="date" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="checker" label="盘点人">
<Input placeholder="可选" />
</Form.Item>
</Col>
</Row>
<Form.Item name="remark" label="备注">
<Input.TextArea rows={2} />
</Form.Item>
</Form>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<span style={{ fontWeight: 500 }}></span>
<Button size="small" icon={<PlusOutlined />} onClick={addRow}></Button>
</div>
<Table
size="small"
rowKey="key"
dataSource={details}
pagination={false}
columns={[
{
title: '产品',
render: (_, __, idx) => (
<Select
style={{ width: '100%' }}
showSearch
placeholder="选择产品"
value={details[idx].productId}
optionFilterProp="label"
options={products.map((p) => ({ value: p.productId, label: `${p.productName}${p.spec ? ' ' + p.spec : ''}${p.color ? ' ' + p.color : ''}` }))}
onChange={(v) => updateRow(idx, 'productId', v)}
/>
),
},
{
title: '实盘数量(米)',
width: 140,
render: (_, __, idx) => (
<Input
type="number"
min={0}
step="0.01"
value={details[idx].actualQuantity}
onChange={(e) => updateRow(idx, 'actualQuantity', e.target.value)}
/>
),
},
{
title: '',
width: 40,
render: (_, __, idx) => (
<Button type="text" danger icon={<DeleteOutlined />} onClick={() => removeRow(idx)} />
),
},
]}
/>
</Modal>
</>
);
};
export default ChecksPage;