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,6 +3,7 @@ import { | ||
3 | postServiceConstInvoiceReissueRecordStatus, | 3 | postServiceConstInvoiceReissueRecordStatus, |
4 | postServiceConstPayees, | 4 | postServiceConstPayees, |
5 | postServiceConstProductCollectBillStatus, | 5 | postServiceConstProductCollectBillStatus, |
6 | + postServiceConstProducts, | ||
6 | postServiceConstStores, | 7 | postServiceConstStores, |
7 | } from '@/services'; | 8 | } from '@/services'; |
8 | import { useCallback } from 'react'; | 9 | import { useCallback } from 'react'; |
@@ -28,11 +29,16 @@ export default () => { | @@ -28,11 +29,16 @@ export default () => { | ||
28 | const result = await postServiceConstStores(); | 29 | const result = await postServiceConstStores(); |
29 | return result.data; | 30 | return result.data; |
30 | }, []); | 31 | }, []); |
32 | + const getProducts = useCallback(async () => { | ||
33 | + const res = await postServiceConstProducts(); | ||
34 | + return res.data; | ||
35 | + }, []); | ||
31 | return { | 36 | return { |
32 | getPayees, | 37 | getPayees, |
33 | getInvoiceReissueRecordStatus, | 38 | getInvoiceReissueRecordStatus, |
34 | getInvoiceFlushStatus, | 39 | getInvoiceFlushStatus, |
35 | getProductCollectBillAuditStatus, | 40 | getProductCollectBillAuditStatus, |
36 | getWarehouse, | 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 | import { RESPONSE_CODE } from '@/constants/enum'; | 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 | export default () => { | 9 | export default () => { |
10 | + const actionRef = useRef<ActionType>(); | ||
8 | const columns = [ | 11 | const columns = [ |
9 | { | 12 | { |
10 | title: '创建时间', | 13 | title: '创建时间', |
@@ -48,10 +51,40 @@ export default () => { | @@ -48,10 +51,40 @@ export default () => { | ||
48 | width: 180, | 51 | width: 180, |
49 | hideInSearch: true, | 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 | return ( | 85 | return ( |
54 | <ProTable | 86 | <ProTable |
87 | + actionRef={actionRef} | ||
55 | columns={columns} | 88 | columns={columns} |
56 | request={async (params) => { | 89 | request={async (params) => { |
57 | const res = await postProcureBillPage({ | 90 | const res = await postProcureBillPage({ |
@@ -78,8 +111,24 @@ export default () => { | @@ -78,8 +111,24 @@ export default () => { | ||
78 | expandedRowRender: (record) => ( | 111 | expandedRowRender: (record) => ( |
79 | <ProTable | 112 | <ProTable |
80 | columns={[ | 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 | { title: '数量', dataIndex: 'number', key: 'number' }, | 129 | { title: '数量', dataIndex: 'number', key: 'number' }, |
130 | + { title: '小计', dataIndex: 'totalPrice', key: 'totalPrice' }, | ||
131 | + { title: '附件', dataIndex: 'annex', key: 'annex' }, | ||
83 | { title: '备注', dataIndex: 'notes', key: 'notes' }, | 132 | { title: '备注', dataIndex: 'notes', key: 'notes' }, |
84 | ]} | 133 | ]} |
85 | headerTitle={false} | 134 | headerTitle={false} |
@@ -92,17 +141,14 @@ export default () => { | @@ -92,17 +141,14 @@ export default () => { | ||
92 | }} | 141 | }} |
93 | search={false} | 142 | search={false} |
94 | dateFormatter="string" | 143 | dateFormatter="string" |
95 | - headerTitle="嵌套表格" | 144 | + headerTitle="采购管理" |
96 | options={false} | 145 | options={false} |
97 | toolBarRender={() => [ | 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,14 +7,6 @@ import { ProTable } from '@ant-design/pro-components'; | ||
7 | import { message } from 'antd'; | 7 | import { message } from 'antd'; |
8 | import { useRef } from 'react'; | 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 | export default () => { | 10 | export default () => { |
19 | const actionRef = useRef<ActionType>(); | 11 | const actionRef = useRef<ActionType>(); |
20 | const columns: ProColumns[] = [ | 12 | const columns: ProColumns[] = [ |
src/pages/product/productCollect/index.tsx
@@ -96,8 +96,6 @@ export default () => { | @@ -96,8 +96,6 @@ export default () => { | ||
96 | hideInTable: true, | 96 | hideInTable: true, |
97 | request: async () => { | 97 | request: async () => { |
98 | const res = await postServiceConstStores(); | 98 | const res = await postServiceConstStores(); |
99 | - console.log('Stores' + JSON.stringify(res)); | ||
100 | - console.log('Stores' + JSON.stringify(res.data)); | ||
101 | return enumToSelect(res.data); | 99 | return enumToSelect(res.data); |
102 | }, | 100 | }, |
103 | }, | 101 | }, |
src/services/request.ts
@@ -16346,6 +16346,60 @@ export const postServiceConstProductCollectBillStatus = /* #__PURE__ */ (() => { | @@ -16346,6 +16346,60 @@ export const postServiceConstProductCollectBillStatus = /* #__PURE__ */ (() => { | ||
16346 | return request; | 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 | /** @description response type for postServiceConstStores */ | 16403 | /** @description response type for postServiceConstStores */ |
16350 | export interface PostServiceConstStoresResponse { | 16404 | export interface PostServiceConstStoresResponse { |
16351 | /** | 16405 | /** |