Commit 576a38ce95fa74baff8d977d2480d9d06bbdfffd

Authored by boyang
2 parents 3fa11755 850ffdd2

Merge remote-tracking branch 'origin/IntegralRecord'

# Conflicts:
#	src/pages/ResearchGroup/constant.tsx
src/pages/Order/FeedBack/OrderDrawer copy.tsx deleted 100644 → 0
1 -import { RESPONSE_CODE } from '@/constants/enum';  
2 -import {  
3 - postKingdeeRepCustomer,  
4 - postKingdeeRepCustomerDetail,  
5 - postKingdeeRepMaterial,  
6 - postKingdeeRepMaterialUnit,  
7 - postKingdeeRepMeasureUnit,  
8 - postServiceOrderAddOrder,  
9 - postServiceOrderQuerySalesCode,  
10 - postServiceOrderUpdateOrder,  
11 -} from '@/services';  
12 -import {  
13 - enumToSelect,  
14 - getAliYunOSSFileNameFromUrl,  
15 - getUserInfo,  
16 -} from '@/utils';  
17 -import { getTeacherCustomFieldNumber } from '@/utils/kingdee';  
18 -import {  
19 - DrawerForm,  
20 - FormListActionType,  
21 - ProCard,  
22 - ProFormDateTimePicker,  
23 - ProFormDigit,  
24 - ProFormList,  
25 - ProFormSelect,  
26 - ProFormText,  
27 - ProFormTextArea,  
28 - ProFormUploadDragger,  
29 -} from '@ant-design/pro-components';  
30 -import { Button, Form, message } from 'antd';  
31 -import { cloneDeep } from 'lodash';  
32 -import { useEffect, useRef, useState } from 'react';  
33 -import KingdeeCustomerModal from './KingdeeCustomerModal';  
34 -import {  
35 - INVOCING_STATUS_OPTIONS,  
36 - INVOCING_STATUS_OPTIONS_OLD,  
37 - PAYMENT_CHANNEL_OPTIONS,  
38 - PAYMENT_METHOD_OPTIONS,  
39 - PRODUCT_BELONG_DEPARTMENT_OPTIONS,  
40 -} from './constant';  
41 -  
42 -export default ({ onClose, data, subOrders, orderOptType }) => {  
43 - const [invoicingStatus, setInvoicingStatus] = useState('');  
44 - const [salesCodeOptions, setSalesCodeOptions] = useState([]);  
45 - const [customer, setCustomer] = useState({});  
46 - const [kingdeeCstomerModalVisible, setKingdeeCstomerModalVisible] =  
47 - useState(false);  
48 - const [  
49 - productParametersDisabledFlagList,  
50 - setProductParametersDisabledFlagList,  
51 - ] = useState([]);  
52 - // const [productInvStockOptionsList, setProductInvStockOptionsList] = useState(  
53 - // [],  
54 - // ); //商品的仓库选项  
55 - const [productUnitOptionsList, setProductUnitOptionsList] = useState([]); //商品的单位选项  
56 - const [productCustomerContactOptions, setProductCustomerContactOptions] =  
57 - useState([]); //客户的收货人选项  
58 - const [form] = Form.useForm<{  
59 - salesCode: '';  
60 - customerName: '';  
61 - customerContactNumber: '';  
62 - institution: '';  
63 - institutionContactName: '';  
64 - customerShippingAddress: '';  
65 - totalPayment: '';  
66 - paymentChannel: '';  
67 - paymentMethod: '';  
68 - productBelongBusiness: '';  
69 - invoicingStatus: '';  
70 - invoiceIdentificationNumber: '';  
71 - invoicingTime: '';  
72 - bank: '';  
73 - bankAccountNumber: '';  
74 - deleteSubOrderLists: [];  
75 - notes: '';  
76 - list: [  
77 - {  
78 - productCode: '';  
79 - productName: '';  
80 - quantity: '';  
81 - productPrice: '';  
82 - parameters: '';  
83 - subOrderPayment: '';  
84 - unit: '';  
85 - serialNumber: '';  
86 - notes: '';  
87 - },  
88 - ];  
89 - }>();  
90 -  
91 - let originSubOrders = cloneDeep(subOrders);  
92 - /**  
93 - * 获取当前的操作类型boolean值  
94 - * @param type 操作类型,如果与当前匹配返回true  
95 - */  
96 - function optType(type: string) {  
97 - return orderOptType === type;  
98 - }  
99 -  
100 - /**  
101 - *  
102 - * @returns 获取开票选项  
103 - */  
104 - function getInvoicingSelect() {  
105 - if (optType('edit')) {  
106 - return enumToSelect(INVOCING_STATUS_OPTIONS_OLD);  
107 - }  
108 - return enumToSelect(INVOCING_STATUS_OPTIONS);  
109 - }  
110 -  
111 - const fileList: any = [];  
112 -  
113 - const getSalesCodeOptions = async () => {  
114 - const res = await postServiceOrderQuerySalesCode();  
115 - let options = res.data?.map((item) => {  
116 - return {  
117 - label: item.userName,  
118 - value: item.userName,  
119 - number: item.number,  
120 - };  
121 - });  
122 - setSalesCodeOptions(options);  
123 -  
124 - if (optType('copy') || optType('edit')) {  
125 - let includeFlag = false;  
126 - //销售代码校验,如果是旧的销售代码,则提示并清空  
127 - for (let option of options) {  
128 - if (option.value === data.salesCode) {  
129 - includeFlag = true;  
130 - }  
131 - }  
132 - if (!includeFlag) {  
133 - form.resetFields(['salesCode']);  
134 - message.warning('检测到销售代码为旧的,已清空,请重新选择');  
135 - }  
136 - }  
137 - };  
138 -  
139 - //复制的时候,如果是不需要开票,要把开票信息清空  
140 - if (optType('copy') && data.invoicingStatus === 'UN_INVOICE') {  
141 - data.invoiceIdentificationNumber = undefined;  
142 - }  
143 -  
144 - if (subOrders !== undefined && subOrders.length > 0) {  
145 - data.list = subOrders;  
146 - }  
147 -  
148 - const actionRef = useRef<  
149 - FormListActionType<{  
150 - name: string;  
151 - }>  
152 - >();  
153 -  
154 - useEffect(() => {  
155 - form.setFieldsValue({ ...data });  
156 - //如果是新建,需要清空list  
157 - if (optType('add')) {  
158 - form.resetFields(['list']);  
159 - }  
160 - }, [data]);  
161 -  
162 - /**  
163 - * 选择客户后自动为收货人Select添加选项,填充课题组和单位信息  
164 - * @param option 客户选项  
165 - */  
166 - async function autoFillCustomerContactSelectOptions(customerId: any) {  
167 - //查询单位详细信息  
168 - let res = await postKingdeeRepCustomerDetail({  
169 - data: {  
170 - id: customerId,  
171 - },  
172 - });  
173 -  
174 - //erp客户名称  
175 - form.setFieldValue('erpCustomerName', res?.name);  
176 -  
177 - //重新设置当前option  
178 - form.setFieldValue('erpCustomerId', {  
179 - label: res?.name,  
180 - value: res?.id,  
181 - id: res?.id,  
182 - });  
183 -  
184 - //查询客户自定义字段,课题组  
185 - let entity_number = await getTeacherCustomFieldNumber();  
186 -  
187 - //在单位详细信息中拿到自定义字段的值  
188 - let customField = res?.custom_field;  
189 - if (customField) {  
190 - let teacherName = customField[entity_number];  
191 - //填充到课题组老师表单字段中  
192 - form.setFieldValue('institutionContactName', teacherName);  
193 - }  
194 -  
195 - //单位名称,从客户名称中获取,客户名称规则<单位名称>-<联系人名称和电话>  
196 - let namePortions = res?.name?.split('-');  
197 - if (namePortions && namePortions.length >= 2) {  
198 - form.setFieldValue('institution', namePortions[0]);  
199 - }  
200 -  
201 - //如果原来的收货信息没有包含在这次查询出来的收货人选项中,那么清除原来的收货人信息  
202 - let existFlag = false;  
203 -  
204 - //填充收货人选项  
205 - let newProductCustomerContactOptions = res?.bomentity?.map((item) => {  
206 - let address =  
207 - item.contact_person + ',' + item.mobile + ',' + item.contact_address;  
208 - if (address === data.contactAddress) {  
209 - existFlag = true;  
210 - }  
211 - return { ...item, label: address, value: address };  
212 - });  
213 -  
214 - setProductCustomerContactOptions(newProductCustomerContactOptions);  
215 -  
216 - if (!existFlag) {  
217 - //清空原来的收货人信息  
218 - form.setFieldValue('customerShippingAddress', undefined);  
219 - form.setFieldValue('customerContactNumber', undefined);  
220 - form.setFieldValue('customerName', undefined);  
221 - form.setFieldValue('erpCustomerAddress', undefined);  
222 - }  
223 - }  
224 -  
225 - /**  
226 - * 回显金蝶信息  
227 - */  
228 - async function showKindeeInfo() {  
229 - //客户信息  
230 - if (data.customerId) {  
231 - //客户回显  
232 - autoFillCustomerContactSelectOptions(data.customerId);  
233 - }  
234 -  
235 - //商品单位回显  
236 - let list = data?.subOrderInformationLists;  
237 - if (list) {  
238 - let newProductUnitOptionsList = [...productUnitOptionsList];  
239 - for (let i = 0; i < list.length; i++) {  
240 - newProductUnitOptionsList[i] = [  
241 - { label: list[i].unit, value: list[i].unitId },  
242 - ];  
243 - }  
244 - setProductUnitOptionsList(newProductUnitOptionsList);  
245 - }  
246 - }  
247 -  
248 - /**  
249 - *  
250 - * @param option 商品名称所对应的商品数据  
251 - * @param currentRowData list中当前行的数据  
252 - */  
253 - async function autoFillProductInfo(  
254 - option: any,  
255 - currentRowData: any,  
256 - index: any,  
257 - ) {  
258 - let newProductParametersDisabledFlagList = [  
259 - ...productParametersDisabledFlagList,  
260 - ];  
261 - let newProductUnitOptionsList = [...productUnitOptionsList];  
262 - newProductUnitOptionsList[index] = [];  
263 -  
264 - //是新增商品  
265 - if (option.type === 'add') {  
266 - //商品参数开放权限可以编辑  
267 - newProductParametersDisabledFlagList[index] = false;  
268 -  
269 - //清空商品信息  
270 - let copyList = form.getFieldValue('list');  
271 - let currentData = copyList[index];  
272 - currentData.productCode = undefined;  
273 - currentData.parameters = undefined;  
274 - currentData.unit = undefined;  
275 - currentData.subOrderPayment = undefined;  
276 - currentData.quantity = undefined;  
277 - currentData.notes = undefined;  
278 - currentData.productPrice = undefined;  
279 - form.setFieldValue('list', copyList);  
280 -  
281 - //查询计量单价列表  
282 - let res = await postKingdeeRepMeasureUnit({ data: {} });  
283 - if (res && res?.rows) {  
284 - for (let row of res?.rows) {  
285 - newProductUnitOptionsList[index].push({  
286 - label: row.name,  
287 - value: row.id,  
288 - });  
289 - }  
290 - }  
291 - } else {  
292 - //选择的是已有的商品,进行内容自动填充  
293 - let copyList = form.getFieldValue('list');  
294 - let currentData = copyList[index];  
295 - currentData.productCode = option?.number;  
296 - currentData.parameters = option?.model;  
297 - currentData.unit = option?.base_unit_name;  
298 -  
299 - //商品id  
300 - currentData.materialId = option?.id;  
301 -  
302 - //单位  
303 - currentData.unit = option.base_unit_name;  
304 - currentData.unitId = option.base_unit_id;  
305 -  
306 - form.setFieldValue('list', copyList);  
307 -  
308 - //商品所在的仓库选项填充  
309 - // let res = await postKingdeeRepMaterialStock({  
310 - // data: {  
311 - // material_id: option.id,  
312 - // },  
313 - // });  
314 - // let newProductInvStockOptionsList = [...productInvStockOptionsList];  
315 - // newProductInvStockOptionsList[index] = res?.rows?.map((item) => {  
316 - // return { label: item.inv_stock, value: item.inv_stock_id };  
317 - // });  
318 - // setProductInvStockOptionsList(newProductInvStockOptionsList);  
319 -  
320 - //商品单位填充,查询商品单位列表  
321 - let res = await postKingdeeRepMaterialUnit({  
322 - data: { material_id: option.id },  
323 - });  
324 - if (res && res.rows) {  
325 - for (let row of res.rows) {  
326 - newProductUnitOptionsList[index].push({  
327 - label: row.unit_name,  
328 - value: row.unit_id,  
329 - });  
330 - }  
331 - }  
332 - //商品参数不允许编辑  
333 - newProductParametersDisabledFlagList[index] = true;  
334 - }  
335 -  
336 - setProductParametersDisabledFlagList(newProductParametersDisabledFlagList);  
337 - setProductUnitOptionsList(newProductUnitOptionsList);  
338 - }  
339 -  
340 - /**  
341 - * 选择收货人后自动填充信息  
342 - * @param option 收货人信息  
343 - */  
344 - async function autoFillCustomerInfo(option: any) {  
345 - form.setFieldValue('customerShippingAddress', option.contact_address);  
346 - form.setFieldValue('customerContactNumber', option.mobile);  
347 - form.setFieldValue('customerName', option.contact_person);  
348 -  
349 - //erp收货地址:需要与客户联系人中的地址一样:姓名,手机号,地址  
350 - form.setFieldValue('contactAddress', option.value);  
351 - }  
352 -  
353 - /**  
354 - * 填充销售代表的信息  
355 - * @param option  
356 - */  
357 - function autoFillSalesInfo(option: any) {  
358 - console.log(option);  
359 - //销售代表对应职员编码填充  
360 - form.setFieldValue('empNumber', option.number);  
361 - }  
362 -  
363 - /**  
364 - * 选择商品单位后自动填充  
365 - * @param option  
366 - * @param index  
367 - */  
368 - function autoFillUnit(option: any, index: any) {  
369 - let copyList = form.getFieldValue('list');  
370 - let currentData = copyList[index];  
371 - currentData.unit = option?.label;  
372 - form.setFieldValue('list', copyList);  
373 - }  
374 -  
375 - /**  
376 - * 计算子订单金额  
377 - * @param listMeta 当前商品信息  
378 - */  
379 - function computeSubOrderPayment(listMeta: any) {  
380 - let quantity = listMeta?.record?.quantity;  
381 - let productPrice = listMeta?.record?.productPrice;  
382 - quantity = quantity === '' || quantity === undefined ? 0 : quantity;  
383 - productPrice =  
384 - productPrice === '' || productPrice === undefined ? 0 : productPrice;  
385 -  
386 - listMeta.subOrderPayment = quantity * productPrice;  
387 - let list = form.getFieldValue('list');  
388 - list[listMeta?.index].subOrderPayment = quantity * productPrice;  
389 - form.setFieldValue('list', list);  
390 - }  
391 -  
392 - /**  
393 - * 计算支付总额  
394 - */  
395 - function computeTotalPayment() {  
396 - let list = form.getFieldValue('list');  
397 - let totalPayment = 0;  
398 - list?.forEach((subOrder: any) => {  
399 - let subOrderPayment = subOrder?.subOrderPayment;  
400 - if (subOrderPayment === '' || subOrderPayment === undefined) {  
401 - totalPayment += 0;  
402 - } else {  
403 - totalPayment += subOrderPayment;  
404 - }  
405 - });  
406 - form.setFieldValue('totalPayment', totalPayment);  
407 - }  
408 -  
409 - useEffect(() => {  
410 - getSalesCodeOptions();  
411 - showKindeeInfo();  
412 - }, []);  
413 -  
414 - useEffect(() => {  
415 - // 在组件挂载或数据变化时,更新组件状态  
416 - if (data) {  
417 - setInvoicingStatus(data.invoicingStatus);  
418 - }  
419 - }, [data]);  
420 -  
421 - // let mainInfoDisbled = optType('edit');  
422 - if (optType('edit') || optType('copy')) {  
423 - //如果是复制,需要开票,不回显是否需要开票字段  
424 - if (optType('copy')) {  
425 - if (data.invoicingStatus === 'INVOICED') {  
426 - data.invoicingStatus = undefined;  
427 - }  
428 - }  
429 - //订单修改和新增的子订单列表命名是list  
430 - data.list = data.subOrderInformationLists;  
431 - //主订单事业部默认显示子订单第一条的事业部  
432 - data.productBelongBusiness = data.list[0].productBelongBusiness;  
433 - data.paymentMethod = data.list[0].paymentMethod;  
434 - data.paymentChannel = data.list[0].paymentChannel;  
435 - data.invoicingStatus = data.list[0].invoicingStatus;  
436 -  
437 - data.list = data.list?.map((item) => {  
438 - item.filePaths = item.listAnnex?.map((path) => {  
439 - let i = 0;  
440 - return {  
441 - uid: i++,  
442 - name: getAliYunOSSFileNameFromUrl(path),  
443 - status: 'uploaded',  
444 - url: path,  
445 - response: { data: [path] },  
446 - };  
447 - });  
448 - return item;  
449 - });  
450 - }  
451 -  
452 - return (  
453 - <>  
454 - <DrawerForm<{  
455 - deleteSubOrderLists: any;  
456 - name: string;  
457 - company: string;  
458 - }>  
459 - open  
460 - width="35%"  
461 - title={optType('add') || optType('copy') ? '新建订单' : '修改订单'}  
462 - resize={{  
463 - onResize() {  
464 - console.log('resize!');  
465 - },  
466 - maxWidth: window.innerWidth * 0.8,  
467 - minWidth: 400,  
468 - }}  
469 - // layout="horizontal"  
470 - // labelCol={{ span: 8 }}  
471 - form={form}  
472 - autoFocusFirstInput  
473 - drawerProps={{  
474 - destroyOnClose: true,  
475 - maskClosable: false,  
476 - }}  
477 - submitTimeout={2000}  
478 - onFinish={async (values) => {  
479 - let res = {};  
480 - //附件处理  
481 - let list = values.list;  
482 - // console.log(list);  
483 - list = list.map((item) => {  
484 - item.filePaths = item.filePaths?.map((file) => {  
485 - console.log(file);  
486 - return { url: file.response.data[0] };  
487 - });  
488 - return item;  
489 - });  
490 -  
491 - values.list = list;  
492 - values.institution = values.institution?.trim();  
493 - values.institutionContactName = values.institutionContactName?.trim();  
494 -  
495 - if (typeof values.erpCustomerId !== 'string') {  
496 - values.erpCustomerId = values.erpCustomerId?.id;  
497 - }  
498 -  
499 - if (optType('add') || optType('copy')) {  
500 - res = await postServiceOrderAddOrder({ data: values });  
501 - } else {  
502 - //计算已删除的子订单id  
503 - const originIds = originSubOrders.map((item) => {  
504 - return item.id;  
505 - });  
506 - const curIds = form.getFieldValue('list')?.map((item) => {  
507 - return item.id;  
508 - });  
509 - let diff = originIds.filter((item) => !curIds.includes(item));  
510 - values.deleteSubOrderLists = diff;  
511 - res = await postServiceOrderUpdateOrder({ data: values });  
512 - }  
513 -  
514 - if (res.result === RESPONSE_CODE.SUCCESS) {  
515 - message.success(res.message);  
516 - // 不返回不会关闭弹框  
517 - onClose(true);  
518 - return true;  
519 - }  
520 - }}  
521 - onOpenChange={(val) => {  
522 - return !val && onClose();  
523 - }}  
524 - >  
525 - <h2>订单基本信息</h2>  
526 - <ProFormText  
527 - key="id"  
528 - name="id"  
529 - width="lg"  
530 - disabled  
531 - label="id"  
532 - placeholder="id"  
533 - hidden  
534 - />  
535 -  
536 - <ProFormText  
537 - key="empNumber"  
538 - name="empNumber"  
539 - width="lg"  
540 - label="销售职员编码"  
541 - placeholder="销售职员编码"  
542 - hidden  
543 - />  
544 -  
545 - <ProFormSelect  
546 - name="salesCode"  
547 - key="salesCode"  
548 - width="lg"  
549 - showSearch  
550 - label="销售代表"  
551 - placeholder="请输入销售代表"  
552 - rules={[{ required: true, message: '销售代表必填' }]}  
553 - options={salesCodeOptions}  
554 - onChange={(_, option) => {  
555 - autoFillSalesInfo(option);  
556 - }}  
557 - // disabled={mainInfoDisbled}  
558 - />  
559 -  
560 - <ProFormText  
561 - key="erpCustomerName"  
562 - name="erpCustomerName"  
563 - hidden  
564 - ></ProFormText>  
565 -  
566 - <ProFormText  
567 - key="contactAddress"  
568 - name="contactAddress"  
569 - hidden  
570 - ></ProFormText>  
571 -  
572 - <ProFormSelect  
573 - name="erpCustomerId"  
574 - key="erpCustomerId"  
575 - width="lg"  
576 - showSearch  
577 - label={  
578 - <>  
579 - <span>客户</span>  
580 - <span  
581 - className="pl-2 text-xs text-[#1677ff] cursor-pointer"  
582 - onClick={() => {  
583 - let customerId = form.getFieldValue('erpCustomerId');  
584 - if (typeof customerId === 'string') {  
585 - setCustomer({ ...customer, id: customerId });  
586 - } else {  
587 - setCustomer({ ...customer, id: customerId.id });  
588 - }  
589 - setKingdeeCstomerModalVisible(true);  
590 - }}  
591 - >  
592 - 编辑客户信息  
593 - </span>  
594 - </>  
595 - }  
596 - placeholder="请选择客户"  
597 - rules={[{ required: true, message: '客户必填' }]}  
598 - onChange={(_, option) => {  
599 - //新增客户  
600 - if (option.type === 'add') {  
601 - setCustomer({ name: option.name });  
602 - setKingdeeCstomerModalVisible(true);  
603 - return;  
604 - }  
605 - autoFillCustomerContactSelectOptions(option.id);  
606 - }}  
607 - initialValue={{  
608 - label: data?.erpCustomerName,  
609 - value: data?.customerId,  
610 - id: data?.customerId,  
611 - }}  
612 - fieldProps={{  
613 - optionItemRender(item) {  
614 - if (item.type === 'add') {  
615 - return (  
616 - <div title={item.name + '(新增客户)'}>  
617 - <span style={{ color: '#333333' }}>{item.name}</span>  
618 - {' | '}  
619 - <span style={{ color: 'orange' }}>自定义</span>  
620 - </div>  
621 - );  
622 - }  
623 - return (  
624 - <div  
625 - title={  
626 - item.name +  
627 - ' | ' +  
628 - item.customerContactNumber +  
629 - ' | ' +  
630 - (item.customerShippingAddress === undefined  
631 - ? '无地址'  
632 - : item.customerShippingAddress) +  
633 - ' | ' +  
634 - item.institutionContactName +  
635 - ' | ' +  
636 - item.institution  
637 - }  
638 - >  
639 - <span style={{ color: '#333333' }}>{item.name}</span>  
640 - </div>  
641 - );  
642 - },  
643 - }}  
644 - debounceTime={1000}  
645 - request={async (value, {}) => {  
646 - const keywords = value.keyWords;  
647 - const res = await postKingdeeRepCustomer({  
648 - data: { search: keywords },  
649 - });  
650 - console.log(res);  
651 -  
652 - let options = res?.rows?.map((c: any) => {  
653 - return {  
654 - ...c,  
655 - label: c.name,  
656 - value: c.id,  
657 - key: c.id,  
658 - };  
659 - });  
660 -  
661 - //第一个商品默认为要新增客户  
662 - if (keywords.trim() !== '') {  
663 - options.unshift({  
664 - name: keywords,  
665 - type: 'add',  
666 - label: keywords,  
667 - value: 3.1415926,  
668 - key: keywords,  
669 - });  
670 - }  
671 - return options;  
672 - }}  
673 - />  
674 - <ProFormSelect  
675 - key="customerName"  
676 - label="收货人"  
677 - width="lg"  
678 - showSearch  
679 - name="customerName"  
680 - placeholder="请选择收货人"  
681 - rules={[{ required: true, message: '收货人必填' }]}  
682 - onChange={(_, option) => {  
683 - autoFillCustomerInfo(option);  
684 - }}  
685 - initialValue={data.contactAddress}  
686 - options={productCustomerContactOptions}  
687 - />  
688 - <ProFormText  
689 - width="lg"  
690 - key="customerContactNumber"  
691 - name="customerContactNumber"  
692 - label="联系方式"  
693 - placeholder="请输入联系方式"  
694 - rules={[{ required: true, message: '联系方式必填' }]}  
695 - disabled  
696 - />  
697 - <ProFormText  
698 - width="lg"  
699 - key="institution"  
700 - name="institution"  
701 - label="单位"  
702 - placeholder="请输入单位"  
703 - rules={[{ required: true, message: '单位必填' }]}  
704 - disabled  
705 - />  
706 - <ProFormText  
707 - width="lg"  
708 - key="institutionContactName"  
709 - name="institutionContactName"  
710 - label="课题组"  
711 - placeholder="请输入课题组"  
712 - rules={[{ required: true, message: '课题组必填' }]}  
713 - disabled  
714 - />  
715 - <ProFormTextArea  
716 - width="lg"  
717 - key="customerShippingAddress"  
718 - name="customerShippingAddress"  
719 - label="收货地址"  
720 - placeholder="请输入收货地址"  
721 - rules={[{ required: true, message: '收货地址必填' }]}  
722 - disabled  
723 - />  
724 - <div id="total-payment">  
725 - <ProFormDigit  
726 - name="totalPayment"  
727 - width="lg"  
728 - key="totalPayment"  
729 - label="支付总额(¥)"  
730 - rules={[{ required: true, message: '支付总额必填' }]}  
731 - tooltip="点击计算,合计所有子订单金额"  
732 - fieldProps={{  
733 - addonAfter: (  
734 - <Button  
735 - className="rounded-l-none"  
736 - type="primary"  
737 - onClick={computeTotalPayment}  
738 - >  
739 - 计算  
740 - </Button>  
741 - ),  
742 - }}  
743 - // disabled={mainInfoDisbled}  
744 - />  
745 - </div>  
746 -  
747 - <ProFormSelect  
748 - placeholder="请输入支付渠道"  
749 - name="paymentChannel"  
750 - width="lg"  
751 - key="paymentChannel"  
752 - label="支付渠道"  
753 - options={enumToSelect(PAYMENT_CHANNEL_OPTIONS)}  
754 - rules={[{ required: true, message: '支付渠道必填' }]}  
755 - // disabled={mainInfoDisbled}  
756 - />  
757 - <ProFormSelect  
758 - placeholder="请输入支付方式"  
759 - name="paymentMethod"  
760 - width="lg"  
761 - key="paymentMethod"  
762 - label="支付方式"  
763 - options={enumToSelect(PAYMENT_METHOD_OPTIONS)}  
764 - rules={[{ required: true, message: '支付方式必填' }]}  
765 - // disabled={mainInfoDisbled}  
766 - />  
767 - <ProFormSelect  
768 - placeholder="选择是否需要开票"  
769 - name="invoicingStatus"  
770 - width="lg"  
771 - key="invoicingStatus"  
772 - label="是否需要开票"  
773 - options={getInvoicingSelect()}  
774 - // disabled={mainInfoDisbled}  
775 - onChange={(_, option) => {  
776 - setInvoicingStatus(option.value);  
777 - if (option.value === 'UN_INVOICE') {  
778 - form.setFieldValue('invoiceIdentificationNumber', undefined);  
779 - form.setFieldValue('bank', undefined);  
780 - form.setFieldValue('bankAccountNumber', undefined);  
781 - }  
782 - }}  
783 - rules={[{ required: true, message: '是否需要开票必填' }]}  
784 - />  
785 - <ProFormText  
786 - width="lg"  
787 - name="invoiceIdentificationNumber"  
788 - label="开票信息"  
789 - key="invoiceIdentificationNumber"  
790 - // disabled={mainInfoDisbled}  
791 - hidden={invoicingStatus === 'UN_INVOICE'}  
792 - placeholder="请输入开票信息"  
793 - rules={[  
794 - {  
795 - required: invoicingStatus === 'UN_INVOICE' ? false : true,  
796 - message: '开票信息必填',  
797 - },  
798 - ]}  
799 - />  
800 -  
801 - {getUserInfo().roleSmallVO?.code === 'admin' ? (  
802 - <ProFormDateTimePicker  
803 - width="lg"  
804 - key="invoicingTime"  
805 - name="invoicingTime"  
806 - // disabled={mainInfoDisbled}  
807 - hidden={invoicingStatus === 'UN_INVOICE'}  
808 - label="开票时间"  
809 - placeholder="请输入开票时间"  
810 - />  
811 - ) : (  
812 - ''  
813 - )}  
814 - <ProFormText  
815 - width="lg"  
816 - name="bank"  
817 - key="bank"  
818 - label="开户银行"  
819 - // disabled={mainInfoDisbled}  
820 - hidden={invoicingStatus === 'UN_INVOICE'}  
821 - placeholder="请输入开户银行"  
822 - />  
823 - <ProFormText  
824 - width="lg"  
825 - key="bankAccountNumber"  
826 - name="bankAccountNumber"  
827 - hidden={invoicingStatus === 'UN_INVOICE'}  
828 - label="银行账号"  
829 - // disabled={mainInfoDisbled}  
830 - placeholder="请输入银行账号"  
831 - />  
832 - <ProFormTextArea  
833 - width="lg"  
834 - name="notes"  
835 - label="备注"  
836 - key="notes"  
837 - // disabled={mainInfoDisbled}  
838 - placeholder="请输入备注"  
839 - rules={[  
840 - {  
841 - max: 120, // 最大长度为120个字符  
842 - message: '备注不能超过120个字符',  
843 - },  
844 - ]}  
845 - />  
846 -  
847 - <h2>商品信息</h2>  
848 - <ProFormList  
849 - creatorButtonProps={{ disabled: false }}  
850 - name="list"  
851 - label=""  
852 - copyIconProps={false} //复制按钮不显示  
853 - initialValue={[  
854 - {  
855 - productCode: '',  
856 - productName: '',  
857 - quantity: '',  
858 - productPrice: '',  
859 - parameters: '',  
860 - subOrderPayment: '',  
861 - },  
862 - ]}  
863 - actionGuard={{  
864 - beforeRemoveRow: async (index) => {  
865 - return new Promise((resolve) => {  
866 - if (index === 0) {  
867 - message.error('第一行数据不能删除');  
868 - resolve(false);  
869 - return;  
870 - }  
871 - resolve(true);  
872 - });  
873 - },  
874 - }}  
875 - itemRender={(doms, listMeta) => {  
876 - if (optType('edit')) {  
877 - let i = 0;  
878 - let defaultFileList = listMeta.record?.listAnnex?.map((annex) => {  
879 - return {  
880 - uid: i++,  
881 - name: annex,  
882 - status: 'uploaded',  
883 - url: annex,  
884 - response: { data: [annex] },  
885 - };  
886 - });  
887 - fileList[listMeta.index] = defaultFileList;  
888 - }  
889 - let itemFileList = fileList[listMeta.index];  
890 - return (  
891 - <ProCard  
892 - bordered  
893 - extra={doms.action}  
894 - title={'商品' + (listMeta.index + 1)}  
895 - style={{  
896 - marginBlockEnd: 8,  
897 - }}  
898 - >  
899 - {[  
900 - <ProFormText  
901 - key={'material' + listMeta.index}  
902 - name="materialId"  
903 - hidden  
904 - ></ProFormText>,  
905 - <ProFormSelect  
906 - key="key"  
907 - label="商品名称"  
908 - width="lg"  
909 - showSearch  
910 - name="productName"  
911 - // options={options}  
912 - placeholder="请搜索商品"  
913 - rules={[{ required: true, message: '商品名称必填' }]}  
914 - onChange={(_, option) => {  
915 - autoFillProductInfo(option, listMeta, listMeta.index);  
916 - }}  
917 - initialValue={{  
918 - label: listMeta?.record?.productName,  
919 - value: listMeta?.record?.materialId,  
920 - }}  
921 - fieldProps={{  
922 - optionItemRender(item) {  
923 - if (item.type === 'add') {  
924 - return (  
925 - <div title={item.name + '(新增商品信息)'}>  
926 - <span style={{ color: '#333333' }}>  
927 - {item.label}  
928 - </span>  
929 - {' | '}  
930 - <span style={{ color: 'orange' }}>新增商品</span>  
931 - </div>  
932 - );  
933 - }  
934 - return (  
935 - <div  
936 - title={  
937 - item.label +  
938 - ' | ' +  
939 - (item.model === undefined  
940 - ? '无参数'  
941 - : item.model) +  
942 - ' | ' +  
943 - item.base_unit_name  
944 - }  
945 - >  
946 - <span style={{ color: '#333333' }}>  
947 - {item.label}  
948 - </span>  
949 - {' | '}  
950 - <span style={{ color: '#339999' }}>  
951 - {item.model === undefined ? '无参数' : item.model}  
952 - </span>  
953 - {' | '}  
954 - <span style={{ color: '#666666' }}>  
955 - {item.base_unit_name === undefined  
956 - ? '无单位'  
957 - : item.base_unit_name}  
958 - </span>  
959 - </div>  
960 - );  
961 - },  
962 - }}  
963 - debounceTime={1000}  
964 - request={async (value) => {  
965 - const keywords = value.keyWords;  
966 - const res = await postKingdeeRepMaterial({  
967 - data: { search: keywords },  
968 - });  
969 - let options = res?.rows?.map((p: any) => {  
970 - return {  
971 - ...p,  
972 - label: p.name,  
973 - value: p.id + '|' + p.name,  
974 - key: p.id,  
975 - };  
976 - });  
977 -  
978 - //第一个商品默认为要新增的商品  
979 - if (keywords.trim() !== '') {  
980 - options.unshift({  
981 - productName: keywords,  
982 - type: 'add',  
983 - label: keywords,  
984 - value: 13 + '|' + keywords,  
985 - key: keywords,  
986 - });  
987 - }  
988 - return options;  
989 - }}  
990 - />,  
991 - <ProFormText  
992 - key={'productCode' + listMeta.index}  
993 - width="lg"  
994 - name="productCode"  
995 - disabled  
996 - label={  
997 - <>  
998 - <span>商品编码</span>  
999 - <span className="pl-2 text-xs text-gray-400">  
1000 - 新增商品时,商品编码由系统自动生成  
1001 - </span>  
1002 - </>  
1003 - }  
1004 - placeholder="商品编码"  
1005 - />,  
1006 - // <ProFormSelect  
1007 - // key="inv_stock"  
1008 - // placeholder="请选择仓库"  
1009 - // name="invStockId"  
1010 - // width="lg"  
1011 - // label="仓库"  
1012 - // options={productInvStockOptionsList[listMeta.index]}  
1013 - // />,  
1014 - <ProFormText  
1015 - key={'parameters' + listMeta.index}  
1016 - width="lg"  
1017 - name="parameters"  
1018 - label="商品参数"  
1019 - placeholder="请输入商品参数"  
1020 - rules={[{ required: true, message: '商品参数必填' }]}  
1021 - disabled={  
1022 - productParametersDisabledFlagList[listMeta.index] !==  
1023 - false  
1024 - }  
1025 - />,  
1026 - <ProFormDigit  
1027 - key={'quantity' + listMeta.index}  
1028 - width="lg"  
1029 - name="quantity"  
1030 - label="商品数量"  
1031 - fieldProps={{  
1032 - onChange: (value) => {  
1033 - listMeta.record.quantity = value;  
1034 - computeSubOrderPayment(listMeta);  
1035 - },  
1036 - }}  
1037 - placeholder="请输入商品数量"  
1038 - rules={[{ required: true, message: '商品数量必填' }]}  
1039 - />,  
1040 -  
1041 - <ProFormDigit  
1042 - key={'productPrice' + listMeta.index}  
1043 - width="lg"  
1044 - name="productPrice"  
1045 - label="商品单价"  
1046 - fieldProps={{  
1047 - onChange: (value) => {  
1048 - listMeta.record.productPrice = value;  
1049 - computeSubOrderPayment(listMeta);  
1050 - },  
1051 - }}  
1052 - placeholder="请输入商品单价"  
1053 - rules={[{ required: true, message: '商品单价必填' }]}  
1054 - />,  
1055 -  
1056 - <ProFormSelect  
1057 - key="unitId"  
1058 - placeholder="请选择单位"  
1059 - name="unitId"  
1060 - width="lg"  
1061 - label="单位"  
1062 - showSearch  
1063 - onChange={(_, option) => {  
1064 - autoFillUnit(option, listMeta.index);  
1065 - }}  
1066 - options={productUnitOptionsList[listMeta.index]}  
1067 - rules={[{ required: true, message: '商品单位必填' }]}  
1068 - />,  
1069 - <ProFormText  
1070 - key={'unit' + listMeta.index}  
1071 - width="lg"  
1072 - name="unit"  
1073 - label="商品单位"  
1074 - placeholder="请输入商品单位"  
1075 - rules={[{ required: true, message: '商品单位必填' }]}  
1076 - hidden  
1077 - />,  
1078 -  
1079 - <ProFormDigit  
1080 - width="lg"  
1081 - key={'subOrderPayment' + listMeta.index}  
1082 - name="subOrderPayment"  
1083 - label="子订单金额"  
1084 - placeholder="请输入子订单金额"  
1085 - tooltip="商品数量和单价变化后会自动计算子订单金额"  
1086 - rules={[{ required: true, message: '子订单金额必填' }]}  
1087 - />,  
1088 - <ProFormSelect  
1089 - key={'productBelongBusiness' + listMeta.index}  
1090 - placeholder="请输入所属事业部"  
1091 - name="productBelongBusiness"  
1092 - width="lg"  
1093 - label="所属事业部"  
1094 - options={enumToSelect(PRODUCT_BELONG_DEPARTMENT_OPTIONS)}  
1095 - initialValue={'EXPERIMENTAL_CONSUMABLES'}  
1096 - rules={[{ required: true, message: '所属事业部必填' }]}  
1097 - // disabled={mainInfoDisbled}  
1098 - />,  
1099 - <ProFormTextArea  
1100 - key={'notes' + listMeta.index}  
1101 - width="lg"  
1102 - name="notes"  
1103 - label={  
1104 - <div>  
1105 - <span>备注</span>  
1106 - <span className="pl-2 text-xs text-gray-400">  
1107 - 备注将体现在出货单上,请将需要仓管看见的信息写在备注上,例如需要开收据等信息。  
1108 - </span>  
1109 - </div>  
1110 - }  
1111 - placeholder="请输入备注"  
1112 - rules={[  
1113 - {  
1114 - max: 120, // 最大长度为120个字符  
1115 - message: '备注不能超过120个字符',  
1116 - },  
1117 - ]}  
1118 - />,  
1119 - <>  
1120 - <ProFormUploadDragger  
1121 - key={'filePaths' + listMeta.index}  
1122 - label="附件"  
1123 - name="filePaths"  
1124 - action="/api/service/order/fileProcess"  
1125 - fieldProps={{  
1126 - headers: {  
1127 - Authorization: localStorage.getItem('token'),  
1128 - },  
1129 - itemFileList,  
1130 - }}  
1131 - />  
1132 - </>,  
1133 - ]}  
1134 - </ProCard>  
1135 - );  
1136 - }}  
1137 - actionRef={actionRef}  
1138 - ></ProFormList>  
1139 - </DrawerForm>  
1140 -  
1141 - {kingdeeCstomerModalVisible && (  
1142 - <KingdeeCustomerModal  
1143 - setVisible={setKingdeeCstomerModalVisible}  
1144 - data={customer}  
1145 - onClose={(customerId: any) => {  
1146 - setKingdeeCstomerModalVisible(false);  
1147 - //回显已经新建好的客户  
1148 - autoFillCustomerContactSelectOptions(customerId);  
1149 - }}  
1150 - />  
1151 - )}  
1152 - </>  
1153 - );  
1154 -};  
src/pages/Order/OrderList/OrderDrawer.tsx
@@ -32,8 +32,8 @@ import { getTeacherCustomFieldNumber } from &#39;@/utils/kingdee&#39;; @@ -32,8 +32,8 @@ import { getTeacherCustomFieldNumber } from &#39;@/utils/kingdee&#39;;
32 import { getSalesCodeOptions } from '@/utils/order'; 32 import { getSalesCodeOptions } from '@/utils/order';
33 import { getDefaultString } from '@/utils/StringUtil'; 33 import { getDefaultString } from '@/utils/StringUtil';
34 import { 34 import {
35 - DrawerForm,  
36 FormListActionType, 35 FormListActionType,
  36 + ModalForm,
37 ProCard, 37 ProCard,
38 ProFormDatePicker, 38 ProFormDatePicker,
39 ProFormDateTimePicker, 39 ProFormDateTimePicker,
@@ -760,14 +760,14 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -760,14 +760,14 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
760 760
761 return ( 761 return (
762 <> 762 <>
763 - <DrawerForm<{ 763 + <ModalForm<{
764 isLocalData: any; 764 isLocalData: any;
765 deleteSubOrderLists: any; 765 deleteSubOrderLists: any;
766 name: string; 766 name: string;
767 company: string; 767 company: string;
768 }> 768 }>
769 open 769 open
770 - width="35%" 770 + width={1000}
771 title={drawerTitle} 771 title={drawerTitle}
772 resize={{ 772 resize={{
773 onResize() { 773 onResize() {
@@ -1913,6 +1913,9 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1913,6 +1913,9 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1913 value: listMeta?.record?.materialId, 1913 value: listMeta?.record?.materialId,
1914 }} 1914 }}
1915 fieldProps={{ 1915 fieldProps={{
  1916 + popupMatchSelectWidth: false,
  1917 + listHeight: 400,
  1918 + dropdownStyle: { width: '55%' },
1916 filterOption() { 1919 filterOption() {
1917 return true; 1920 return true;
1918 }, 1921 },
@@ -2230,7 +2233,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -2230,7 +2233,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
2230 }} 2233 }}
2231 actionRef={actionRef} 2234 actionRef={actionRef}
2232 ></ProFormList> 2235 ></ProFormList>
2233 - </DrawerForm> 2236 + </ModalForm>
2234 {kingdeeCstomerModalVisible && ( 2237 {kingdeeCstomerModalVisible && (
2235 <KingdeeCustomerModal 2238 <KingdeeCustomerModal
2236 setVisible={setKingdeeCstomerModalVisible} 2239 setVisible={setKingdeeCstomerModalVisible}
src/pages/Prepaid/components/PointsExchangeModal.tsx 0 → 100644
  1 +import { postIntegralExchangeIntegral } from '@/services/request';
  2 +import {
  3 + ModalForm,
  4 + ProFormInstance,
  5 + ProFormTextArea,
  6 +} from '@ant-design/pro-components';
  7 +import { Form, Input, message } from 'antd';
  8 +import React, { useEffect, useRef, useState } from 'react';
  9 +import '../index.less';
  10 +
  11 +interface PointsExchangeModalProps {
  12 + setVisible: (visible: boolean) => void;
  13 + userInfoObj: {
  14 + uid: string;
  15 + nickname?: string;
  16 + realName?: string;
  17 + [key: string]: any;
  18 + };
  19 + onClose: () => void;
  20 +}
  21 +
  22 +const PointsExchangeModal: React.FC<PointsExchangeModalProps> = ({
  23 + setVisible,
  24 + userInfoObj,
  25 + onClose,
  26 +}) => {
  27 + const [form] = Form.useForm<{ delta: string; remark: string }>();
  28 + const formRef = useRef<ProFormInstance>();
  29 + const uid = userInfoObj?.uid;
  30 + const userName = userInfoObj?.nickname || userInfoObj?.realName || '';
  31 + const [accountPoints, setAccountPoints] = useState<number>(0);
  32 +
  33 + useEffect(() => {
  34 + if (userInfoObj && userInfoObj.delta) {
  35 + setAccountPoints(Number(userInfoObj.delta) || 0);
  36 + }
  37 + }, [userInfoObj]);
  38 +
  39 + // Validate that delta is not greater than available points
  40 + const validateDelta = (rule: any, value: string) => {
  41 + const deltaValue = Number(value);
  42 + if (isNaN(deltaValue)) {
  43 + return Promise.reject('请输入有效的积分数值');
  44 + }
  45 + if (deltaValue <= 0) {
  46 + return Promise.reject('兑换积分必须大于0');
  47 + }
  48 + if (deltaValue > accountPoints) {
  49 + return Promise.reject('兑换积分不能大于账户积分');
  50 + }
  51 + return Promise.resolve();
  52 + };
  53 +
  54 + return (
  55 + <div className="prepaid-index">
  56 + <ModalForm<{
  57 + delta: string;
  58 + remark: string;
  59 + }>
  60 + width={600}
  61 + open
  62 + title="积分兑换"
  63 + form={form}
  64 + formRef={formRef}
  65 + autoFocusFirstInput
  66 + submitter={{
  67 + searchConfig: {
  68 + submitText: '确认兑换',
  69 + resetText: '取消',
  70 + },
  71 + }}
  72 + modalProps={{
  73 + destroyOnClose: true,
  74 + onCancel: () => {
  75 + setVisible(false);
  76 + },
  77 + }}
  78 + onFinish={async (values) => {
  79 + try {
  80 + // 调用积分兑换API
  81 + await postIntegralExchangeIntegral({
  82 + data: {
  83 + id: Number(uid), // 使用用户的uid作为id参数
  84 + delta: values.delta, // 将delta设为负数
  85 + remark: values.remark, // 兑换说明
  86 + relationEntityType: 'USER', // 关联实体类型
  87 + createByName: userName, // 使用昵称或真实姓名作为createByName
  88 + },
  89 + });
  90 +
  91 + message.success('积分兑换成功');
  92 + setVisible(false);
  93 + onClose();
  94 + return true;
  95 + } catch (error) {
  96 + console.error(error);
  97 + return false;
  98 + }
  99 + }}
  100 + onOpenChange={setVisible}
  101 + >
  102 + <Form.Item
  103 + label="兑换积分"
  104 + name="delta"
  105 + rules={[
  106 + { required: true, message: '请输入兑换积分' },
  107 + { validator: validateDelta },
  108 + ]}
  109 + >
  110 + <Input style={{ height: '30px' }} placeholder="请输入兑换积分数量" />
  111 + </Form.Item>
  112 +
  113 + <Form.Item
  114 + label="兑换说明"
  115 + name="remark"
  116 + rules={[{ required: true, message: '请输入兑换说明' }]}
  117 + >
  118 + <ProFormTextArea
  119 + style={{ height: '100px' }}
  120 + placeholder="请输入兑换说明"
  121 + />
  122 + </Form.Item>
  123 + </ModalForm>
  124 + </div>
  125 + );
  126 +};
  127 +
  128 +export default PointsExchangeModal;
src/pages/Prepaid/components/PointsExchangeRecordsModal.tsx 0 → 100644
  1 +import { postIntegralUserExchangeRecords } from '@/services';
  2 +import { formatDateTime } from '@/utils';
  3 +import { Button, DatePicker, Empty, Modal, Space, Table, Tabs } from 'antd';
  4 +import React, { useEffect, useState } from 'react';
  5 +import '../index.less';
  6 +
  7 +interface PointsExchangeRecordsModalProps {
  8 + setVisible: (visible: boolean) => void;
  9 + userInfoObj: {
  10 + uid: string;
  11 + phone?: string;
  12 + nickname?: string;
  13 + realName?: string;
  14 + [key: string]: any;
  15 + };
  16 + onClose: () => void;
  17 +}
  18 +
  19 +interface RecordItem {
  20 + createByName: string | null;
  21 + createTime: string;
  22 + delta: number;
  23 + remark: string | null;
  24 + sourceId: string | null;
  25 +}
  26 +
  27 +interface RecordsData {
  28 + exchangeRecords: RecordItem[];
  29 + collectedRecords: RecordItem[];
  30 + pendingRecords: RecordItem[];
  31 +}
  32 +
  33 +const PointsExchangeRecordsModal: React.FC<PointsExchangeRecordsModalProps> = ({
  34 + setVisible,
  35 + userInfoObj,
  36 + onClose,
  37 +}) => {
  38 + const [activeTab, setActiveTab] = useState<string>('1');
  39 + const [loading, setLoading] = useState<boolean>(false);
  40 + const [recordsData, setRecordsData] = useState<RecordsData>({
  41 + exchangeRecords: [],
  42 + collectedRecords: [],
  43 + pendingRecords: [],
  44 + });
  45 +
  46 + // Add date range state for each tab
  47 + const [exchangeStartTime, setExchangeStartTime] = useState<string | null>(
  48 + null,
  49 + );
  50 + const [exchangeEndTime, setExchangeEndTime] = useState<string | null>(null);
  51 + const [collectedStartTime, setCollectedStartTime] = useState<string | null>(
  52 + null,
  53 + );
  54 + const [collectedEndTime, setCollectedEndTime] = useState<string | null>(null);
  55 + const [pendingStartTime, setPendingStartTime] = useState<string | null>(null);
  56 + const [pendingEndTime, setPendingEndTime] = useState<string | null>(null);
  57 +
  58 + const uid = userInfoObj?.uid;
  59 + const phone = userInfoObj?.phone;
  60 +
  61 + // Function to fetch records data from API with date parameters
  62 + const fetchRecordsData = async (
  63 + params: {
  64 + startTime?: string | null;
  65 + endTime?: string | null;
  66 + tabKey?: string;
  67 + } = {},
  68 + ) => {
  69 + try {
  70 + setLoading(true);
  71 + const { startTime, endTime, tabKey } = params;
  72 +
  73 + // Prepare API request data
  74 + const requestData: any = {
  75 + id: Number(uid),
  76 + phone: phone,
  77 + };
  78 +
  79 + // Add date range if provided
  80 + if (startTime) requestData.startTime = startTime;
  81 + if (endTime) requestData.endTime = endTime;
  82 +
  83 + const response = await postIntegralUserExchangeRecords({
  84 + data: requestData,
  85 + });
  86 +
  87 + if (response && response.data) {
  88 + // Update only the data for the active tab if tabKey is provided
  89 + if (tabKey) {
  90 + setRecordsData((prevData) => {
  91 + const newData = { ...prevData };
  92 +
  93 + if (tabKey === '1') {
  94 + newData.exchangeRecords = response.data.exchangeRecords || [];
  95 + } else if (tabKey === '2') {
  96 + newData.collectedRecords = response.data.collectedRecords || [];
  97 + } else if (tabKey === '3') {
  98 + newData.pendingRecords = response.data.pendingRecords || [];
  99 + }
  100 +
  101 + return newData;
  102 + });
  103 + } else {
  104 + // Update all data if no specific tab is targeted
  105 + setRecordsData({
  106 + exchangeRecords: response.data.exchangeRecords || [],
  107 + collectedRecords: response.data.collectedRecords || [],
  108 + pendingRecords: response.data.pendingRecords || [],
  109 + });
  110 + }
  111 + }
  112 + } catch (error) {
  113 + console.error('Failed to fetch exchange records:', error);
  114 + } finally {
  115 + setLoading(false);
  116 + }
  117 + };
  118 +
  119 + // Fetch records data when component mounts
  120 + useEffect(() => {
  121 + if (uid || phone) {
  122 + fetchRecordsData();
  123 + }
  124 + }, [uid, phone]);
  125 +
  126 + // Exchange records columns
  127 + const exchangeRecordsColumns = [
  128 + {
  129 + title: '兑换日期',
  130 + dataIndex: 'createTime',
  131 + key: 'createTime',
  132 + width: 160,
  133 + render: (text: string) => formatDateTime(text),
  134 + },
  135 + {
  136 + title: '扣除积分',
  137 + dataIndex: 'delta',
  138 + key: 'delta',
  139 + width: 100,
  140 + render: (delta: number) => (
  141 + <span style={{ color: delta < 0 ? 'green' : 'red' }}>{delta}</span>
  142 + ),
  143 + },
  144 + {
  145 + title: '操作人',
  146 + dataIndex: 'createByName',
  147 + key: 'createByName',
  148 + width: 100,
  149 + render: (text: string | null) => text || '-',
  150 + },
  151 + {
  152 + title: '积分用途',
  153 + dataIndex: 'remark',
  154 + key: 'remark',
  155 + render: (text: string | null) => text || '-',
  156 + },
  157 + ];
  158 +
  159 + // Collected records columns
  160 + const collectedRecordsColumns = [
  161 + {
  162 + title: '订单号',
  163 + dataIndex: 'sourceId',
  164 + key: 'sourceId',
  165 + render: (text: string | null) => text || '-',
  166 + },
  167 + {
  168 + title: '领取积分',
  169 + dataIndex: 'delta',
  170 + key: 'delta',
  171 + render: (delta: number) => (
  172 + <span style={{ color: delta > 0 ? 'green' : 'red' }}>{delta}</span>
  173 + ),
  174 + },
  175 + {
  176 + title: '领取日期',
  177 + dataIndex: 'createTime',
  178 + key: 'createTime',
  179 + render: (text: string) => formatDateTime(text),
  180 + },
  181 + ];
  182 +
  183 + // Pending records columns
  184 + const pendingRecordsColumns = [
  185 + {
  186 + title: '订单号',
  187 + dataIndex: 'sourceId',
  188 + key: 'sourceId',
  189 + render: (text: string | null) => text || '-',
  190 + },
  191 + {
  192 + title: '领取积分',
  193 + dataIndex: 'delta',
  194 + key: 'delta',
  195 + render: (delta: number) => (
  196 + <span style={{ color: delta > 0 ? 'green' : 'red' }}>{delta}</span>
  197 + ),
  198 + },
  199 + {
  200 + title: '过期日期',
  201 + dataIndex: 'createTime',
  202 + key: 'createTime',
  203 + render: (text: string) => formatDateTime(text),
  204 + },
  205 + ];
  206 +
  207 + // Tab items
  208 + const tabItems = [
  209 + {
  210 + key: '1',
  211 + label: '兑换记录',
  212 + children: (
  213 + <>
  214 + <div
  215 + style={{ marginBottom: 16, display: 'flex', alignItems: 'center' }}
  216 + >
  217 + <span>兑换日期:</span>
  218 + <Space>
  219 + <DatePicker
  220 + placeholder="开始日期"
  221 + onChange={(date, dateString) =>
  222 + setExchangeStartTime(dateString)
  223 + }
  224 + style={{ width: 150 }}
  225 + />
  226 + <DatePicker
  227 + placeholder="结束日期"
  228 + onChange={(date, dateString) => setExchangeEndTime(dateString)}
  229 + style={{ width: 150 }}
  230 + />
  231 + <Button
  232 + type="primary"
  233 + onClick={() =>
  234 + fetchRecordsData({
  235 + startTime: exchangeStartTime,
  236 + endTime: exchangeEndTime,
  237 + tabKey: '1',
  238 + })
  239 + }
  240 + >
  241 + 查询
  242 + </Button>
  243 + </Space>
  244 + </div>
  245 + <Table
  246 + columns={exchangeRecordsColumns}
  247 + dataSource={recordsData.exchangeRecords.map((item, index) => ({
  248 + ...item,
  249 + key: `exchange-${index}`,
  250 + }))}
  251 + pagination={{ pageSize: 5 }}
  252 + loading={loading && activeTab === '1'}
  253 + locale={{
  254 + emptyText: <Empty description="暂无兑换记录" />,
  255 + }}
  256 + />
  257 + </>
  258 + ),
  259 + },
  260 + {
  261 + key: '2',
  262 + label: '已领取积分',
  263 + children: (
  264 + <>
  265 + <div
  266 + style={{ marginBottom: 16, display: 'flex', alignItems: 'center' }}
  267 + >
  268 + <span>领取日期:</span>
  269 + <Space>
  270 + <DatePicker
  271 + placeholder="开始日期"
  272 + onChange={(date, dateString) =>
  273 + setCollectedStartTime(dateString)
  274 + }
  275 + style={{ width: 150 }}
  276 + />
  277 + <DatePicker
  278 + placeholder="结束日期"
  279 + onChange={(date, dateString) => setCollectedEndTime(dateString)}
  280 + style={{ width: 150 }}
  281 + />
  282 + <Button
  283 + type="primary"
  284 + onClick={() =>
  285 + fetchRecordsData({
  286 + startTime: collectedStartTime,
  287 + endTime: collectedEndTime,
  288 + tabKey: '2',
  289 + })
  290 + }
  291 + >
  292 + 查询
  293 + </Button>
  294 + </Space>
  295 + </div>
  296 + <Table
  297 + columns={collectedRecordsColumns}
  298 + dataSource={recordsData.collectedRecords.map((item, index) => ({
  299 + ...item,
  300 + key: `collected-${index}`,
  301 + }))}
  302 + pagination={{ pageSize: 5 }}
  303 + loading={loading && activeTab === '2'}
  304 + locale={{
  305 + emptyText: <Empty description="暂无已领取积分记录" />,
  306 + }}
  307 + />
  308 + </>
  309 + ),
  310 + },
  311 + {
  312 + key: '3',
  313 + label: '待领取积分',
  314 + children: (
  315 + <>
  316 + <div
  317 + style={{ marginBottom: 16, display: 'flex', alignItems: 'center' }}
  318 + >
  319 + <span>过期日期:</span>
  320 + <Space>
  321 + <DatePicker
  322 + placeholder="开始日期"
  323 + onChange={(date, dateString) => setPendingStartTime(dateString)}
  324 + style={{ width: 150 }}
  325 + />
  326 + <DatePicker
  327 + placeholder="结束日期"
  328 + onChange={(date, dateString) => setPendingEndTime(dateString)}
  329 + style={{ width: 150 }}
  330 + />
  331 + <Button
  332 + type="primary"
  333 + onClick={() =>
  334 + fetchRecordsData({
  335 + startTime: pendingStartTime,
  336 + endTime: pendingEndTime,
  337 + tabKey: '3',
  338 + })
  339 + }
  340 + >
  341 + 查询
  342 + </Button>
  343 + </Space>
  344 + </div>
  345 + <Table
  346 + columns={pendingRecordsColumns}
  347 + dataSource={recordsData.pendingRecords.map((item, index) => ({
  348 + ...item,
  349 + key: `pending-${index}`,
  350 + }))}
  351 + pagination={{ pageSize: 5 }}
  352 + loading={loading && activeTab === '3'}
  353 + locale={{
  354 + emptyText: <Empty description="暂无待领取积分记录" />,
  355 + }}
  356 + />
  357 + </>
  358 + ),
  359 + },
  360 + ];
  361 +
  362 + return (
  363 + <Modal
  364 + open={true}
  365 + title="积分兑换记录"
  366 + width={1000}
  367 + onCancel={() => {
  368 + setVisible(false);
  369 + onClose();
  370 + }}
  371 + footer={null}
  372 + destroyOnClose
  373 + >
  374 + <Tabs
  375 + activeKey={activeTab}
  376 + onChange={(key) => setActiveTab(key)}
  377 + items={tabItems}
  378 + />
  379 + </Modal>
  380 + );
  381 +};
  382 +
  383 +export default PointsExchangeRecordsModal;
src/pages/Prepaid/constant.tsx
@@ -187,6 +187,27 @@ export const ACCOUNT_COLUMNS = [ @@ -187,6 +187,27 @@ export const ACCOUNT_COLUMNS = [
187 hideInSearch: true, 187 hideInSearch: true,
188 }, 188 },
189 { 189 {
  190 + title: '账户积分',
  191 + dataIndex: 'delta',
  192 + key: 'delta',
  193 + valueType: 'number',
  194 + hideInSearch: true,
  195 + },
  196 + {
  197 + title: '待领取积分',
  198 + dataIndex: 'pendingDelta',
  199 + key: 'pendingDelta',
  200 + valueType: 'number',
  201 + hideInSearch: true,
  202 + },
  203 + {
  204 + title: '已扣除积分',
  205 + dataIndex: 'deleteDelta',
  206 + key: 'deleteDelta',
  207 + valueType: 'number',
  208 + hideInSearch: true,
  209 + },
  210 + {
190 title: '账号', 211 title: '账号',
191 dataIndex: 'account', 212 dataIndex: 'account',
192 key: 'account', 213 key: 'account',
src/pages/Prepaid/index.tsx
@@ -16,6 +16,8 @@ import React, { useRef, useState } from &#39;react&#39;; @@ -16,6 +16,8 @@ import React, { useRef, useState } from &#39;react&#39;;
16 import CheckModal from '../Order/Order/components/CheckModal'; 16 import CheckModal from '../Order/Order/components/CheckModal';
17 import { CHECK_TYPE } from '../Order/constant'; 17 import { CHECK_TYPE } from '../Order/constant';
18 import BalanceChangeRecordsModal from './components/BalanceChangeRecordsModal'; 18 import BalanceChangeRecordsModal from './components/BalanceChangeRecordsModal';
  19 +import PointsExchangeModal from './components/PointsExchangeModal';
  20 +import PointsExchangeRecordsModal from './components/PointsExchangeRecordsModal';
19 import RechargePrepaymentModal from './components/RechargePrepaymentModal'; 21 import RechargePrepaymentModal from './components/RechargePrepaymentModal';
20 import { 22 import {
21 ACCOUNT_COLUMNS, 23 ACCOUNT_COLUMNS,
@@ -23,18 +25,25 @@ import { @@ -23,18 +25,25 @@ import {
23 SALES_RECHARGE_PREPAYMENT_COLUMNS, 25 SALES_RECHARGE_PREPAYMENT_COLUMNS,
24 } from './constant'; 26 } from './constant';
25 import './index.less'; 27 import './index.less';
  28 +
26 const PrepaidPage = () => { 29 const PrepaidPage = () => {
27 const prepaidActionRef = useRef<ActionType>(); 30 const prepaidActionRef = useRef<ActionType>();
28 const accountActionRef = useRef<ActionType>(); 31 const accountActionRef = useRef<ActionType>();
29 const [rechargePrepaymentModalVisible, setRechargePrepaymentModalVisible] = 32 const [rechargePrepaymentModalVisible, setRechargePrepaymentModalVisible] =
30 - useState(false); 33 + useState<boolean>(false);
31 const [currentOptPrepaymentObj, setCurrentOptPrepaymentObj] = useState(null); 34 const [currentOptPrepaymentObj, setCurrentOptPrepaymentObj] = useState(null);
32 const [currentOptUserObj, setCurrentOptUserObj] = useState(null); 35 const [currentOptUserObj, setCurrentOptUserObj] = useState(null);
33 - const [checkVisible, setCheckVisible] = useState(false); 36 + const [checkVisible, setCheckVisible] = useState<boolean>(false);
34 const [ 37 const [
35 balanceChangeRecordsModalVisible, 38 balanceChangeRecordsModalVisible,
36 setBalanceChangeRecordsModalVisible, 39 setBalanceChangeRecordsModalVisible,
37 - ] = useState(false); 40 + ] = useState<boolean>(false);
  41 + const [pointsExchangeModalVisible, setPointsExchangeModalVisible] =
  42 + useState<boolean>(false);
  43 + const [
  44 + pointsExchangeRecordsModalVisible,
  45 + setPointsExchangeRecordsModalVisible,
  46 + ] = useState<boolean>(false);
38 47
39 const reloadPrepaidTable = () => { 48 const reloadPrepaidTable = () => {
40 prepaidActionRef.current?.reload(); 49 prepaidActionRef.current?.reload();
@@ -194,11 +203,24 @@ const PrepaidPage = () =&gt; { @@ -194,11 +203,24 @@ const PrepaidPage = () =&gt; {
194 valueType: 'option', 203 valueType: 'option',
195 key: 'option', 204 key: 'option',
196 fixed: 'right', 205 fixed: 'right',
197 - width: 120, 206 + width: 240,
198 render: (text, record) => { 207 render: (text, record) => {
199 let btns = []; 208 let btns = [];
200 btns.push( 209 btns.push(
201 <Button 210 <Button
  211 + className="p-0 ml-2"
  212 + key="points"
  213 + type="link"
  214 + onClick={() => {
  215 + setCurrentOptUserObj(record);
  216 + setPointsExchangeModalVisible(true);
  217 + }}
  218 + >
  219 + 积分兑换
  220 + </Button>,
  221 + );
  222 + btns.push(
  223 + <Button
202 className="p-0" 224 className="p-0"
203 key="view" 225 key="view"
204 type="link" 226 type="link"
@@ -210,6 +232,19 @@ const PrepaidPage = () =&gt; { @@ -210,6 +232,19 @@ const PrepaidPage = () =&gt; {
210 消费记录 232 消费记录
211 </Button>, 233 </Button>,
212 ); 234 );
  235 + btns.push(
  236 + <Button
  237 + className="p-0 ml-2"
  238 + key="pointsRecords"
  239 + type="link"
  240 + onClick={() => {
  241 + setCurrentOptUserObj(record);
  242 + setPointsExchangeRecordsModalVisible(true);
  243 + }}
  244 + >
  245 + 积分兑换记录
  246 + </Button>,
  247 + );
213 return btns; 248 return btns;
214 }, 249 },
215 }); 250 });
@@ -377,6 +412,31 @@ const PrepaidPage = () =&gt; { @@ -377,6 +412,31 @@ const PrepaidPage = () =&gt; {
377 }} 412 }}
378 /> 413 />
379 )} 414 )}
  415 +
  416 + {pointsExchangeModalVisible && currentOptUserObj && (
  417 + <PointsExchangeModal
  418 + setVisible={(val: boolean) => {
  419 + setPointsExchangeModalVisible(val);
  420 + }}
  421 + userInfoObj={currentOptUserObj}
  422 + onClose={() => {
  423 + setPointsExchangeModalVisible(false);
  424 + reloadAccountTable();
  425 + }}
  426 + />
  427 + )}
  428 +
  429 + {pointsExchangeRecordsModalVisible && currentOptUserObj && (
  430 + <PointsExchangeRecordsModal
  431 + setVisible={(val: boolean) => {
  432 + setPointsExchangeRecordsModalVisible(val);
  433 + }}
  434 + userInfoObj={currentOptUserObj}
  435 + onClose={() => {
  436 + setPointsExchangeRecordsModalVisible(false);
  437 + }}
  438 + />
  439 + )}
380 </div> 440 </div>
381 ); 441 );
382 }; 442 };
src/pages/ResearchGroup/components/PointsExchangeModal.tsx 0 → 100644
  1 +import { postIntegralExchangeIntegral } from '@/services/request';
  2 +import { getUserInfo } from '@/utils';
  3 +import {
  4 + ModalForm,
  5 + ProFormInstance,
  6 + ProFormTextArea,
  7 +} from '@ant-design/pro-components';
  8 +import { Form, Input, message } from 'antd';
  9 +import React, { useEffect, useRef, useState } from 'react';
  10 +import '../index.less';
  11 +
  12 +interface PointsExchangeModalProps {
  13 + setVisible: (visible: boolean) => void;
  14 + record: any;
  15 + onClose: () => void;
  16 +}
  17 +const userInfo = getUserInfo();
  18 +const PointsExchangeModal: React.FC<PointsExchangeModalProps> = ({
  19 + setVisible,
  20 + record,
  21 + onClose,
  22 +}) => {
  23 + const [form] = Form.useForm<{ delta: string; remark: string }>();
  24 + const formRef = useRef<ProFormInstance>();
  25 + const [accountPoints, setAccountPoints] = useState<number>(0);
  26 +
  27 + // Get account points from record
  28 + useEffect(() => {
  29 + if (record && record.delta) {
  30 + setAccountPoints(Number(record.delta) || 0);
  31 + }
  32 + }, [record]);
  33 +
  34 + // Validate that delta is not greater than available points
  35 + const validateDelta = (rule: any, value: string) => {
  36 + const deltaValue = Number(value);
  37 + if (isNaN(deltaValue)) {
  38 + return Promise.reject('请输入有效的积分数值');
  39 + }
  40 + if (deltaValue <= 0) {
  41 + return Promise.reject('兑换积分必须大于0');
  42 + }
  43 + if (deltaValue > accountPoints) {
  44 + return Promise.reject('兑换积分不能大于账户积分');
  45 + }
  46 + return Promise.resolve();
  47 + };
  48 +
  49 + return (
  50 + <div className="prepaid-index">
  51 + <ModalForm<{
  52 + delta: string;
  53 + remark: string;
  54 + }>
  55 + width={600}
  56 + open
  57 + title="确认兑换"
  58 + form={form}
  59 + formRef={formRef}
  60 + autoFocusFirstInput
  61 + submitter={{
  62 + searchConfig: {
  63 + submitText: '确认兑换',
  64 + resetText: '取消',
  65 + },
  66 + }}
  67 + modalProps={{
  68 + destroyOnClose: true,
  69 + onCancel: () => {
  70 + setVisible(false);
  71 + },
  72 + }}
  73 + onFinish={async (values) => {
  74 + try {
  75 + // 调用积分兑换API
  76 + await postIntegralExchangeIntegral({
  77 + data: {
  78 + id: Number(record.id),
  79 + delta: Number(values.delta),
  80 + remark: values.remark,
  81 + relationEntityType: 'RESEARCH_GROUP',
  82 + createByName: userInfo?.username,
  83 + },
  84 + });
  85 +
  86 + message.success('积分兑换成功');
  87 + setVisible(false);
  88 + onClose();
  89 + return true;
  90 + } catch (error) {
  91 + console.error(error);
  92 + return false;
  93 + }
  94 + }}
  95 + onOpenChange={setVisible}
  96 + >
  97 + <Form.Item
  98 + label="兑换积分"
  99 + name="delta"
  100 + rules={[
  101 + { required: true, message: '请输入兑换积分' },
  102 + { validator: validateDelta },
  103 + ]}
  104 + >
  105 + <Input style={{ height: '30px' }} placeholder="请输入兑换积分数量" />
  106 + </Form.Item>
  107 +
  108 + <Form.Item
  109 + label="兑换说明"
  110 + name="remark"
  111 + rules={[{ required: true, message: '请输入兑换说明' }]}
  112 + >
  113 + <ProFormTextArea
  114 + style={{ height: '100px' }}
  115 + placeholder="请输入兑换说明"
  116 + />
  117 + </Form.Item>
  118 + </ModalForm>
  119 + </div>
  120 + );
  121 +};
  122 +
  123 +export default PointsExchangeModal;
src/pages/ResearchGroup/components/PointsExchangeRecordsModal.tsx 0 → 100644
  1 +import { postIntegralGroupExchangeRecords } from '@/services/request';
  2 +import { ModalForm, ProTable } from '@ant-design/pro-components';
  3 +import { Button, DatePicker, Space, Tabs, message } from 'antd';
  4 +import dayjs from 'dayjs';
  5 +import React, { useState } from 'react';
  6 +import '../index.less';
  7 +
  8 +interface PointsExchangeRecordsModalProps {
  9 + setVisible: (visible: boolean) => void;
  10 + record: any;
  11 +}
  12 +
  13 +// Define a type for the records data
  14 +type RecordsDataType = {
  15 + exchangeRecords: any[];
  16 + collectedRecords: any[];
  17 + pendingRecords: any[];
  18 +};
  19 +
  20 +const PointsExchangeRecordsModal: React.FC<PointsExchangeRecordsModalProps> = ({
  21 + setVisible,
  22 + record,
  23 +}) => {
  24 + const [activeTab, setActiveTab] = useState('1');
  25 +
  26 + // Separate date ranges for each tab
  27 + const [exchangeDateRange, setExchangeDateRange] = useState<any[]>([]);
  28 + const [collectedDateRange, setCollectedDateRange] = useState<any[]>([]);
  29 + const [pendingDateRange, setPendingDateRange] = useState<any[]>([]);
  30 +
  31 + const [loading, setLoading] = useState(false);
  32 + const [recordsData, setRecordsData] = useState<RecordsDataType>({
  33 + exchangeRecords: [], // 兑换记录
  34 + collectedRecords: [], // 已领取积分
  35 + pendingRecords: [], // 待领取积分
  36 + });
  37 +
  38 + // Function to fetch records data from API with date parameters
  39 + const fetchRecordsData = async (params: {
  40 + startTime?: string | null;
  41 + endTime?: string | null;
  42 + tabKey: string;
  43 + }) => {
  44 + try {
  45 + setLoading(true);
  46 + const { startTime, endTime, tabKey } = params;
  47 +
  48 + // Prepare API request data
  49 + const requestDto: any = {
  50 + id: Number(record.id),
  51 + };
  52 +
  53 + // Add date range if provided
  54 + if (startTime) requestDto.startTime = startTime;
  55 + if (endTime) requestDto.endTime = endTime;
  56 +
  57 + console.log(`Fetching data for tab: ${tabKey}`);
  58 +
  59 + // Use postIntegralGroupExchangeRecords instead of postIntegralUserExchangeRecords
  60 + const response = await postIntegralGroupExchangeRecords({
  61 + data: requestDto,
  62 + });
  63 +
  64 + if (response && response.data) {
  65 + // Update only the data for the active tab
  66 + setRecordsData((prevData: RecordsDataType) => {
  67 + const newData = { ...prevData };
  68 +
  69 + switch (tabKey) {
  70 + case '1': // 兑换记录
  71 + newData.exchangeRecords = response.data.exchangeRecords || [];
  72 + break;
  73 + case '2': // 已领取积分
  74 + newData.collectedRecords = response.data.collectedRecords || [];
  75 + break;
  76 + case '3': // 待领取积分
  77 + newData.pendingRecords = response.data.pendingRecords || [];
  78 + break;
  79 + }
  80 +
  81 + return newData;
  82 + });
  83 + } else {
  84 + message.error('获取积分记录失败');
  85 + }
  86 + } catch (error) {
  87 + console.error(error);
  88 + message.error('获取积分记录失败');
  89 + } finally {
  90 + setLoading(false);
  91 + }
  92 + };
  93 +
  94 + // Initial data fetch for all tabs
  95 + React.useEffect(() => {
  96 + const loadAllTabs = async () => {
  97 + try {
  98 + setLoading(true);
  99 + const requestDto = {
  100 + id: Number(record.id),
  101 + };
  102 +
  103 + const response = await postIntegralGroupExchangeRecords({
  104 + data: requestDto,
  105 + });
  106 +
  107 + if (response && response.data) {
  108 + setRecordsData({
  109 + exchangeRecords: response.data.exchangeRecords || [],
  110 + collectedRecords: response.data.collectedRecords || [],
  111 + pendingRecords: response.data.pendingRecords || [],
  112 + });
  113 + } else {
  114 + message.error('获取积分记录失败');
  115 + }
  116 + } catch (error) {
  117 + console.error(error);
  118 + message.error('获取积分记录失败');
  119 + } finally {
  120 + setLoading(false);
  121 + }
  122 + };
  123 +
  124 + loadAllTabs();
  125 + }, [record.id]);
  126 +
  127 + const handleTabChange = (key: string) => {
  128 + setActiveTab(key);
  129 + };
  130 +
  131 + // Get current date range based on active tab
  132 + const getCurrentDateRange = () => {
  133 + switch (activeTab) {
  134 + case '1':
  135 + return exchangeDateRange;
  136 + case '2':
  137 + return collectedDateRange;
  138 + case '3':
  139 + return pendingDateRange;
  140 + default:
  141 + return [];
  142 + }
  143 + };
  144 +
  145 + // Set date range for current tab
  146 + const handleDateRangeChange = (dates: any) => {
  147 + switch (activeTab) {
  148 + case '1':
  149 + setExchangeDateRange(dates);
  150 + break;
  151 + case '2':
  152 + setCollectedDateRange(dates);
  153 + break;
  154 + case '3':
  155 + setPendingDateRange(dates);
  156 + break;
  157 + }
  158 + };
  159 +
  160 + const handleSearchClick = () => {
  161 + const dateRange = getCurrentDateRange();
  162 + // Only get the dates that are actually selected
  163 + const startTime = dateRange?.[0]
  164 + ? dayjs(dateRange[0]).format('YYYY-MM-DD')
  165 + : null;
  166 + const endTime = dateRange?.[1]
  167 + ? dayjs(dateRange[1]).format('YYYY-MM-DD')
  168 + : null;
  169 +
  170 + fetchRecordsData({
  171 + startTime,
  172 + endTime,
  173 + tabKey: activeTab,
  174 + });
  175 + };
  176 +
  177 + const handleClearClick = () => {
  178 + // Clear date range for current tab
  179 + switch (activeTab) {
  180 + case '1':
  181 + setExchangeDateRange([]);
  182 + break;
  183 + case '2':
  184 + setCollectedDateRange([]);
  185 + break;
  186 + case '3':
  187 + setPendingDateRange([]);
  188 + break;
  189 + }
  190 +
  191 + // Fetch data without date filters
  192 + fetchRecordsData({
  193 + startTime: null,
  194 + endTime: null,
  195 + tabKey: activeTab,
  196 + });
  197 + };
  198 +
  199 + // 兑换记录表格列定义
  200 + const exchangeColumns = [
  201 + {
  202 + title: '兑换日期',
  203 + dataIndex: 'createTime',
  204 + key: 'createTime',
  205 + width: 30,
  206 + },
  207 + {
  208 + title: '扣除积分',
  209 + dataIndex: 'delta',
  210 + key: 'delta',
  211 + width: 20,
  212 + },
  213 + {
  214 + title: '操作人',
  215 + dataIndex: 'createByName',
  216 + key: 'createByName',
  217 + width: 20,
  218 + },
  219 + {
  220 + title: '积分用途',
  221 + dataIndex: 'remark',
  222 + key: 'remark',
  223 + width: 100,
  224 + },
  225 + ];
  226 +
  227 + // 已领取积分表格列定义
  228 + const collectedColumns = [
  229 + {
  230 + title: '订单号',
  231 + dataIndex: 'sourceId',
  232 + key: 'sourceId',
  233 + },
  234 + {
  235 + title: '领取积分',
  236 + dataIndex: 'delta',
  237 + key: 'delta',
  238 + },
  239 + {
  240 + title: '领取日期',
  241 + dataIndex: 'createTime',
  242 + key: 'createTime',
  243 + },
  244 + ];
  245 +
  246 + // 待领取积分表格列定义
  247 + const pendingColumns = [
  248 + {
  249 + title: '订单号',
  250 + dataIndex: 'sourceId',
  251 + key: 'sourceId',
  252 + },
  253 + {
  254 + title: '领取积分',
  255 + dataIndex: 'delta',
  256 + key: 'delta',
  257 + },
  258 + {
  259 + title: '过期日期',
  260 + dataIndex: 'createTime',
  261 + key: 'createTime',
  262 + },
  263 + ];
  264 +
  265 + // Render date picker and search buttons for current tab
  266 + const renderDateRangePicker = () => {
  267 + return (
  268 + <Space style={{ marginBottom: 16 }}>
  269 + <DatePicker.RangePicker
  270 + value={getCurrentDateRange()}
  271 + onChange={handleDateRangeChange}
  272 + allowEmpty={[true, true]}
  273 + />
  274 + <Button type="primary" onClick={handleSearchClick}>
  275 + 搜索
  276 + </Button>
  277 + <Button onClick={handleClearClick}>重置</Button>
  278 + </Space>
  279 + );
  280 + };
  281 +
  282 + return (
  283 + <div className="prepaid-index">
  284 + <ModalForm
  285 + width={1000}
  286 + open
  287 + title="积分兑换记录"
  288 + submitter={false}
  289 + modalProps={{
  290 + destroyOnClose: true,
  291 + onCancel: () => {
  292 + setVisible(false);
  293 + },
  294 + }}
  295 + >
  296 + <Tabs activeKey={activeTab} onChange={handleTabChange}>
  297 + <Tabs.TabPane tab="兑换记录" key="1">
  298 + {renderDateRangePicker()}
  299 + <ProTable
  300 + headerTitle={false}
  301 + search={false}
  302 + options={false}
  303 + pagination={{
  304 + pageSize: 10,
  305 + }}
  306 + loading={loading && activeTab === '1'}
  307 + dataSource={recordsData.exchangeRecords}
  308 + columns={exchangeColumns}
  309 + rowKey="id"
  310 + />
  311 + </Tabs.TabPane>
  312 + <Tabs.TabPane tab="已领取积分" key="2">
  313 + {renderDateRangePicker()}
  314 + <ProTable
  315 + headerTitle={false}
  316 + search={false}
  317 + options={false}
  318 + pagination={{
  319 + pageSize: 10,
  320 + }}
  321 + loading={loading && activeTab === '2'}
  322 + dataSource={recordsData.collectedRecords}
  323 + columns={collectedColumns}
  324 + rowKey="id"
  325 + />
  326 + </Tabs.TabPane>
  327 + <Tabs.TabPane tab="待领取积分" key="3">
  328 + {renderDateRangePicker()}
  329 + <ProTable
  330 + headerTitle={false}
  331 + search={false}
  332 + options={false}
  333 + pagination={{
  334 + pageSize: 10,
  335 + }}
  336 + loading={loading && activeTab === '3'}
  337 + dataSource={recordsData.pendingRecords}
  338 + columns={pendingColumns}
  339 + rowKey="id"
  340 + />
  341 + </Tabs.TabPane>
  342 + </Tabs>
  343 + </ModalForm>
  344 + </div>
  345 + );
  346 +};
  347 +
  348 +export default PointsExchangeRecordsModal;
src/pages/ResearchGroup/constant.tsx
1 -import { enumToProTableEnumValue } from '@/utils';  
2 - 1 +import { postServiceConstListResearchGroupsStatus } from '@/services';
  2 +import { enumToProTableEnumValue, enumToSelect } from '@/utils';
  3 +import { MessageTwoTone } from '@ant-design/icons';
  4 +import { Space, Tooltip } from 'antd';
3 export const AUDIT_STATUS_OPTIONS = { 5 export const AUDIT_STATUS_OPTIONS = {
4 CREATED: '未审核', 6 CREATED: '未审核',
5 AUDIT_PASS: '审核通过', 7 AUDIT_PASS: '审核通过',
@@ -53,6 +55,59 @@ export const RESEARCH_GROUP_COLUMNS = [ @@ -53,6 +55,59 @@ export const RESEARCH_GROUP_COLUMNS = [
53 hideInSearch: true, 55 hideInSearch: true,
54 }, 56 },
55 { 57 {
  58 + title: '账户积分',
  59 + dataIndex: 'delta',
  60 + key: 'delta',
  61 + valueType: 'number',
  62 + hideInSearch: true,
  63 + },
  64 + {
  65 + title: '待领取积分',
  66 + dataIndex: 'pendingDelta',
  67 + key: 'pendingDelta',
  68 + valueType: 'number',
  69 + hideInSearch: true,
  70 + },
  71 + {
  72 + title: '已扣除积分',
  73 + dataIndex: 'deleteDelta',
  74 + key: 'deleteDelta',
  75 + valueType: 'number',
  76 + hideInSearch: true,
  77 + },
  78 + {
  79 + title: '状态',
  80 + dataIndex: 'statusText',
  81 + key: 'statusText',
  82 + hideInSearch: true,
  83 + render: (_, record) => {
  84 + console.log('1111' + JSON.stringify(record));
  85 + return (
  86 + <>
  87 + <Space>
  88 + {record.statusText}
  89 + {record.statusNotes && (
  90 + <Tooltip title={record.statusNotes}>
  91 + <MessageTwoTone />
  92 + </Tooltip>
  93 + )}
  94 + </Space>
  95 + </>
  96 + );
  97 + },
  98 + },
  99 + {
  100 + title: '状态',
  101 + dataIndex: 'status',
  102 + key: 'status',
  103 + valueType: 'select',
  104 + request: async () => {
  105 + const groupStatus = await postServiceConstListResearchGroupsStatus();
  106 + return enumToSelect(groupStatus.data);
  107 + },
  108 + hideInTable: true,
  109 + },
  110 + {
56 title: '预存手机号', 111 title: '预存手机号',
57 dataIndex: 'accountPhone', 112 dataIndex: 'accountPhone',
58 key: 'accountPhone', 113 key: 'accountPhone',
src/pages/ResearchGroup/index.tsx
@@ -29,6 +29,8 @@ import { @@ -29,6 +29,8 @@ import {
29 import React, { useRef, useState } from 'react'; 29 import React, { useRef, useState } from 'react';
30 import AuditModal from './components/AuditModal'; 30 import AuditModal from './components/AuditModal';
31 import ImportModal from './components/ImportModal'; 31 import ImportModal from './components/ImportModal';
  32 +import PointsExchangeModal from './components/PointsExchangeModal';
  33 +import PointsExchangeRecordsModal from './components/PointsExchangeRecordsModal';
32 import ResearchGroupAddModal from './components/ResearchGroupAddModal'; 34 import ResearchGroupAddModal from './components/ResearchGroupAddModal';
33 import ResearchGroupMemberRequestAddModal from './components/ResearchGroupMemberRequestAddModal'; 35 import ResearchGroupMemberRequestAddModal from './components/ResearchGroupMemberRequestAddModal';
34 import { 36 import {
@@ -60,6 +62,13 @@ const PrepaidPage = () =&gt; { @@ -60,6 +62,13 @@ const PrepaidPage = () =&gt; {
60 const [accountInfoLoading, setAccountInfoLoading] = useState(false); 62 const [accountInfoLoading, setAccountInfoLoading] = useState(false);
61 const [perms, setPerms] = useState<string[]>([]); 63 const [perms, setPerms] = useState<string[]>([]);
62 const [optRecordId, setOptRecordId] = useState<any>(null); 64 const [optRecordId, setOptRecordId] = useState<any>(null);
  65 + const [pointsExchangeModalVisible, setPointsExchangeModalVisible] =
  66 + useState<boolean>(false);
  67 + const [
  68 + pointsExchangeRecordsModalVisible,
  69 + setPointsExchangeRecordsModalVisible,
  70 + ] = useState<boolean>(false);
  71 + const [currentRecord, setCurrentRecord] = useState<any>(null);
63 72
64 const reloadResearchGroupTable = () => { 73 const reloadResearchGroupTable = () => {
65 researchGroupActionRef.current?.reload(); 74 researchGroupActionRef.current?.reload();
@@ -306,9 +315,39 @@ const PrepaidPage = () =&gt; { @@ -306,9 +315,39 @@ const PrepaidPage = () =&gt; {
306 valueType: 'option', 315 valueType: 'option',
307 key: 'option', 316 key: 'option',
308 fixed: 'right', 317 fixed: 'right',
309 - width: 120, 318 + width: 240,
310 render: (text, record) => { 319 render: (text, record) => {
311 let btns = []; 320 let btns = [];
  321 +
  322 + // Add Points Exchange button if there are accounts
  323 + btns.push(
  324 + <Button
  325 + className="p-0"
  326 + key="points-exchange"
  327 + type="link"
  328 + onClick={() => {
  329 + setCurrentRecord(record);
  330 + setPointsExchangeModalVisible(true);
  331 + }}
  332 + >
  333 + 积分兑换
  334 + </Button>,
  335 + );
  336 +
  337 + btns.push(
  338 + <Button
  339 + className="p-0"
  340 + key="points-records"
  341 + type="link"
  342 + onClick={() => {
  343 + setCurrentRecord(record);
  344 + setPointsExchangeRecordsModalVisible(true);
  345 + }}
  346 + >
  347 + 积分兑换记录
  348 + </Button>,
  349 + );
  350 +
312 if (perms?.includes('modify')) { 351 if (perms?.includes('modify')) {
313 btns.push( 352 btns.push(
314 <Button 353 <Button
@@ -794,6 +833,24 @@ const PrepaidPage = () =&gt; { @@ -794,6 +833,24 @@ const PrepaidPage = () =&gt; {
794 }} 833 }}
795 /> 834 />
796 )} 835 )}
  836 +
  837 + {pointsExchangeModalVisible && currentRecord && (
  838 + <PointsExchangeModal
  839 + setVisible={setPointsExchangeModalVisible}
  840 + record={currentRecord}
  841 + onClose={() => {
  842 + reloadResearchGroupTable();
  843 + }}
  844 + />
  845 + )}
  846 +
  847 + {pointsExchangeRecordsModalVisible && currentRecord && (
  848 + <PointsExchangeRecordsModal
  849 + setVisible={setPointsExchangeRecordsModalVisible}
  850 + record={currentRecord}
  851 + onClose={() => {}}
  852 + />
  853 + )}
797 </div> 854 </div>
798 ); 855 );
799 }; 856 };
src/services/definition.ts
@@ -1521,36 +1521,70 @@ export interface Entry { @@ -1521,36 +1521,70 @@ export interface Entry {
1521 unEmpInsuranceC?: string; 1521 unEmpInsuranceC?: string;
1522 } 1522 }
1523 1523
1524 -export interface File {  
1525 - absolute?: boolean;  
1526 - absoluteFile?: File;  
1527 - absolutePath?: string;  
1528 - canonicalFile?: File;  
1529 - canonicalPath?: string;  
1530 - directory?: boolean;  
1531 - executable?: boolean;  
1532 - file?: boolean;  
1533 - /** @format int64 */  
1534 - freeSpace?: number;  
1535 - hidden?: boolean;  
1536 - /** @format int64 */  
1537 - lastModified?: number;  
1538 - name?: string;  
1539 - parent?: string;  
1540 - parentFile?: File;  
1541 - path?: string;  
1542 - readable?: boolean;  
1543 - /** @format int64 */  
1544 - totalSpace?: number; 1524 +export interface ExchangeIntegralDto {
  1525 + createByName?: string;
  1526 + delta?: number;
1545 /** @format int64 */ 1527 /** @format int64 */
1546 - usableSpace?: number;  
1547 - writable?: boolean; 1528 + id?: number;
  1529 + relationEntityType?: string;
  1530 + remark?: string;
  1531 +}
  1532 +
  1533 +export interface ExchangeRecordsRequestDto {
  1534 + createByName?: string;
  1535 + createByNameLike?: string;
  1536 + /** @format date-time */
  1537 + createTimeGe?: string;
  1538 + /** @format date-time */
  1539 + createTimeLe?: string;
  1540 + /** @format int32 */
  1541 + current?: number;
  1542 + /** @format int32 */
  1543 + end?: number;
  1544 + /** @format date-time */
  1545 + endTime?: string;
  1546 + /** @format int32 */
  1547 + id?: number;
  1548 + /** @format int32 */
  1549 + pageSize?: number;
  1550 + phone?: string;
  1551 + /** @format int32 */
  1552 + start?: number;
  1553 + /** @format date-time */
  1554 + startTime?: string;
  1555 + /** @format int32 */
  1556 + total?: number;
1548 } 1557 }
1549 1558
1550 export interface FilePathDto { 1559 export interface FilePathDto {
1551 url?: string; 1560 url?: string;
1552 } 1561 }
1553 1562
  1563 +export interface GroupExchangeRecordsRequestDto {
  1564 + createByName?: string;
  1565 + createByNameLike?: string;
  1566 + /** @format date-time */
  1567 + createTimeGe?: string;
  1568 + /** @format date-time */
  1569 + createTimeLe?: string;
  1570 + /** @format int32 */
  1571 + current?: number;
  1572 + /** @format int32 */
  1573 + end?: number;
  1574 + /** @format date-time */
  1575 + endTime?: string;
  1576 + /** @format int64 */
  1577 + id?: number;
  1578 + /** @format int32 */
  1579 + pageSize?: number;
  1580 + /** @format int32 */
  1581 + start?: number;
  1582 + /** @format date-time */
  1583 + startTime?: string;
  1584 + /** @format int32 */
  1585 + total?: number;
  1586 +}
  1587 +
1554 export type InputStream = any; 1588 export type InputStream = any;
1555 1589
1556 export interface InventoryMaterialStockReq { 1590 export interface InventoryMaterialStockReq {
@@ -3727,6 +3761,16 @@ export interface ResearchGroupEditRequest { @@ -3727,6 +3761,16 @@ export interface ResearchGroupEditRequest {
3727 createTime?: string; 3761 createTime?: string;
3728 /** 3762 /**
3729 * @description 3763 * @description
  3764 + * 已扣除积分
  3765 + */
  3766 + deleteDelta?: number;
  3767 + /**
  3768 + * @description
  3769 + * 账户积分
  3770 + */
  3771 + delta?: number;
  3772 + /**
  3773 + * @description
3730 * 课题组名称 3774 * 课题组名称
3731 */ 3775 */
3732 groupName?: string; 3776 groupName?: string;
@@ -3744,6 +3788,11 @@ export interface ResearchGroupEditRequest { @@ -3744,6 +3788,11 @@ export interface ResearchGroupEditRequest {
3744 logicDelete?: boolean; 3788 logicDelete?: boolean;
3745 members?: Array<ResearchGroupMembers>; 3789 members?: Array<ResearchGroupMembers>;
3746 paths?: Array<string>; 3790 paths?: Array<string>;
  3791 + /**
  3792 + * @description
  3793 + * 待领取积分
  3794 + */
  3795 + pendingDelta?: number;
3747 requestStatus?: string; 3796 requestStatus?: string;
3748 /** 3797 /**
3749 * @description 3798 * @description
@@ -4062,6 +4111,16 @@ export interface ResearchGroupsDTO { @@ -4062,6 +4111,16 @@ export interface ResearchGroupsDTO {
4062 createTime?: string; 4111 createTime?: string;
4063 /** 4112 /**
4064 * @description 4113 * @description
  4114 + * 已扣除积分
  4115 + */
  4116 + deleteDelta?: number;
  4117 + /**
  4118 + * @description
  4119 + * 账户积分
  4120 + */
  4121 + delta?: number;
  4122 + /**
  4123 + * @description
4065 * 课题组名称 4124 * 课题组名称
4066 */ 4125 */
4067 groupName?: string; 4126 groupName?: string;
@@ -4079,6 +4138,11 @@ export interface ResearchGroupsDTO { @@ -4079,6 +4138,11 @@ export interface ResearchGroupsDTO {
4079 logicDelete?: boolean; 4138 logicDelete?: boolean;
4080 members?: Array<ResearchGroupMembers>; 4139 members?: Array<ResearchGroupMembers>;
4081 paths?: Array<string>; 4140 paths?: Array<string>;
  4141 + /**
  4142 + * @description
  4143 + * 待领取积分
  4144 + */
  4145 + pendingDelta?: number;
4082 requestStatus?: string; 4146 requestStatus?: string;
4083 /** 4147 /**
4084 * @description 4148 * @description
@@ -4107,7 +4171,7 @@ export interface ResetPwdVO { @@ -4107,7 +4171,7 @@ export interface ResetPwdVO {
4107 4171
4108 export interface Resource { 4172 export interface Resource {
4109 description?: string; 4173 description?: string;
4110 - file?: File; 4174 + file?: TsgFile;
4111 filename?: string; 4175 filename?: string;
4112 inputStream?: InputStream; 4176 inputStream?: InputStream;
4113 open?: boolean; 4177 open?: boolean;
@@ -4815,6 +4879,32 @@ export interface CompanyInfo { @@ -4815,6 +4879,32 @@ export interface CompanyInfo {
4815 taxIdIsNotNull?: boolean; 4879 taxIdIsNotNull?: boolean;
4816 } 4880 }
4817 4881
  4882 +export interface TsgFile {
  4883 + absolute?: boolean;
  4884 + absoluteFile?: TsgFile;
  4885 + absolutePath?: string;
  4886 + canonicalFile?: TsgFile;
  4887 + canonicalPath?: string;
  4888 + directory?: boolean;
  4889 + executable?: boolean;
  4890 + file?: boolean;
  4891 + /** @format int64 */
  4892 + freeSpace?: number;
  4893 + hidden?: boolean;
  4894 + /** @format int64 */
  4895 + lastModified?: number;
  4896 + name?: string;
  4897 + parent?: string;
  4898 + parentFile?: TsgFile;
  4899 + path?: string;
  4900 + readable?: boolean;
  4901 + /** @format int64 */
  4902 + totalSpace?: number;
  4903 + /** @format int64 */
  4904 + usableSpace?: number;
  4905 + writable?: boolean;
  4906 +}
  4907 +
4818 export interface InvoiceDetail { 4908 export interface InvoiceDetail {
4819 createByName?: string; 4909 createByName?: string;
4820 /** @format date-time */ 4910 /** @format date-time */
src/services/request.ts
@@ -56,7 +56,10 @@ import type { @@ -56,7 +56,10 @@ import type {
56 DistrictDo, 56 DistrictDo,
57 DistrictSearchDo, 57 DistrictSearchDo,
58 Dto, 58 Dto,
  59 + ExchangeIntegralDto,
  60 + ExchangeRecordsRequestDto,
59 FeedbackRegistrationDTO, 61 FeedbackRegistrationDTO,
  62 + GroupExchangeRecordsRequestDto,
60 InventoryMaterialStockReq, 63 InventoryMaterialStockReq,
61 InvoiceBatchDownloadDto, 64 InvoiceBatchDownloadDto,
62 InvoiceDto, 65 InvoiceDto,
@@ -71,7 +74,6 @@ import type { @@ -71,7 +74,6 @@ import type {
71 MeasureUnitListRes, 74 MeasureUnitListRes,
72 MergeIntegralDto, 75 MergeIntegralDto,
73 MessageQueryDTO, 76 MessageQueryDTO,
74 - ModelAndView,  
75 OrderAddVO, 77 OrderAddVO,
76 OrderAuditLogQueryVO, 78 OrderAuditLogQueryVO,
77 OrderBaseInfoQueryVO, 79 OrderBaseInfoQueryVO,
@@ -3830,7 +3832,9 @@ export interface GetErrorResponse { @@ -3830,7 +3832,9 @@ export interface GetErrorResponse {
3830 * @description 3832 * @description
3831 * OK 3833 * OK
3832 */ 3834 */
3833 - 200: ModelAndView; 3835 + 200: {
  3836 + [propertyName: string]: any;
  3837 + };
3834 /** 3838 /**
3835 * @description 3839 * @description
3836 * Unauthorized 3840 * Unauthorized
@@ -3851,9 +3855,9 @@ export interface GetErrorResponse { @@ -3851,9 +3855,9 @@ export interface GetErrorResponse {
3851 export type GetErrorResponseSuccess = GetErrorResponse[200]; 3855 export type GetErrorResponseSuccess = GetErrorResponse[200];
3852 /** 3856 /**
3853 * @description 3857 * @description
3854 - * errorHtml 3858 + * error
3855 * @tags basic-error-controller 3859 * @tags basic-error-controller
3856 - * @produces text/html 3860 + * @produces *
3857 */ 3861 */
3858 export const getError = /* #__PURE__ */ (() => { 3862 export const getError = /* #__PURE__ */ (() => {
3859 const method = 'get'; 3863 const method = 'get';
@@ -3877,7 +3881,9 @@ export interface PutErrorResponse { @@ -3877,7 +3881,9 @@ export interface PutErrorResponse {
3877 * @description 3881 * @description
3878 * OK 3882 * OK
3879 */ 3883 */
3880 - 200: ModelAndView; 3884 + 200: {
  3885 + [propertyName: string]: any;
  3886 + };
3881 /** 3887 /**
3882 * @description 3888 * @description
3883 * Created 3889 * Created
@@ -3903,9 +3909,9 @@ export interface PutErrorResponse { @@ -3903,9 +3909,9 @@ export interface PutErrorResponse {
3903 export type PutErrorResponseSuccess = PutErrorResponse[200]; 3909 export type PutErrorResponseSuccess = PutErrorResponse[200];
3904 /** 3910 /**
3905 * @description 3911 * @description
3906 - * errorHtml 3912 + * error
3907 * @tags basic-error-controller 3913 * @tags basic-error-controller
3908 - * @produces text/html 3914 + * @produces *
3909 * @consumes application/json 3915 * @consumes application/json
3910 */ 3916 */
3911 export const putError = /* #__PURE__ */ (() => { 3917 export const putError = /* #__PURE__ */ (() => {
@@ -3930,7 +3936,9 @@ export interface PostErrorResponse { @@ -3930,7 +3936,9 @@ export interface PostErrorResponse {
3930 * @description 3936 * @description
3931 * OK 3937 * OK
3932 */ 3938 */
3933 - 200: ModelAndView; 3939 + 200: {
  3940 + [propertyName: string]: any;
  3941 + };
3934 /** 3942 /**
3935 * @description 3943 * @description
3936 * Created 3944 * Created
@@ -3956,9 +3964,9 @@ export interface PostErrorResponse { @@ -3956,9 +3964,9 @@ export interface PostErrorResponse {
3956 export type PostErrorResponseSuccess = PostErrorResponse[200]; 3964 export type PostErrorResponseSuccess = PostErrorResponse[200];
3957 /** 3965 /**
3958 * @description 3966 * @description
3959 - * errorHtml 3967 + * error
3960 * @tags basic-error-controller 3968 * @tags basic-error-controller
3961 - * @produces text/html 3969 + * @produces *
3962 * @consumes application/json 3970 * @consumes application/json
3963 */ 3971 */
3964 export const postError = /* #__PURE__ */ (() => { 3972 export const postError = /* #__PURE__ */ (() => {
@@ -3983,7 +3991,9 @@ export interface DeleteErrorResponse { @@ -3983,7 +3991,9 @@ export interface DeleteErrorResponse {
3983 * @description 3991 * @description
3984 * OK 3992 * OK
3985 */ 3993 */
3986 - 200: ModelAndView; 3994 + 200: {
  3995 + [propertyName: string]: any;
  3996 + };
3987 /** 3997 /**
3988 * @description 3998 * @description
3989 * No Content 3999 * No Content
@@ -4004,9 +4014,9 @@ export interface DeleteErrorResponse { @@ -4004,9 +4014,9 @@ export interface DeleteErrorResponse {
4004 export type DeleteErrorResponseSuccess = DeleteErrorResponse[200]; 4014 export type DeleteErrorResponseSuccess = DeleteErrorResponse[200];
4005 /** 4015 /**
4006 * @description 4016 * @description
4007 - * errorHtml 4017 + * error
4008 * @tags basic-error-controller 4018 * @tags basic-error-controller
4009 - * @produces text/html 4019 + * @produces *
4010 */ 4020 */
4011 export const deleteError = /* #__PURE__ */ (() => { 4021 export const deleteError = /* #__PURE__ */ (() => {
4012 const method = 'delete'; 4022 const method = 'delete';
@@ -4030,7 +4040,9 @@ export interface OptionsErrorResponse { @@ -4030,7 +4040,9 @@ export interface OptionsErrorResponse {
4030 * @description 4040 * @description
4031 * OK 4041 * OK
4032 */ 4042 */
4033 - 200: ModelAndView; 4043 + 200: {
  4044 + [propertyName: string]: any;
  4045 + };
4034 /** 4046 /**
4035 * @description 4047 * @description
4036 * No Content 4048 * No Content
@@ -4051,9 +4063,9 @@ export interface OptionsErrorResponse { @@ -4051,9 +4063,9 @@ export interface OptionsErrorResponse {
4051 export type OptionsErrorResponseSuccess = OptionsErrorResponse[200]; 4063 export type OptionsErrorResponseSuccess = OptionsErrorResponse[200];
4052 /** 4064 /**
4053 * @description 4065 * @description
4054 - * errorHtml 4066 + * error
4055 * @tags basic-error-controller 4067 * @tags basic-error-controller
4056 - * @produces text/html 4068 + * @produces *
4057 * @consumes application/json 4069 * @consumes application/json
4058 */ 4070 */
4059 export const optionsError = /* #__PURE__ */ (() => { 4071 export const optionsError = /* #__PURE__ */ (() => {
@@ -4078,7 +4090,9 @@ export interface HeadErrorResponse { @@ -4078,7 +4090,9 @@ export interface HeadErrorResponse {
4078 * @description 4090 * @description
4079 * OK 4091 * OK
4080 */ 4092 */
4081 - 200: ModelAndView; 4093 + 200: {
  4094 + [propertyName: string]: any;
  4095 + };
4082 /** 4096 /**
4083 * @description 4097 * @description
4084 * No Content 4098 * No Content
@@ -4099,9 +4113,9 @@ export interface HeadErrorResponse { @@ -4099,9 +4113,9 @@ export interface HeadErrorResponse {
4099 export type HeadErrorResponseSuccess = HeadErrorResponse[200]; 4113 export type HeadErrorResponseSuccess = HeadErrorResponse[200];
4100 /** 4114 /**
4101 * @description 4115 * @description
4102 - * errorHtml 4116 + * error
4103 * @tags basic-error-controller 4117 * @tags basic-error-controller
4104 - * @produces text/html 4118 + * @produces *
4105 * @consumes application/json 4119 * @consumes application/json
4106 */ 4120 */
4107 export const headError = /* #__PURE__ */ (() => { 4121 export const headError = /* #__PURE__ */ (() => {
@@ -4126,7 +4140,9 @@ export interface PatchErrorResponse { @@ -4126,7 +4140,9 @@ export interface PatchErrorResponse {
4126 * @description 4140 * @description
4127 * OK 4141 * OK
4128 */ 4142 */
4129 - 200: ModelAndView; 4143 + 200: {
  4144 + [propertyName: string]: any;
  4145 + };
4130 /** 4146 /**
4131 * @description 4147 * @description
4132 * No Content 4148 * No Content
@@ -4147,9 +4163,9 @@ export interface PatchErrorResponse { @@ -4147,9 +4163,9 @@ export interface PatchErrorResponse {
4147 export type PatchErrorResponseSuccess = PatchErrorResponse[200]; 4163 export type PatchErrorResponseSuccess = PatchErrorResponse[200];
4148 /** 4164 /**
4149 * @description 4165 * @description
4150 - * errorHtml 4166 + * error
4151 * @tags basic-error-controller 4167 * @tags basic-error-controller
4152 - * @produces text/html 4168 + * @produces *
4153 * @consumes application/json 4169 * @consumes application/json
4154 */ 4170 */
4155 export const patchError = /* #__PURE__ */ (() => { 4171 export const patchError = /* #__PURE__ */ (() => {
@@ -4238,6 +4254,77 @@ export const postFileDirectDown = /* #__PURE__ */ (() =&gt; { @@ -4238,6 +4254,77 @@ export const postFileDirectDown = /* #__PURE__ */ (() =&gt; {
4238 return request; 4254 return request;
4239 })(); 4255 })();
4240 4256
  4257 +/** @description request parameter type for postIntegralExchangeIntegral */
  4258 +export interface PostIntegralExchangeIntegralOption {
  4259 + /**
  4260 + * @description
  4261 + * dto
  4262 + */
  4263 + body: {
  4264 + /**
  4265 + @description
  4266 + dto */
  4267 + dto: ExchangeIntegralDto;
  4268 + };
  4269 +}
  4270 +
  4271 +/** @description response type for postIntegralExchangeIntegral */
  4272 +export interface PostIntegralExchangeIntegralResponse {
  4273 + /**
  4274 + * @description
  4275 + * OK
  4276 + */
  4277 + 200: ServerResult;
  4278 + /**
  4279 + * @description
  4280 + * Created
  4281 + */
  4282 + 201: any;
  4283 + /**
  4284 + * @description
  4285 + * Unauthorized
  4286 + */
  4287 + 401: any;
  4288 + /**
  4289 + * @description
  4290 + * Forbidden
  4291 + */
  4292 + 403: any;
  4293 + /**
  4294 + * @description
  4295 + * Not Found
  4296 + */
  4297 + 404: any;
  4298 +}
  4299 +
  4300 +export type PostIntegralExchangeIntegralResponseSuccess =
  4301 + PostIntegralExchangeIntegralResponse[200];
  4302 +/**
  4303 + * @description
  4304 + * 积分兑换
  4305 + * @tags 积分接口
  4306 + * @produces *
  4307 + * @consumes application/json
  4308 + */
  4309 +export const postIntegralExchangeIntegral = /* #__PURE__ */ (() => {
  4310 + const method = 'post';
  4311 + const url = '/integral/exchangeIntegral';
  4312 + function request(
  4313 + option: PostIntegralExchangeIntegralOption,
  4314 + ): Promise<PostIntegralExchangeIntegralResponseSuccess> {
  4315 + return requester(request.url, {
  4316 + method: request.method,
  4317 + ...option,
  4318 + }) as unknown as Promise<PostIntegralExchangeIntegralResponseSuccess>;
  4319 + }
  4320 +
  4321 + /** http method */
  4322 + request.method = method;
  4323 + /** request url */
  4324 + request.url = url;
  4325 + return request;
  4326 +})();
  4327 +
4241 /** @description response type for postIntegralGetIntegralRecordTypes */ 4328 /** @description response type for postIntegralGetIntegralRecordTypes */
4242 export interface PostIntegralGetIntegralRecordTypesResponse { 4329 export interface PostIntegralGetIntegralRecordTypesResponse {
4243 /** 4330 /**
@@ -4292,6 +4379,77 @@ export const postIntegralGetIntegralRecordTypes = /* #__PURE__ */ (() =&gt; { @@ -4292,6 +4379,77 @@ export const postIntegralGetIntegralRecordTypes = /* #__PURE__ */ (() =&gt; {
4292 return request; 4379 return request;
4293 })(); 4380 })();
4294 4381
  4382 +/** @description request parameter type for postIntegralGroupExchangeRecords */
  4383 +export interface PostIntegralGroupExchangeRecordsOption {
  4384 + /**
  4385 + * @description
  4386 + * dto
  4387 + */
  4388 + body: {
  4389 + /**
  4390 + @description
  4391 + dto */
  4392 + dto: GroupExchangeRecordsRequestDto;
  4393 + };
  4394 +}
  4395 +
  4396 +/** @description response type for postIntegralGroupExchangeRecords */
  4397 +export interface PostIntegralGroupExchangeRecordsResponse {
  4398 + /**
  4399 + * @description
  4400 + * OK
  4401 + */
  4402 + 200: ServerResult;
  4403 + /**
  4404 + * @description
  4405 + * Created
  4406 + */
  4407 + 201: any;
  4408 + /**
  4409 + * @description
  4410 + * Unauthorized
  4411 + */
  4412 + 401: any;
  4413 + /**
  4414 + * @description
  4415 + * Forbidden
  4416 + */
  4417 + 403: any;
  4418 + /**
  4419 + * @description
  4420 + * Not Found
  4421 + */
  4422 + 404: any;
  4423 +}
  4424 +
  4425 +export type PostIntegralGroupExchangeRecordsResponseSuccess =
  4426 + PostIntegralGroupExchangeRecordsResponse[200];
  4427 +/**
  4428 + * @description
  4429 + * 课题组积分兑换记录
  4430 + * @tags 积分接口
  4431 + * @produces *
  4432 + * @consumes application/json
  4433 + */
  4434 +export const postIntegralGroupExchangeRecords = /* #__PURE__ */ (() => {
  4435 + const method = 'post';
  4436 + const url = '/integral/groupExchangeRecords';
  4437 + function request(
  4438 + option: PostIntegralGroupExchangeRecordsOption,
  4439 + ): Promise<PostIntegralGroupExchangeRecordsResponseSuccess> {
  4440 + return requester(request.url, {
  4441 + method: request.method,
  4442 + ...option,
  4443 + }) as unknown as Promise<PostIntegralGroupExchangeRecordsResponseSuccess>;
  4444 + }
  4445 +
  4446 + /** http method */
  4447 + request.method = method;
  4448 + /** request url */
  4449 + request.url = url;
  4450 + return request;
  4451 +})();
  4452 +
4295 /** @description request parameter type for postIntegralMergeUserIntegralToGroup */ 4453 /** @description request parameter type for postIntegralMergeUserIntegralToGroup */
4296 export interface PostIntegralMergeUserIntegralToGroupOption { 4454 export interface PostIntegralMergeUserIntegralToGroupOption {
4297 /** 4455 /**
@@ -4647,6 +4805,77 @@ export const postIntegralQuerySumByUser = /* #__PURE__ */ (() =&gt; { @@ -4647,6 +4805,77 @@ export const postIntegralQuerySumByUser = /* #__PURE__ */ (() =&gt; {
4647 return request; 4805 return request;
4648 })(); 4806 })();
4649 4807
  4808 +/** @description request parameter type for postIntegralUserExchangeRecords */
  4809 +export interface PostIntegralUserExchangeRecordsOption {
  4810 + /**
  4811 + * @description
  4812 + * dto
  4813 + */
  4814 + body: {
  4815 + /**
  4816 + @description
  4817 + dto */
  4818 + dto: ExchangeRecordsRequestDto;
  4819 + };
  4820 +}
  4821 +
  4822 +/** @description response type for postIntegralUserExchangeRecords */
  4823 +export interface PostIntegralUserExchangeRecordsResponse {
  4824 + /**
  4825 + * @description
  4826 + * OK
  4827 + */
  4828 + 200: ServerResult;
  4829 + /**
  4830 + * @description
  4831 + * Created
  4832 + */
  4833 + 201: any;
  4834 + /**
  4835 + * @description
  4836 + * Unauthorized
  4837 + */
  4838 + 401: any;
  4839 + /**
  4840 + * @description
  4841 + * Forbidden
  4842 + */
  4843 + 403: any;
  4844 + /**
  4845 + * @description
  4846 + * Not Found
  4847 + */
  4848 + 404: any;
  4849 +}
  4850 +
  4851 +export type PostIntegralUserExchangeRecordsResponseSuccess =
  4852 + PostIntegralUserExchangeRecordsResponse[200];
  4853 +/**
  4854 + * @description
  4855 + * 用户积分兑换记录
  4856 + * @tags 积分接口
  4857 + * @produces *
  4858 + * @consumes application/json
  4859 + */
  4860 +export const postIntegralUserExchangeRecords = /* #__PURE__ */ (() => {
  4861 + const method = 'post';
  4862 + const url = '/integral/userExchangeRecords';
  4863 + function request(
  4864 + option: PostIntegralUserExchangeRecordsOption,
  4865 + ): Promise<PostIntegralUserExchangeRecordsResponseSuccess> {
  4866 + return requester(request.url, {
  4867 + method: request.method,
  4868 + ...option,
  4869 + }) as unknown as Promise<PostIntegralUserExchangeRecordsResponseSuccess>;
  4870 + }
  4871 +
  4872 + /** http method */
  4873 + request.method = method;
  4874 + /** request url */
  4875 + request.url = url;
  4876 + return request;
  4877 +})();
  4878 +
4650 /** @description request parameter type for postKingdeeRepCustomer */ 4879 /** @description request parameter type for postKingdeeRepCustomer */
4651 export interface PostKingdeeRepCustomerOption { 4880 export interface PostKingdeeRepCustomerOption {
4652 /** 4881 /**