| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | <template> | 
					
						
							|  |  |  |  |   <div class="p-6 bg-gray-50"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     <!-- 外层容器添加横向滚动支持 --> | 
					
						
							|  |  |  |  |     <div class="overflow-x-auto"> | 
					
						
							|  |  |  |  |       <div class="appointment mx-auto bg-white rounded-xl shadow-sm overflow-hidden transition-all duration-300 hover:shadow-md min-w-[1200px]"> | 
					
						
							|  |  |  |  |         <!-- 表单标题区域 --> | 
					
						
							|  |  |  |  |         <div class="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-6"> | 
					
						
							|  |  |  |  |           <h2 class="text-2xl font-bold flex items-center"><i class="el-icon-user-circle mr-3"></i>人员配置</h2> | 
					
						
							|  |  |  |  |           <p class="text-blue-100 mt-2 opacity-90">请配置项目相关负责人员信息</p> | 
					
						
							|  |  |  |  |           <el-button @click="disabledForm = false" class="px-8 py-2.5 transition-all duration-300 font-medium" v-if="disabledForm"> | 
					
						
							|  |  |  |  |             点击编辑 | 
					
						
							|  |  |  |  |           </el-button> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |         </div> | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |         <!-- 表单内容区域 --> | 
					
						
							|  |  |  |  |         <el-form ref="leaveFormRef" :model="form" :disabled="disabledForm" :rules="rules" label-width="120px" class="p-6 space-y-6"> | 
					
						
							|  |  |  |  |           <!-- 设计负责人 --> | 
					
						
							|  |  |  |  |           <div class="fonts"> | 
					
						
							|  |  |  |  |             <el-row> | 
					
						
							|  |  |  |  |               <el-col :span="8"> | 
					
						
							|  |  |  |  |                 <el-form-item label="设计负责人" prop="designLeader" class="mb-4"> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   <el-select | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                     v-model="form.designLeader" | 
					
						
							|  |  |  |  |                     placeholder="请选择设计负责人" | 
					
						
							|  |  |  |  |                     class="w-full transition-all duration-300 border-gray-300 focus:border-blue-400 focus:ring-1 focus:ring-blue-400" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   > | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                     <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   </el-select> | 
					
						
							|  |  |  |  |                 </el-form-item> | 
					
						
							|  |  |  |  |               </el-col> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |             </el-row> | 
					
						
							|  |  |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |           <!-- 专业人员配置:专业 + 设计 + 校审 + 审定 + 审核 横向排列 --> | 
					
						
							|  |  |  |  |           <div class="border border-gray-200 rounded-lg p-5 transition-all duration-300 hover:shadow-md bg-gray-50"> | 
					
						
							|  |  |  |  |             <div class="flex justify-between items-center mb-5"> | 
					
						
							|  |  |  |  |               <h3 class="text-lg font-semibold text-gray-700 flex items-center"><i class="el-icon-users mr-2 text-blue-500"></i>专业人员配置</h3> | 
					
						
							|  |  |  |  |               <div class="flex gap-3"> | 
					
						
							|  |  |  |  |                 <!-- 新增专业按钮 --> | 
					
						
							|  |  |  |  |                 <el-button type="primary" size="small" :disabled="disabledForm" @click="addMajor"> | 
					
						
							|  |  |  |  |                   <i class="el-icon-plus mr-1"></i>新增专业 | 
					
						
							|  |  |  |  |                 </el-button> | 
					
						
							|  |  |  |  |               </div> | 
					
						
							|  |  |  |  |             </div> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             <!-- 表头:总和24确保一行显示 --> | 
					
						
							|  |  |  |  |             <el-row :gutter="8" class="mb-3 font-medium text-gray-700 whitespace-nowrap"> | 
					
						
							|  |  |  |  |               <el-col :span="4">专业</el-col> | 
					
						
							|  |  |  |  |               <el-col :span="5">设计人员(可多选)</el-col> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |               <el-col :span="5">校审人员</el-col> | 
					
						
							|  |  |  |  |               <el-col :span="5">审定人员</el-col> | 
					
						
							|  |  |  |  |               <el-col :span="4">审核人员</el-col> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |               <el-col :span="3"></el-col> | 
					
						
							|  |  |  |  |             </el-row> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             <!-- 分割线 --> | 
					
						
							|  |  |  |  |             <el-divider class="my-4" /> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             <!-- 专业配置行:循环渲染 + 专业同步逻辑 --> | 
					
						
							|  |  |  |  |             <div | 
					
						
							|  |  |  |  |               v-for="(majorConfig, configIndex) in combinedConfigs" | 
					
						
							|  |  |  |  |               :key="`major-${configIndex}`" | 
					
						
							|  |  |  |  |               style="background: aliceblue; border-radius: 10px; padding: 12px 0" | 
					
						
							|  |  |  |  |               class="mb-5 animate-fadeIn" | 
					
						
							|  |  |  |  |             > | 
					
						
							|  |  |  |  |               <el-row :gutter="8" class="items-top"> | 
					
						
							|  |  |  |  |                 <!-- 1. 专业选择(核心:统一所有角色的专业来源) --> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                 <el-col :span="3" class="mb-4 sm:mb-0 pl-4" style="margin-top:8px;"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                   <el-form-item | 
					
						
							|  |  |  |  |                     :prop="`designers.${configIndex}.userMajor`" | 
					
						
							|  |  |  |  |                     :rules="[ | 
					
						
							|  |  |  |  |                       { required: true, message: '请选择专业', trigger: 'change' }, | 
					
						
							|  |  |  |  |                       { validator: validateMajor, trigger: 'change' } | 
					
						
							|  |  |  |  |                     ]" | 
					
						
							|  |  |  |  |                     class="mb-0" | 
					
						
							|  |  |  |  |                     label-width="60px" | 
					
						
							|  |  |  |  |                     label="专业" | 
					
						
							|  |  |  |  |                   > | 
					
						
							| 
									
										
										
										
											2025-08-29 20:57:49 +08:00
										 |  |  |  |                     <el-select filterable | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                       v-model="form.designers[configIndex].userMajor" | 
					
						
							|  |  |  |  |                       placeholder="请选择专业" | 
					
						
							|  |  |  |  |                       class="w-full transition-all duration-300 border-gray-300" | 
					
						
							|  |  |  |  |                       @change="(val) => handleMajorChange(val, configIndex)" | 
					
						
							|  |  |  |  |                       clearable | 
					
						
							|  |  |  |  |                     > | 
					
						
							|  |  |  |  |                       <template v-if="des_user_major && des_user_major.length > 0"> | 
					
						
							|  |  |  |  |                         <el-option v-for="item in des_user_major" :key="`major-item-${item.value}`" :label="item.label" :value="item.value" /> | 
					
						
							|  |  |  |  |                       </template> | 
					
						
							|  |  |  |  |                       <template v-else> | 
					
						
							|  |  |  |  |                         <el-option label="无专业数据" value="" disabled /> | 
					
						
							|  |  |  |  |                       </template> | 
					
						
							|  |  |  |  |                     </el-select> | 
					
						
							|  |  |  |  |                   </el-form-item> | 
					
						
							|  |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 <!-- 2. 设计人员 --> | 
					
						
							|  |  |  |  |                 <el-col :span="4" class="mb-4 sm:mb-0"> | 
					
						
							|  |  |  |  |                   <div class="pl-2 border-l-2 border-blue-200 py-2"> | 
					
						
							|  |  |  |  |                     <div class="space-y-3"> | 
					
						
							|  |  |  |  |                       <div | 
					
						
							|  |  |  |  |                         v-for="(person, personIndex) in majorConfig.designPersons" | 
					
						
							|  |  |  |  |                         :key="`design-${configIndex}-${personIndex}`" | 
					
						
							|  |  |  |  |                         class="flex items-center" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                       > | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                         <el-form-item | 
					
						
							|  |  |  |  |                           :prop="`designers.${configIndex}.persons.${personIndex}.userId`" | 
					
						
							|  |  |  |  |                           :rules="{ required: true, message: '请选择设计人员', trigger: 'change' }" | 
					
						
							|  |  |  |  |                           class="flex-1 mr-2 mb-0" | 
					
						
							|  |  |  |  |                           label="设计" | 
					
						
							|  |  |  |  |                           label-width="50px" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                         > | 
					
						
							| 
									
										
										
										
											2025-08-29 20:57:49 +08:00
										 |  |  |  |                           <el-select filterable | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                             v-model="person.userId" | 
					
						
							|  |  |  |  |                             placeholder="选择人员" | 
					
						
							|  |  |  |  |                             class="w-full transition-all duration-300 border-gray-300" | 
					
						
							|  |  |  |  |                             @change="() => checkDuplicate(person, 'designers', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-option v-for="item in userList" :key="`user-${item.userId}`" :label="item.nickName" :value="item.userId" /> | 
					
						
							|  |  |  |  |                           </el-select> | 
					
						
							|  |  |  |  |                         </el-form-item> | 
					
						
							|  |  |  |  |                         <div class="flex gap-1"> | 
					
						
							|  |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="danger" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="removePerson('designers', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 hover:bg-red-600" | 
					
						
							|  |  |  |  |                             :disabled="majorConfig.designPersons.length <= 1 || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Delete /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							|  |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="success" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="addPerson('designers', configIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 transform hover:scale-105" | 
					
						
							|  |  |  |  |                             :disabled="!form.designers[configIndex].userMajor || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Plus /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							|  |  |  |  |                         </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                       </div> | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                     <div | 
					
						
							|  |  |  |  |                       v-if="majorConfig.designPersons.length == 0" | 
					
						
							|  |  |  |  |                       class="text-gray-500 text-xs py-2 bg-gray-100 rounded border border-dashed border-gray-200" | 
					
						
							|  |  |  |  |                     > | 
					
						
							|  |  |  |  |                       点击"添加" | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   </div> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 <!-- 3. 校审人员 --> | 
					
						
							|  |  |  |  |                 <el-col :span="5" class="mb-4 sm:mb-0"> | 
					
						
							|  |  |  |  |                   <div class="pl-2 border-l-2 border-green-200 py-2"> | 
					
						
							|  |  |  |  |                     <div class="space-y-3"> | 
					
						
							|  |  |  |  |                       <div | 
					
						
							|  |  |  |  |                         v-for="(person, personIndex) in majorConfig.reviewPersons" | 
					
						
							|  |  |  |  |                         :key="`review-${configIndex}-${personIndex}`" | 
					
						
							|  |  |  |  |                         class="flex items-center" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                       > | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                         <el-form-item | 
					
						
							|  |  |  |  |                           :prop="`reviewers.${configIndex}.persons.${personIndex}.userId`" | 
					
						
							|  |  |  |  |                           :rules="{ required: true, message: '请选择校审人员', trigger: 'change' }" | 
					
						
							|  |  |  |  |                           class="flex-1 mr-2 mb-0" | 
					
						
							|  |  |  |  |                           label="校审" | 
					
						
							|  |  |  |  |                           label-width="50px" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                         > | 
					
						
							| 
									
										
										
										
											2025-08-29 20:57:49 +08:00
										 |  |  |  |                           <el-select filterable | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                             v-model="person.userId" | 
					
						
							|  |  |  |  |                             placeholder="选择人员" | 
					
						
							|  |  |  |  |                             class="w-full transition-all duration-300 border-gray-300" | 
					
						
							|  |  |  |  |                             @change="() => checkDuplicate(person, 'reviewers', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-option v-for="item in userList" :key="`user-${item.userId}`" :label="item.nickName" :value="item.userId" /> | 
					
						
							|  |  |  |  |                           </el-select> | 
					
						
							|  |  |  |  |                         </el-form-item> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         <!-- <div class="flex gap-1"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="danger" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="removePerson('reviewers', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 hover:bg-red-600" | 
					
						
							|  |  |  |  |                             :disabled="majorConfig.reviewPersons.length <= 1 || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Delete /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							|  |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="success" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="addPerson('reviewers', configIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 transform hover:scale-105" | 
					
						
							|  |  |  |  |                             :disabled="!form.designers[configIndex].userMajor || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Plus /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         </div> --> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                       </div> | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							|  |  |  |  |                     <div | 
					
						
							|  |  |  |  |                       v-if="majorConfig.reviewPersons.length == 0" | 
					
						
							|  |  |  |  |                       class="text-gray-500 text-xs py-2 bg-gray-100 rounded border border-dashed border-gray-200" | 
					
						
							|  |  |  |  |                     > | 
					
						
							|  |  |  |  |                       点击"添加" | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							|  |  |  |  |                   </div> | 
					
						
							|  |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 <!-- 4. 审定人员 --> | 
					
						
							|  |  |  |  |                 <el-col :span="5" class="mb-4 sm:mb-0"> | 
					
						
							|  |  |  |  |                   <div class="pl-2 border-l-2 border-orange-200 py-2"> | 
					
						
							|  |  |  |  |                     <div class="space-y-3"> | 
					
						
							|  |  |  |  |                       <div | 
					
						
							|  |  |  |  |                         v-for="(person, personIndex) in majorConfig.approvedPersons" | 
					
						
							|  |  |  |  |                         :key="`approved-${configIndex}-${personIndex}`" | 
					
						
							|  |  |  |  |                         class="flex items-center" | 
					
						
							|  |  |  |  |                       > | 
					
						
							|  |  |  |  |                         <el-form-item | 
					
						
							|  |  |  |  |                           :prop="`approved.${configIndex}.persons.${personIndex}.userId`" | 
					
						
							|  |  |  |  |                           :rules="{ required: true, message: '请选择审定人员', trigger: 'change' }" | 
					
						
							|  |  |  |  |                           class="flex-1 mr-2 mb-0" | 
					
						
							|  |  |  |  |                           label="审定" | 
					
						
							|  |  |  |  |                           label-width="50px" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                         > | 
					
						
							| 
									
										
										
										
											2025-08-29 20:57:49 +08:00
										 |  |  |  |                           <el-select filterable | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                             v-model="person.userId" | 
					
						
							|  |  |  |  |                             placeholder="选择人员" | 
					
						
							|  |  |  |  |                             class="w-full transition-all duration-300 border-gray-300" | 
					
						
							|  |  |  |  |                             @change="() => checkDuplicate(person, 'approved', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-option v-for="item in userList" :key="`user-${item.userId}`" :label="item.nickName" :value="item.userId" /> | 
					
						
							|  |  |  |  |                           </el-select> | 
					
						
							|  |  |  |  |                         </el-form-item> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         <!-- <div class="flex gap-1"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="danger" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="removePerson('approved', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 hover:bg-red-600" | 
					
						
							|  |  |  |  |                             :disabled="majorConfig.approvedPersons.length <= 1 || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Delete /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							|  |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="success" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="addPerson('approved', configIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 transform hover:scale-105" | 
					
						
							|  |  |  |  |                             :disabled="!form.designers[configIndex].userMajor || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Plus /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         </div> --> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                       </div> | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							|  |  |  |  |                     <div | 
					
						
							|  |  |  |  |                       v-if="majorConfig.approvedPersons.length == 0" | 
					
						
							|  |  |  |  |                       class="text-gray-500 text-xs py-2 bg-gray-100 rounded border border-dashed border-gray-200" | 
					
						
							|  |  |  |  |                     > | 
					
						
							|  |  |  |  |                       点击"添加" | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							|  |  |  |  |                   </div> | 
					
						
							|  |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 <!-- 5. 审核人员 --> | 
					
						
							|  |  |  |  |                 <el-col :span="5" class="mb-4 sm:mb-0"> | 
					
						
							|  |  |  |  |                   <div class="pl-2 border-l-2 border-purple-200 py-2"> | 
					
						
							|  |  |  |  |                     <div class="space-y-3"> | 
					
						
							|  |  |  |  |                       <div | 
					
						
							|  |  |  |  |                         v-for="(person, personIndex) in majorConfig.auditorPersons" | 
					
						
							|  |  |  |  |                         :key="`auditor-${configIndex}-${personIndex}`" | 
					
						
							|  |  |  |  |                         class="flex items-center" | 
					
						
							|  |  |  |  |                       > | 
					
						
							|  |  |  |  |                         <el-form-item | 
					
						
							|  |  |  |  |                           :prop="`auditor.${configIndex}.persons.${personIndex}.userId`" | 
					
						
							|  |  |  |  |                           :rules="{ required: true, message: '请选择审核人员', trigger: 'change' }" | 
					
						
							|  |  |  |  |                           class="flex-1 mr-2 mb-0" | 
					
						
							|  |  |  |  |                           label="审核" | 
					
						
							|  |  |  |  |                           label-width="50px" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                         > | 
					
						
							| 
									
										
										
										
											2025-08-29 20:57:49 +08:00
										 |  |  |  |                           <el-select filterable | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                             v-model="person.userId" | 
					
						
							|  |  |  |  |                             placeholder="选择人员" | 
					
						
							|  |  |  |  |                             class="w-full transition-all duration-300 border-gray-300" | 
					
						
							|  |  |  |  |                             @change="() => checkDuplicate(person, 'auditor', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-option v-for="item in userList" :key="`user-${item.userId}`" :label="item.nickName" :value="item.userId" /> | 
					
						
							|  |  |  |  |                           </el-select> | 
					
						
							|  |  |  |  |                         </el-form-item> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         <!-- <div class="flex gap-1"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="danger" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="removePerson('auditor', configIndex, personIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 hover:bg-red-600" | 
					
						
							|  |  |  |  |                             :disabled="majorConfig.auditorPersons.length <= 1 || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Delete /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							|  |  |  |  |                           <el-button | 
					
						
							|  |  |  |  |                             type="success" | 
					
						
							|  |  |  |  |                             size="small" | 
					
						
							|  |  |  |  |                             @click="addPerson('auditor', configIndex)" | 
					
						
							|  |  |  |  |                             class="transition-all duration-300 transform hover:scale-105" | 
					
						
							|  |  |  |  |                             :disabled="!form.designers[configIndex].userMajor || disabledForm" | 
					
						
							|  |  |  |  |                           > | 
					
						
							|  |  |  |  |                             <el-icon :size="14"><Plus /></el-icon> | 
					
						
							|  |  |  |  |                           </el-button> | 
					
						
							| 
									
										
										
										
											2025-08-29 16:50:24 +08:00
										 |  |  |  |                         </div> --> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                       </div> | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                     <div | 
					
						
							|  |  |  |  |                       v-if="majorConfig.auditorPersons.length == 0" | 
					
						
							|  |  |  |  |                       class="text-gray-500 text-xs py-2 bg-gray-100 rounded border border-dashed border-gray-200" | 
					
						
							|  |  |  |  |                     > | 
					
						
							|  |  |  |  |                       点击"添加" | 
					
						
							|  |  |  |  |                     </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   </div> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 <!-- 操作列 --> | 
					
						
							|  |  |  |  |                 <el-col :span="2" class="text-right pr-4"> | 
					
						
							|  |  |  |  |                   <el-button | 
					
						
							|  |  |  |  |                     type="text" | 
					
						
							|  |  |  |  |                     class="text-red-500 hover:text-red-700 transition-colors" | 
					
						
							|  |  |  |  |                     @click="removeMajor(configIndex)" | 
					
						
							|  |  |  |  |                     :disabled="combinedConfigs.length <= 1 || disabledForm" | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |                   > | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |                     <i class="el-icon-delete mr-1"></i>删除专业 | 
					
						
							|  |  |  |  |                   </el-button> | 
					
						
							|  |  |  |  |                 </el-col> | 
					
						
							|  |  |  |  |               </el-row> | 
					
						
							|  |  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |           </div> | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |           <!-- 提交按钮区域 --> | 
					
						
							|  |  |  |  |           <div v-if="!disabledForm" class="flex justify-center space-x-6 mt-8 pt-6 border-t border-gray-100"> | 
					
						
							|  |  |  |  |             <el-button | 
					
						
							|  |  |  |  |               type="primary" | 
					
						
							|  |  |  |  |               size="large" | 
					
						
							|  |  |  |  |               v-hasPermi="['design:user:batch']" | 
					
						
							|  |  |  |  |               @click="submitForm" | 
					
						
							|  |  |  |  |               class="px-8 py-2.5 transition-all duration-300 transform hover:scale-105 bg-blue-500 hover:bg-blue-600 text-white font-medium" | 
					
						
							|  |  |  |  |             > | 
					
						
							|  |  |  |  |               <i class="el-icon-check mr-2"></i>确认提交 | 
					
						
							|  |  |  |  |             </el-button> | 
					
						
							|  |  |  |  |             <el-button size="large" @click="resetForm" class="px-8 py-2.5 transition-all duration-300 border-gray-300 hover:bg-gray-100 font-medium"> | 
					
						
							|  |  |  |  |               <i class="el-icon-refresh mr-2"></i>重置 | 
					
						
							|  |  |  |  |             </el-button> | 
					
						
							|  |  |  |  |           </div> | 
					
						
							|  |  |  |  |         </el-form> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     </div> | 
					
						
							|  |  |  |  |   </div> | 
					
						
							|  |  |  |  | </template> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <script setup name="PersonnelForm" lang="ts"> | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | import { ref, reactive, computed, onMounted, toRefs, watch, WatchStopHandle, type FormItemRule } from 'vue'; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | import { getCurrentInstance } from 'vue'; | 
					
						
							|  |  |  |  | import type { ComponentInternalInstance } from 'vue'; | 
					
						
							|  |  |  |  | import { useUserStoreHook } from '@/store/modules/user'; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | import { ElMessage, ElLoading, ElForm } from 'element-plus'; | 
					
						
							|  |  |  |  | import { Delete, Plus } from '@element-plus/icons-vue'; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | import { designUserAdd, designUserList, systemUserList } from '@/api/design/appointment'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // 获取当前实例
 | 
					
						
							|  |  |  |  | const { proxy } = getCurrentInstance() as ComponentInternalInstance; | 
					
						
							|  |  |  |  | // 获取用户 store
 | 
					
						
							|  |  |  |  | const userStore = useUserStoreHook(); | 
					
						
							|  |  |  |  | // 从 store 中获取当前选中的项目
 | 
					
						
							|  |  |  |  | const currentProject = computed(() => userStore.selectedProject); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 专业字典数据(确保默认空数组)
 | 
					
						
							|  |  |  |  | const { des_user_major = ref<Array<{ label: string; value: string }>>([]) } = toRefs<any>(proxy?.useDict('des_user_major') || {}); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 表单数据定义(明确类型)
 | 
					
						
							|  |  |  |  | interface Person { | 
					
						
							|  |  |  |  |   userId: number | null; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | interface MajorGroup { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   userMajor: string | null; // 专业值(与字典value对应)
 | 
					
						
							|  |  |  |  |   persons: Person[]; // 该专业下的人员
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | const form = reactive({ | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   projectId: currentProject.value?.id || null, | 
					
						
							|  |  |  |  |   designLeader: null as number | null, // 设计负责人
 | 
					
						
							|  |  |  |  |   designers: [] as MajorGroup[], // 设计人员(userType=2)
 | 
					
						
							|  |  |  |  |   reviewers: [] as MajorGroup[], // 校审人员(userType=3)
 | 
					
						
							|  |  |  |  |   approved: [] as MajorGroup[], // 审定人员(userType=4)
 | 
					
						
							|  |  |  |  |   auditor: [] as MajorGroup[] // 审核人员(userType=5)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 组合配置:统一所有角色的专业和人员(确保长度一致)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const combinedConfigs = computed(() => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 取所有角色的最大长度,确保数组长度统一
 | 
					
						
							|  |  |  |  |   const maxLength = Math.max(form.designers.length, form.reviewers.length, form.approved.length, form.auditor.length); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 补全空分组(确保每个角色数组长度相同,避免渲染异常)
 | 
					
						
							|  |  |  |  |   while (form.designers.length < maxLength) form.designers.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |   while (form.reviewers.length < maxLength) form.reviewers.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |   while (form.approved.length < maxLength) form.approved.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |   while (form.auditor.length < maxLength) form.auditor.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 组合数据:所有角色共用designers的专业值(确保同步)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   return form.designers.map((designerGroup, index) => ({ | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     userMajor: designerGroup.userMajor, // 统一专业来源
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     designPersons: designerGroup.persons, | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     reviewPersons: form.reviewers[index].persons, | 
					
						
							|  |  |  |  |     approvedPersons: form.approved[index].persons, | 
					
						
							|  |  |  |  |     auditorPersons: form.auditor[index].persons | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   })); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 表单验证规则(新增专业有效性校验)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const rules = reactive({ | 
					
						
							|  |  |  |  |   designLeader: [{ required: true, message: '请选择设计负责人', trigger: 'change' }] | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 自定义校验:确保专业值在字典中存在(避免无效值)
 | 
					
						
							|  |  |  |  | const validateMajor: FormItemRule = (rule, value, callback) => { | 
					
						
							|  |  |  |  |   if (!value) { | 
					
						
							|  |  |  |  |     callback(new Error('请选择专业')); | 
					
						
							|  |  |  |  |   } else { | 
					
						
							|  |  |  |  |     // 检查专业值是否在字典中存在
 | 
					
						
							|  |  |  |  |     const majorExists = des_user_major.value.some((item) => item.value == value); | 
					
						
							|  |  |  |  |     if (majorExists) { | 
					
						
							|  |  |  |  |       callback(); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       callback(new Error('选择的专业无效,请重新选择')); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 其他基础变量
 | 
					
						
							|  |  |  |  | const userList = ref<Array<{ userId: number; nickName: string }>>([]); | 
					
						
							|  |  |  |  | const leaveFormRef = ref<InstanceType<typeof ElForm> | null>(null); | 
					
						
							|  |  |  |  | const disabledForm = ref(true); // 初始禁用表单(编辑时开启)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 查询当前部门的所有用户(确保用户列表有效) */ | 
					
						
							|  |  |  |  | const getDeptAllUser = async (deptId: number | undefined) => { | 
					
						
							| 
									
										
										
										
											2025-08-29 16:12:14 +08:00
										 |  |  |  |   console.log(1111111111111); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!deptId) { | 
					
						
							|  |  |  |  |     ElMessage.warning('请先选择部门'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   try { | 
					
						
							|  |  |  |  |     const res = await systemUserList({ deptId }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     userList.value = res.rows || []; | 
					
						
							|  |  |  |  |     if (userList.value.length == 0) { | 
					
						
							|  |  |  |  |       ElMessage.info('当前部门暂无用户数据,请先添加用户'); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } catch (error) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     ElMessage.error('获取用户列表失败,请刷新重试'); | 
					
						
							|  |  |  |  |     console.error('获取用户列表异常:', error); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 查询当前表单数据并回显(确保专业值正确赋值) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const designUser = async () => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   const projectId = currentProject.value?.id; | 
					
						
							|  |  |  |  |   if (!projectId) { | 
					
						
							|  |  |  |  |     ElMessage.warning('请先选择项目'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   const loading = ElLoading.service({ | 
					
						
							|  |  |  |  |     lock: true, | 
					
						
							|  |  |  |  |     text: '加载配置数据中...', | 
					
						
							|  |  |  |  |     background: 'rgba(255, 255, 255, 0.7)' | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     const res = await designUserList({ projectId }); | 
					
						
							|  |  |  |  |     // 清空现有数据(避免残留旧值)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     form.designLeader = null; | 
					
						
							|  |  |  |  |     form.designers = []; | 
					
						
							|  |  |  |  |     form.reviewers = []; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     form.approved = []; | 
					
						
							|  |  |  |  |     form.auditor = []; | 
					
						
							| 
									
										
										
										
											2025-09-01 09:15:27 +08:00
										 |  |  |  |     disabledForm.value = false; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     if (res.code == 200 && Array.isArray(res.rows) && res.rows.length > 0) { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |       disabledForm.value = true; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       // 1. 按用户类型分类数据(明确类型)
 | 
					
						
							|  |  |  |  |       const designLeader = res.rows.find((item: any) => item.userType == 1); | 
					
						
							|  |  |  |  |       const designerItems = res.rows.filter((item: any) => item.userType == 2); | 
					
						
							|  |  |  |  |       const reviewerItems = res.rows.filter((item: any) => item.userType == 3); | 
					
						
							|  |  |  |  |       const approvedItems = res.rows.filter((item: any) => item.userType == 4); | 
					
						
							|  |  |  |  |       const auditorItems = res.rows.filter((item: any) => item.userType == 5); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 2. 回显设计负责人
 | 
					
						
							|  |  |  |  |       if (designLeader) form.designLeader = designLeader.userId; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       // 3. 按专业分组回显各角色人员(确保专业值非空)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |       form.designers = groupPersonByMajor(designerItems); | 
					
						
							|  |  |  |  |       form.reviewers = groupPersonByMajor(reviewerItems); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       form.approved = groupPersonByMajor(approvedItems); | 
					
						
							|  |  |  |  |       form.auditor = groupPersonByMajor(auditorItems); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 4. 补全空分组(确保至少有一个专业)
 | 
					
						
							|  |  |  |  |       if (form.designers.length == 0) form.designers.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |       // 同步其他角色的分组长度
 | 
					
						
							|  |  |  |  |       while (form.reviewers.length < form.designers.length) form.reviewers.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |       while (form.approved.length < form.designers.length) form.approved.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  |       while (form.auditor.length < form.designers.length) form.auditor.push(createEmptyMajorGroup()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 5. 确保所有角色的专业值与designers同步(回显时可能存在差异)
 | 
					
						
							|  |  |  |  |       form.designers.forEach((designerGroup, index) => { | 
					
						
							|  |  |  |  |         if (designerGroup.userMajor) { | 
					
						
							|  |  |  |  |           form.reviewers[index].userMajor = designerGroup.userMajor; | 
					
						
							|  |  |  |  |           form.approved[index].userMajor = designerGroup.userMajor; | 
					
						
							|  |  |  |  |           form.auditor[index].userMajor = designerGroup.userMajor; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       // 无数据时初始化默认分组
 | 
					
						
							|  |  |  |  |       form.designers = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |       form.reviewers = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |       form.approved = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |       form.auditor = [createEmptyMajorGroup()]; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } catch (error) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     ElMessage.error('获取配置数据失败,请刷新重试'); | 
					
						
							|  |  |  |  |     console.error('获取配置数据异常:', error); | 
					
						
							|  |  |  |  |     // 异常时初始化默认分组(避免页面空白)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     form.designers = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |     form.reviewers = [createEmptyMajorGroup()]; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     form.approved = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |     form.auditor = [createEmptyMajorGroup()]; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } finally { | 
					
						
							|  |  |  |  |     loading.close(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 辅助函数:创建空的专业分组(确保初始结构正确) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const createEmptyMajorGroup = (): MajorGroup => ({ | 
					
						
							|  |  |  |  |   userMajor: null, | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   persons: [{ userId: null }] // 初始一个空人员
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 辅助函数:按专业分组整理人员数据(确保专业值非空) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const groupPersonByMajor = (items: any[]): MajorGroup[] => { | 
					
						
							|  |  |  |  |   const groupMap: Record<string, MajorGroup> = {}; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 过滤无效数据(确保item有userMajor和userId)
 | 
					
						
							|  |  |  |  |   const validItems = items.filter((item) => item?.userMajor && item?.userId); | 
					
						
							|  |  |  |  |   if (validItems.length == 0) { | 
					
						
							|  |  |  |  |     return [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   validItems.forEach((item) => { | 
					
						
							|  |  |  |  |     const major = item.userMajor; | 
					
						
							|  |  |  |  |     // 初始化分组(确保不重复)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     if (!groupMap[major]) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       groupMap[major] = { userMajor: major, persons: [] }; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     // 添加人员(避免重复人员)
 | 
					
						
							|  |  |  |  |     const personExists = groupMap[major].persons.some((p) => p.userId == item.userId); | 
					
						
							|  |  |  |  |     if (!personExists) { | 
					
						
							|  |  |  |  |       groupMap[major].persons.push({ userId: item.userId }); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 确保每个分组至少有一个人员
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   Object.values(groupMap).forEach((group) => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     if (group.persons.length == 0) { | 
					
						
							|  |  |  |  |       group.persons.push({ userId: null }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   return Object.values(groupMap); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 新增专业配置行(确保所有角色同步新增) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const addMajor = () => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   const newGroup = createEmptyMajorGroup(); | 
					
						
							|  |  |  |  |   form.designers.push(newGroup); | 
					
						
							|  |  |  |  |   form.reviewers.push({ ...newGroup }); // 深拷贝避免引用问题
 | 
					
						
							|  |  |  |  |   form.approved.push({ ...newGroup }); | 
					
						
							|  |  |  |  |   form.auditor.push({ ...newGroup }); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 滚动到新增行(提升用户体验)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   setTimeout(() => { | 
					
						
							|  |  |  |  |     const groups = document.querySelectorAll(`[data-v-${proxy?.$options.__scopeId}] .animate-fadeIn`); | 
					
						
							|  |  |  |  |     if (groups.length > 0) { | 
					
						
							|  |  |  |  |       groups[groups.length - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, 100); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 删除专业配置行(确保所有角色同步删除) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const removeMajor = (configIndex: number) => { | 
					
						
							|  |  |  |  |   if (form.designers.length <= 1) { | 
					
						
							|  |  |  |  |     ElMessage.warning('至少保留一个专业配置'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 同步删除所有角色的对应分组
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   form.designers.splice(configIndex, 1); | 
					
						
							|  |  |  |  |   form.reviewers.splice(configIndex, 1); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   form.approved.splice(configIndex, 1); | 
					
						
							|  |  |  |  |   form.auditor.splice(configIndex, 1); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 给指定专业配置行添加人员(支持所有角色) */ | 
					
						
							|  |  |  |  | const addPerson = (type: 'designers' | 'reviewers' | 'approved' | 'auditor', configIndex: number) => { | 
					
						
							|  |  |  |  |   // 确保分组存在
 | 
					
						
							|  |  |  |  |   if (form[type].length <= configIndex) { | 
					
						
							|  |  |  |  |     ElMessage.warning('专业配置不存在'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   // 添加空人员(后续选择)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   form[type][configIndex].persons.push({ userId: null }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 滚动到新增的人员选择框
 | 
					
						
							|  |  |  |  |   setTimeout(() => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     const selects = document.querySelectorAll(`[data-v-${proxy?.$options.__scopeId}] .el-select`); | 
					
						
							|  |  |  |  |     if (selects.length > 0) { | 
					
						
							|  |  |  |  |       selects[selects.length - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   }, 100); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 从指定专业配置行删除人员(支持所有角色) */ | 
					
						
							|  |  |  |  | const removePerson = (type: 'designers' | 'reviewers' | 'approved' | 'auditor', configIndex: number, personIndex: number) => { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   const targetGroup = form[type][configIndex]; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!targetGroup) { | 
					
						
							|  |  |  |  |     ElMessage.warning('专业配置不存在'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   if (targetGroup.persons.length <= 1) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     const roleNames = { | 
					
						
							|  |  |  |  |       designers: '设计', | 
					
						
							|  |  |  |  |       reviewers: '校审', | 
					
						
							|  |  |  |  |       approved: '审定', | 
					
						
							|  |  |  |  |       auditor: '审核' | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |     ElMessage.warning(`该专业至少保留一个${roleNames[type]}人员`); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   targetGroup.persons.splice(personIndex, 1); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 专业变更时:同步所有角色的专业值并重置人员(核心修复) */ | 
					
						
							|  |  |  |  | const handleMajorChange = (newMajor: string | null, configIndex: number) => { | 
					
						
							|  |  |  |  |   // 过滤无效专业值
 | 
					
						
							|  |  |  |  |   if (!newMajor || !des_user_major.value.some((item) => item.value == newMajor)) { | 
					
						
							|  |  |  |  |     ElMessage.warning('选择的专业无效,请重新选择'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 1. 同步所有角色的专业值(确保统一)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   form.designers[configIndex].userMajor = newMajor; | 
					
						
							|  |  |  |  |   form.reviewers[configIndex].userMajor = newMajor; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   form.approved[configIndex].userMajor = newMajor; | 
					
						
							|  |  |  |  |   form.auditor[configIndex].userMajor = newMajor; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 2. 重置当前专业下的所有人员(避免专业与人员不匹配)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   form.designers[configIndex].persons = [{ userId: null }]; | 
					
						
							|  |  |  |  |   form.reviewers[configIndex].persons = [{ userId: null }]; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   form.approved[configIndex].persons = [{ userId: null }]; | 
					
						
							|  |  |  |  |   form.auditor[configIndex].persons = [{ userId: null }]; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   ElMessage.success(`已切换为「${getMajorLabel(newMajor)}」专业,请重新选择人员`); | 
					
						
							|  |  |  |  | }; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 重复校验:同一角色内「专业+人员」组合唯一 */ | 
					
						
							|  |  |  |  | const checkDuplicate = ( | 
					
						
							|  |  |  |  |   currentPerson: Person, | 
					
						
							|  |  |  |  |   role: 'designers' | 'reviewers' | 'approved' | 'auditor', | 
					
						
							|  |  |  |  |   configIndex: number, | 
					
						
							|  |  |  |  |   personIndex: number | 
					
						
							|  |  |  |  | ) => { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   const currentGroup = form[role][configIndex]; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   const currentMajor = form.designers[configIndex].userMajor; // 统一专业来源
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   // 未选专业/人员时不校验
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!currentMajor || !currentPerson.userId) return; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   const currentKey = `${currentMajor}-${currentPerson.userId}`; | 
					
						
							|  |  |  |  |   let duplicateItem: Person | null = null; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 1. 检查当前专业行内是否有重复人员
 | 
					
						
							|  |  |  |  |   duplicateItem = currentGroup.persons.find((item, idx) => idx !== personIndex && item.userId == currentPerson.userId); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   if (duplicateItem) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     ElMessage.warning(`当前专业下「${getUserName(currentPerson.userId)}」已存在,请重新选择`); | 
					
						
							|  |  |  |  |     currentPerson.userId = null; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 2. 检查同一角色其他专业行是否有重复
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   form[role].forEach((group, gIdx) => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     if (gIdx == configIndex) return; | 
					
						
							|  |  |  |  |     const groupMajor = form.designers[gIdx].userMajor; | 
					
						
							|  |  |  |  |     if (!groupMajor) return; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     group.persons.forEach((item) => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       if (item.userId && `${groupMajor}-${item.userId}` == currentKey) { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |         duplicateItem = item; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (duplicateItem) { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     ElMessage.warning(`「${getMajorLabel(currentMajor)}+${getUserName(currentPerson.userId)}」组合已存在,请重新选择`); | 
					
						
							|  |  |  |  |     currentPerson.userId = null; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 辅助函数:通过专业值获取专业名称(避免显示value) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const getMajorLabel = (majorValue: string | null) => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!majorValue) return ''; | 
					
						
							|  |  |  |  |   const major = des_user_major.value.find((item) => item.value == majorValue); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   return major ? major.label : majorValue; | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /** 辅助函数:通过用户ID获取用户名 */ | 
					
						
							|  |  |  |  | const getUserName = (userId: number | null) => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!userId) return ''; | 
					
						
							|  |  |  |  |   const user = userList.value.find((item) => item.userId == userId); | 
					
						
							|  |  |  |  |   return user ? user.nickName : `${userId}`; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 提交表单(核心:确保专业值正确传递) */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const submitForm = async () => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   if (!leaveFormRef.value) { | 
					
						
							|  |  |  |  |     ElMessage.warning('表单实例未加载,请刷新重试'); | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     // 1. 基础表单验证(确保所有必填项已填)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     await leaveFormRef.value.validate(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     // 2. 提交前二次校验:专业值非空(双重保障)
 | 
					
						
							|  |  |  |  |     const hasEmptyMajor = form.designers.some((group) => !group.userMajor); | 
					
						
							|  |  |  |  |     if (hasEmptyMajor) { | 
					
						
							|  |  |  |  |       ElMessage.error('存在未选择专业的配置,请完善后提交'); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 3. 提交前二次校验:「专业+人员」组合唯一(所有角色)
 | 
					
						
							|  |  |  |  |     let hasDuplicate = false; | 
					
						
							|  |  |  |  |     const validateRoleDuplicate = (roleGroups: MajorGroup[], roleName: string) => { | 
					
						
							|  |  |  |  |       const keys: string[] = []; | 
					
						
							|  |  |  |  |       roleGroups.forEach((group, gIdx) => { | 
					
						
							|  |  |  |  |         const major = form.designers[gIdx].userMajor; | 
					
						
							|  |  |  |  |         if (!major) return; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |         group.persons.forEach((person) => { | 
					
						
							|  |  |  |  |           if (!person.userId) return; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |           const key = `${major}-${person.userId}`; | 
					
						
							|  |  |  |  |           if (keys.includes(key)) { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |             hasDuplicate = true; | 
					
						
							|  |  |  |  |             ElMessage.error(`${roleName}中存在重复的「专业+人员」组合,请检查`); | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |           keys.push(key); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |         }); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     validateRoleDuplicate(form.designers, '设计人员'); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     if (hasDuplicate) return; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     validateRoleDuplicate(form.reviewers, '校审人员'); | 
					
						
							|  |  |  |  |     if (hasDuplicate) return; | 
					
						
							|  |  |  |  |     validateRoleDuplicate(form.approved, '审定人员'); | 
					
						
							|  |  |  |  |     if (hasDuplicate) return; | 
					
						
							|  |  |  |  |     validateRoleDuplicate(form.auditor, '审核人员'); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     if (hasDuplicate) return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     // 4. 构建提交数据(确保专业值正确传递)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     const submitData = { | 
					
						
							|  |  |  |  |       projectId: form.projectId, | 
					
						
							|  |  |  |  |       personnel: [ | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |         // 设计负责人(userType=1)
 | 
					
						
							|  |  |  |  |         form.designLeader | 
					
						
							|  |  |  |  |           ? { | 
					
						
							|  |  |  |  |               userId: form.designLeader, | 
					
						
							|  |  |  |  |               userType: 'designLeader', | 
					
						
							|  |  |  |  |               userMajor: null | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |           : null, | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 设计人员(userType=2)
 | 
					
						
							|  |  |  |  |         ...form.designers.flatMap((group, gIdx) => { | 
					
						
							|  |  |  |  |           const major = form.designers[gIdx].userMajor; // 统一专业来源
 | 
					
						
							|  |  |  |  |           return group.persons | 
					
						
							|  |  |  |  |             .filter((person) => person.userId) // 过滤空人员
 | 
					
						
							|  |  |  |  |             .map((person) => ({ | 
					
						
							|  |  |  |  |               userId: person.userId, | 
					
						
							|  |  |  |  |               userType: 'designer', | 
					
						
							|  |  |  |  |               userMajor: major // 确保专业值非空
 | 
					
						
							|  |  |  |  |             })); | 
					
						
							|  |  |  |  |         }), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 校审人员(userType=3)
 | 
					
						
							|  |  |  |  |         ...form.reviewers.flatMap((group, gIdx) => { | 
					
						
							|  |  |  |  |           const major = form.designers[gIdx].userMajor; | 
					
						
							|  |  |  |  |           return group.persons | 
					
						
							|  |  |  |  |             .filter((person) => person.userId) | 
					
						
							|  |  |  |  |             .map((person) => ({ | 
					
						
							|  |  |  |  |               userId: person.userId, | 
					
						
							|  |  |  |  |               userType: 'reviewer', | 
					
						
							|  |  |  |  |               userMajor: major | 
					
						
							|  |  |  |  |             })); | 
					
						
							|  |  |  |  |         }), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 审定人员(userType=4)
 | 
					
						
							|  |  |  |  |         ...form.approved.flatMap((group, gIdx) => { | 
					
						
							|  |  |  |  |           const major = form.designers[gIdx].userMajor; | 
					
						
							|  |  |  |  |           return group.persons | 
					
						
							|  |  |  |  |             .filter((person) => person.userId) | 
					
						
							|  |  |  |  |             .map((person) => ({ | 
					
						
							|  |  |  |  |               userId: person.userId, | 
					
						
							|  |  |  |  |               userType: 'approved', | 
					
						
							|  |  |  |  |               userMajor: major | 
					
						
							|  |  |  |  |             })); | 
					
						
							|  |  |  |  |         }), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 审核人员(userType=5)
 | 
					
						
							|  |  |  |  |         ...form.auditor.flatMap((group, gIdx) => { | 
					
						
							|  |  |  |  |           const major = form.designers[gIdx].userMajor; | 
					
						
							|  |  |  |  |           return group.persons | 
					
						
							|  |  |  |  |             .filter((person) => person.userId) | 
					
						
							|  |  |  |  |             .map((person) => ({ | 
					
						
							|  |  |  |  |               userId: person.userId, | 
					
						
							|  |  |  |  |               userType: 'auditor', | 
					
						
							|  |  |  |  |               userMajor: major | 
					
						
							|  |  |  |  |             })); | 
					
						
							|  |  |  |  |         }) | 
					
						
							|  |  |  |  |       ].filter(Boolean) // 过滤null值(如未选设计负责人)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 5. 数据格式转换(适配后端userType定义)
 | 
					
						
							|  |  |  |  |     const submitList = submitData.personnel.map((item: any) => { | 
					
						
							|  |  |  |  |       let userType = 1; // 默认设计负责人
 | 
					
						
							|  |  |  |  |       if (item.userType == 'designer') userType = 2; | 
					
						
							|  |  |  |  |       else if (item.userType == 'reviewer') userType = 3; | 
					
						
							|  |  |  |  |       else if (item.userType == 'approved') userType = 4; | 
					
						
							|  |  |  |  |       else if (item.userType == 'auditor') userType = 5; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 查找用户名(确保userName非空)
 | 
					
						
							|  |  |  |  |       const user = userList.value.find((u) => u.userId == item.userId); | 
					
						
							|  |  |  |  |       const userName = user ? user.nickName : `用户${item.userId}`; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return { | 
					
						
							|  |  |  |  |         userName, | 
					
						
							|  |  |  |  |         projectId: submitData.projectId, | 
					
						
							|  |  |  |  |         userId: item.userId, | 
					
						
							|  |  |  |  |         userType, | 
					
						
							|  |  |  |  |         userMajor: item.userMajor // 最终传递的专业值(确保非空)
 | 
					
						
							|  |  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     // 6. 提交到后端(确保参数正确)
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     const loading = ElLoading.service({ text: '提交中...', background: 'rgba(255,255,255,0.7)' }); | 
					
						
							|  |  |  |  |     const res = await designUserAdd({ | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       list: submitList, | 
					
						
							|  |  |  |  |       projectId: form.projectId | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     if (res.code == 200) { | 
					
						
							|  |  |  |  |       disabledForm.value = true; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       ElMessage.success('提交成功,已保存人员配置'); | 
					
						
							|  |  |  |  |       // 提交成功后重新加载数据(确保回显正确)
 | 
					
						
							|  |  |  |  |       await designUser(); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       ElMessage.error(res.msg || '提交失败,请重试'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } catch (error: any) { | 
					
						
							|  |  |  |  |     // 捕获表单验证错误或其他异常
 | 
					
						
							|  |  |  |  |     if (error.name == 'ValidationError') { | 
					
						
							|  |  |  |  |       ElMessage.error('请完善表单必填项后再提交'); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       ElMessage.error('提交过程异常,请刷新页面重试'); | 
					
						
							|  |  |  |  |       console.error('表单提交异常:', error); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } finally { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     ElLoading.service().close(); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 重置表单(确保恢复初始状态) */ | 
					
						
							|  |  |  |  | const resetForm = async () => { | 
					
						
							|  |  |  |  |   if (!leaveFormRef.value) return; | 
					
						
							|  |  |  |  |   // 重置表单字段
 | 
					
						
							|  |  |  |  |   leaveFormRef.value.resetFields(); | 
					
						
							|  |  |  |  |   // 重置数据结构(恢复为初始空分组)
 | 
					
						
							|  |  |  |  |   form.designers = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |   form.reviewers = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |   form.approved = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |   form.auditor = [createEmptyMajorGroup()]; | 
					
						
							|  |  |  |  |   form.designLeader = null; | 
					
						
							|  |  |  |  |   ElMessage.info('表单已重置,请重新配置人员'); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | /** 监听项目ID变化:刷新用户列表和表单数据 */ | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | const listeningProject: WatchStopHandle = watch( | 
					
						
							|  |  |  |  |   () => currentProject.value?.id, | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   async (newProjectId) => { | 
					
						
							|  |  |  |  |     if (newProjectId) { | 
					
						
							|  |  |  |  |       form.projectId = newProjectId; | 
					
						
							|  |  |  |  |       // 先获取用户列表,再加载表单数据
 | 
					
						
							|  |  |  |  |       await getDeptAllUser(userStore.deptId); | 
					
						
							|  |  |  |  |       await designUser(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, | 
					
						
							|  |  |  |  |   { immediate: true } // 初始加载时触发
 | 
					
						
							|  |  |  |  | ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /** 监听专业字典变化:确保专业下拉框数据最新 */ | 
					
						
							|  |  |  |  | watch( | 
					
						
							|  |  |  |  |   () => des_user_major.value, | 
					
						
							|  |  |  |  |   (newMajorList) => { | 
					
						
							|  |  |  |  |     if (newMajorList.length == 0) { | 
					
						
							|  |  |  |  |       ElMessage.warning('专业字典数据为空,请联系管理员配置'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, | 
					
						
							|  |  |  |  |   { deep: true } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // 页面生命周期
 | 
					
						
							|  |  |  |  | onUnmounted(() => { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 清除监听
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   listeningProject(); | 
					
						
							|  |  |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | onMounted(async () => { | 
					
						
							|  |  |  |  |   // 初始加载:先获取部门用户,再加载表单数据
 | 
					
						
							|  |  |  |  |   await getDeptAllUser(userStore.deptId); | 
					
						
							|  |  |  |  |   await designUser(); | 
					
						
							|  |  |  |  |   // 打印初始数据(调试用)
 | 
					
						
							|  |  |  |  |   console.log('初始专业字典:', des_user_major.value); | 
					
						
							|  |  |  |  |   console.log('初始用户列表:', userList.value); | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | </script> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <style lang="scss" scoped> | 
					
						
							| 
									
										
										
										
											2025-08-19 14:56:48 +08:00
										 |  |  |  | .appointment { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   width: 100%; | 
					
						
							|  |  |  |  |   max-width: 1800px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   .el-select__wrapper { | 
					
						
							|  |  |  |  |     width: 100% !important; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   .fonts { | 
					
						
							|  |  |  |  |     .el-form-item--default .el-form-item__label { | 
					
						
							|  |  |  |  |       font-size: 18px !important; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |       font-weight: 600; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 自定义动画:新增专业行时的过渡效果
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | @keyframes fadeIn { | 
					
						
							|  |  |  |  |   from { | 
					
						
							|  |  |  |  |     opacity: 0; | 
					
						
							|  |  |  |  |     transform: translateY(10px); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   to { | 
					
						
							|  |  |  |  |     opacity: 1; | 
					
						
							|  |  |  |  |     transform: translateY(0); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | .animate-fadeIn { | 
					
						
							|  |  |  |  |   animation: fadeIn 0.3s ease-out forwards; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 表单样式优化:统一间距和字体
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | ::v-deep .el-form { | 
					
						
							|  |  |  |  |   --el-form-item-margin-bottom: 0; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::v-deep .el-form-item { | 
					
						
							|  |  |  |  |   margin-bottom: 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   &__label { | 
					
						
							|  |  |  |  |     font-weight: 500; | 
					
						
							|  |  |  |  |     color: #4e5969; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     font-size: 14px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   &__content { | 
					
						
							|  |  |  |  |     padding: 0; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     font-size: 14px; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 表单验证错误提示样式
 | 
					
						
							|  |  |  |  |   &__error { | 
					
						
							|  |  |  |  |     font-size: 12px; | 
					
						
							|  |  |  |  |     margin-top: 4px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 选择器样式:统一高度和边框
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | ::v-deep .el-select { | 
					
						
							|  |  |  |  |   width: 100%; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   .el-input__inner { | 
					
						
							|  |  |  |  |     border-radius: 6px; | 
					
						
							|  |  |  |  |     transition: all 0.3s ease; | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |     padding: 0 12px; | 
					
						
							|  |  |  |  |     height: 32px; // 统一高度,避免布局错乱
 | 
					
						
							|  |  |  |  |     font-size: 14px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   &:hover .el-input__inner { | 
					
						
							|  |  |  |  |     border-color: #66b1ff; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   &.el-select-focus .el-input__inner { | 
					
						
							|  |  |  |  |     border-color: #409eff; | 
					
						
							|  |  |  |  |     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 按钮样式:统一大小和颜色
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | ::v-deep .el-button { | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   border-radius: 4px; | 
					
						
							|  |  |  |  |   padding: 4px 8px; | 
					
						
							|  |  |  |  |   font-size: 12px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   &--small { | 
					
						
							|  |  |  |  |     padding: 2px 6px; | 
					
						
							|  |  |  |  |     font-size: 12px; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 角色区分色(提升视觉识别)
 | 
					
						
							|  |  |  |  |   &.el-button--success { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     background-color: #67c23a; | 
					
						
							|  |  |  |  |     border-color: #67c23a; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     &:hover { | 
					
						
							|  |  |  |  |       background-color: #85ce61; | 
					
						
							|  |  |  |  |       border-color: #85ce61; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     &:disabled { | 
					
						
							|  |  |  |  |       background-color: #b3e099; | 
					
						
							|  |  |  |  |       border-color: #b3e099; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   &.el-button--danger { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     background-color: #f56c6c; | 
					
						
							|  |  |  |  |     border-color: #f56c6c; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     &:hover { | 
					
						
							|  |  |  |  |       background-color: #f78989; | 
					
						
							|  |  |  |  |       border-color: #f78989; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     &:disabled { | 
					
						
							|  |  |  |  |       background-color: #ffcccc; | 
					
						
							|  |  |  |  |       border-color: #ffbbbb; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   &.el-button--text { | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |     color: #f56c6c; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     &:hover { | 
					
						
							|  |  |  |  |       color: #f78989; | 
					
						
							|  |  |  |  |       background-color: rgba(245, 108, 108, 0.05); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 确保表头不换行
 | 
					
						
							|  |  |  |  | .whitespace-nowrap { | 
					
						
							|  |  |  |  |   white-space: nowrap; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | // 响应式处理:小屏幕横向滚动,避免挤压
 | 
					
						
							|  |  |  |  | @media (max-width: 1200px) { | 
					
						
							|  |  |  |  |   ::v-deep .el-row { | 
					
						
							|  |  |  |  |     flex-wrap: nowrap !important; // 强制不换行
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   ::v-deep .el-col { | 
					
						
							|  |  |  |  |     flex-shrink: 0 !important; // 禁止列收缩
 | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   .appointment { | 
					
						
							|  |  |  |  |     width: 98vw; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  |   // 小屏幕下调整标签宽度,节省空间
 | 
					
						
							|  |  |  |  |   ::v-deep .el-form-item--default .el-form-item__label { | 
					
						
							|  |  |  |  |     width: 50px !important; | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-28 23:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | // 空状态样式优化:统一视觉效果
 | 
					
						
							|  |  |  |  | ::v-deep .text-gray-500.text-xs { | 
					
						
							|  |  |  |  |   text-align: center; | 
					
						
							|  |  |  |  |   background-color: #f9fafb; | 
					
						
							|  |  |  |  |   border-color: #e5e7eb; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-19 10:19:29 +08:00
										 |  |  |  | </style> |