فهرست منبع

feat: 更新培训信息管理界面,优化表单字段和交互逻辑,增加课程详情查询功能,修复隐患台账整改问题

sunqijun 2 ماه پیش
والد
کامیت
cbfd5ca518

+ 20 - 10
src/api/production-education-training-plan-dept/index.ts

@@ -27,20 +27,20 @@ export interface FormDataType {
   studyHours: number | string;
 }
 export interface FormSubmitDataType {
-  id?: number;
-  courseType: number;
-  petpiId: number;
+  id?: number|string; // 编辑时使用
+  courseType: number|string;
+  petpiId?: number | string;
   courseName: string;
   trainingMethod: string;
   trainingTeacher: string;
-  planNumOfParticipants: number;
-  groupOfParticipants: Array<number>;
-  startDate: Date;
-  endDate: Date;
+  planNumOfParticipants: number | string;
+  groupOfParticipants: any;
+  startDate: string;
+  endDate: string;
   courseIntroduction: string;
-  courseContent: string;
-  isSign: number;
-  courseImg: string;
+  courseContent: any;
+  isSign: number | string;
+  courseImg?: any;
 }
 
 export interface TableParamsType {
@@ -114,6 +114,16 @@ export function deleteTrainingInformation(id: string | number) {
     method: 'delete',
   });
 }
