Commit 4fff2654574fbdf3dc9459b5e2ab6610736d98e5
1 parent
a1e377cf
feat(product): 采购管理添加产品选择功能
- 在采购管理页面添加产品选择下拉框 - 实现产品数据的异步加载和动态更新 - 优化采购单的创建和编辑功能,支持产品信息的自动填充
Showing
6 changed files
with
360 additions
and
24 deletions
src/models/enum.ts
... | ... | @@ -3,6 +3,7 @@ import { |
3 | 3 | postServiceConstInvoiceReissueRecordStatus, |
4 | 4 | postServiceConstPayees, |
5 | 5 | postServiceConstProductCollectBillStatus, |
6 | + postServiceConstProducts, | |
6 | 7 | postServiceConstStores, |
7 | 8 | } from '@/services'; |
8 | 9 | import { useCallback } from 'react'; |
... | ... | @@ -28,11 +29,16 @@ export default () => { |
28 | 29 | const result = await postServiceConstStores(); |
29 | 30 | return result.data; |
30 | 31 | }, []); |
32 | + const getProducts = useCallback(async () => { | |
33 | + const res = await postServiceConstProducts(); | |
34 | + return res.data; | |
35 | + }, []); | |
31 | 36 | return { |
32 | 37 | getPayees, |
33 | 38 | getInvoiceReissueRecordStatus, |
34 | 39 | getInvoiceFlushStatus, |
35 | 40 | getProductCollectBillAuditStatus, |
36 | 41 | getWarehouse, |
42 | + getProducts, | |
37 | 43 | }; |
38 | 44 | }; | ... | ... |
src/pages/product/procure/components/AddOrUpdate.tsx
0 → 100644
1 | +import { postProcureBillAddOrModify } from '@/services'; | |
2 | +import { useModel } from '@@/exports'; | |
3 | +import { | |
4 | + EditableProTable, | |
5 | + ModalForm, | |
6 | + ProCard, | |
7 | + ProColumns, | |
8 | + ProForm, | |
9 | + ProFormDependency, | |
10 | + ProFormField, | |
11 | + ProFormSwitch, | |
12 | + ProFormTextArea, | |
13 | +} from '@ant-design/pro-components'; | |
14 | +import { Button, Form, message } from 'antd'; | |
15 | +import React, { useEffect, useRef, useState } from 'react'; | |
16 | + | |
17 | +export default ({ record, onfinish }) => { | |
18 | + const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]); | |
19 | + const [controlled, setControlled] = useState<boolean>(false); | |
20 | + const [processedRecord, setProcessedRecord] = useState(record); | |
21 | + const formRef = useRef(null); | |
22 | + const editorFormRef = useRef(null); | |
23 | + const { getProducts } = useModel('enum'); | |
24 | + | |
25 | + // 使用 useEffect 为 procureBillDetailList 中的每个元素添加 key | |
26 | + useEffect(() => { | |
27 | + if (record?.procureBillDetailList) { | |
28 | + const updatedProcureBillDetailList = record.procureBillDetailList.map( | |
29 | + (item) => ({ | |
30 | + ...item, | |
31 | + key: item.key || `key-${Math.random().toString(36).substr(2, 9)}`, // 动态生成唯一 key | |
32 | + }), | |
33 | + ); | |
34 | + setProcessedRecord({ | |
35 | + ...record, | |
36 | + procureBillDetailList: updatedProcureBillDetailList, | |
37 | + }); | |
38 | + } | |
39 | + }, [record]); | |
40 | + | |
41 | + const columns: ProColumns[] = [ | |
42 | + { | |
43 | + title: '商品', | |
44 | + dataIndex: 'productId', | |
45 | + valueType: 'select', | |
46 | + request: async () => { | |
47 | + const res = await getProducts(); | |
48 | + return res.map((item) => ({ | |
49 | + ...item, | |
50 | + label: item.name, | |
51 | + value: item.id, | |
52 | + productUnitName: item.baseUnitName, | |
53 | + productUnitPrice: item.unitPrice, | |
54 | + })); | |
55 | + }, | |
56 | + fieldProps: (_, { rowIndex }) => ({ | |
57 | + onSelect: (value, option) => { | |
58 | + console.log('option111111' + JSON.stringify(option)); | |
59 | + const currentTableData = editorFormRef.current?.getRowsData?.(); | |
60 | + if (currentTableData) { | |
61 | + const updatedData = [...currentTableData]; | |
62 | + updatedData[rowIndex] = { | |
63 | + ...updatedData[rowIndex], | |
64 | + productUnitName: option.productUnitName, | |
65 | + productUnitPrice: option.productUnitPrice, | |
66 | + }; | |
67 | + formRef.current?.setFieldsValue({ | |
68 | + procureBillDetailList: updatedData, | |
69 | + }); | |
70 | + } | |
71 | + }, | |
72 | + }), | |
73 | + }, | |
74 | + { | |
75 | + title: '单位', | |
76 | + dataIndex: 'productUnitName', | |
77 | + valueType: 'text', | |
78 | + editable: false, | |
79 | + }, | |
80 | + { | |
81 | + title: '单价', | |
82 | + dataIndex: 'productUnitPrice', | |
83 | + valueType: 'digit', | |
84 | + editable: false, | |
85 | + }, | |
86 | + { | |
87 | + title: '数量', | |
88 | + dataIndex: 'number', | |
89 | + valueType: 'digit', | |
90 | + }, | |
91 | + { | |
92 | + title: '备注', | |
93 | + dataIndex: 'created_at', | |
94 | + valueType: 'textarea', | |
95 | + }, | |
96 | + { | |
97 | + title: '附件', | |
98 | + dataIndex: 'annex', | |
99 | + render: () => {}, | |
100 | + renderFormItem: () => {}, | |
101 | + }, | |
102 | + { | |
103 | + title: '操作', | |
104 | + valueType: 'option', | |
105 | + render: (text, record, _, action) => [ | |
106 | + <a | |
107 | + key="editable" | |
108 | + onClick={() => { | |
109 | + action?.startEditable?.(record.key); | |
110 | + }} | |
111 | + > | |
112 | + 编辑 | |
113 | + </a>, | |
114 | + <a | |
115 | + key="delete" | |
116 | + onClick={() => { | |
117 | + const tableDataSource = formRef.current?.getFieldValue( | |
118 | + 'procureBillDetailList', | |
119 | + ); | |
120 | + formRef.current?.setFieldsValue({ | |
121 | + table: tableDataSource.filter((item) => item.key !== record.key), | |
122 | + }); | |
123 | + }} | |
124 | + > | |
125 | + 删除 | |
126 | + </a>, | |
127 | + ], | |
128 | + }, | |
129 | + ]; | |
130 | + | |
131 | + const [form] = Form.useForm(); | |
132 | + return ( | |
133 | + <ModalForm | |
134 | + formRef={formRef} | |
135 | + initialValues={processedRecord} | |
136 | + validateTrigger="onBlur" | |
137 | + title="新建表单" | |
138 | + trigger={ | |
139 | + record?.id ? ( | |
140 | + <Button type="link">修改</Button> | |
141 | + ) : ( | |
142 | + <Button type="primary">新建</Button> | |
143 | + ) | |
144 | + } | |
145 | + form={form} | |
146 | + autoFocusFirstInput | |
147 | + modalProps={{ | |
148 | + destroyOnClose: true, | |
149 | + onCancel: () => console.log('run'), | |
150 | + }} | |
151 | + submitTimeout={2000} | |
152 | + onFinish={async (values) => { | |
153 | + const res = await postProcureBillAddOrModify({ | |
154 | + data: { | |
155 | + ...record, | |
156 | + ...values, | |
157 | + }, | |
158 | + }); | |
159 | + if (res) { | |
160 | + message.success(res.message); | |
161 | + onfinish(); | |
162 | + } | |
163 | + return true; | |
164 | + }} | |
165 | + > | |
166 | + <EditableProTable | |
167 | + rowKey="key" | |
168 | + scroll={{ | |
169 | + x: 960, | |
170 | + }} | |
171 | + editableFormRef={editorFormRef} | |
172 | + headerTitle="可编辑表格" | |
173 | + maxLength={5} | |
174 | + name="procureBillDetailList" | |
175 | + controlled={controlled} | |
176 | + recordCreatorProps={{ | |
177 | + position: 'bottom', | |
178 | + record: () => ({ | |
179 | + key: `key-${Math.random().toString(36).substr(2, 9)}`, | |
180 | + }), | |
181 | + }} | |
182 | + toolBarRender={() => [ | |
183 | + <ProFormSwitch | |
184 | + key="render" | |
185 | + fieldProps={{ | |
186 | + style: { | |
187 | + marginBlockEnd: 0, | |
188 | + }, | |
189 | + checked: controlled, | |
190 | + onChange: (value) => { | |
191 | + setControlled(value); | |
192 | + }, | |
193 | + }} | |
194 | + checkedChildren="数据更新通知 Form" | |
195 | + unCheckedChildren="保存后通知 Form" | |
196 | + noStyle | |
197 | + />, | |
198 | + <Button | |
199 | + key="rows" | |
200 | + onClick={() => { | |
201 | + const rows = editorFormRef.current?.getRowsData?.(); | |
202 | + console.log(rows); | |
203 | + }} | |
204 | + > | |
205 | + 获取 table 的数据 | |
206 | + </Button>, | |
207 | + ]} | |
208 | + columns={columns} | |
209 | + editable={{ | |
210 | + type: 'multiple', | |
211 | + editableKeys, | |
212 | + onChange: setEditableRowKeys, | |
213 | + actionRender: (row, config, defaultDom) => { | |
214 | + return [defaultDom.save, defaultDom.delete, defaultDom.cancel]; | |
215 | + }, | |
216 | + }} | |
217 | + /> | |
218 | + <ProForm.Item> | |
219 | + <ProCard title="表格数据" headerBordered collapsible defaultCollapsed> | |
220 | + <ProFormDependency name={['procureBillDetailList']}> | |
221 | + {({ procureBillDetailList }) => ( | |
222 | + <ProFormField | |
223 | + ignoreFormItem | |
224 | + fieldProps={{ | |
225 | + style: { | |
226 | + width: '100%', | |
227 | + }, | |
228 | + }} | |
229 | + mode="read" | |
230 | + valueType="jsonCode" | |
231 | + text={JSON.stringify(procureBillDetailList)} | |
232 | + /> | |
233 | + )} | |
234 | + </ProFormDependency> | |
235 | + </ProCard> | |
236 | + </ProForm.Item> | |
237 | + <ProFormTextArea name="notes" label="备注" /> | |
238 | + </ModalForm> | |
239 | + ); | |
240 | +}; | ... | ... |
src/pages/product/procure/index.tsx
1 | +import ButtonConfirm from '@/components/ButtomConfirm'; | |
1 | 2 | import { RESPONSE_CODE } from '@/constants/enum'; |
2 | -import { postProcureBillPage } from '@/services'; | |
3 | -import { DownOutlined } from '@ant-design/icons'; | |
4 | -import { ProTable } from '@ant-design/pro-components'; | |
5 | -import { Button } from 'antd'; | |
3 | +import AddOrUpdate from '@/pages/product/procure/components/AddOrUpdate'; | |
4 | +import { postProcureBillDelete, postProcureBillPage } from '@/services'; | |
5 | +import { ProTable, type ActionType } from '@ant-design/pro-components'; | |
6 | +import { message } from 'antd'; | |
7 | +import { useRef } from 'react'; | |
6 | 8 | |
7 | 9 | export default () => { |
10 | + const actionRef = useRef<ActionType>(); | |
8 | 11 | const columns = [ |
9 | 12 | { |
10 | 13 | title: '创建时间', |
... | ... | @@ -48,10 +51,40 @@ export default () => { |
48 | 51 | width: 180, |
49 | 52 | hideInSearch: true, |
50 | 53 | }, |
54 | + { | |
55 | + title: '操作', | |
56 | + valueType: 'option', | |
57 | + key: 'option', | |
58 | + render: (text, record) => [ | |
59 | + <AddOrUpdate | |
60 | + key="update" | |
61 | + record={record} | |
62 | + onfinish={() => { | |
63 | + actionRef.current?.reload(); | |
64 | + }} | |
65 | + />, | |
66 | + <ButtonConfirm | |
67 | + key="delete" | |
68 | + className="p-0" | |
69 | + title={'确认删除该记录?'} | |
70 | + text="删除" | |
71 | + onConfirm={async () => { | |
72 | + let res = await postProcureBillDelete({ | |
73 | + query: { id: record.id }, | |
74 | + }); | |
75 | + if (res) { | |
76 | + message.success(res.message); | |
77 | + actionRef.current?.reload(); | |
78 | + } | |
79 | + }} | |
80 | + />, | |
81 | + ], | |
82 | + }, | |
51 | 83 | ]; |
52 | 84 | |
53 | 85 | return ( |
54 | 86 | <ProTable |
87 | + actionRef={actionRef} | |
55 | 88 | columns={columns} |
56 | 89 | request={async (params) => { |
57 | 90 | const res = await postProcureBillPage({ |
... | ... | @@ -78,8 +111,24 @@ export default () => { |
78 | 111 | expandedRowRender: (record) => ( |
79 | 112 | <ProTable |
80 | 113 | columns={[ |
81 | - { title: '商品名称', dataIndex: 'totalPrice', key: 'totalPrice' }, | |
114 | + { | |
115 | + title: '商品名称', | |
116 | + dataIndex: 'productName', | |
117 | + key: 'productName', | |
118 | + }, | |
119 | + { | |
120 | + title: '单位', | |
121 | + dataIndex: 'productUnitName', | |
122 | + key: 'productUnitName', | |
123 | + }, | |
124 | + { | |
125 | + title: '单价', | |
126 | + dataIndex: 'productUnitPrice', | |
127 | + key: 'productUnitPrice', | |
128 | + }, | |
82 | 129 | { title: '数量', dataIndex: 'number', key: 'number' }, |
130 | + { title: '小计', dataIndex: 'totalPrice', key: 'totalPrice' }, | |
131 | + { title: '附件', dataIndex: 'annex', key: 'annex' }, | |
83 | 132 | { title: '备注', dataIndex: 'notes', key: 'notes' }, |
84 | 133 | ]} |
85 | 134 | headerTitle={false} |
... | ... | @@ -92,17 +141,14 @@ export default () => { |
92 | 141 | }} |
93 | 142 | search={false} |
94 | 143 | dateFormatter="string" |
95 | - headerTitle="嵌套表格" | |
144 | + headerTitle="采购管理" | |
96 | 145 | options={false} |
97 | 146 | toolBarRender={() => [ |
98 | - <Button key="show">查看日志</Button>, | |
99 | - <Button key="out"> | |
100 | - 导出数据 | |
101 | - <DownOutlined /> | |
102 | - </Button>, | |
103 | - <Button key="primary" type="primary"> | |
104 | - 创建应用 | |
105 | - </Button>, | |
147 | + <AddOrUpdate | |
148 | + key="add" | |
149 | + record={undefined} | |
150 | + onfinish={() => actionRef.current?.reload()} | |
151 | + />, | |
106 | 152 | ]} |
107 | 153 | /> |
108 | 154 | ); | ... | ... |
src/pages/product/product/index.tsx
... | ... | @@ -7,14 +7,6 @@ import { ProTable } from '@ant-design/pro-components'; |
7 | 7 | import { message } from 'antd'; |
8 | 8 | import { useRef } from 'react'; |
9 | 9 | |
10 | -export const waitTimePromise = async (time: number = 100) => { | |
11 | - return new Promise((resolve) => { | |
12 | - setTimeout(() => { | |
13 | - resolve(true); | |
14 | - }, time); | |
15 | - }); | |
16 | -}; | |
17 | - | |
18 | 10 | export default () => { |
19 | 11 | const actionRef = useRef<ActionType>(); |
20 | 12 | const columns: ProColumns[] = [ | ... | ... |
src/pages/product/productCollect/index.tsx
... | ... | @@ -96,8 +96,6 @@ export default () => { |
96 | 96 | hideInTable: true, |
97 | 97 | request: async () => { |
98 | 98 | const res = await postServiceConstStores(); |
99 | - console.log('Stores' + JSON.stringify(res)); | |
100 | - console.log('Stores' + JSON.stringify(res.data)); | |
101 | 99 | return enumToSelect(res.data); |
102 | 100 | }, |
103 | 101 | }, | ... | ... |
src/services/request.ts
... | ... | @@ -16346,6 +16346,60 @@ export const postServiceConstProductCollectBillStatus = /* #__PURE__ */ (() => { |
16346 | 16346 | return request; |
16347 | 16347 | })(); |
16348 | 16348 | |
16349 | +/** @description response type for postServiceConstProducts */ | |
16350 | +export interface PostServiceConstProductsResponse { | |
16351 | + /** | |
16352 | + * @description | |
16353 | + * OK | |
16354 | + */ | |
16355 | + 200: ServerResult; | |
16356 | + /** | |
16357 | + * @description | |
16358 | + * Created | |
16359 | + */ | |
16360 | + 201: any; | |
16361 | + /** | |
16362 | + * @description | |
16363 | + * Unauthorized | |
16364 | + */ | |
16365 | + 401: any; | |
16366 | + /** | |
16367 | + * @description | |
16368 | + * Forbidden | |
16369 | + */ | |
16370 | + 403: any; | |
16371 | + /** | |
16372 | + * @description | |
16373 | + * Not Found | |
16374 | + */ | |
16375 | + 404: any; | |
16376 | +} | |
16377 | + | |
16378 | +export type PostServiceConstProductsResponseSuccess = | |
16379 | + PostServiceConstProductsResponse[200]; | |
16380 | +/** | |
16381 | + * @description | |
16382 | + * 商品列表 | |
16383 | + * @tags front-const-controller | |
16384 | + * @produces * | |
16385 | + * @consumes application/json | |
16386 | + */ | |
16387 | +export const postServiceConstProducts = /* #__PURE__ */ (() => { | |
16388 | + const method = 'post'; | |
16389 | + const url = '/service/const/products'; | |
16390 | + function request(): Promise<PostServiceConstProductsResponseSuccess> { | |
16391 | + return requester(request.url, { | |
16392 | + method: request.method, | |
16393 | + }) as unknown as Promise<PostServiceConstProductsResponseSuccess>; | |
16394 | + } | |
16395 | + | |
16396 | + /** http method */ | |
16397 | + request.method = method; | |
16398 | + /** request url */ | |
16399 | + request.url = url; | |
16400 | + return request; | |
16401 | +})(); | |
16402 | + | |
16349 | 16403 | /** @description response type for postServiceConstStores */ |
16350 | 16404 | export interface PostServiceConstStoresResponse { |
16351 | 16405 | /** | ... | ... |