Commit b19689c219949e29d74c4c895bdb3a17124a5467

Authored by boyang
1 parent 576a38ce

feat: 课题组风险名单

Showing 20 changed files with 1739 additions and 380 deletions
.umirc.ts
@@ -139,9 +139,20 @@ export default defineConfig({ @@ -139,9 +139,20 @@ export default defineConfig({
139 { 139 {
140 name: '课题组管理', 140 name: '课题组管理',
141 path: '/researchGroup', 141 path: '/researchGroup',
142 - component: './ResearchGroup',  
143 icon: 'AccountBookOutlined', 142 icon: 'AccountBookOutlined',
144 access: 'canReadAdminAndSales', 143 access: 'canReadAdminAndSales',
  144 + routes: [
  145 + {
  146 + name: '课题组列表',
  147 + path: 'researchGroup',
  148 + component: './ResearchGroup/ResearchGroup',
  149 + },
  150 + {
  151 + name: '课题组风险名单',
  152 + path: 'researchGroupAccess',
  153 + component: './ResearchGroup/ResearchGroupAccess',
  154 + },
  155 + ],
145 }, 156 },
146 { 157 {
147 name: '分期账单', 158 name: '分期账单',
src/pages/Order/OrderList/OrderDrawer.tsx
@@ -768,73 +768,69 @@ export default ({ onClose, data, subOrders, orderOptType }) => { @@ -768,73 +768,69 @@ export default ({ onClose, data, subOrders, orderOptType }) => {
768 }> 768 }>
769 open 769 open
770 width={1000} 770 width={1000}
771 - title={drawerTitle}  
772 - resize={{  
773 - onResize() {  
774 - console.log('resize!');  
775 - },  
776 - maxWidth: window.innerWidth * 0.8,  
777 - minWidth: 400, 771 + modalProps={{
  772 + destroyOnClose: true,
  773 + maskClosable: true,
  774 + title: (
  775 + <div
  776 + style={{ display: 'flex', alignItems: 'center', width: '100%' }}
  777 + >
  778 + <span>{drawerTitle}</span>
  779 + {hasLocalData && (
  780 + <Button
  781 + key="useLocalData"
  782 + type="link"
  783 + onClick={() => {
  784 + useLocalFormData();
  785 + }}
  786 + >
  787 + 使用草稿
  788 + </Button>
  789 + )}
  790 + </div>
  791 + ),
778 }} 792 }}
779 onFinishFailed={() => { 793 onFinishFailed={() => {
780 message.error('表单项存在错误,请检查'); 794 message.error('表单项存在错误,请检查');
781 setSubmitBtnLoading(false); 795 setSubmitBtnLoading(false);
782 }} 796 }}
783 submitter={{ 797 submitter={{
784 - render: (props) => {  
785 - return [  
786 - <Button  
787 - key="cancel"  
788 - onClick={() => {  
789 - onClose();  
790 - }}  
791 - >  
792 - 取消  
793 - </Button>,  
794 - <Button  
795 - key="localSave"  
796 - loading={localSaveLoading}  
797 - hidden={!optType('add') && !optType('copy')}  
798 - onClick={() => {  
799 - setLocalSaveLoading(true);  
800 - saveFormDataToLocal();  
801 - }}  
802 - >  
803 - 本地保存  
804 - </Button>,  
805 - <Button  
806 - key="ok"  
807 - type="primary"  
808 - loading={submitBtnLoading}  
809 - disabled={optType('after-sales-check')}  
810 - onClick={() => {  
811 - setSubmitBtnLoading(true);  
812 - props.submit();  
813 - }}  
814 - >  
815 - 提交  
816 - </Button>,  
817 - ];  
818 - },  
819 - }}  
820 - form={form}  
821 - autoFocusFirstInput  
822 - drawerProps={{  
823 - destroyOnClose: true,  
824 - maskClosable: false,  
825 - extra: [ 798 + render: (props) => [
  799 + <Button
  800 + key="cancel"
  801 + onClick={() => {
  802 + onClose();
  803 + }}
  804 + >
  805 + 取消
  806 + </Button>,
826 <Button 807 <Button
827 - key="useLocalData"  
828 - hidden={!hasLocalData}  
829 - type="link" 808 + key="localSave"
  809 + loading={localSaveLoading}
  810 + hidden={!optType('add') && !optType('copy')}
830 onClick={() => { 811 onClick={() => {
831 - useLocalFormData(); 812 + setLocalSaveLoading(true);
  813 + saveFormDataToLocal();
832 }} 814 }}
833 > 815 >
834 - 使用草稿 816 + 本地保存
  817 + </Button>,
  818 + <Button
  819 + key="ok"
  820 + type="primary"
  821 + loading={submitBtnLoading}
  822 + disabled={optType('after-sales-check')}
  823 + onClick={() => {
  824 + setSubmitBtnLoading(true);
  825 + props.submit();
  826 + }}
  827 + >
  828 + 提交
835 </Button>, 829 </Button>,
836 ], 830 ],
837 }} 831 }}
  832 + form={form}
  833 + autoFocusFirstInput
838 submitTimeout={2000} 834 submitTimeout={2000}
839 onFinish={async (values) => { 835 onFinish={async (values) => {
840 let res = {}; 836 let res = {};
src/pages/Order/OrderWarning/components/OrderDrawer.tsx
@@ -11,6 +11,10 @@ import { @@ -11,6 +11,10 @@ import {
11 postKingdeeRepMaterialUnit, 11 postKingdeeRepMaterialUnit,
12 postKingdeeRepMeasureUnit, 12 postKingdeeRepMeasureUnit,
13 postPrepaidPhoneAvailableList, 13 postPrepaidPhoneAvailableList,
  14 + postResearchGroupsNameSet,
  15 + postServiceConstCompanyType,
  16 + postServiceConstOrderSource,
  17 + postServiceConstPlatformType,
14 postServiceOrderAddOrder, 18 postServiceOrderAddOrder,
15 postServiceOrderAfterSalesQuerySnapshotOrder, 19 postServiceOrderAfterSalesQuerySnapshotOrder,
16 postServiceOrderApplyAfterSales, 20 postServiceOrderApplyAfterSales,
@@ -28,17 +32,21 @@ import { getTeacherCustomFieldNumber } from &#39;@/utils/kingdee&#39;; @@ -28,17 +32,21 @@ import { getTeacherCustomFieldNumber } from &#39;@/utils/kingdee&#39;;
28 import { getSalesCodeOptions } from '@/utils/order'; 32 import { getSalesCodeOptions } from '@/utils/order';
29 import { getDefaultString } from '@/utils/StringUtil'; 33 import { getDefaultString } from '@/utils/StringUtil';
30 import { 34 import {
31 - DrawerForm,  
32 FormListActionType, 35 FormListActionType,
  36 + ModalForm,
33 ProCard, 37 ProCard,
  38 + ProFormDatePicker,
34 ProFormDateTimePicker, 39 ProFormDateTimePicker,
  40 + ProFormDependency,
35 ProFormDigit, 41 ProFormDigit,
36 ProFormList, 42 ProFormList,
  43 + ProFormRadio,
37 ProFormSelect, 44 ProFormSelect,
38 ProFormText, 45 ProFormText,
39 ProFormTextArea, 46 ProFormTextArea,
40 ProFormUploadDragger, 47 ProFormUploadDragger,
41 } from '@ant-design/pro-components'; 48 } from '@ant-design/pro-components';
  49 +import { Group } from '@ant-design/pro-form';
42 import { Button, Form, message, Modal } from 'antd'; 50 import { Button, Form, message, Modal } from 'antd';
43 import { cloneDeep } from 'lodash'; 51 import { cloneDeep } from 'lodash';
44 import { useEffect, useRef, useState } from 'react'; 52 import { useEffect, useRef, useState } from 'react';
@@ -48,15 +56,15 @@ import { @@ -48,15 +56,15 @@ import {
48 INVOCING_STATUS_OPTIONS_OLD, 56 INVOCING_STATUS_OPTIONS_OLD,
49 PAYEE_OPTIONS, 57 PAYEE_OPTIONS,
50 PAYMENT_CHANNEL_OPTIONS, 58 PAYMENT_CHANNEL_OPTIONS,
51 - PAYMENT_METHOD_OPTIONS, 59 + PAYMENT_METHOD_OPTIONS_4_ADD,
52 PRODUCT_BELONG_DEPARTMENT_OPTIONS, 60 PRODUCT_BELONG_DEPARTMENT_OPTIONS,
53 SHIPPING_WAREHOUSE_OPTIONS, 61 SHIPPING_WAREHOUSE_OPTIONS,
54 -} from '../../constant'; 62 +} from '../constant';
55 import KingdeeCustomerModal from './KingdeeCustomerModal'; 63 import KingdeeCustomerModal from './KingdeeCustomerModal';
56 64
57 export default ({ onClose, data, subOrders, orderOptType }) => { 65 export default ({ onClose, data, subOrders, orderOptType }) => {
58 const [invoicingStatus, setInvoicingStatus] = useState(''); 66 const [invoicingStatus, setInvoicingStatus] = useState('');
59 - const [salesCodeOptions] = useState([]); 67 + const [salesCodeOptions, setSalesCodeOptions] = useState([]);
60 const [submitBtnLoading, setSubmitBtnLoading] = useState(false); 68 const [submitBtnLoading, setSubmitBtnLoading] = useState(false);
61 const [drawerTitle, setDrawerTitle] = useState(''); 69 const [drawerTitle, setDrawerTitle] = useState('');
62 const [hasLocalData, setHasLocalData] = useState(false); 70 const [hasLocalData, setHasLocalData] = useState(false);
@@ -80,41 +88,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -80,41 +88,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
80 const [district, setDistrict] = useState(''); 88 const [district, setDistrict] = useState('');
81 // const [productCustomerContactOptions, setProductCustomerContactOptions] = 89 // const [productCustomerContactOptions, setProductCustomerContactOptions] =
82 // useState([]); //客户的收货人选项 90 // useState([]); //客户的收货人选项
83 - const [form] = Form.useForm<{  
84 - isLocalData: boolean;  
85 - salesCode: '';  
86 - customerName: '';  
87 - customerContactNumber: '';  
88 - institution: '';  
89 - institutionContactName: '';  
90 - customerShippingAddress: '';  
91 - totalPayment: '';  
92 - paymentChannel: '';  
93 - paymentMethod: '';  
94 - productBelongBusiness: '';  
95 - invoicingStatus: '';  
96 - invoiceIdentificationNumber: '';  
97 - invoicingTime: '';  
98 - bank: '';  
99 - bankAccountNumber: '';  
100 - deleteSubOrderLists: [];  
101 - filePaths: [];  
102 - notes: '';  
103 - invoiceFirst: boolean;  
104 - list: [  
105 - {  
106 - productCode: '';  
107 - productName: '';  
108 - quantity: '';  
109 - productPrice: '';  
110 - parameters: '';  
111 - subOrderPayment: '';  
112 - unit: '';  
113 - serialNumber: '';  
114 - notes: '';  
115 - },  
116 - ];  
117 - }>(); 91 + const [form] = Form.useForm();
118 const [accountOptions, setAccountOptions] = useState<any>([]); 92 const [accountOptions, setAccountOptions] = useState<any>([]);
119 93
120 let copyData = cloneDeep(data); 94 let copyData = cloneDeep(data);
@@ -134,7 +108,8 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -134,7 +108,8 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
134 */ 108 */
135 const loadSalesCodeOptions = async () => { 109 const loadSalesCodeOptions = async () => {
136 let options = await getSalesCodeOptions(); 110 let options = await getSalesCodeOptions();
137 - console.log('options ', JSON.stringify(options)); 111 + setSalesCodeOptions(options);
  112 +
138 if (optType('copy') || optType('edit')) { 113 if (optType('copy') || optType('edit')) {
139 let includeFlag = false; 114 let includeFlag = false;
140 //销售代码校验,如果是旧的销售代码,则提示并清空 115 //销售代码校验,如果是旧的销售代码,则提示并清空
@@ -623,6 +598,21 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -623,6 +598,21 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
623 form.setFieldValue('totalPayment', totalPayment); 598 form.setFieldValue('totalPayment', totalPayment);
624 } 599 }
625 600
  601 + /*function computeTotalPayment() {
  602 + let list = form.getFieldValue('list');
  603 + let totalPaymentInMicro = 0; // 以"1 万分"为单位计算
  604 +
  605 + list?.forEach((subOrder: any) => {
  606 + let subOrderPayment = subOrder?.subOrderPayment;
  607 + if (subOrderPayment !== '' && subOrderPayment !== undefined) {
  608 + totalPaymentInMicro += Math.round(subOrderPayment * 10000); // 转换成整数(1 万分)
  609 + }
  610 + });
  611 +
  612 + let totalPayment = totalPaymentInMicro / 10000; // 计算完后转换回元
  613 + form.setFieldValue('totalPayment', totalPayment.toFixed(2)); // 保留 4 位小数
  614 + }*/
  615 +
626 /** 616 /**
627 * 检查用户额度 617 * 检查用户额度
628 * @param option 618 * @param option
@@ -751,18 +741,6 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -751,18 +741,6 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
751 } 741 }
752 } 742 }
753 743
754 - const validateContactNumber = (_: any, value: any) => {  
755 - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;  
756 - const phoneRegex = /^\d{1,11}(-\d{1,11})?$/;  
757 -  
758 - if (emailRegex.test(value) || phoneRegex.test(value)) {  
759 - return Promise.resolve();  
760 - }  
761 - return Promise.reject(  
762 - new Error('联系方式必须是邮箱或手机号格式(不能包含空格等特殊符号)'),  
763 - );  
764 - };  
765 -  
766 /** 744 /**
767 * 刪除草稿数据 745 * 刪除草稿数据
768 */ 746 */
@@ -782,22 +760,14 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -782,22 +760,14 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
782 760
783 return ( 761 return (
784 <> 762 <>
785 - <DrawerForm<{ 763 + <ModalForm<{
786 isLocalData: any; 764 isLocalData: any;
787 deleteSubOrderLists: any; 765 deleteSubOrderLists: any;
788 name: string; 766 name: string;
789 company: string; 767 company: string;
790 }> 768 }>
791 open 769 open
792 - width="35%"  
793 - title={drawerTitle}  
794 - resize={{  
795 - onResize() {  
796 - console.log('resize!');  
797 - },  
798 - maxWidth: window.innerWidth * 0.8,  
799 - minWidth: 400,  
800 - }} 770 + width={1000}
801 onFinishFailed={() => { 771 onFinishFailed={() => {
802 message.error('表单项存在错误,请检查'); 772 message.error('表单项存在错误,请检查');
803 setSubmitBtnLoading(false); 773 setSubmitBtnLoading(false);
@@ -841,21 +811,27 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -841,21 +811,27 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
841 }} 811 }}
842 form={form} 812 form={form}
843 autoFocusFirstInput 813 autoFocusFirstInput
844 - drawerProps={{ 814 + modalProps={{
845 destroyOnClose: true, 815 destroyOnClose: true,
846 - maskClosable: false,  
847 - extra: [  
848 - <Button  
849 - key="useLocalData"  
850 - hidden={!hasLocalData}  
851 - type="link"  
852 - onClick={() => {  
853 - useLocalFormData();  
854 - }} 816 + maskClosable: true,
  817 + title: (
  818 + <div
  819 + style={{ display: 'flex', alignItems: 'center', width: '100%' }}
855 > 820 >
856 - 使用草稿  
857 - </Button>,  
858 - ], 821 + <span>{drawerTitle}</span>
  822 + {hasLocalData && (
  823 + <Button
  824 + key="useLocalData"
  825 + type="link"
  826 + onClick={() => {
  827 + useLocalFormData();
  828 + }}
  829 + >
  830 + 使用草稿
  831 + </Button>
  832 + )}
  833 + </div>
  834 + ),
859 }} 835 }}
860 submitTimeout={2000} 836 submitTimeout={2000}
861 onFinish={async (values) => { 837 onFinish={async (values) => {
@@ -869,11 +845,46 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -869,11 +845,46 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
869 }); 845 });
870 return item; 846 return item;
871 }); 847 });
872 - 848 + list = list.map((item, index) => {
  849 + // //记录部门修改时间
  850 + if (
  851 + optType('edit') &&
  852 + copyData?.subOrderInformationLists[index]
  853 + ?.productBelongBusiness &&
  854 + item.productBelongBusiness !==
  855 + copyData?.subOrderInformationLists[index]?.productBelongBusiness
  856 + ) {
  857 + const date = new Date();
  858 + const year = date.getFullYear();
  859 + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
  860 + const day = String(date.getDate()).padStart(2, '0');
  861 + const hours = String(date.getHours()).padStart(2, '0');
  862 + const minutes = String(date.getMinutes()).padStart(2, '0');
  863 + const seconds = String(date.getSeconds()).padStart(2, '0');
  864 +
  865 + const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  866 + values.productBelongBusinessUpdateTime = formattedDate;
  867 + } else if (optType('add') || optType('copy')) {
  868 + const date = new Date();
  869 + const year = date.getFullYear();
  870 + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
  871 + const day = String(date.getDate()).padStart(2, '0');
  872 + const hours = String(date.getHours()).padStart(2, '0');
  873 + const minutes = String(date.getMinutes()).padStart(2, '0');
  874 + const seconds = String(date.getSeconds()).padStart(2, '0');
  875 + const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  876 + values.productBelongBusinessUpdateTime = formattedDate;
  877 + } else {
  878 + values.productBelongBusinessUpdateTime =
  879 + data.productBelongBusinessUpdateTime;
  880 + }
  881 + return item;
  882 + });
873 values.list = list; 883 values.list = list;
874 values.institution = values.institution?.trim(); 884 values.institution = values.institution?.trim();
875 values.institutionContactName = values.institutionContactName?.trim(); 885 values.institutionContactName = values.institutionContactName?.trim();
876 values.customerName = values.customerNameString.trim(); 886 values.customerName = values.customerNameString.trim();
  887 +
877 // values.customerShippingAddress = 888 // values.customerShippingAddress =
878 // province + city + district + values.customerShippingAddress; 889 // province + city + district + values.customerShippingAddress;
879 890
@@ -907,7 +918,6 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -907,7 +918,6 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
907 }); 918 });
908 let diff = originIds.filter((item) => !curIds.includes(item)); 919 let diff = originIds.filter((item) => !curIds.includes(item));
909 values.deleteSubOrderLists = diff; 920 values.deleteSubOrderLists = diff;
910 -  
911 if (optType('edit')) { 921 if (optType('edit')) {
912 values.province = province; 922 values.province = province;
913 values.city = city; 923 values.city = city;
@@ -1129,7 +1139,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1129,7 +1139,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1129 console.log(form.getFieldValue('id')); 1139 console.log(form.getFieldValue('id'));
1130 if (form.getFieldValue('id') !== undefined) { 1140 if (form.getFieldValue('id') !== undefined) {
1131 const resp = await postDistrictSelOrderProvince({ 1141 const resp = await postDistrictSelOrderProvince({
1132 - data: form.getFieldValue('id'), 1142 + data: {
  1143 + oId: form.getFieldValue('id'),
  1144 + orderType: orderOptType,
  1145 + },
1133 }); 1146 });
1134 if (resp && resp.data) { 1147 if (resp && resp.data) {
1135 if (resp.data.province) { 1148 if (resp.data.province) {
@@ -1193,10 +1206,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1193,10 +1206,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1193 loadAccountOptions(v.target.value); 1206 loadAccountOptions(v.target.value);
1194 }, 1207 },
1195 }} 1208 }}
1196 - rules={[  
1197 - { required: true, message: '联系方式必填' },  
1198 - { validator: validateContactNumber },  
1199 - ]} 1209 + rules={[{ required: true, message: '联系方式必填' }]}
1200 /> 1210 />
1201 <ProFormText 1211 <ProFormText
1202 width="lg" 1212 width="lg"
@@ -1206,45 +1216,163 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1206,45 +1216,163 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1206 placeholder="请输入单位" 1216 placeholder="请输入单位"
1207 rules={[{ required: true, message: '单位必填' }]} 1217 rules={[{ required: true, message: '单位必填' }]}
1208 /> 1218 />
1209 - <ProFormText 1219 + {/*<ProFormText
1210 width="lg" 1220 width="lg"
1211 key="institutionContactName" 1221 key="institutionContactName"
1212 name="institutionContactName" 1222 name="institutionContactName"
1213 label="课题组" 1223 label="课题组"
1214 placeholder="请输入课题组" 1224 placeholder="请输入课题组"
1215 rules={[{ required: true, message: '课题组必填' }]} 1225 rules={[{ required: true, message: '课题组必填' }]}
1216 - />  
1217 - {/*<ProFormSelect  
1218 - key={'institutionContactName'} 1226 + />*/}
  1227 + <Group>
  1228 + <ProFormSelect
  1229 + request={async () => {
  1230 + const res = await postServiceConstCompanyType();
  1231 + return Object.entries(res?.data).map(([value, label]) => ({
  1232 + label,
  1233 + value,
  1234 + }));
  1235 + }}
1219 width="md" 1236 width="md"
1220 - showSearch  
1221 - name="institutionContactName"  
1222 - rules={[{ required: true, message: '请输入课题组名称!' }]}  
1223 - request={async (value) => {  
1224 - const keywords = value.keyWords;  
1225 - const res = await postResearchGroupsNameSet({  
1226 - data: {  
1227 - groupName: keywords,  
1228 - },  
1229 - });  
1230 - let options = res?.data?.map((c: any) => {  
1231 - return {  
1232 - label: c,  
1233 - value: c,  
1234 - key: c,  
1235 - };  
1236 - });  
1237 - return options; 1237 + onChange={() => {
  1238 + form.setFieldValue('platformType', '');
1238 }} 1239 }}
1239 - fieldProps={{  
1240 - filterOption() {  
1241 - return true;  
1242 - }, 1240 + rules={[{ required: true, message: '单位类型必填' }]}
  1241 + name="companyType"
  1242 + label="单位类型"
  1243 + />
  1244 + <ProFormDependency name={['companyType']}>
  1245 + {({ companyType }) => {
  1246 + const renderInstitutionContactName = () => (
  1247 + <>
  1248 + <ProFormSelect
  1249 + key="institutionContactName"
  1250 + width="md"
  1251 + showSearch
  1252 + name="institutionContactName"
  1253 + rules={[{ required: true, message: '请输入课题组名称!' }]}
  1254 + request={async (value) => {
  1255 + const keywords = value?.keyWords || '';
  1256 + const res = await postResearchGroupsNameSet({
  1257 + data: { status: 'ADD_AUDIT_PASS', groupName: keywords },
  1258 + });
  1259 + return Object.entries(res?.data || {}).map(
  1260 + ([researchGroupsId, researchGroupsName]) => ({
  1261 + label: researchGroupsName,
  1262 + value: researchGroupsName, // 使用 researchGroupsId 作为 value
  1263 + key: researchGroupsId,
  1264 + id: researchGroupsId,
  1265 + }),
  1266 + );
  1267 + }}
  1268 + fieldProps={{
  1269 + filterOption: () => true,
  1270 + onChange: (_, option) => {
  1271 + form.setFieldsValue({
  1272 + researchGroupId: option?.id || '',
  1273 + });
  1274 + },
  1275 + }}
  1276 + debounceTime={1000}
  1277 + label="课题组名称"
  1278 + placeholder="请输入名称"
  1279 + />
  1280 + <ProFormText
  1281 + hidden={true}
  1282 + key="researchGroupId"
  1283 + name="researchGroupId"
  1284 + ></ProFormText>
  1285 + </>
  1286 + );
  1287 + const renderPlatformType = (fieldKey) => (
  1288 + <ProFormSelect
  1289 + key={fieldKey}
  1290 + width="md"
  1291 + showSearch
  1292 + name="platformType"
  1293 + rules={[{ required: true, message: '请选择平台类型!' }]}
  1294 + request={async () => {
  1295 + const res = await postServiceConstPlatformType({
  1296 + query: { companyType },
  1297 + });
  1298 + return Object.entries(res?.data).map(([value, label]) => ({
  1299 + label,
  1300 + value,
  1301 + }));
  1302 + }}
  1303 + fieldProps={{
  1304 + filterOption: (input, option) =>
  1305 + option?.label.toLowerCase().includes(input.toLowerCase()), // 自定义搜索过滤逻辑
  1306 + }}
  1307 + debounceTime={1000}
  1308 + label="平台类型"
  1309 + placeholder="请输入平台类型"
  1310 + />
  1311 + );
  1312 +
  1313 + if (companyType === 'school') {
  1314 + return renderInstitutionContactName();
  1315 + } else if (
  1316 + ['firm', 'ECommercePlatform', 'otherPlatform'].includes(
  1317 + companyType,
  1318 + )
  1319 + ) {
  1320 + return (
  1321 + <Group>
  1322 + {['ECommercePlatform', 'otherPlatform'].includes(
  1323 + companyType,
  1324 + ) && renderPlatformType(`platformType_${companyType}`)}
  1325 + <ProFormText
  1326 + width="md"
  1327 + name="institutionContactName"
  1328 + label="课题组名称"
  1329 + placeholder="请输入名称"
  1330 + />
  1331 + </Group>
  1332 + );
  1333 + } else if (companyType === 'officialWebsite') {
  1334 + return (
  1335 + <Group>
  1336 + <ProFormSelect
  1337 + request={async () => {
  1338 + const res = await postServiceConstOrderSource();
  1339 + return Object.entries(res?.data).map(
  1340 + ([value, label]) => ({
  1341 + label,
  1342 + value,
  1343 + }),
  1344 + );
  1345 + }}
  1346 + width="md"
  1347 + name="orderSource"
  1348 + label="订单来源"
  1349 + />
  1350 + <ProFormDependency name={['orderSource']}>
  1351 + {({ orderSource }) => {
  1352 + if (orderSource === 'school') {
  1353 + return renderInstitutionContactName();
  1354 + } else if (orderSource === 'company') {
  1355 + return (
  1356 + <ProFormText
  1357 + width="md"
  1358 + name="institutionContactName"
  1359 + label="课题组名称"
  1360 + placeholder="请输入名称"
  1361 + />
  1362 + );
  1363 + }
  1364 + return null;
  1365 + }}
  1366 + </ProFormDependency>
  1367 + </Group>
  1368 + );
  1369 + } else {
  1370 + return renderInstitutionContactName();
  1371 + }
1243 }} 1372 }}
1244 - debounceTime={1000}  
1245 - label="课题组名称"  
1246 - placeholder="请输入名称"  
1247 - />*/} 1373 + </ProFormDependency>
  1374 + </Group>
  1375 +
1248 <div 1376 <div
1249 style={{ 1377 style={{
1250 display: 'flex', 1378 display: 'flex',
@@ -1315,7 +1443,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1315,7 +1443,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1315 console.log(form.getFieldValue('id')); 1443 console.log(form.getFieldValue('id'));
1316 if (form.getFieldValue('id')) { 1444 if (form.getFieldValue('id')) {
1317 const resp = await postDistrictSelOrderProvince({ 1445 const resp = await postDistrictSelOrderProvince({
1318 - data: form.getFieldValue('id'), 1446 + data: {
  1447 + oId: form.getFieldValue('id'),
  1448 + orderType: orderOptType,
  1449 + },
1319 }); 1450 });
1320 if ( 1451 if (
1321 resp.data.province !== null && 1452 resp.data.province !== null &&
@@ -1375,7 +1506,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1375,7 +1506,10 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1375 let districtOptions = []; 1506 let districtOptions = [];
1376 if (form.getFieldValue('id')) { 1507 if (form.getFieldValue('id')) {
1377 const resp = await postDistrictSelOrderProvince({ 1508 const resp = await postDistrictSelOrderProvince({
1378 - data: form.getFieldValue('id'), 1509 + data: {
  1510 + oId: form.getFieldValue('id'),
  1511 + orderType: orderOptType,
  1512 + },
1379 }); 1513 });
1380 if (resp.data.city !== null && resp.data.city !== undefined) { 1514 if (resp.data.city !== null && resp.data.city !== undefined) {
1381 let res = await postDistrictSelectByNameAndLevel({ 1515 let res = await postDistrictSelectByNameAndLevel({
@@ -1477,7 +1611,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1477,7 +1611,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1477 onChange={(val: any) => { 1611 onChange={(val: any) => {
1478 setPaymentMethod(val); 1612 setPaymentMethod(val);
1479 }} 1613 }}
1480 - options={enumToSelect(PAYMENT_METHOD_OPTIONS)} 1614 + options={enumToSelect(PAYMENT_METHOD_OPTIONS_4_ADD)}
1481 rules={[{ required: true, message: '支付方式必填' }]} 1615 rules={[{ required: true, message: '支付方式必填' }]}
1482 disabled={optType('after-sales-check')} 1616 disabled={optType('after-sales-check')}
1483 /> 1617 />
@@ -1561,7 +1695,34 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1561,7 +1695,34 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1561 form.setFieldValue('invoiceFirst', false); 1695 form.setFieldValue('invoiceFirst', false);
1562 } 1696 }
1563 }} 1697 }}
1564 - rules={[{ required: true, message: '是否需要开票必填' }]} 1698 + rules={[
  1699 + { required: true, message: '是否需要开票必填' },
  1700 + {
  1701 + validator: (_, value) => {
  1702 + // 自定义校验逻辑
  1703 + if (
  1704 + form.getFieldValue('paymentMethod') ===
  1705 + 'WITHHOLDING_ADVANCE_DEPOSIT' &&
  1706 + value !== 'UN_INVOICE'
  1707 + ) {
  1708 + return Promise.reject('扣预存订单不能开票');
  1709 + }
  1710 + return Promise.resolve();
  1711 + },
  1712 + },
  1713 + {
  1714 + validator: (_, value) => {
  1715 + // 自定义校验逻辑
  1716 + if (
  1717 + form.getFieldValue('totalPayment') === 0 &&
  1718 + value !== 'UN_INVOICE'
  1719 + ) {
  1720 + return Promise.reject('金额为0订单不能开票');
  1721 + }
  1722 + return Promise.resolve();
  1723 + },
  1724 + },
  1725 + ]}
1565 /> 1726 />
1566 <ProFormSelect 1727 <ProFormSelect
1567 placeholder="是否开票后发货" 1728 placeholder="是否开票后发货"
@@ -1750,6 +1911,9 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1750,6 +1911,9 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1750 value: listMeta?.record?.materialId, 1911 value: listMeta?.record?.materialId,
1751 }} 1912 }}
1752 fieldProps={{ 1913 fieldProps={{
  1914 + popupMatchSelectWidth: false,
  1915 + listHeight: 400,
  1916 + dropdownStyle: { width: '55%' },
1753 filterOption() { 1917 filterOption() {
1754 return true; 1918 return true;
1755 }, 1919 },
@@ -1946,6 +2110,76 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1946,6 +2110,76 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1946 rules={[{ required: true, message: '所属事业部必填' }]} 2110 rules={[{ required: true, message: '所属事业部必填' }]}
1947 disabled={optType('after-sales-check')} 2111 disabled={optType('after-sales-check')}
1948 />, 2112 />,
  2113 +
  2114 + <Group key="selfDevelop">
  2115 + <ProFormRadio.Group
  2116 + key="selfDevelop"
  2117 + name="selfDevelop"
  2118 + label="是否自研产品"
  2119 + initialValue={false}
  2120 + options={[
  2121 + {
  2122 + label: '是',
  2123 + value: true,
  2124 + },
  2125 + {
  2126 + label: '否',
  2127 + value: false,
  2128 + },
  2129 + ]}
  2130 + rules={[{ required: true, message: '是否自研产品必填' }]}
  2131 + />
  2132 + <ProFormDependency name={['selfDevelop']}>
  2133 + {({ selfDevelop }) => {
  2134 + if (selfDevelop) {
  2135 + return (
  2136 + <ProFormDatePicker
  2137 + name="deliveryDatetime"
  2138 + label="产品交期(填写前请先与工程师沟通)"
  2139 + rules={[
  2140 + { required: true, message: '产品交期必填' },
  2141 + ]}
  2142 + />
  2143 + );
  2144 + }
  2145 + }}
  2146 + </ProFormDependency>
  2147 + </Group>,
  2148 + <ProFormRadio.Group
  2149 + key="proxy"
  2150 + name="proxy"
  2151 + label="是否代买代购"
  2152 + //hidden={true}
  2153 + initialValue={true}
  2154 + options={[
  2155 + {
  2156 + label: '是',
  2157 + value: true,
  2158 + },
  2159 + {
  2160 + label: '否',
  2161 + value: false,
  2162 + },
  2163 + ]}
  2164 + />,
  2165 + <ProFormRadio.Group
  2166 + key="discount"
  2167 + name="discount"
  2168 + label="是否竞标/打折"
  2169 + //hidden={true}
  2170 + initialValue={true}
  2171 + options={[
  2172 + {
  2173 + label: '是',
  2174 + value: true,
  2175 + },
  2176 + {
  2177 + label: '否',
  2178 + value: false,
  2179 + },
  2180 + ]}
  2181 + />,
  2182 +
1949 <ProFormSelect 2183 <ProFormSelect
1950 key={'shippingWarehouse' + listMeta.index} 2184 key={'shippingWarehouse' + listMeta.index}
1951 placeholder="请选择发货仓库" 2185 placeholder="请选择发货仓库"
@@ -1997,7 +2231,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; { @@ -1997,7 +2231,7 @@ export default ({ onClose, data, subOrders, orderOptType }) =&gt; {
1997 }} 2231 }}
1998 actionRef={actionRef} 2232 actionRef={actionRef}
1999 ></ProFormList> 2233 ></ProFormList>
2000 - </DrawerForm> 2234 + </ModalForm>
2001 {kingdeeCstomerModalVisible && ( 2235 {kingdeeCstomerModalVisible && (
2002 <KingdeeCustomerModal 2236 <KingdeeCustomerModal
2003 setVisible={setKingdeeCstomerModalVisible} 2237 setVisible={setKingdeeCstomerModalVisible}
src/pages/ResearchGroup/components/AuditModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/AuditModal.tsx
src/pages/ResearchGroup/components/ImportModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/ImportModal.tsx
src/pages/ResearchGroup/components/PointsExchangeModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/PointsExchangeModal.tsx
src/pages/ResearchGroup/components/PointsExchangeRecordsModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/PointsExchangeRecordsModal.tsx
src/pages/ResearchGroup/components/ResearchGroupAddModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/ResearchGroupAddModal.tsx
src/pages/ResearchGroup/components/ResearchGroupMemberRequestAddModal.tsx renamed to src/pages/ResearchGroup/ResearchGroup/components/ResearchGroupMemberRequestAddModal.tsx
1 import { RESPONSE_CODE } from '@/constants/enum'; 1 import { RESPONSE_CODE } from '@/constants/enum';
2 import { 2 import {
3 - postCanrdApiUserAddressList, postCanrdApiUserList, 3 + postCanrdApiUserAddressList,
  4 + postCanrdApiUserList,
4 postResearchGroupMemberRequestsAdd, 5 postResearchGroupMemberRequestsAdd,
5 postResearchGroupMemberRequestsDetail, 6 postResearchGroupMemberRequestsDetail,
6 postResearchGroupMemberRequestsEdit, 7 postResearchGroupMemberRequestsEdit,
@@ -25,7 +26,7 @@ import { useEffect, useState } from &#39;react&#39;; @@ -25,7 +26,7 @@ import { useEffect, useState } from &#39;react&#39;;
25 import '../index.less'; 26 import '../index.less';
26 27
27 // import { cloneDeep } from 'lodash'; 28 // import { cloneDeep } from 'lodash';
28 -export default ({ setVisible, requestId, onClose,type }) => { 29 +export default ({ setVisible, requestId, onClose, type }) => {
29 const [form] = Form.useForm(); 30 const [form] = Form.useForm();
30 const [memberOptions, setMemberOptions] = useState<any[]>([]); 31 const [memberOptions, setMemberOptions] = useState<any[]>([]);
31 const [requestInfo, setRequestInfo] = useState<any>(null); 32 const [requestInfo, setRequestInfo] = useState<any>(null);
@@ -201,7 +202,7 @@ export default ({ setVisible, requestId, onClose,type }) =&gt; { @@ -201,7 +202,7 @@ export default ({ setVisible, requestId, onClose,type }) =&gt; {
201 } 202 }
202 values.members = memberObjs; 203 values.members = memberObjs;
203 } 204 }
204 -//预存账号对象封装 205 + //预存账号对象封装
205 if (values.accounts) { 206 if (values.accounts) {
206 let accountObjs: any[] = []; 207 let accountObjs: any[] = [];
207 for (let accountOption of accountOptions) { 208 for (let accountOption of accountOptions) {
@@ -349,214 +350,215 @@ export default ({ setVisible, requestId, onClose,type }) =&gt; { @@ -349,214 +350,215 @@ export default ({ setVisible, requestId, onClose,type }) =&gt; {
349 }} 350 }}
350 /> 351 />
351 </ProForm.Group> 352 </ProForm.Group>
352 - {type==='ADD_ACCOUNT'&& 353 + {type === 'ADD_ACCOUNT' && (
  354 + <ProFormSelect
  355 + name="accounts"
  356 + key="accounts"
  357 + width="lg"
  358 + showSearch
  359 + label="绑定预存账号(可多选)"
  360 + placeholder="请选择预存账号"
  361 + onChange={(_, option) => {
  362 + autoAccountSelectOptions(option);
  363 + }}
  364 + //rules={[{ required: true, message: '请至少选择绑定一个预存账号' }]}
  365 + fieldProps={{
  366 + mode: 'multiple',
  367 + filterOption() {
  368 + return true;
  369 + },
  370 + optionItemRender(item: any) {
  371 + let name =
  372 + item.label +
  373 + ' | ' +
  374 + item.institution +
  375 + ' | ' +
  376 + item.nowMoney +
  377 + '¥' +
  378 + ' | ' +
  379 + item.phone;
  380 + return (
  381 + <div title={name}>
  382 + <span style={{ color: '#333333' }}>{name}</span>
  383 + </div>
  384 + );
  385 + },
  386 + }}
  387 + rules={[{ required: true, message: '请至少添加一个账号' }]}
  388 + debounceTime={1000}
  389 + request={async (value, {}) => {
  390 + const keywords = value.keyWords;
  391 + let body = {
  392 + keywords: keywords,
  393 + pageSize: 20,
  394 + researchGroupId: undefined,
  395 + };
  396 +
  397 + if (requestCount === 1) {
  398 + body.researchGroupId = form.getFieldValue('groupId');
  399 + }
  400 +
  401 + const res = await postCanrdApiUserList({
  402 + data: body,
  403 + });
  404 + let options = res?.data?.data?.map((c: any) => {
  405 + return {
  406 + ...c,
  407 + label: c.realName,
  408 + value: c.uid,
  409 + key: c.uid,
  410 + };
  411 + });
  412 +
  413 + setRequestCount(requestCount + 1);
  414 + return options;
  415 + }}
  416 + />
  417 + )}
  418 + {type !== 'ADD_ACCOUNT' && (
  419 + <>
353 <ProFormSelect 420 <ProFormSelect
354 - name="accounts"  
355 - key="accounts"  
356 - width="lg"  
357 - showSearch  
358 - label="绑定预存账号(可多选)"  
359 - placeholder="请选择预存账号"  
360 - onChange={(_, option) => {  
361 - autoAccountSelectOptions(option);  
362 - }}  
363 - //rules={[{ required: true, message: '请至少选择绑定一个预存账号' }]}  
364 - fieldProps={{  
365 - mode: 'multiple',  
366 - filterOption() {  
367 - return true;  
368 - },  
369 - optionItemRender(item: any) {  
370 - let name =  
371 - item.label +  
372 - ' | ' +  
373 - item.institution +  
374 - ' | ' +  
375 - item.nowMoney +  
376 - '¥' +  
377 - ' | ' +  
378 - item.phone;  
379 - return (  
380 - <div title={name}>  
381 - <span style={{ color: '#333333' }}>{name}</span>  
382 - </div>  
383 - );  
384 - },  
385 - }}  
386 - rules={[{ required: true, message: '请至少添加一个账号' }]}  
387 - debounceTime={1000}  
388 - request={async (value, {}) => {  
389 - const keywords = value.keyWords;  
390 - let body = {  
391 - keywords: keywords,  
392 - pageSize: 20,  
393 - researchGroupId: undefined,  
394 - };  
395 -  
396 - if (requestCount === 1) {  
397 - body.researchGroupId = form.getFieldValue('groupId');  
398 - }  
399 -  
400 - const res = await postCanrdApiUserList({  
401 - data: body,  
402 - });  
403 - let options = res?.data?.data?.map((c: any) => {  
404 - return {  
405 - ...c,  
406 - label: c.realName,  
407 - value: c.uid,  
408 - key: c.uid,  
409 - };  
410 - });  
411 -  
412 - setRequestCount(requestCount + 1);  
413 - return options;  
414 - }} 421 + name="members"
  422 + key="members"
  423 + width="lg"
  424 + showSearch
  425 + label="课题组成员"
  426 + placeholder="请添加课题组成员"
  427 + rules={[{ required: true, message: '请至少添加一个成员' }]}
  428 + fieldProps={{
  429 + mode: 'multiple',
  430 + filterOption() {
  431 + return true;
  432 + },
  433 + optionItemRender(item: any) {
  434 + let name = item.realName + ' | ' + item.phone;
  435 + return (
  436 + <div title={name}>
  437 + <span style={{ color: '#333333' }}>{name}</span>
  438 + </div>
  439 + );
  440 + },
  441 + }}
  442 + options={memberOptions}
415 /> 443 />
416 - }  
417 - {  
418 - type!=='ADD_ACCOUNT'&&  
419 - <>  
420 - <ProFormSelect  
421 - name="members"  
422 - key="members"  
423 - width="lg"  
424 - showSearch  
425 - label="课题组成员"  
426 - placeholder="请添加课题组成员"  
427 - rules={[{ required: true, message: '请至少添加一个成员' }]}  
428 - fieldProps={{  
429 - mode: 'multiple',  
430 - filterOption() {  
431 - return true;  
432 - },  
433 - optionItemRender(item: any) {  
434 - let name = item.realName + ' | ' + item.phone;  
435 - return (  
436 - <div title={name}>  
437 - <span style={{ color: '#333333' }}>{name}</span> 444 +
  445 + <ProCard
  446 + title="选择或自定义课题组成员信息"
  447 + bordered
  448 + tooltip="从【客户信息】选择框中可以直接搜索客户,选中后自动添加到【课题组成员】中。也可以自定义输入【客户名称】和【手机号】,点击添加按钮手动添加到【课题组成员】中。"
  449 + >
  450 + <ProForm.Group>
  451 + <ProFormSelect
  452 + key="customerName"
  453 + label="客户信息(选择)"
  454 + width="lg"
  455 + showSearch
  456 + name="customerName"
  457 + placeholder="请选择客户信息"
  458 + onChange={(_, option) => {
  459 + autoFillCustomerInfo(option);
  460 + }}
  461 + fieldProps={{
  462 + filterOption() {
  463 + return true;
  464 + },
  465 + optionItemRender(item: any) {
  466 + if (item.type === 'add') {
  467 + return (
  468 + <div title={item.name + '(新增客户)'}>
  469 + <span style={{ color: '#333333' }}>
  470 + {item.name}
  471 + </span>
  472 + {' | '}
  473 + <span style={{ color: 'orange' }}>自定义</span>
  474 + </div>
  475 + );
  476 + }
  477 +
  478 + let title = '';
  479 + let spanText = '';
  480 + let realName = item.realName;
  481 + let phone = item.phone;
  482 +
  483 + title =
  484 + getDefaultString(realName) +
  485 + '|' +
  486 + getDefaultString(phone);
  487 +
  488 + spanText =
  489 + getDefaultString(realName) +
  490 + '|' +
  491 + getDefaultString(phone);
  492 + return (
  493 + <div title={title}>
  494 + <span style={{ color: '#333333' }}>{spanText}</span>
438 </div> 495 </div>
439 - );  
440 - }, 496 + );
  497 + },
  498 + }}
  499 + debounceTime={1000}
  500 + request={async (value, {}) => {
  501 + const keywords = value.keyWords;
  502 + if (keywords === '') {
  503 + return [];
  504 + }
  505 + const res = await postCanrdApiUserAddressList({
  506 + data: { keywords: keywords },
  507 + });
  508 + let options = res?.data?.map((c: any) => {
  509 + return {
  510 + ...c,
  511 + label: c.name,
  512 + value: c.id,
  513 + key: c.id,
  514 + };
  515 + });
  516 +
  517 + //对options去重,realName和phone唯一
  518 + options = deduplicateOptions(options);
  519 +
  520 + //第一个商品默认为要新增客户
  521 + if (keywords.trim() !== '') {
  522 + options.unshift({
  523 + name: keywords,
  524 + type: 'add',
  525 + label: keywords,
  526 + value: 3.1415926,
  527 + key: keywords,
  528 + });
  529 + }
  530 +
  531 + return options;
  532 + }}
  533 + />
  534 + </ProForm.Group>
  535 +
  536 + <ProForm.Group>
  537 + <ProFormText
  538 + name="realName"
  539 + label="客户名称(自定义)"
  540 + placeholder="请输入客户名称"
  541 + rules={[{ required: false, message: '请输入客户名称' }]}
  542 + />
  543 + <ProFormText
  544 + name="phone"
  545 + label="手机号(自定义)"
  546 + width="md"
  547 + placeholder="请输入手机号"
  548 + rules={[{ required: false, message: '请输入手机号' }]}
  549 + />
  550 + </ProForm.Group>
  551 + <Button
  552 + type="primary"
  553 + onClick={() => {
  554 + addCustomMember();
441 }} 555 }}
442 - options={memberOptions}  
443 - />  
444 -  
445 - <ProCard  
446 - title="选择或自定义课题组成员信息"  
447 - bordered  
448 - tooltip="从【客户信息】选择框中可以直接搜索客户,选中后自动添加到【课题组成员】中。也可以自定义输入【客户名称】和【手机号】,点击添加按钮手动添加到【课题组成员】中。"  
449 > 556 >
450 - <ProForm.Group>  
451 - <ProFormSelect  
452 - key="customerName"  
453 - label="客户信息(选择)"  
454 - width="lg"  
455 - showSearch  
456 - name="customerName"  
457 - placeholder="请选择客户信息"  
458 - onChange={(_, option) => {  
459 - autoFillCustomerInfo(option);  
460 - }}  
461 - fieldProps={{  
462 - filterOption() {  
463 - return true;  
464 - },  
465 - optionItemRender(item: any) {  
466 - if (item.type === 'add') {  
467 - return (  
468 - <div title={item.name + '(新增客户)'}>  
469 - <span style={{ color: '#333333' }}>{item.name}</span>  
470 - {' | '}  
471 - <span style={{ color: 'orange' }}>自定义</span>  
472 - </div>  
473 - );  
474 - }  
475 -  
476 - let title = '';  
477 - let spanText = '';  
478 - let realName = item.realName;  
479 - let phone = item.phone;  
480 -  
481 - title =  
482 - getDefaultString(realName) +  
483 - '|' +  
484 - getDefaultString(phone);  
485 -  
486 - spanText =  
487 - getDefaultString(realName) +  
488 - '|' +  
489 - getDefaultString(phone);  
490 - return (  
491 - <div title={title}>  
492 - <span style={{ color: '#333333' }}>{spanText}</span>  
493 - </div>  
494 - );  
495 - },  
496 - }}  
497 - debounceTime={1000}  
498 - request={async (value, {}) => {  
499 - const keywords = value.keyWords;  
500 - if (keywords === '') {  
501 - return [];  
502 - }  
503 - const res = await postCanrdApiUserAddressList({  
504 - data: { keywords: keywords },  
505 - });  
506 - let options = res?.data?.map((c: any) => {  
507 - return {  
508 - ...c,  
509 - label: c.name,  
510 - value: c.id,  
511 - key: c.id,  
512 - };  
513 - });  
514 -  
515 - //对options去重,realName和phone唯一  
516 - options = deduplicateOptions(options);  
517 -  
518 - //第一个商品默认为要新增客户  
519 - if (keywords.trim() !== '') {  
520 - options.unshift({  
521 - name: keywords,  
522 - type: 'add',  
523 - label: keywords,  
524 - value: 3.1415926,  
525 - key: keywords,  
526 - });  
527 - }  
528 -  
529 - return options;  
530 - }}  
531 - />  
532 - </ProForm.Group>  
533 -  
534 - <ProForm.Group>  
535 - <ProFormText  
536 - name="realName"  
537 - label="客户名称(自定义)"  
538 - placeholder="请输入客户名称"  
539 - rules={[{ required: false, message: '请输入客户名称' }]}  
540 - />  
541 - <ProFormText  
542 - name="phone"  
543 - label="手机号(自定义)"  
544 - width="md"  
545 - placeholder="请输入手机号"  
546 - rules={[{ required: false, message: '请输入手机号' }]}  
547 - />  
548 - </ProForm.Group>  
549 - <Button  
550 - type="primary"  
551 - onClick={() => {  
552 - addCustomMember();  
553 - }}  
554 - >  
555 - 添加  
556 - </Button>  
557 - </ProCard>  
558 - </>  
559 - } 557 + 添加
  558 + </Button>
  559 + </ProCard>
  560 + </>
  561 + )}
560 562
561 <ProFormTextArea 563 <ProFormTextArea
562 name="requestNotes" 564 name="requestNotes"
src/pages/ResearchGroup/constant.tsx renamed to src/pages/ResearchGroup/ResearchGroup/constant.tsx
src/pages/ResearchGroup/index.css renamed to src/pages/ResearchGroup/ResearchGroup/index.css
src/pages/ResearchGroup/index.less renamed to src/pages/ResearchGroup/ResearchGroup/index.less
src/pages/ResearchGroup/index.tsx renamed to src/pages/ResearchGroup/ResearchGroup/index.tsx
@@ -38,7 +38,7 @@ import { @@ -38,7 +38,7 @@ import {
38 RESEARCH_GROUP_MEMBER_REQUEST_COLUMNS, 38 RESEARCH_GROUP_MEMBER_REQUEST_COLUMNS,
39 } from './constant'; 39 } from './constant';
40 import './index.less'; 40 import './index.less';
41 -const PrepaidPage = () => { 41 +const ResearchGroupListPage = () => {
42 const researchGroupActionRef = useRef<ActionType>(); 42 const researchGroupActionRef = useRef<ActionType>();
43 const memberApplyActionRef = useRef<ActionType>(); 43 const memberApplyActionRef = useRef<ActionType>();
44 const [researchGroupAddModalVisible, setResearchGroupAddModalVisible] = 44 const [researchGroupAddModalVisible, setResearchGroupAddModalVisible] =
@@ -855,4 +855,4 @@ const PrepaidPage = () =&gt; { @@ -855,4 +855,4 @@ const PrepaidPage = () =&gt; {
855 ); 855 );
856 }; 856 };
857 857
858 -export default PrepaidPage; 858 +export default ResearchGroupListPage;
src/pages/ResearchGroup/ResearchGroupAccess/components/AddModal.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postResearchGroupsAccessAddBlackList,
  4 + postResearchGroupsAccessAddWhiteList,
  5 + postResearchGroupsList,
  6 + postResearchGroupsNameSet,
  7 +} from '@/services';
  8 +import { Form, Input, Modal, Select, message } from 'antd';
  9 +import { forwardRef, useImperativeHandle, useState } from 'react';
  10 +
  11 +import '../index.css';
  12 +
  13 +export type AddModalProps = {
  14 + setVisible: (visible: boolean) => void;
  15 +};
  16 +
  17 +export type AddModalRef = {
  18 + show: (accessType: 'WHITELIST' | 'BLACKLIST', onSuccess: () => void) => void;
  19 +};
  20 +
  21 +const AddModal = forwardRef<AddModalRef, AddModalProps>((props, ref) => {
  22 + const { setVisible } = props;
  23 + const [form] = Form.useForm();
  24 + const [visible, setModalVisible] = useState(false);
  25 + const [loading, setLoading] = useState(false);
  26 + const [accessTypeState, setAccessTypeState] = useState<
  27 + 'WHITELIST' | 'BLACKLIST'
  28 + >('WHITELIST');
  29 + const [onSuccessCallback, setOnSuccessCallback] = useState<() => void>(
  30 + () => {},
  31 + );
  32 + const [groupOptions, setGroupOptions] = useState<
  33 + { label: string; value: string; id: string }[]
  34 + >([]);
  35 + const [companyOptions, setCompanyOptions] = useState<
  36 + { label: string; value: string; id: string }[]
  37 + >([]);
  38 + const [searchLoading, setSearchLoading] = useState(false);
  39 + const [companyLoading, setCompanyLoading] = useState(false);
  40 +
  41 + useImperativeHandle(ref, () => ({
  42 + show: (accessType, onSuccess) => {
  43 + form.resetFields();
  44 + setAccessTypeState(accessType);
  45 + setOnSuccessCallback(() => onSuccess);
  46 + setModalVisible(true);
  47 + // 重置选项
  48 + setCompanyOptions([]);
  49 + setGroupOptions([]);
  50 + },
  51 + }));
  52 +
  53 + const handleCancel = () => {
  54 + setModalVisible(false);
  55 + setVisible(false);
  56 + };
  57 +
  58 + const handleOk = async () => {
  59 + try {
  60 + const values = await form.validateFields();
  61 + setLoading(true);
  62 +
  63 + const requestData = {
  64 + ...values,
  65 + accessType: accessTypeState,
  66 + };
  67 +
  68 + let res;
  69 + if (accessTypeState === 'WHITELIST') {
  70 + res = await postResearchGroupsAccessAddWhiteList({ data: requestData });
  71 + } else {
  72 + res = await postResearchGroupsAccessAddBlackList({ data: requestData });
  73 + }
  74 +
  75 + if (res && res.result === RESPONSE_CODE.SUCCESS) {
  76 + message.success('添加成功');
  77 + setModalVisible(false);
  78 + setVisible(false);
  79 + onSuccessCallback();
  80 + } else {
  81 + message.error(res?.message || '添加失败');
  82 + }
  83 + } catch (error) {
  84 + console.error('验证表单失败:', error);
  85 + } finally {
  86 + setLoading(false);
  87 + }
  88 + };
  89 +
  90 + // 搜索课题组
  91 + const handleSearch = async (value: string) => {
  92 + if (!value) return;
  93 +
  94 + try {
  95 + setSearchLoading(true);
  96 + // 完全模仿原始代码,使用data包裹参数
  97 + const res = await postResearchGroupsNameSet({
  98 + data: { status: 'ADD_AUDIT_PASS', groupName: value },
  99 + });
  100 +
  101 + if (res?.data) {
  102 + const options = Object.entries(res.data).map(
  103 + ([researchGroupsId, researchGroupsName]) => ({
  104 + label: researchGroupsName as string,
  105 + value: researchGroupsName as string,
  106 + key: researchGroupsId,
  107 + id: researchGroupsId,
  108 + }),
  109 + );
  110 + setGroupOptions(options);
  111 + }
  112 + } catch (error) {
  113 + console.error('获取课题组列表失败', error);
  114 + } finally {
  115 + setSearchLoading(false);
  116 + }
  117 + };
  118 +
  119 + // 根据课题组名称查询单位名称列表
  120 + const fetchCompanyNamesByGroupName = async (groupName: string) => {
  121 + if (!groupName) return;
  122 +
  123 + try {
  124 + setCompanyLoading(true);
  125 + // 使用postResearchGroupsList接口查询单位名称
  126 + const res = await postResearchGroupsList({
  127 + data: {
  128 + current: 1,
  129 + pageSize: 100,
  130 + groupName: groupName,
  131 + },
  132 + });
  133 +
  134 + if (res?.data?.data) {
  135 + // 提取所有相同groupName的不同companyName
  136 + const companySet = new Set<string>();
  137 + const companyIdMap = new Map<string, string>();
  138 +
  139 + // 假设接口返回数据包含列表项,每项有companyName和id
  140 + res.data.data.forEach((item: any) => {
  141 + if (item.groupName === groupName && item.companyName) {
  142 + companySet.add(item.companyName);
  143 + companyIdMap.set(item.companyName, item.id); // 保存id用于提交
  144 + }
  145 + });
  146 +
  147 + // 转换为选项格式
  148 + const companies = Array.from(companySet).map((name) => ({
  149 + label: name,
  150 + value: name,
  151 + id: companyIdMap.get(name) || '',
  152 + }));
  153 +
  154 + setCompanyOptions(companies);
  155 +
  156 + // 如果只有一个选项,自动选中
  157 + if (companies.length === 1) {
  158 + form.setFieldsValue({
  159 + companyName: companies[0].value,
  160 + groupId: companies[0].id,
  161 + });
  162 + }
  163 + }
  164 + } catch (error) {
  165 + console.error('获取单位名称列表失败', error);
  166 + } finally {
  167 + setCompanyLoading(false);
  168 + }
  169 + };
  170 +
  171 + const title =
  172 + accessTypeState === 'WHITELIST' ? '添加课题组白名单' : '添加课题组风险名单';
  173 +
  174 + return (
  175 + <Modal
  176 + title={title}
  177 + open={visible}
  178 + onOk={handleOk}
  179 + onCancel={handleCancel}
  180 + confirmLoading={loading}
  181 + maskClosable={true}
  182 + destroyOnClose
  183 + >
  184 + <Form form={form} layout="vertical" name="add_form" initialValues={{}}>
  185 + <Form.Item name="groupId" style={{ display: 'none' }}>
  186 + <Input type="hidden" />
  187 + </Form.Item>
  188 +
  189 + <Form.Item
  190 + name="groupName"
  191 + label="课题组名称"
  192 + rules={[{ required: true, message: '请输入课题组名称!' }]}
  193 + >
  194 + <Select
  195 + showSearch
  196 + placeholder="请输入名称"
  197 + filterOption={false}
  198 + onSearch={handleSearch}
  199 + loading={searchLoading}
  200 + options={groupOptions}
  201 + onChange={(value, option: any) => {
  202 + // 清空公司选项
  203 + form.setFieldsValue({
  204 + companyName: undefined,
  205 + });
  206 +
  207 + // 保存研究组ID
  208 + if (option) {
  209 + form.setFieldsValue({
  210 + groupId: option.id || '',
  211 + });
  212 +
  213 + // 触发查询关联的单位名称
  214 + fetchCompanyNamesByGroupName(value);
  215 + }
  216 + }}
  217 + />
  218 + </Form.Item>
  219 +
  220 + <Form.Item
  221 + name="companyName"
  222 + label="单位名称"
  223 + rules={[{ required: true, message: '请选择单位名称' }]}
  224 + >
  225 + <Select
  226 + placeholder="请选择单位名称"
  227 + loading={companyLoading}
  228 + options={companyOptions}
  229 + disabled={companyOptions.length === 0}
  230 + onChange={(_, option: any) => {
  231 + if (option && option.id) {
  232 + form.setFieldsValue({
  233 + groupId: option.id,
  234 + });
  235 + }
  236 + }}
  237 + />
  238 + </Form.Item>
  239 +
  240 + <Form.Item name="remark" label="添加原因">
  241 + <Input.TextArea rows={3} placeholder="请输入添加原因" />
  242 + </Form.Item>
  243 + </Form>
  244 + </Modal>
  245 + );
  246 +});
  247 +
  248 +export default AddModal;
src/pages/ResearchGroup/ResearchGroupAccess/constant.tsx 0 → 100644
  1 +import { formatDateTime } from '@/utils';
  2 +
  3 +export const RESEARCH_GROUP_ACCESS_WHITELIST_COLUMNS = [
  4 + {
  5 + title: '序号',
  6 + dataIndex: 'index',
  7 + valueType: 'index',
  8 + width: 70,
  9 + },
  10 + {
  11 + title: 'ID',
  12 + dataIndex: 'id',
  13 + key: 'id',
  14 + hideInSearch: true,
  15 + hideInTable: true,
  16 + },
  17 + {
  18 + title: '课题组名称',
  19 + dataIndex: 'groupName',
  20 + key: 'groupName',
  21 + fieldProps: {
  22 + placeholder: '请输入课题组名称',
  23 + },
  24 + },
  25 + {
  26 + title: '单位名称',
  27 + dataIndex: 'companyName',
  28 + key: 'companyName',
  29 + hideInSearch: true,
  30 + },
  31 + {
  32 + title: '添加原因',
  33 + dataIndex: 'remark',
  34 + key: 'remark',
  35 + hideInSearch: true,
  36 + },
  37 + {
  38 + title: '添加时间',
  39 + dataIndex: 'createTime',
  40 + key: 'createTime',
  41 + valueType: 'text',
  42 + hideInSearch: true,
  43 + render: (_: any, record: any) =>
  44 + record.createTime ? formatDateTime(record.createTime) : '-',
  45 + },
  46 + {
  47 + title: '添加人',
  48 + dataIndex: 'createByName',
  49 + key: 'createByName',
  50 + hideInSearch: true,
  51 + },
  52 +];
  53 +
  54 +export const RESEARCH_GROUP_ACCESS_BLACKLIST_COLUMNS = [
  55 + {
  56 + title: '序号',
  57 + dataIndex: 'index',
  58 + valueType: 'index',
  59 + width: 70,
  60 + },
  61 + {
  62 + title: 'ID',
  63 + dataIndex: 'id',
  64 + key: 'id',
  65 + hideInSearch: true,
  66 + hideInTable: true,
  67 + },
  68 + {
  69 + title: '课题组名称',
  70 + dataIndex: 'groupName',
  71 + key: 'groupName',
  72 + fieldProps: {
  73 + placeholder: '请输入课题组名称',
  74 + },
  75 + },
  76 + {
  77 + title: '单位名称',
  78 + dataIndex: 'companyName',
  79 + key: 'companyName',
  80 + hideInSearch: true,
  81 + },
  82 + {
  83 + title: '添加原因',
  84 + dataIndex: 'remark',
  85 + key: 'remark',
  86 + hideInSearch: true,
  87 + },
  88 + {
  89 + title: '添加时间',
  90 + dataIndex: 'createTime',
  91 + key: 'createTime',
  92 + valueType: 'text',
  93 + hideInSearch: true,
  94 + render: (_: any, record: any) =>
  95 + record.createTime ? formatDateTime(record.createTime) : '-',
  96 + },
  97 + {
  98 + title: '添加人',
  99 + dataIndex: 'createByName',
  100 + key: 'createByName',
  101 + hideInSearch: true,
  102 + },
  103 +];
src/pages/ResearchGroup/ResearchGroupAccess/index.css 0 → 100644
  1 +.research-group-index td,
  2 +.research-group-access-container td {
  3 + font-family: 'San Francisco', 'Helvetica Neue', Helvetica, Arial,
  4 + 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Heiti SC',
  5 + 'WenQuanYi Micro Hei', sans-serif;
  6 + font-size: 13px;
  7 +}
src/pages/ResearchGroup/ResearchGroupAccess/index.less 0 → 100644
  1 +.research-group-access-container {
  2 + width: 100%;
  3 + padding: 16px;
  4 + background-color: #fff;
  5 +
  6 + .ant-pro-table {
  7 + .ant-pro-card-body {
  8 + padding: 16px;
  9 + }
  10 + }
  11 +}
src/pages/ResearchGroup/ResearchGroupAccess/index.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import { getUserInfo } from '@/utils';
  3 +import { PlusOutlined } from '@ant-design/icons';
  4 +import { ActionType, ProTable } from '@ant-design/pro-components';
  5 +import { Button, Popconfirm, Tabs, message } from 'antd';
  6 +import { useMemo, useRef, useState } from 'react';
  7 +
  8 +import {
  9 + postResearchGroupsAccessBlackList,
  10 + postResearchGroupsAccessDeleteBlackList,
  11 + postResearchGroupsAccessDeleteWhiteList,
  12 + postResearchGroupsAccessWhiteList,
  13 +} from '@/services';
  14 +
  15 +import './index.css';
  16 +import './index.less';
  17 +
  18 +import {
  19 + RESEARCH_GROUP_ACCESS_BLACKLIST_COLUMNS,
  20 + RESEARCH_GROUP_ACCESS_WHITELIST_COLUMNS,
  21 +} from './constant';
  22 +
  23 +import AddModal, { AddModalRef } from './components/AddModal';
  24 +
  25 +const ResearchGroupAccessPage = () => {
  26 + const whitelistActionRef = useRef<ActionType>();
  27 + const blacklistActionRef = useRef<ActionType>();
  28 + const [activeKey, setActiveKey] = useState<string>('1');
  29 + const addModalRef = useRef<AddModalRef>(null);
  30 +
  31 + const reloadWhitelistTable = () => {
  32 + whitelistActionRef.current?.reload();
  33 + };
  34 +
  35 + const reloadBlacklistTable = () => {
  36 + blacklistActionRef.current?.reload();
  37 + };
  38 +
  39 + const handleDeleteWhitelist = async (id: number) => {
  40 + const res = await postResearchGroupsAccessDeleteWhiteList({ data: { id } });
  41 + if (res && res.result === RESPONSE_CODE.SUCCESS) {
  42 + message.success('删除成功');
  43 + reloadWhitelistTable();
  44 + } else {
  45 + message.error(res?.message || '删除失败');
  46 + }
  47 + };
  48 +
  49 + const handleDeleteBlacklist = async (id: number) => {
  50 + const res = await postResearchGroupsAccessDeleteBlackList({ data: { id } });
  51 + if (res && res.result === RESPONSE_CODE.SUCCESS) {
  52 + message.success('删除成功');
  53 + reloadBlacklistTable();
  54 + } else {
  55 + message.error(res?.message || '删除失败');
  56 + }
  57 + };
  58 +
  59 + const handleAddClick = () => {
  60 + const accessType = activeKey === '1' ? 'WHITELIST' : 'BLACKLIST';
  61 + const onSuccess =
  62 + activeKey === '1' ? reloadWhitelistTable : reloadBlacklistTable;
  63 + addModalRef.current?.show(accessType, onSuccess);
  64 + };
  65 +
  66 + const WhitelistTab = () => {
  67 + // Check if the current user has permission to add/delete
  68 + const hasPermission = useMemo(() => {
  69 + const userInfo = getUserInfo();
  70 + return userInfo?.username === 'canrd' || userInfo?.username === 'D-Tina';
  71 + }, []);
  72 + return (
  73 + <ProTable
  74 + actionRef={whitelistActionRef}
  75 + rowKey="id"
  76 + search={{
  77 + labelWidth: 120,
  78 + defaultCollapsed: false,
  79 + }}
  80 + pagination={{
  81 + pageSize: 10,
  82 + }}
  83 + toolBarRender={() =>
  84 + hasPermission
  85 + ? [
  86 + <Button key="add" type="primary" onClick={handleAddClick}>
  87 + <PlusOutlined /> 添加
  88 + </Button>,
  89 + ]
  90 + : []
  91 + }
  92 + request={async (params) => {
  93 + const { current, pageSize, ...rest } = params;
  94 + const res = await postResearchGroupsAccessWhiteList({
  95 + data: {
  96 + current: current || 1,
  97 + pageSize: pageSize || 10,
  98 + ...rest,
  99 + },
  100 + });
  101 + if (res && res.result === RESPONSE_CODE.SUCCESS) {
  102 + return {
  103 + data: res.data?.data || [],
  104 + success: true,
  105 + total: res.data?.total || 0,
  106 + };
  107 + }
  108 + return {
  109 + data: [],
  110 + success: false,
  111 + total: 0,
  112 + };
  113 + }}
  114 + columns={[
  115 + ...RESEARCH_GROUP_ACCESS_WHITELIST_COLUMNS,
  116 + {
  117 + title: '操作',
  118 + dataIndex: 'option',
  119 + valueType: 'option',
  120 + render: (_, record) =>
  121 + hasPermission
  122 + ? [
  123 + <Popconfirm
  124 + key="delete"
  125 + title="确定要删除吗?"
  126 + onConfirm={() => handleDeleteWhitelist(record.id)}
  127 + >
  128 + <a style={{ color: '#ff4d4f' }}>删除</a>
  129 + </Popconfirm>,
  130 + ]
  131 + : [],
  132 + },
  133 + ]}
  134 + />
  135 + );
  136 + };
  137 +
  138 + const BlacklistTab = () => {
  139 + // Check if the current user has permission to add/delete
  140 + const hasPermission = useMemo(() => {
  141 + const userInfo = getUserInfo();
  142 + return userInfo?.username === 'canrd' || userInfo?.username === 'D-Tina';
  143 + }, []);
  144 + return (
  145 + <ProTable
  146 + actionRef={blacklistActionRef}
  147 + rowKey="id"
  148 + search={{
  149 + labelWidth: 120,
  150 + defaultCollapsed: false,
  151 + }}
  152 + pagination={{
  153 + pageSize: 10,
  154 + }}
  155 + toolBarRender={() =>
  156 + hasPermission
  157 + ? [
  158 + <Button key="add" type="primary" onClick={handleAddClick}>
  159 + <PlusOutlined /> 添加
  160 + </Button>,
  161 + ]
  162 + : []
  163 + }
  164 + request={async (params) => {
  165 + const { current, pageSize, ...rest } = params;
  166 + const res = await postResearchGroupsAccessBlackList({
  167 + data: {
  168 + current: current || 1,
  169 + pageSize: pageSize || 10,
  170 + ...rest,
  171 + },
  172 + });
  173 + if (res && res.result === RESPONSE_CODE.SUCCESS) {
  174 + return {
  175 + data: res.data?.data || [],
  176 + success: true,
  177 + total: res.data?.total || 0,
  178 + };
  179 + }
  180 + return {
  181 + data: [],
  182 + success: false,
  183 + total: 0,
  184 + };
  185 + }}
  186 + columns={[
  187 + ...RESEARCH_GROUP_ACCESS_BLACKLIST_COLUMNS,
  188 + {
  189 + title: '操作',
  190 + dataIndex: 'option',
  191 + valueType: 'option',
  192 + render: (_, record) =>
  193 + hasPermission
  194 + ? [
  195 + <Popconfirm
  196 + key="delete"
  197 + title="确定要删除吗?"
  198 + onConfirm={() => handleDeleteBlacklist(record.id)}
  199 + >
  200 + <a style={{ color: '#ff4d4f' }}>删除</a>
  201 + </Popconfirm>,
  202 + ]
  203 + : [],
  204 + },
  205 + ]}
  206 + />
  207 + );
  208 + };
  209 +
  210 + // 空函数保留作为回调,但不做任何操作
  211 + // eslint-disable-next-line @typescript-eslint/no-unused-vars
  212 + const setAddModalVisible = (_: boolean) => {
  213 + // 我们不再需要在父组件中跟踪模态框状态
  214 + };
  215 +
  216 + return (
  217 + <div className="research-group-index">
  218 + <Tabs
  219 + defaultActiveKey="whitelist"
  220 + onChange={setActiveKey}
  221 + items={[
  222 + {
  223 + key: 'whitelist',
  224 + label: '课题组白名单',
  225 + children: <WhitelistTab />,
  226 + },
  227 + {
  228 + key: 'blacklist',
  229 + label: '课题组风险名单',
  230 + children: <BlacklistTab />,
  231 + },
  232 + ]}
  233 + />
  234 + <AddModal ref={addModalRef} setVisible={setAddModalVisible} />
  235 + </div>
  236 + );
  237 +};
  238 +
  239 +export default ResearchGroupAccessPage;
src/services/definition.ts
@@ -4099,6 +4099,79 @@ export interface ResearchGroupRequestsDto { @@ -4099,6 +4099,79 @@ export interface ResearchGroupRequestsDto {
4099 updateTime?: string; 4099 updateTime?: string;
4100 } 4100 }
4101 4101
  4102 +export interface ResearchGroupsAccessDTO {
  4103 + /**
  4104 + * @description
  4105 + * 访问类型: WHITELIST, BLACKLIST
  4106 + */
  4107 + accessType?: string;
  4108 + /**
  4109 + * @description
  4110 + * 单位名称
  4111 + */
  4112 + companyName?: string;
  4113 + /**
  4114 + * @description
  4115 + * 创建人ID
  4116 + */
  4117 + createBy?: string;
  4118 + /**
  4119 + * @description
  4120 + * 创建人姓名
  4121 + */
  4122 + createByName?: string;
  4123 + /**
  4124 + * @description
  4125 + * 创建时间
  4126 + * @format date-time
  4127 + */
  4128 + createTime?: string;
  4129 + /**
  4130 + * @description
  4131 + * 删除标志
  4132 + * @format int32
  4133 + */
  4134 + deleteFlag?: number;
  4135 + /**
  4136 + * @description
  4137 + * 课题组名称
  4138 + */
  4139 + groupName?: string;
  4140 + /**
  4141 + * @description
  4142 + * 主键id
  4143 + * @format int64
  4144 + */
  4145 + id?: number;
  4146 + /**
  4147 + * @description
  4148 + * 添加原因
  4149 + */
  4150 + remark?: string;
  4151 + /**
  4152 + * @description
  4153 + * 更新人ID
  4154 + */
  4155 + updateBy?: string;
  4156 + /**
  4157 + * @description
  4158 + * 更新人姓名
  4159 + */
  4160 + updateByName?: string;
  4161 + /**
  4162 + * @description
  4163 + * 更新时间
  4164 + * @format date-time
  4165 + */
  4166 + updateTime?: string;
  4167 + /**
  4168 + * @description
  4169 + * 版本号
  4170 + * @format int32
  4171 + */
  4172 + version?: number;
  4173 +}
  4174 +
4102 export interface ResearchGroupsDTO { 4175 export interface ResearchGroupsDTO {
4103 accounts?: Array<ResearchGroupAccounts>; 4176 accounts?: Array<ResearchGroupAccounts>;
4104 /** 4177 /**
@@ -5609,6 +5682,13 @@ export interface SalesRechargePrepaymentUpdateRequest { @@ -5609,6 +5682,13 @@ export interface SalesRechargePrepaymentUpdateRequest {
5609 salesCode?: string; 5682 salesCode?: string;
5610 } 5683 }
5611 5684
  5685 +export interface ServerResultTsgBoolean {
  5686 + data?: boolean;
  5687 + message?: string;
  5688 + /** @format int32 */
  5689 + result?: number;
  5690 +}
  5691 +
5612 /** 5692 /**
5613 * @description 5693 * @description
5614 * 开票添加对象 5694 * 开票添加对象
src/services/request.ts
@@ -134,6 +134,7 @@ import type { @@ -134,6 +134,7 @@ import type {
134 ResearchGroupMemberRequestDetailRequest, 134 ResearchGroupMemberRequestDetailRequest,
135 ResearchGroupMemberRequestsRequest, 135 ResearchGroupMemberRequestsRequest,
136 ResearchGroupRequestsDto, 136 ResearchGroupRequestsDto,
  137 + ResearchGroupsAccessDTO,
137 ResearchGroupsDTO, 138 ResearchGroupsDTO,
138 ResetPwdVO, 139 ResetPwdVO,
139 SalOrderSaveDto, 140 SalOrderSaveDto,
@@ -144,6 +145,7 @@ import type { @@ -144,6 +145,7 @@ import type {
144 SalesRechargePrepaymentUpdateRequest, 145 SalesRechargePrepaymentUpdateRequest,
145 SaveReply, 146 SaveReply,
146 ServerResult, 147 ServerResult,
  148 + ServerResultTsgBoolean,
147 ShippingWarehouseChangeDto, 149 ShippingWarehouseChangeDto,
148 StoreOrderInvoiceRequest, 150 StoreOrderInvoiceRequest,
149 SysLogQueryVO, 151 SysLogQueryVO,
@@ -14332,6 +14334,432 @@ export const postResearchGroupMemberRequestsList = /* #__PURE__ */ (() =&gt; { @@ -14332,6 +14334,432 @@ export const postResearchGroupMemberRequestsList = /* #__PURE__ */ (() =&gt; {
14332 return request; 14334 return request;
14333 })(); 14335 })();
14334 14336
  14337 +/** @description request parameter type for postResearchGroupsAccessAddBlackList */
  14338 +export interface PostResearchGroupsAccessAddBlackListOption {
  14339 + /**
  14340 + * @description
  14341 + * request
  14342 + */
  14343 + body: {
  14344 + /**
  14345 + @description
  14346 + request */
  14347 + request: ResearchGroupsAccessDTO;
  14348 + };
  14349 +}
  14350 +
  14351 +/** @description response type for postResearchGroupsAccessAddBlackList */
  14352 +export interface PostResearchGroupsAccessAddBlackListResponse {
  14353 + /**
  14354 + * @description
  14355 + * OK
  14356 + */
  14357 + 200: ServerResultTsgBoolean;
  14358 + /**
  14359 + * @description
  14360 + * Created
  14361 + */
  14362 + 201: any;
  14363 + /**
  14364 + * @description
  14365 + * Unauthorized
  14366 + */
  14367 + 401: any;
  14368 + /**
  14369 + * @description
  14370 + * Forbidden
  14371 + */
  14372 + 403: any;
  14373 + /**
  14374 + * @description
  14375 + * Not Found
  14376 + */
  14377 + 404: any;
  14378 +}
  14379 +
  14380 +export type PostResearchGroupsAccessAddBlackListResponseSuccess =
  14381 + PostResearchGroupsAccessAddBlackListResponse[200];
  14382 +/**
  14383 + * @description
  14384 + * 添加黑名单
  14385 + * @tags research-groups-access-controller
  14386 + * @produces *
  14387 + * @consumes application/json
  14388 + */
  14389 +export const postResearchGroupsAccessAddBlackList = /* #__PURE__ */ (() => {
  14390 + const method = 'post';
  14391 + const url = '/research/groups/access/addBlackList';
  14392 + function request(
  14393 + option: PostResearchGroupsAccessAddBlackListOption,
  14394 + ): Promise<PostResearchGroupsAccessAddBlackListResponseSuccess> {
  14395 + return requester(request.url, {
  14396 + method: request.method,
  14397 + ...option,
  14398 + }) as unknown as Promise<PostResearchGroupsAccessAddBlackListResponseSuccess>;
  14399 + }
  14400 +
  14401 + /** http method */
  14402 + request.method = method;
  14403 + /** request url */
  14404 + request.url = url;
  14405 + return request;
  14406 +})();
  14407 +
  14408 +/** @description request parameter type for postResearchGroupsAccessAddWhiteList */
  14409 +export interface PostResearchGroupsAccessAddWhiteListOption {
  14410 + /**
  14411 + * @description
  14412 + * request
  14413 + */
  14414 + body: {
  14415 + /**
  14416 + @description
  14417 + request */
  14418 + request: ResearchGroupsAccessDTO;
  14419 + };
  14420 +}
  14421 +
  14422 +/** @description response type for postResearchGroupsAccessAddWhiteList */
  14423 +export interface PostResearchGroupsAccessAddWhiteListResponse {
  14424 + /**
  14425 + * @description
  14426 + * OK
  14427 + */
  14428 + 200: ServerResultTsgBoolean;
  14429 + /**
  14430 + * @description
  14431 + * Created
  14432 + */
  14433 + 201: any;
  14434 + /**
  14435 + * @description
  14436 + * Unauthorized
  14437 + */
  14438 + 401: any;
  14439 + /**
  14440 + * @description
  14441 + * Forbidden
  14442 + */
  14443 + 403: any;
  14444 + /**
  14445 + * @description
  14446 + * Not Found
  14447 + */
  14448 + 404: any;
  14449 +}
  14450 +
  14451 +export type PostResearchGroupsAccessAddWhiteListResponseSuccess =
  14452 + PostResearchGroupsAccessAddWhiteListResponse[200];
  14453 +/**
  14454 + * @description
  14455 + * 添加白名单
  14456 + * @tags research-groups-access-controller
  14457 + * @produces *
  14458 + * @consumes application/json
  14459 + */
  14460 +export const postResearchGroupsAccessAddWhiteList = /* #__PURE__ */ (() => {
  14461 + const method = 'post';
  14462 + const url = '/research/groups/access/addWhiteList';
  14463 + function request(
  14464 + option: PostResearchGroupsAccessAddWhiteListOption,
  14465 + ): Promise<PostResearchGroupsAccessAddWhiteListResponseSuccess> {
  14466 + return requester(request.url, {
  14467 + method: request.method,
  14468 + ...option,
  14469 + }) as unknown as Promise<PostResearchGroupsAccessAddWhiteListResponseSuccess>;
  14470 + }
  14471 +
  14472 + /** http method */
  14473 + request.method = method;
  14474 + /** request url */
  14475 + request.url = url;
  14476 + return request;
  14477 +})();
  14478 +
  14479 +/** @description request parameter type for postResearchGroupsAccessBlackList */
  14480 +export interface PostResearchGroupsAccessBlackListOption {
  14481 + /**
  14482 + * @description
  14483 + * request
  14484 + */
  14485 + body: {
  14486 + /**
  14487 + @description
  14488 + request */
  14489 + request: ResearchGroupListRequest;
  14490 + };
  14491 +}
  14492 +
  14493 +/** @description response type for postResearchGroupsAccessBlackList */
  14494 +export interface PostResearchGroupsAccessBlackListResponse {
  14495 + /**
  14496 + * @description
  14497 + * OK
  14498 + */
  14499 + 200: ServerResult;
  14500 + /**
  14501 + * @description
  14502 + * Created
  14503 + */
  14504 + 201: any;
  14505 + /**
  14506 + * @description
  14507 + * Unauthorized
  14508 + */
  14509 + 401: any;
  14510 + /**
  14511 + * @description
  14512 + * Forbidden
  14513 + */
  14514 + 403: any;
  14515 + /**
  14516 + * @description
  14517 + * Not Found
  14518 + */
  14519 + 404: any;
  14520 +}
  14521 +
  14522 +export type PostResearchGroupsAccessBlackListResponseSuccess =
  14523 + PostResearchGroupsAccessBlackListResponse[200];
  14524 +/**
  14525 + * @description
  14526 + * 查询黑名单列表
  14527 + * @tags research-groups-access-controller
  14528 + * @produces *
  14529 + * @consumes application/json
  14530 + */
  14531 +export const postResearchGroupsAccessBlackList = /* #__PURE__ */ (() => {
  14532 + const method = 'post';
  14533 + const url = '/research/groups/access/blackList';
  14534 + function request(
  14535 + option: PostResearchGroupsAccessBlackListOption,
  14536 + ): Promise<PostResearchGroupsAccessBlackListResponseSuccess> {
  14537 + return requester(request.url, {
  14538 + method: request.method,
  14539 + ...option,
  14540 + }) as unknown as Promise<PostResearchGroupsAccessBlackListResponseSuccess>;
  14541 + }
  14542 +
  14543 + /** http method */
  14544 + request.method = method;
  14545 + /** request url */
  14546 + request.url = url;
  14547 + return request;
  14548 +})();
  14549 +
  14550 +/** @description request parameter type for postResearchGroupsAccessDeleteBlackList */
  14551 +export interface PostResearchGroupsAccessDeleteBlackListOption {
  14552 + /**
  14553 + * @description
  14554 + * request
  14555 + */
  14556 + body: {
  14557 + /**
  14558 + @description
  14559 + request */
  14560 + request: ResearchGroupsAccessDTO;
  14561 + };
  14562 +}
  14563 +
  14564 +/** @description response type for postResearchGroupsAccessDeleteBlackList */
  14565 +export interface PostResearchGroupsAccessDeleteBlackListResponse {
  14566 + /**
  14567 + * @description
  14568 + * OK
  14569 + */
  14570 + 200: ServerResultTsgBoolean;
  14571 + /**
  14572 + * @description
  14573 + * Created
  14574 + */
  14575 + 201: any;
  14576 + /**
  14577 + * @description
  14578 + * Unauthorized
  14579 + */
  14580 + 401: any;
  14581 + /**
  14582 + * @description
  14583 + * Forbidden
  14584 + */
  14585 + 403: any;
  14586 + /**
  14587 + * @description
  14588 + * Not Found
  14589 + */
  14590 + 404: any;
  14591 +}
  14592 +
  14593 +export type PostResearchGroupsAccessDeleteBlackListResponseSuccess =
  14594 + PostResearchGroupsAccessDeleteBlackListResponse[200];
  14595 +/**
  14596 + * @description
  14597 + * 删除黑名单
  14598 + * @tags research-groups-access-controller
  14599 + * @produces *
  14600 + * @consumes application/json
  14601 + */
  14602 +export const postResearchGroupsAccessDeleteBlackList = /* #__PURE__ */ (() => {
  14603 + const method = 'post';
  14604 + const url = '/research/groups/access/deleteBlackList';
  14605 + function request(
  14606 + option: PostResearchGroupsAccessDeleteBlackListOption,
  14607 + ): Promise<PostResearchGroupsAccessDeleteBlackListResponseSuccess> {
  14608 + return requester(request.url, {
  14609 + method: request.method,
  14610 + ...option,
  14611 + }) as unknown as Promise<PostResearchGroupsAccessDeleteBlackListResponseSuccess>;
  14612 + }
  14613 +
  14614 + /** http method */
  14615 + request.method = method;
  14616 + /** request url */
  14617 + request.url = url;
  14618 + return request;
  14619 +})();
  14620 +
  14621 +/** @description request parameter type for postResearchGroupsAccessDeleteWhiteList */
  14622 +export interface PostResearchGroupsAccessDeleteWhiteListOption {
  14623 + /**
  14624 + * @description
  14625 + * request
  14626 + */
  14627 + body: {
  14628 + /**
  14629 + @description
  14630 + request */
  14631 + request: ResearchGroupsAccessDTO;
  14632 + };
  14633 +}
  14634 +
  14635 +/** @description response type for postResearchGroupsAccessDeleteWhiteList */
  14636 +export interface PostResearchGroupsAccessDeleteWhiteListResponse {
  14637 + /**
  14638 + * @description
  14639 + * OK
  14640 + */
  14641 + 200: ServerResultTsgBoolean;
  14642 + /**
  14643 + * @description
  14644 + * Created
  14645 + */
  14646 + 201: any;
  14647 + /**
  14648 + * @description
  14649 + * Unauthorized
  14650 + */
  14651 + 401: any;
  14652 + /**
  14653 + * @description
  14654 + * Forbidden
  14655 + */
  14656 + 403: any;
  14657 + /**
  14658 + * @description
  14659 + * Not Found
  14660 + */
  14661 + 404: any;
  14662 +}
  14663 +
  14664 +export type PostResearchGroupsAccessDeleteWhiteListResponseSuccess =
  14665 + PostResearchGroupsAccessDeleteWhiteListResponse[200];
  14666 +/**
  14667 + * @description
  14668 + * 删除白名单
  14669 + * @tags research-groups-access-controller
  14670 + * @produces *
  14671 + * @consumes application/json
  14672 + */
  14673 +export const postResearchGroupsAccessDeleteWhiteList = /* #__PURE__ */ (() => {
  14674 + const method = 'post';
  14675 + const url = '/research/groups/access/deleteWhiteList';
  14676 + function request(
  14677 + option: PostResearchGroupsAccessDeleteWhiteListOption,
  14678 + ): Promise<PostResearchGroupsAccessDeleteWhiteListResponseSuccess> {
  14679 + return requester(request.url, {
  14680 + method: request.method,
  14681 + ...option,
  14682 + }) as unknown as Promise<PostResearchGroupsAccessDeleteWhiteListResponseSuccess>;
  14683 + }
  14684 +
  14685 + /** http method */
  14686 + request.method = method;
  14687 + /** request url */
  14688 + request.url = url;
  14689 + return request;
  14690 +})();
  14691 +
  14692 +/** @description request parameter type for postResearchGroupsAccessWhiteList */
  14693 +export interface PostResearchGroupsAccessWhiteListOption {
  14694 + /**
  14695 + * @description
  14696 + * request
  14697 + */
  14698 + body: {
  14699 + /**
  14700 + @description
  14701 + request */
  14702 + request: ResearchGroupListRequest;
  14703 + };
  14704 +}
  14705 +
  14706 +/** @description response type for postResearchGroupsAccessWhiteList */
  14707 +export interface PostResearchGroupsAccessWhiteListResponse {
  14708 + /**
  14709 + * @description
  14710 + * OK
  14711 + */
  14712 + 200: ServerResult;
  14713 + /**
  14714 + * @description
  14715 + * Created
  14716 + */
  14717 + 201: any;
  14718 + /**
  14719 + * @description
  14720 + * Unauthorized
  14721 + */
  14722 + 401: any;
  14723 + /**
  14724 + * @description
  14725 + * Forbidden
  14726 + */
  14727 + 403: any;
  14728 + /**
  14729 + * @description
  14730 + * Not Found
  14731 + */
  14732 + 404: any;
  14733 +}
  14734 +
  14735 +export type PostResearchGroupsAccessWhiteListResponseSuccess =
  14736 + PostResearchGroupsAccessWhiteListResponse[200];
  14737 +/**
  14738 + * @description
  14739 + * 查询白名单列表
  14740 + * @tags research-groups-access-controller
  14741 + * @produces *
  14742 + * @consumes application/json
  14743 + */
  14744 +export const postResearchGroupsAccessWhiteList = /* #__PURE__ */ (() => {
  14745 + const method = 'post';
  14746 + const url = '/research/groups/access/whiteList';
  14747 + function request(
  14748 + option: PostResearchGroupsAccessWhiteListOption,
  14749 + ): Promise<PostResearchGroupsAccessWhiteListResponseSuccess> {
  14750 + return requester(request.url, {
  14751 + method: request.method,
  14752 + ...option,
  14753 + }) as unknown as Promise<PostResearchGroupsAccessWhiteListResponseSuccess>;
  14754 + }
  14755 +
  14756 + /** http method */
  14757 + request.method = method;
  14758 + /** request url */
  14759 + request.url = url;
  14760 + return request;
  14761 +})();
  14762 +
14335 /** @description request parameter type for postResearchGroupsAdd */ 14763 /** @description request parameter type for postResearchGroupsAdd */
14336 export interface PostResearchGroupsAddOption { 14764 export interface PostResearchGroupsAddOption {
14337 /** 14765 /**