+/**
+ * 安全教育培训 教育培训计划管理(部门)-培训计划中的课程详细
+ */
+export function queryEducationTrainingPlanCourseDetail(id: string | number) {
+  return http.request({
+    url: `/educationTrainingPlanIssuance/queryEducationTrainingPlanCourseDetail?id=${id}`,
+    method: 'get',
+  });
+}
+
 
 /**
  * 教育培训计划管理(部门)-详情

+ 114 - 20
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/components/hiddenTroubleAccountManagementDetail.vue

@@ -1,5 +1,4 @@
 <template>
-  <main class="safety-platform-container__main">
     <el-alert
       v-if="isRectifyMode && detailReviewRejectReason"
       type="error"
@@ -7,6 +6,8 @@
       show-icon
       class="detail-reject-alert"
     />
+  <main class="safety-platform-container__main">
+    
     <el-form label-width="150px" :model="ruleFormData" :rules="isViewMode ? undefined : formRules" ref="basicFormRef">
       <el-form-item label="隐患问题类别:" prop="typeId">
         <el-select
@@ -153,6 +154,62 @@
             :disabled="isViewMode"
           />
         </el-form-item>
+        
+      </section>
+      <section v-if="detailStatusOrder>=2">
+        <el-form-item label="整改完成情况:" prop="rectificationCompletionStatus">
+          <el-input
+            v-model="ruleFormData.rectificationCompletionStatus"
+            placeholder="请输入整改完成情况"
+            show-word-limit
+            :disabled="detailStatusOrder>=3"
+            style="width: 450px"
+          />
+        </el-form-item>
+        <el-form-item label="整改完成时间:" prop="rectificationCompletionTime">
+            <el-date-picker
+            v-model="ruleFormData.rectificationCompletionTime"
+            type="date"
+            :disabled="detailStatusOrder>=3"
+            value-format="YYYY-MM-DD"
+            placeholder="请选择整改完成时间"
+            style="width: 450px"
+          />
+        </el-form-item>
+        <el-form-item label="附件上传" prop="attachments">
+            <UploadFiles
+                label="选择附件"
+                :maxCount="1"
+                v-if="detailStatusOrder==2"
+                :disabled="detailStatusOrder>=3"
+                :file-list="attachmentsFileList"
+                :allow-all-file-types="true"
+                @uploadSuccess="(list: FileItem[]) => handleAttachmentsUploadSuccess(list)"
+                @preview="handlePreview"
+                />
+                <div class="file-list" v-if="detailStatusOrder>=3">
+                    <div class="file-item" v-for="file in attachmentsFileList" :key="file.fileId">
+                        <span class="file-item--name">{{ file.fileName }}</span>
+                        <div class="file-item--footer">
+                            <el-button link type="primary" @click="previewOnline(file.fileUrl, file.fileType)"
+                                >预览</el-button
+                            >
+                        </div>
+                    </div>
+                </div>
+        </el-form-item>
+        <el-form-item label="复查意见:" prop="reviewComments" v-if="detailStatusOrder>=3" >
+            <el-input
+                v-model="ruleFormData.reviewComments"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入复查意见(选填),限300字"
+                maxlength="300"
+                style="width: 450px"
+                show-word-limit
+                :disabled="isViewMode && !isReviewMode"
+                />
+        </el-form-item>
       </section>
     </el-form>
   </main>
@@ -172,7 +229,8 @@
     </template>
     <!-- 纯查看时仅保留上面的返回,不显示其他按钮 -->
   </footer>
-
+  <!-- 预览 -->
+  <PreviewOnline ref="previewOnlineRef" />
   <!-- 复查弹窗 -->
   <el-dialog v-model="showReviewDialog" title="复查隐患" width="520px" destroy-on-close @close="resetReviewForm">
     <el-form ref="reviewFormRef" :model="reviewForm" :rules="reviewRules" label-width="120px">
@@ -298,6 +356,7 @@
   import { getAllDepartments } from '@/api/auth/dept';
   import type { DeptTree } from '@/types/dept/type';
   import { queryAvailableUserList } from '@/api/production-safety/responsibility-implementation';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
 
   const router = useRouter();
   const route = useRoute();
@@ -321,7 +380,7 @@
     HIDDEN_DANGER_FORM_DATA,
     HIDDEN_DANGER_FORM_RULES,
   );
-
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
   /** 查看详情:与复查时字段一致,全部为禁用状态 */
   const viewFormConfig = computed(() =>
     HIDDEN_DANGER_REVIEW_FORM_CONFIG.map((item) => ({
@@ -357,23 +416,49 @@
       return { fileId: index + 1, fileName, fileType, fileSize: '0', fileUrl: url };
     });
   }
-  const attachmentsFileList = computed(() => convertAttachmentsToFileItems(ruleFormData.attachments || ''));
+//   const attachmentsFileList = computed(() => convertAttachmentsToFileItems(ruleFormData.attachments || ''));
+
+const attachmentsFileList = ref([]) as any
   async function handleAttachmentsUploadSuccess(files: FileItem[]) {
     if (!files?.length) {
       ruleFormData.attachments = '';
       return;
     }
-    try {
-      const list = await formatAttachmentList(files);
-      ruleFormData.attachments = (list || [])
-        .map((r) => r.fileUrl)
-        .filter(Boolean)
-        .join(',');
-    } catch (e) {
-      console.error('附件上传失败:', e);
-      ElMessage.error(e?.message || e?.data || '附件上传失败,请重试');
-    }
+    console.log(files)
+    attachmentsFileList.value = files
+    ruleFormData.attachments = JSON.stringify(files) || '';
+    // try {
+    //   const list = await formatAttachmentList(files);
+    //   ruleFormData.attachments = (list || [])
+    //     .map((r) => r.fileUrl)
+    //     .filter(Boolean)
+    //     .join(',');
+    // } catch (e) {
+    //   console.error('附件上传失败:', e);
+    //   ElMessage.error(e?.message || e?.data || '附件上传失败,请重试');
+    // }
   }
+  const handlePreview = (url: string) => {
+    if (url) {
+      // 根据文件扩展名判断文件类型
+      const extension = url.split('.').pop()?.toLowerCase() || '';
+      let fileType: 'pdf' | 'word' | 'excel' | 'ppt' = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+      previewOnlineRef.value?.open(url, fileType);
+    }
+  };
+
+   const previewOnline = (url: string | undefined, type) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
 
   const basicFormRef = ref<InstanceType<typeof BasicForm>>();
 
@@ -475,6 +560,8 @@
         ruleFormData.rectificationCompletionStatus = d.rectificationCompletionStatus ?? '';
         ruleFormData.rectificationCompletionTime = d.rectificationCompletionTime ?? '';
         ruleFormData.attachments = d.attachments ?? '';
+        attachmentsFileList.value = JSON.parse(d.attachments|| '[]');
+        ruleFormData.reviewComments = d.reviewComments?? '';
         // 整改页:保存审查不通过原因供 el-alert 展示(后端字段 reviewReason)
         if (isRectifyMode.value && d.reviewReason != null && String(d.reviewReason).trim()) {
           detailReviewRejectReason.value = String(d.reviewReason).trim();
@@ -482,9 +569,9 @@
           detailReviewRejectReason.value = '';
         }
       }
