Commit 386bf8b4d3ac6c6637f40861ed42aab056a11905
1 parent
a46cc9ad
feat(product): 优化采购管理功能
- 重构采购管理页面,增加更多筛选条件和操作功能- 新增采购单号、创建人等搜索条件 - 添加采购明细的可编辑表格功能 - 集成产品选择接口,实现产品信息自动填充 -优化附件展示方式,支持多附件查看 - 新增审核功能,支持采购单审核操作
Showing
2 changed files
with
313 additions
and
74 deletions
src/pages/product/procure/index.tsx
1 | 1 | import ButtonConfirm from '@/components/ButtomConfirm'; |
2 | 2 | import { RESPONSE_CODE } from '@/constants/enum'; |
3 | 3 | import AddOrUpdate from '@/pages/product/procure/components/AddOrUpdate'; |
4 | -import { postProcureBillDelete, postProcureBillPage } from '@/services'; | |
4 | +import { | |
5 | + postProcureBillDelete, | |
6 | + postProcureBillPage, | |
7 | + postServiceConstProcureBillAuditStatus, | |
8 | +} from '@/services'; | |
9 | +import { enumToSelect } from '@/utils'; | |
5 | 10 | import { ProTable, type ActionType } from '@ant-design/pro-components'; |
6 | 11 | import { message } from 'antd'; |
7 | 12 | import { useRef } from 'react'; |
13 | +import Audit from './components/Audit'; | |
8 | 14 | |
9 | 15 | export default () => { |
10 | 16 | const actionRef = useRef<ActionType>(); |
11 | 17 | const columns = [ |
12 | 18 | { |
19 | + title: '单号', | |
20 | + dataIndex: 'id', | |
21 | + ellipsis: true, | |
22 | + hideInSearch: true, | |
23 | + }, | |
24 | + { | |
13 | 25 | title: '创建时间', |
14 | 26 | dataIndex: 'createTime', |
15 | 27 | ellipsis: true, |
... | ... | @@ -52,32 +64,84 @@ export default () => { |
52 | 64 | hideInSearch: true, |
53 | 65 | }, |
54 | 66 | { |
67 | + title: '采购单号', | |
68 | + dataIndex: 'id', | |
69 | + ellipsis: true, | |
70 | + hideInTable: true, | |
71 | + }, | |
72 | + { | |
73 | + title: '创建人', | |
74 | + dataIndex: 'createByNameLike', | |
75 | + ellipsis: true, | |
76 | + hideInTable: true, | |
77 | + }, | |
78 | + { | |
79 | + title: '创建时间', | |
80 | + valueType: 'dateTimeRange', | |
81 | + hideInTable: true, | |
82 | + search: { | |
83 | + transform: (value) => { | |
84 | + if (value) { | |
85 | + return { | |
86 | + createTimeGe: value[0], | |
87 | + createTimeLe: value[1], | |
88 | + }; | |
89 | + } | |
90 | + }, | |
91 | + }, | |
92 | + }, | |
93 | + { | |
94 | + title: '审核状态', | |
95 | + valueType: 'select', | |
96 | + key: 'auditStatus', | |
97 | + dataIndex: 'auditStatus', | |
98 | + filters: true, | |
99 | + onFilter: true, | |
100 | + hideInTable: true, | |
101 | + request: async () => { | |
102 | + const res = await postServiceConstProcureBillAuditStatus(); | |
103 | + return enumToSelect(res.data); | |
104 | + }, | |
105 | + }, | |
106 | + { | |
55 | 107 | title: '操作', |
56 | 108 | valueType: 'option', |
57 | 109 | key: 'option', |
58 | 110 | 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); | |
111 | + record.paths?.includes('UPDATE') && ( | |
112 | + <AddOrUpdate | |
113 | + key="update" | |
114 | + record={record} | |
115 | + onfinish={() => { | |
77 | 116 | actionRef.current?.reload(); |
78 | - } | |
79 | - }} | |
80 | - />, | |
117 | + }} | |
118 | + /> | |
119 | + ), | |
120 | + record.paths?.includes('UPDATE') && ( | |
121 | + <ButtonConfirm | |
122 | + key="delete" | |
123 | + className="p-0" | |
124 | + title={'确认删除该记录?'} | |
125 | + text="删除" | |
126 | + onConfirm={async () => { | |
127 | + let res = await postProcureBillDelete({ | |
128 | + query: { id: record.id }, | |
129 | + }); | |
130 | + if (res) { | |
131 | + message.success(res.message); | |
132 | + actionRef.current?.reload(); | |
133 | + } | |
134 | + }} | |
135 | + /> | |
136 | + ), | |
137 | + record.paths?.includes('AUDIT') && ( | |
138 | + <Audit | |
139 | + recordId={record.id} | |
140 | + onClose={() => { | |
141 | + actionRef.current?.reload(); | |
142 | + }} | |
143 | + ></Audit> | |
144 | + ), | |
81 | 145 | ], |
82 | 146 | }, |
83 | 147 | ]; |
... | ... | @@ -128,7 +192,41 @@ export default () => { |
128 | 192 | }, |
129 | 193 | { title: '数量', dataIndex: 'number', key: 'number' }, |
130 | 194 | { title: '小计', dataIndex: 'totalPrice', key: 'totalPrice' }, |
131 | - { title: '附件', dataIndex: 'annex', key: 'annex' }, | |
195 | + { | |
196 | + title: '附件', | |
197 | + dataIndex: 'annex', | |
198 | + key: 'annex', | |
199 | + render: (_, record) => ( | |
200 | + <div> | |
201 | + {record.annexList?.map((url, index) => { | |
202 | + const shortName = | |
203 | + url.split('/').pop()?.slice(0, 15) || | |
204 | + `附件 ${index + 1}`; | |
205 | + return ( | |
206 | + <a | |
207 | + key={index} | |
208 | + href={url} | |
209 | + target="_blank" | |
210 | + rel="noopener noreferrer" | |
211 | + title={url} // 悬停显示完整链接 | |
212 | + style={{ | |
213 | + display: 'block', | |
214 | + marginBottom: '4px', | |
215 | + whiteSpace: 'nowrap', | |
216 | + overflow: 'hidden', | |
217 | + textOverflow: 'ellipsis', | |
218 | + maxWidth: '200px', // 限制显示宽度 | |
219 | + }} | |
220 | + > | |
221 | + {shortName.length < url.split('/').pop()?.length | |
222 | + ? `${shortName}...` | |
223 | + : shortName} | |
224 | + </a> | |
225 | + ); | |
226 | + })} | |
227 | + </div> | |
228 | + ), | |
229 | + }, | |
132 | 230 | { title: '备注', dataIndex: 'notes', key: 'notes' }, |
133 | 231 | ]} |
134 | 232 | headerTitle={false} |
... | ... | @@ -139,7 +237,6 @@ export default () => { |
139 | 237 | /> |
140 | 238 | ), |
141 | 239 | }} |
142 | - search={false} | |
143 | 240 | dateFormatter="string" |
144 | 241 | headerTitle="采购管理" |
145 | 242 | options={false} |
... | ... |
src/pages/product/productCollect/components/AddOrUpdate.tsx
1 | 1 | import { postProductCollectBillAddOrModify } from '@/services'; |
2 | 2 | import { useModel } from '@@/exports'; |
3 | 3 | import { |
4 | + ActionType, | |
5 | + EditableProTable, | |
4 | 6 | ModalForm, |
5 | - ProFormDigit, | |
6 | - ProFormSelect, | |
7 | - ProFormText, | |
7 | + ProCard, | |
8 | + ProColumns, | |
9 | + ProForm, | |
10 | + ProFormDependency, | |
11 | + ProFormField, | |
12 | + ProFormSwitch, | |
8 | 13 | ProFormTextArea, |
9 | 14 | } from '@ant-design/pro-components'; |
10 | 15 | import { Button, Form, message } from 'antd'; |
16 | +import React, { useEffect, useRef, useState } from 'react'; | |
17 | + | |
18 | +export default ({ record, onfinish }) => { | |
19 | + const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]); | |
20 | + const [controlled, setControlled] = useState<boolean>(false); | |
21 | + const [processedRecord, setProcessedRecord] = useState(record); | |
22 | + const formRef = useRef(null); | |
23 | + const editorFormRef = useRef(null); | |
24 | + const { getProducts } = useModel('enum'); | |
25 | + const actionRef = useRef<ActionType>(); | |
26 | + useEffect(() => { | |
27 | + if (record?.details) { | |
28 | + const updateddetails = record.details.map((item) => ({ | |
29 | + ...item, | |
30 | + key: item.key || `key-${Math.random().toString(36).substr(2, 9)}`, // 动态生成唯一 key | |
31 | + })); | |
32 | + setProcessedRecord({ | |
33 | + ...record, | |
34 | + details: updateddetails, | |
35 | + }); | |
36 | + } | |
37 | + }, [record]); | |
38 | + | |
39 | + const columns: ProColumns[] = [ | |
40 | + { | |
41 | + title: '商品', | |
42 | + dataIndex: 'productId', | |
43 | + valueType: 'select', | |
44 | + request: async () => { | |
45 | + const res = await getProducts(); | |
46 | + return res.map((item) => ({ | |
47 | + ...item, | |
48 | + label: item.name, | |
49 | + value: item.id, | |
50 | + productUnitName: item.baseUnitName, | |
51 | + productUnitPrice: item.unitPrice, | |
52 | + })); | |
53 | + }, | |
54 | + fieldProps: (_, { rowIndex }) => ({ | |
55 | + onSelect: (value, option) => { | |
56 | + const currentTableData = editorFormRef.current?.getRowsData?.(); | |
57 | + if (currentTableData) { | |
58 | + const updatedData = [...currentTableData]; | |
59 | + updatedData[rowIndex] = { | |
60 | + ...updatedData[rowIndex], | |
61 | + productUnitName: option.productUnitName, | |
62 | + productUnitPrice: option.productUnitPrice, | |
63 | + }; | |
64 | + formRef.current?.setFieldsValue({ | |
65 | + details: updatedData, | |
66 | + }); | |
67 | + } | |
68 | + }, | |
69 | + }), | |
70 | + }, | |
71 | + { | |
72 | + title: '单位', | |
73 | + dataIndex: 'productUnitName', | |
74 | + valueType: 'text', | |
75 | + editable: false, | |
76 | + }, | |
77 | + { | |
78 | + title: '单价', | |
79 | + dataIndex: 'productUnitPrice', | |
80 | + valueType: 'digit', | |
81 | + editable: false, | |
82 | + }, | |
83 | + { | |
84 | + title: '数量', | |
85 | + dataIndex: 'number', | |
86 | + valueType: 'digit', | |
87 | + }, | |
88 | + { | |
89 | + title: '备注', | |
90 | + dataIndex: 'notes', | |
91 | + valueType: 'textarea', | |
92 | + }, | |
93 | + { | |
94 | + title: '操作', | |
95 | + valueType: 'option', | |
96 | + render: (text, record, _, action) => [ | |
97 | + <a | |
98 | + key="editable" | |
99 | + onClick={() => { | |
100 | + action?.startEditable?.(record.key); | |
101 | + }} | |
102 | + > | |
103 | + 编辑 | |
104 | + </a>, | |
105 | + <a | |
106 | + key="delete" | |
107 | + onClick={() => { | |
108 | + const tableDataSource = formRef.current?.getFieldValue('details'); | |
109 | + formRef.current?.setFieldsValue({ | |
110 | + table: tableDataSource.filter((item) => item.key !== record.key), | |
111 | + }); | |
112 | + }} | |
113 | + > | |
114 | + 删除 | |
115 | + </a>, | |
116 | + ], | |
117 | + }, | |
118 | + ]; | |
11 | 119 | |
12 | -export default ({ record, onFinish }) => { | |
13 | 120 | const [form] = Form.useForm(); |
14 | - const { getWarehouse } = useModel('enum'); | |
15 | 121 | return ( |
16 | 122 | <ModalForm |
123 | + formRef={formRef} | |
124 | + initialValues={processedRecord} | |
125 | + validateTrigger="onBlur" | |
17 | 126 | title="新建表单" |
18 | 127 | trigger={ |
19 | 128 | record?.id ? ( |
... | ... | @@ -24,66 +133,99 @@ export default ({ record, onFinish }) => { |
24 | 133 | } |
25 | 134 | form={form} |
26 | 135 | autoFocusFirstInput |
136 | + width={1500} | |
27 | 137 | modalProps={{ |
28 | 138 | destroyOnClose: true, |
29 | 139 | onCancel: () => console.log('run'), |
30 | 140 | }} |
31 | 141 | submitTimeout={2000} |
32 | 142 | onFinish={async (values) => { |
33 | - let res = await postProductCollectBillAddOrModify({ | |
34 | - data: values, | |
143 | + const res = await postProductCollectBillAddOrModify({ | |
144 | + data: { | |
145 | + ...record, | |
146 | + ...values, | |
147 | + }, | |
35 | 148 | }); |
36 | 149 | if (res) { |
37 | 150 | message.success(res.message); |
38 | - onFinish(); | |
151 | + onfinish(); | |
39 | 152 | } |
40 | 153 | return true; |
41 | 154 | }} |
42 | 155 | > |
43 | - <ProFormDigit | |
44 | - label="id" | |
45 | - name="id" | |
46 | - initialValue={record?.id} | |
47 | - width="sm" | |
48 | - hidden={true} | |
49 | - /> | |
50 | - <ProFormText | |
51 | - width="md" | |
52 | - initialValue={record?.productName} | |
53 | - name="productName" | |
54 | - label="申领物品" | |
55 | - rules={[{ required: true, message: '申领物品必填' }]} | |
56 | - /> | |
57 | - <ProFormDigit | |
58 | - label="申领数量" | |
59 | - name="productNumber" | |
60 | - initialValue={record?.productNumber} | |
61 | - width="sm" | |
62 | - min={1} | |
63 | - rules={[{ required: true, message: '申领数量必填' }]} | |
64 | - /> | |
65 | - <ProFormSelect | |
66 | - width="md" | |
67 | - request={async () => { | |
68 | - const res = await getWarehouse(); | |
69 | - console.log('options:' + res); | |
70 | - let options = Object.entries(res).map(([value, label]) => ({ | |
71 | - label, | |
72 | - value, | |
73 | - })); | |
74 | - console.log('options:' + options); | |
75 | - return options; | |
156 | + <EditableProTable | |
157 | + rowKey="key" | |
158 | + scroll={{ | |
159 | + x: 960, | |
160 | + }} | |
161 | + editableFormRef={editorFormRef} | |
162 | + headerTitle="可编辑表格" | |
163 | + maxLength={5} | |
164 | + name="details" | |
165 | + controlled={controlled} | |
166 | + recordCreatorProps={{ | |
167 | + position: 'bottom', | |
168 | + record: () => ({ | |
169 | + key: `key-${Math.random().toString(36).substr(2, 9)}`, | |
170 | + }), | |
171 | + }} | |
172 | + actionRef={actionRef} | |
173 | + toolBarRender={() => [ | |
174 | + <ProFormSwitch | |
175 | + key="render" | |
176 | + fieldProps={{ | |
177 | + style: { | |
178 | + marginBlockEnd: 0, | |
179 | + }, | |
180 | + checked: controlled, | |
181 | + onChange: (value) => { | |
182 | + setControlled(value); | |
183 | + }, | |
184 | + }} | |
185 | + checkedChildren="数据更新通知 Form" | |
186 | + unCheckedChildren="保存后通知 Form" | |
187 | + noStyle | |
188 | + />, | |
189 | + <Button | |
190 | + key="rows" | |
191 | + onClick={() => { | |
192 | + const rows = editorFormRef.current?.getRowsData?.(); | |
193 | + console.log(rows); | |
194 | + }} | |
195 | + > | |
196 | + 获取 table 的数据 | |
197 | + </Button>, | |
198 | + ]} | |
199 | + columns={columns} | |
200 | + editable={{ | |
201 | + type: 'multiple', | |
202 | + editableKeys, | |
203 | + onChange: setEditableRowKeys, | |
204 | + actionRender: (row, config, defaultDom) => { | |
205 | + return [defaultDom.save, defaultDom.delete, defaultDom.cancel]; | |
206 | + }, | |
76 | 207 | }} |
77 | - // initialValue={record?.warehouseCode} | |
78 | - name="warehouseCode" | |
79 | - label="申领仓库" | |
80 | - rules={[{ required: true, message: '申领仓库为必填项' }]} | |
81 | - /> | |
82 | - <ProFormTextArea | |
83 | - initialValue={record?.applyRemarks} | |
84 | - name="applyRemarks" | |
85 | - label="申领备注" | |
86 | 208 | /> |
209 | + <ProForm.Item> | |
210 | + <ProCard title="表格数据" headerBordered collapsible defaultCollapsed> | |
211 | + <ProFormDependency name={['details']}> | |
212 | + {({ details }) => ( | |
213 | + <ProFormField | |
214 | + ignoreFormItem | |
215 | + fieldProps={{ | |
216 | + style: { | |
217 | + width: '100%', | |
218 | + }, | |
219 | + }} | |
220 | + mode="read" | |
221 | + valueType="jsonCode" | |
222 | + text={JSON.stringify(details)} | |
223 | + /> | |
224 | + )} | |
225 | + </ProFormDependency> | |
226 | + </ProCard> | |
227 | + </ProForm.Item> | |
228 | + <ProFormTextArea name="auditRemarks" label="备注" /> | |
87 | 229 | </ModalForm> |
88 | 230 | ); |
89 | 231 | }; |
... | ... |