-      if (isReviewMode.value) {
-        ruleFormData.reviewComments = '';
-      }
+    //   if (isReviewMode.value) {
+    //     ruleFormData.reviewComments = '';
+    //   }
       cloneRuleFormData();
     } catch (e) {
       console.error('获取隐患详情失败:', e);
@@ -551,6 +638,10 @@
   async function handleRectifySubmitDirect() {
     if (!currentId.value) return;
     try {
+
+
+      const uploadedFileList = await formatAttachmentList(attachmentsFileList.value);
+
       await rectifyHiddenDanger({
         dangerId: currentId.value,
         rectificationPlan: '',
@@ -559,7 +650,7 @@
         rectificationEndTime: '',
         rectificationCompletionStatus: ruleFormData.rectificationCompletionStatus || '',
         rectificationCompletionTime: ruleFormData.rectificationCompletionTime || '',
-        attachments: ruleFormData.attachments || '',
+        attachments: JSON.stringify(uploadedFileList) || undefined,
         isCompleted: 1,
       });
       ElMessage.success('整改提交成功');
@@ -730,7 +821,10 @@
 
 <style scoped lang="scss">
   @use '@/styles/page-details-layout.scss' as *;
-  .detail-reject-alert {
-    margin-bottom: 16px;
+  
+  .file-item{
+    display: flex;
+    justify-content: flex-start;
+    gap: 10px;
   }
 </style>

+ 6 - 0
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/configs/form.ts

@@ -353,4 +353,10 @@ export const HIDDEN_DANGER_FORM_RULES = {
       trigger: 'change',
     },
   ],
+  rectificationCompletionStatus:[
+    { required: true, message: '请输入整改完成情况', trigger: 'blur' }
+  ],
+  rectificationCompletionTime: [
+    { required: true, message: '请选择整改完成时间', trigger: 'change' }
+  ]
 };

+ 62 - 9
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/components/addTrainingInformation.vue

@@ -1,5 +1,5 @@
 <script lang="ts" setup>
-  import { saveTrainingInformation, updateTrainingInformation } from '@/api/production-education-training-plan-dept';
+  import { saveTrainingInformation, updateTrainingInformation, queryEducationTrainingPlanCourseDetail } from '@/api/production-education-training-plan-dept';
   import { ref, reactive, onMounted, watch, shallowRef, computed } from 'vue';
   import { UploadFilled, Plus, Delete, Download, ZoomIn } from '@element-plus/icons-vue';
   import { TRAINING_FORM_RULES } from '../configs/form';
@@ -31,7 +31,7 @@
   const isEditMode = ref(props.state === 'edit');
   const isCreateMode = ref(props.state === 'add');
 
-  const emit = defineEmits(['update:visible', 'refreshList']);
+  const emit = defineEmits(['update:visible', 'refreshList', 'saveSuccess']);
   const formRef = ref();
   const rules = ref(TRAINING_FORM_RULES);
 
@@ -40,7 +40,7 @@
     courseName: '',
     trainingMethod: '',
     trainingTeacher: '',
-    planNumOfParticipants: null,
+    planNumOfParticipants: '',
     groupOfParticipants: [],
     startDate: '',
     endDate: '',
@@ -58,11 +58,56 @@
     { label: '特种作业培训考核', value: 3 },
     { label: '特种作业复训考核', value: 4 },
   ]);
-
+  // 打开抽屉时的事件
+  const openDrawerFn = async () => {
+    if ((props.state === 'edit' || props.state === 'view') && props.currentId) {
+      // 编辑/查看模式,加载现有详情数据
+      // 这里可以调用接口获取当前培训课程的信息,并填充到 form 中
+      await queryEducationTrainingPlanCourseDetail(props.currentId).then((res) => {
+        console.log(res, '课程详情',);
+        Object.assign(form, {
+            courseType: res.courseType,
+            courseName: res.courseName,
+            trainingMethod: res.trainingMethod,
+            trainingTeacher: res.trainingTeacher,
+            planNumOfParticipants: res.planNumOfParticipants,
+            startDate: res.startDate ? new Date(res.startDate) : '',
+            endDate: res.endDate ? new Date(res.endDate) : '',
+            courseIntroduction: res.courseIntroduction,
+            isSign: res.isSign,
+        }); 
+        // 
+        form.groupOfParticipants = res.groupOfParticipants ? res.groupOfParticipants.split(',').map(Number) : [];
+        // form.courseImg = res.courseImg ? [JSON.parse(res.courseImg)] : [];
+        // fileList.value = JSON.parse(res.courseContent) || [];
+      });
+    } else if (props.state === 'add') {
+        // 新增模式,重置表单
+        fileList.value = [];
+        Object.assign(form, {
+          courseType: '',
+          courseName: '',
+          trainingMethod: '',
+          trainingTeacher: '',
+          planNumOfParticipants: '',
+          groupOfParticipants: [],
+          startDate: '',
+          endDate: '',
+          courseIntroduction: '',
+          courseContent: [] as FileItem[],
+          isSign: 0,
+          courseImg: [] as FileItem[],
+        });
+    }
+  };
   const handleValidate = async () => {
     if (!formRef.value) return;
-    const res = await formRef.value.validateField();
-    return res;
+    try {
+        await formRef.value.validate();
+        return true;
+    } catch {
+        return false;
+    }
   };
   const handleSave = debounce(async () => {
     const res = await handleValidate();
@@ -72,19 +117,24 @@
         ...form,
         petpiId: props.currentId,
         courseImg: form.courseImg[0],
-        // responsibleDeptIds: form.responsibleDeptIds.toString()
       };
       console.log('提交的表单数据:', basePayload);
+
       if (isCreateMode.value) {
+        // 新增,创建接口
         await saveTrainingInformation(basePayload);
         ElMessage.success('创建成功');
+
       } else if (isEditMode.value && props.currentId) {
+        // 编辑,更新接口
         await updateTrainingInformation({
           id: props.currentId,
           ...basePayload,
         });
         ElMessage.success('保存成功');
+
       }
+      emit('saveSuccess');
     } catch (e) {
       ElMessage.error('保存失败,请重试');
     }
@@ -241,6 +291,7 @@
     console.log('上传成功的文件列表:', files);
     form.courseImg = files;
   };
+  
 
   const groupList = ref<any>([]);
   // 获取级联部门分组数据
@@ -260,10 +311,12 @@
 <template>
   <div class="training-course">
     <el-drawer
-      v-model="props.visible"
+      :model-value="props.visible"
+      @update:model-value="$emit('update:visible', $event)"
       direction="rtl"
       size="45%"
-      :title="state === 'add' ? '新增培训课程' : '编辑培训课程'"
+      @opened="openDrawerFn"
+      :title="state === 'add' ? '新增培训课程': state === 'edit' ? '编辑培训课程' : '查看培训课程'"
       @close="$emit('update:visible', false)"
     >
       <el-form label-position="right" label-width="150px" :model="form" :rules="rules" ref="formRef">

+ 13 - 12
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/components/educationTrainingPlanManagementDeptDetail.vue

@@ -46,7 +46,7 @@
             {{ details.deptName }}
           </el-descriptions-item>
 
-          <el-descriptions-item label="学时"> {{ details.studyHours }} </el-descriptions-item>
+          <el-descriptions-item label="学时"> {{ details.studyHours }} </el-descriptions-item>
           <el-descriptions-item label="培训分组">
             {{ details.groupName }}
           </el-descriptions-item>
@@ -77,21 +77,21 @@
 
           <el-form-item>
             <el-button type="primary" @click="handleSearch"> 查询 </el-button>
-            <el-button @click="handleExport"> 导出 </el-button>
             <el-button type="primary" @click="handleAdd"> 新增课程 </el-button>
+            <el-button @click="handleExport"> 导出 </el-button>
           </el-form-item>
         </el-form>
 
         <!-- 表格 -->
         <el-table :data="tableData" border stripe>
-          <el-table-column prop="id" label="编号" width="80" />
-          <el-table-column prop="trainingDate" label="培训时间" />
-          <el-table-column prop="courseName" label="培训课程名称" />
-          <el-table-column prop="courseTypeName" label="课程所属分类" />
+          <el-table-column prop="id" label="编号" width="80" fixed="left" />
+          <el-table-column prop="courseName" label="培训课程名称" width="240" fixed="left" />
+          <el-table-column prop="trainingDate" width="230" label="培训时间" fixed="left" />
+          <el-table-column prop="courseTypeName" label="课程所属分类" width="180" />
           <el-table-column prop="trainingMethod" label="培训方式" width="120" />
-          <el-table-column prop="courseIntroduction" label="培训课程简述" />
-          <el-table-column prop="trainingTeacher" label="培训课程讲师" />
-          <el-table-column prop="groupOfParticipantsDesc" label="计划参与人数所属分组" />
+          <!-- <el-table-column prop="courseIntroduction" label="培训课程简述" /> -->
+          <el-table-column prop="trainingTeacher" label="培训课程讲师" width="140" />
+          <el-table-column prop="groupOfParticipantsDesc" label="计划参与人数所属分组" width="200" />
           <el-table-column prop="planNumOfParticipants" label="计划参与人数" width="140" />
           <el-table-column prop="signInNum" label="签到人数" width="120" />
 
@@ -117,7 +117,7 @@
         </div>
       </el-card>
     </div>
-    <AddTrainingInformation :state="type" v-model:visible="showAddTrainingInfo" :currentId="currentTableId" />
+    <AddTrainingInformation :state="type" v-model:visible="showAddTrainingInfo" :currentId="currentTableId" @save-success="handleSearch" />
   </main>
 </template>
 
@@ -209,18 +209,19 @@
 
   const handleAdd = () => {
     type.value = 'add';
-    showAddTrainingInfo.value = true;
     currentTableId.value = String(currentId.value);
+    showAddTrainingInfo.value = true;
   };
 
   const handleEdit = (row) => {
     type.value = 'edit';
-    showAddTrainingInfo.value = true;
     currentTableId.value = row.id;
+    showAddTrainingInfo.value = true;
   };
 
   const handleView = (row) => {
     type.value = 'view';
+    currentTableId.value = row.id;
     showAddTrainingInfo.value = true;
   };