Browse Source

feat: 添加特种设备导出功能,优化培训小结上传逻辑,修复相关接口调用

sunqijun 2 months ago
parent
commit
1172778f15

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

@@ -99,7 +99,7 @@ export function queryTrainingTableData(data: QueryPageRequest<TableParamsType>)
  */
 export function updateEducationTrainingPlanCourseSummary(data: any) {
   return http.request({
-    url: '/educationTrainingPlanIssuance/updateEducationTrainingPlanCourseSummary',
+    url: '/educationTrainingPlanIssuance/updateEducationTrainingPlanIssueSummary',
     method: 'put',
     data,
   });

+ 16 - 0
src/api/production-safety/special-equipment.ts

@@ -103,3 +103,19 @@ export function deleteSpecialEquipment(id: number) {
   });
 }
 
+
+
+/**
+ * 导出特种设备设施
+ */
+export function exportSpecialEquipment(query: QueryPageRequest<SpecialEquipmentQueryParam>) {
+  return http.request({
+    url: '/specialEquipment/exportInventory',
+    method: 'post',
+    data: query,
+    // params: classifyName ? { classifyName } : undefined,
+    responseType: 'blob',
+  }, {
+    isTransformResponse: false,
+  });
+}

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

@@ -156,7 +156,7 @@
         </el-form-item>
         
       </section>
-      <section v-if="detailStatusOrder>=2">
+      <section v-if="detailStatusOrder>=2&&!isEditMode">
         <el-form-item label="整改完成情况:" prop="rectificationCompletionStatus">
           <el-input
             v-model="ruleFormData.rectificationCompletionStatus"

+ 13 - 12
src/views/production-safety/implement-safety-duty/non-public-area-responsibilities/list.vue

@@ -18,6 +18,7 @@
           "
           >添加
         </el-button>
+        <el-button @click="batchImportVisible = true">导入</el-button>
       </div>
       <div class="search-form">
         <el-form :inline="true">
@@ -33,11 +34,9 @@
         </el-form>
 
         <div>
-          <!-- <el-button @click="batchImportVisible = true">导入</el-button>
-          <el-button type="primary" @click="handleDownload">导出</el-button> -->
-
           <el-button type="primary" @click="queryTableList">查询</el-button>
           <el-button @click="handleRestParams">重置</el-button>
+          <el-button @click="handleDownload">导出</el-button>
         </div>
       </div>
 
@@ -142,7 +141,8 @@
     :visible="batchImportVisible"
     :importApiUrl="importApiUrl"
     :templateUrl="templateUrl"
-    :templateName="'责任清单-批量导入模版'"
+    :show-template="false"
+    :templateName="'责任区域-批量导入模版'"
     @close="() => (batchImportVisible = false)"
     @update="handleUpdate"
   />
@@ -163,6 +163,7 @@
   import { downloadFile } from '@/views/disaster/utils';
   import { useGlobSetting } from '@/hooks/setting';
   import { useUserInfoHook } from '@/views/disaster/hooks';
+  import { downloadByData } from '@/utils/file/download';
 
   const router = useRouter();
   const activeTab = ref(2);
@@ -183,7 +184,7 @@
   const batchImportVisible = ref(false);
   const { urlPrefix } = useGlobSetting();
   const importApiUrl = ref(urlJoin(urlPrefix, '/areaCheckList/importArea?type=2'));
-  const templateUrl = ref('');
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-law-regulation-template.xlsx');
 
   const handleUpdate = () => {
     batchImportVisible.value = false;
@@ -207,15 +208,15 @@
   };
 
   async function handleDownload() {
-    // getQuery();
     try {
-      const res = await areaCheckListExportArea(queryParams.queryParam);
-      if (res.size === 0) return;
-      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
-      const url = window.URL.createObjectURL(blob);
-      downloadFile(url, '责任清单.xlsx');
+      const response = await areaCheckListExportArea(queryParams.queryParam);
+        if (response) {
+            const fileName = `责任区域(非区域执行人)_${new Date().toISOString().split('T')[0]}.xlsx`;
+            downloadByData(response, fileName);
+            ElMessage.success('导出成功');
+        }
     } catch (e) {
-      ElMessage.error('下载失败');
+      ElMessage.error('导出失败');
       console.log(e);
     }
   }

+ 18 - 10
src/views/production-safety/implement-safety-duty/public-area-responsibilities/list.vue

@@ -18,6 +18,9 @@
           "
           >添加
         </el-button>
+        <el-button plain  @click="batchImportVisible = true">
+            导入
+        </el-button>
       </div>
       <div class="search-form">
         <el-form :inline="true">
@@ -38,6 +41,9 @@
 
           <el-button type="primary" @click="queryTableList">查询</el-button>
           <el-button @click="handleRestParams">重置</el-button>
+          <el-button plain  @click="handleDownload">
+            导出
+          </el-button>
         </div>
       </div>
 
@@ -139,7 +145,8 @@
     :visible="batchImportVisible"
     :importApiUrl="importApiUrl"
     :templateUrl="templateUrl"
-    :templateName="'责任清单-批量导入模版'"
+    :templateName="'责任区域-批量导入模版'"
+    :show-template="false"
     @close="() => (batchImportVisible = false)"
     @update="handleUpdate"
   />
@@ -160,6 +167,7 @@
   import { downloadFile } from '@/views/disaster/utils';
   import { useGlobSetting } from '@/hooks/setting';
   import { useUserInfoHook } from '@/views/disaster/hooks';
+  import { downloadByData } from '@/utils/file/download';
 
   const router = useRouter();
   const activeTab = ref(1);
@@ -179,8 +187,8 @@
   // 批量导入
   const batchImportVisible = ref(false);
   const { urlPrefix } = useGlobSetting();
-  const importApiUrl = ref(urlJoin(urlPrefix, '/areaCheckList/importArea?type=2'));
-  const templateUrl = ref('');
+  const importApiUrl = ref(urlJoin(urlPrefix, '/areaCheckList/importArea?type=1'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-law-regulation-template.xlsx');
 
   const handleUpdate = () => {
     batchImportVisible.value = false;
@@ -204,15 +212,15 @@
   };
 
   async function handleDownload() {
-    // getQuery();
     try {
-      const res = await areaCheckListExportArea(queryParams.queryParam);
-      if (res.size === 0) return;
-      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
-      const url = window.URL.createObjectURL(blob);
-      downloadFile(url, '责任清单.xlsx');
+      const response = await areaCheckListExportArea(queryParams.queryParam);
+        if (response) {
+            const fileName = `责任区域_${new Date().toISOString().split('T')[0]}.xlsx`;
+            downloadByData(response, fileName);
+            ElMessage.success('导出成功');
+        }
     } catch (e) {
-      ElMessage.error('下载失败');
+      ElMessage.error('导出失败');
       console.log(e);
     }
   }

+ 9 - 9
src/views/production-safety/implement-safety-duty/public-list-responsibilities/list.vue

@@ -18,6 +18,7 @@
           "
           >添加
         </el-button>
+        <el-button @click="batchImportVisible = true">导入</el-button>
       </div>
       <div class="search-form">
         <el-form :inline="true">
@@ -56,11 +57,9 @@
         </el-form>
 
         <div>
-          <!-- <el-button @click="batchImportVisible = true">导入</el-button>
-          <el-button type="primary" @click="handleDownload">导出</el-button> -->
-
           <el-button type="primary" @click="queryTableList">查询</el-button>
           <el-button @click="handleRestParams">重置</el-button>
+          <el-button @click="handleDownload">导出</el-button>
         </div>
       </div>
 
@@ -189,6 +188,7 @@
   import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
   import { getAllDepartments } from '@/api/auth/dept';
   import { useUserInfoHook } from '@/views/disaster/hooks';
+  import { downloadByData } from '@/utils/file/download';
   const { id } = useUserInfoHook();
 
   const router = useRouter();
@@ -253,13 +253,13 @@
   };
 
   async function handleDownload() {
-    // getQuery();
     try {
-      const res = await areaCheckListExportArea(queryParams.queryParam);
-      if (res.size === 0) return;
-      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
-      const url = window.URL.createObjectURL(blob);
-      downloadFile(url, '责任清单.xlsx');
+      const response = await areaCheckListExportArea(queryParams.queryParam);
+        if (response) {
+            const fileName = `责任清单_${new Date().toISOString().split('T')[0]}.xlsx`;
+            downloadByData(response, fileName);
+            ElMessage.success('导出成功');
+        }
     } catch (e) {
       ElMessage.error('下载失败');
       console.log(e);

+ 48 - 1
src/views/production-safety/risk-identification-and-control/special-equipment-manage/list.vue

@@ -12,7 +12,10 @@
               class="search-table-container--button"
               @click="$router.push({ name: 'specialEquipmentManageAdd' })"
             >
-              添加
+              添加 
+            </el-button>
+            <el-button plain class="search-table-container--button" @click="handleImport">
+              导入
             </el-button>
           </div>
 
@@ -113,6 +116,9 @@
             <section class="search-btn">
               <el-button type="primary" @click="queryTableList">查询</el-button>
               <el-button @click="handleRestParams">重置</el-button>
+              <el-button plain class="search-table-container--button" @click="handleDownload">
+               导出
+              </el-button>
             </section>
           </div>
         </header>
@@ -151,6 +157,16 @@
         </div>
       </div>
     </main>
+    <BatchImport
+      v-if="batchImportVisible"
+      :visible="batchImportVisible"
+      :import-api-url="importApiUrl"
+      :template-url="templateUrl"
+      template-name="下载模板"
+      :show-template="false"
+      @close="batchImportVisible = false"
+      @update="handleUpdate"
+    />
   </div>
 </template>
 
@@ -161,16 +177,21 @@
   import {
     querySpecialEquipmentPage,
     deleteSpecialEquipment,
+    exportSpecialEquipment,
     type SpecialEquipment,
     type SpecialEquipmentQueryParam,
   } from '@/api/production-safety/special-equipment';
   import { getAllDepartments } from '@/api/auth/dept';
   import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
   import BasicTable from '@/components/BasicTable.vue';
+  import BatchImport from '@/components/batch-import/BatchImport.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
   import ActionButton from '@/components/ActionButton.vue';
   import { TABLE_OPTIONS, SPECIAL_EQUIPMENT_TABLE_COLUMNS } from './configs/tables';
   import { DEVICE_CATEGORY_OPTIONS, DEVICE_TYPE_OPTIONS } from './configs/form';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
+  import { downloadByData } from '@/utils/file/download';
 
   const loading = ref(false);
 
@@ -265,6 +286,32 @@
         tableConfig.loading = false;
       });
   };
+// 导出相关
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/specialEquipment/importInventory'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-law-regulation-template.xlsx');
+
+  const handleImport = () => {
+    batchImportVisible.value = true;
+  };
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    queryTableList();
+  };
+  const handleDownload = async () => {
+    try {
+      const response = await exportSpecialEquipment(queryParams);
+      if (response) {
+        const fileName = `特种设备设施管理_${new Date().toISOString().split('T')[0]}.xlsx`;
+        downloadByData(response, fileName);
+        ElMessage.success('导出成功');
+      }
+    } catch (e) {
+      console.error('导出特种设备设施管理失败:', e);
+      ElMessage.error('导出失败,请重试');
+    }
+  };
 
   const handleDelete = (row: SpecialEquipment) => {
     if (!row.id) return;

+ 150 - 119
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/components/addTrainingInformation.vue

@@ -1,6 +1,6 @@
 <script lang="ts" setup>
   import { saveTrainingInformation, updateTrainingInformation, queryEducationTrainingPlanCourseDetail } from '@/api/production-education-training-plan-dept';
-  import { ref, reactive, onMounted, watch, shallowRef, computed } from 'vue';
+  import { ref, reactive, onMounted, watch, shallowRef, computed, nextTick } from 'vue';
   import { UploadFilled, Plus, Delete, Download, ZoomIn } from '@element-plus/icons-vue';
   import { TRAINING_FORM_RULES } from '../configs/form';
   import { queryUserGroupPage } from '@/api/system/person-group';
@@ -10,9 +10,10 @@
   // @ts-ignore: missing type declarations for CSS side-effect import
   import '@wangeditor/editor/dist/css/style.css';
   import { ElMessage, UploadRawFile } from 'element-plus';
+  import { unformatAttachment, formatAttachmentList } from '@/components/UploadFiles/utils';
+  import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
 
   import { debounce } from 'lodash-es';
-
   const props = defineProps({
     state: {
       type: String,
@@ -27,14 +28,15 @@
       default: null,
     },
   });
-  const isViewMode = ref(props.state === 'view');
-  const isEditMode = ref(props.state === 'edit');
-  const isCreateMode = ref(props.state === 'add');
+  
+  const isViewMode = computed(() => props.state === 'view');
+  const isEditMode = computed(() => props.state === 'edit');
+  const isCreateMode = computed(() => props.state === 'add');
 
   const emit = defineEmits(['update:visible', 'refreshList', 'saveSuccess']);
   const formRef = ref();
   const rules = ref(TRAINING_FORM_RULES);
-
+ 
   const form = reactive({
     courseType: '',
     courseName: '',
@@ -58,8 +60,35 @@
     { label: '特种作业培训考核', value: 3 },
     { label: '特种作业复训考核', value: 4 },
   ]);
+
+   // 上传文件
+ const formatAttachment = async (data: any) => {
+    if (!data) return data;
+    const uuid = Math.random().toString(36).substring(2, 9);
+    const timestamp = Date.now().toString();
+    const random = Math.random().toString(36).substring(2, 4);
+    const fileName = data.name;
+    const res = await uploadFileApi({
+        bizType: UPLOAD_BIZ_TYPE.ATTACHMENT,
+        fileName: `${uuid}-${timestamp}-${random}`,
+        file: data,
+    });
+    const fileType = data.fileType;
+    const fileSize = data.fileSize;
+    const fileId = data.fileId;
+    const fileUrl = res.url;
+    return {
+        fileName,
+        fileType,
+        fileSize,
+        fileUrl,
+        fileId,
+    };
+};
+const openState = ref(false)
   // 打开抽屉时的事件
   const openDrawerFn = async () => {
+    await nextTick();
     if ((props.state === 'edit' || props.state === 'view') && props.currentId) {
       // 编辑/查看模式,加载现有详情数据
       // 这里可以调用接口获取当前培训课程的信息,并填充到 form 中
@@ -76,10 +105,16 @@
             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) || [];
+        // 编辑和查看时 没有回显
+        if(res.courseImg){
+            form.courseImg = [JSON.parse(res.courseImg)]
+        }
+        if(res.courseContent){
+            // handleFileExceed(JSON.parse(res.courseContent))
+            // fileList.value =  JSON.parse(res.courseContent) || [];
+            form.courseContent = res.courseContent
+        }
       });
     } else if (props.state === 'add') {
         // 新增模式,重置表单
@@ -98,58 +133,13 @@
           isSign: 0,
           courseImg: [] as FileItem[],
         });
+        courseContentUpload.value?.clearFiles();
+        courseImgRef.value?.clearFiles();
     }
   };
-  const handleValidate = async () => {
-    if (!formRef.value) return;
-    try {
-        await formRef.value.validate();
-        return true;
-    } catch {
-        return false;
-    }
-  };
-  const handleSave = debounce(async () => {
-    const res = await handleValidate();
-    if (!res) return;
-    try {
-      const basePayload = {
-        ...form,
-        petpiId: props.currentId,
-        courseImg: form.courseImg[0],
-      };
-      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('保存失败,请重试');
-    }
-    emit('update:visible', false);
-  }, 1000);
-  // const handleSave = () => {
-
-  //     // 简单验证,可以根据需要添加更多验证逻辑
-  //     if (!form.courseName || !form.courseType) {
-  //         ElMessage.error('请填写完整信息');
-  //         return;
-  //     }
-  //     emit('refreshList', { ...form }); // 传递表单数据,可以根据实际需求调整
-  //     emit('update:visible', false);
-  // };
   // 富文本编辑器
   const editorRef = shallowRef();
   const editorConfig = computed(() => ({
@@ -157,46 +147,36 @@
     MENU_CONF: {},
   }));
 
-  const beforeUpload = (file) => {
-    const isAllowedType = [
-      'application/rar',
-      'application/zip',
-      'application/msword',
-      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-      'application/pdf',
-      'video/mp4',
-    ].includes(file.type);
-    const isLt20M = file.size / 1024 / 1024 < 20;
-
-    if (!isAllowedType) {
-      ElMessage.error('上传文件格式不正确!');
-    }
-    if (!isLt20M) {
-      ElMessage.error('上传文件大小不能超过20MB!');
-    }
-    return isAllowedType && isLt20M;
+  const handleEditorChange = () => {
+    // 编辑器内容变化时的处理
   };
-
   const handleEditorCreated = (editor: any) => {
     editorRef.value = editor;
   };
 
-  const handleEditorChange = () => {
-    // 编辑器内容变化时的处理
-  };
-  const handleImageUploadChange = (response: any, file: any) => {
-    form.courseImg = [response];
+  //  上传图片
+  const handleImageUploadChange = async (uploadFile: any, uploadFiles: any) => {
+    form.courseImg = uploadFiles;
+    if(uploadFile.raw){
+        let res = await formatAttachment(uploadFile.raw)
+        console.log(res, '图片上传')
+        if(res){
+            ElMessage.success('上传成功')
+        }
+    }
   };
-
+  // 替换图片
   const courseImgRef = ref();
-  const handleImageExceed = (files: any[]) => {
-    console.log(files);
-    courseImgRef.value!.clearFiles(); // 清空文件列表
+  const handleImageExceed = (files) => {
+    courseImgRef.value!.clearFiles(); 
     const file = files[0] as UploadRawFile;
-    courseImgRef.value!.handleStart(file); // 手动触发上传
-    form.courseImg = files[0];
+    console.log(file)
+    if (!validateImage(file)) {
+      return;
+    }
+    courseImgRef.value!.handleStart(file); 
   };
-
+// 图片预览
   const dialogVisible = ref(false);
   const dialogImageUrl = ref('');
   const handlePictureCardPreview = (file: any) => {
@@ -204,11 +184,7 @@
     dialogVisible.value = true;
   };
 
-  const handleImageRemove = (file: any) => {
-    console.log('图片被移除:', file, fileList.value);
-    // console.log('图片被移除:', file, fileList.value);
-    // form.courseImg = fileList.value.filter((item) => item.uid !== file.uid);
-  };
+// 图片格式校验
   const validateImage = (file) => {
     const validMIME = [
       'image/jpeg',
@@ -243,6 +219,29 @@
     return true; // 验证通过
   };
 
+  // 验证文件类型
+    const allowedTypes = [
+      'application/rar',
+      'application/zip',
+      'application/msword',
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'video/mp4',
+    ];
+
+  const beforeUpload = (file) => {
+    const isAllowedType = allowedTypes.includes(file.type);
+    const isLt20M = file.size / 1024 / 1024 < 20;
+
+    if (!isAllowedType) {
+      ElMessage.error('上传文件格式不正确!');
+    }
+    if (!isLt20M) {
+      ElMessage.error('上传文件大小不能超过20MB!');
+    }
+
+    return isAllowedType && isLt20M;
+  };
   // 文件选择更新
   const courseContentUpload = ref();
   const handleFileExceed = (files) => {
@@ -253,17 +252,11 @@
     }
     courseContentUpload.value!.handleStart(file); // 手动触发上传
   };
+
+
   // 课程内容文件上传
-  const handleFileChange = (file, fileList) => {
-    // 1. 验证文件类型和大小
-    const allowedTypes = [
-      'application/rar',
-      'application/zip',
-      'application/msword',
-      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-      'application/pdf',
-      'video/mp4',
-    ];
+  const handleFileChange = async (file, fileList) => {
+    
 
     if (!allowedTypes.includes(file.raw.type)) {
       ElMessage.error('不支持的文件格式');
@@ -274,24 +267,60 @@
       ElMessage.error('文件大小不能超过20MB');
       return;
     }
-    console.log(file, fileList);
-    // 2. 关键修改:强制覆盖旧文件
-    fileList.value = [file.raw]; // 直接替换为新文件
+    
+    if(file.raw){
+        let res = await formatAttachment(file.raw)
+        console.log(res, '文件上传')
+        if(res){
+            ElMessage.success('上传成功')
+        }
+    }
+    // 直接替换为新文件
+    fileList.value = [file.raw]; 
     form.courseContent = file; // 更新表单数据
   };
 
-  const handleUploadSignsUploadSuccess = async (item: any, fileList) => {
-    // const attachment = await formatAttachmentList(fileList);
-    // item.attachment = attachment;
-    // form.courseImg = [...signRecords.value.level1, ...signRecords.value.level2, ...signRecords.value.level3];
+  // 提交保存/编辑
+  const handleValidate = async () => {
+    if (!formRef.value) return;
+    try {
+        await formRef.value.validate();
+        return true;
+    } catch {
+        return false;
+    }
   };
+  const handleSave = debounce(async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      const basePayload = {
+        ...form,
+        petpiId: props.currentId,
+        courseImg: form.courseImg[0],
+      };
+      console.log('提交的表单数据:', basePayload);
+      
+      if (isCreateMode.value) {
+        // 新增,创建接口
+        await saveTrainingInformation(basePayload);
+        ElMessage.success('创建成功');
 
-  // 文件上传
-  const handleUploadSuccess = (files: FileItem[]) => {
-    console.log('上传成功的文件列表:', files);
-    form.courseImg = files;
-  };
-  
+      } else if (isEditMode.value && props.currentId) {
+        // 编辑,更新接口
+        await updateTrainingInformation({
+          id: props.currentId,
+          ...basePayload,
+        });
+        ElMessage.success('保存成功');
+
+      }
+      emit('saveSuccess');
+    } catch (e) {
+      ElMessage.error('保存失败,请重试');
+      emit('update:visible', false);
+    }
+  }, 1000);
 
   const groupList = ref<any>([]);
   // 获取级联部门分组数据
@@ -381,7 +410,8 @@
             <Editor
               style="height: 400px; overflow-y: auto"
               v-model="form.courseIntroduction"
-              mode="default"
+              mode="disabled"
+              :disabled="isViewMode"
               :defaultConfig="editorConfig"
               @on-created="handleEditorCreated"
               @on-change="handleEditorChange"
@@ -394,6 +424,7 @@
             action=""
             ref="courseContentUpload"
             :auto-upload="false"
+            :disabled="isViewMode"
             :on-change="handleFileChange"
             accept=".rar, .zip, .doc, .docx, .pdf, .mp4"
             :file-list="fileList"
@@ -439,7 +470,7 @@
             </el-icon>
 
             <template #tip>
-              <div class="el-upload__tip"> 支持格式:.jpg .png .jpeg,单个文件不能超过300k,设置一个默认图片。 </div>
+              <div class="el-upload__tip"> 支持格式:.jpg .png .jpeg,单个文件不能超过300k,设置一个默认图片。</div>
             </template>
             <template #file="{ file }">
               <div>
@@ -453,7 +484,7 @@
             </template>
           </el-upload>
           <el-dialog v-model="dialogVisible">
-            <img w-full :src="dialogImageUrl" alt="Preview Image" />
+            <img w-full :src="dialogImageUrl" alt="Preview Image" style="width: 100%;" />
           </el-dialog>
         </el-form-item>
       </el-form>

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

@@ -86,7 +86,7 @@
         <el-table :data="tableData" border stripe>
           <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="trainingDate" width="230" label="培训时间" />
           <el-table-column prop="courseTypeName" label="课程所属分类" width="180" />
           <el-table-column prop="trainingMethod" label="培训方式" width="120" />
           <!-- <el-table-column prop="courseIntroduction" label="培训课程简述" /> -->
@@ -100,7 +100,6 @@
               <el-button link type="primary" @click="handleEdit(row)"> 编辑 </el-button>
               <el-button link type="danger" @click="handleDelete(row)"> 删除 </el-button>
               <el-button link @click="handleView(row)"> 查看 </el-button>
-              <el-button link @click="handleSummary(row)"> 小结 </el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -129,7 +128,6 @@
     queryTrainingTableData,
     queryEducationAndTrainingProgramDetail,
     deleteTrainingInformation,
-    updateEducationTrainingPlanCourseSummary,
     type TableParamsType,
   } from '@/api/production-education-training-plan-dept';
   import AddTrainingInformation from './addTrainingInformation.vue';
@@ -225,16 +223,6 @@
     showAddTrainingInfo.value = true;
   };
 
-  const handleSummary = async (row) => {
-    try {
-      await updateEducationTrainingPlanCourseSummary({ id: row.id, summary: row.summary });
-      ElMessage.success('更新小结成功');
-      getTableList();
-    } catch (e) {
-      ElMessage.error('更新小结失败');
-    }
-  };
-
   const handleDelete = (row) => {
     ElMessageBox.confirm(`确认删除课程「${row.courseName}」吗?`, '提示', { type: 'warning' }).then(async () => {
       await deleteTrainingInformation(row.id);

+ 149 - 2
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDept.vue

@@ -80,6 +80,7 @@
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
                 <ActionButton text="查看" @click="handleView(scope.row.id)" />
+                <el-button link v-if="scope.row.status===2" @click="handleSummary(scope.row)"> 小结 </el-button>
               </div>
             </template>
           </BasicTable>
@@ -96,6 +97,43 @@
       @close="batchImportVisible = false"
       @update="handleUpdate"
     />
+    <el-dialog v-model="dialogVisible" title="填写培训小结">
+        <el-form>
+            <el-form-item label="材料上传" required>
+                <el-upload
+                    action=""
+                    ref="courseContentUpload"
+                    :auto-upload="false"
+                    :on-change="handleFileChange"
+                    accept=".rar, .zip, .doc, .docx, .pdf, .mp4"
+                    :file-list="fileList"
+                    :limit="1"
+                    :on-exceed="handleFileExceed"
+                >
+                    <el-button type="default">
+                    <el-icon style="margin-right: 6px">
+                        <UploadFilled />
+                    </el-icon>
+                    选择附件
+                    </el-button>
+                    <template #tip>
+                    <div class="el-upload__tip"> 支持格式:.rar .zip .doc .docx .pdf .mp4,单个文件不能超过20MB </div>
+                    </template>
+                </el-upload>
+            </el-form-item>
+            <el-form-item label="培训小结" required>
+                <el-input type="textarea" v-model="form.trainingSummary"></el-input>
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button @click="handleCancel">取消</el-button>
+                <el-button type="primary" @click="saveSummary" >
+                    确认
+                </el-button>
+            </div>
+        </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -108,11 +146,14 @@
   import { TABLE_OPTIONS, TABLE_COLUMNS, STATUS_OPTIONS } from './configs/tables';
   import { useRouter } from 'vue-router';
   import type { QueryPageRequest } from '@/types/basic-query';
-  import { getEducationAndTrainingProgramList } from '@/api/production-education-training-plan-dept';
+  import { getEducationAndTrainingProgramList, updateEducationTrainingPlanCourseSummary, } from '@/api/production-education-training-plan-dept';
   import { downloadByData } from '@/utils/file/download';
   import BatchImport from '@/components/batch-import/BatchImport.vue';
   import { useGlobSetting } from '@/hooks/setting';
   import urlJoin from 'url-join';
+  import { UploadFilled, Plus, Delete, Download, ZoomIn } from '@element-plus/icons-vue';
+  import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
+
   const router = useRouter();
 
   // 表格
@@ -132,6 +173,15 @@
     { label: '生产作业安全培训', value: '生产作业安全培训' },
     { label: '安全管理人员培训', value: '安全管理人员培训' },
   ]);
+    // 验证文件类型
+    const allowedTypes = [
+      'application/rar',
+      'application/zip',
+      'application/msword',
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'video/mp4',
+    ];
 
   const tableQuery = reactive<QueryPageRequest<any>>({
     pageNumber: pagination.pageNumber,
@@ -144,7 +194,102 @@
       endDate: '',
     },
   });
+  const fileList = ref([])
+  const dialogVisible = ref(false)
+  const handleCancel = ()=>{
+    dialogVisible.value = false
+  }
+  const form = reactive({
+    id:'',
+    uploadAttach: '',
+    trainingSummary: ''
+  })
+  const beforeUpload = (file) => {
+    const isAllowedType = allowedTypes.includes(file.type);
+    const isLt20M = file.size / 1024 / 1024 < 20;
+
+    if (!isAllowedType) {
+      ElMessage.error('上传文件格式不正确!');
+    }
+    if (!isLt20M) {
+      ElMessage.error('上传文件大小不能超过20MB!');
+    }
 
+    return isAllowedType && isLt20M;
+  };
+     // 上传文件
+ const formatAttachment = async (data: any) => {
+    if (!data) return data;
+    const uuid = Math.random().toString(36).substring(2, 9);
+    const timestamp = Date.now().toString();
+    const random = Math.random().toString(36).substring(2, 4);
+    const fileName = data.name;
+    const res = await uploadFileApi({
+        bizType: UPLOAD_BIZ_TYPE.ATTACHMENT,
+        fileName: `${uuid}-${timestamp}-${random}`,
+        file: data,
+    });
+    const fileType = data.fileType;
+    const fileSize = data.fileSize;
+    const fileId = data.fileId;
+    const fileUrl = res.url;
+    return {
+        fileName,
+        fileType,
+        fileSize,
+        fileUrl,
+        fileId,
+    };
+};
+    // 文件选择更新
+  const courseContentUpload = ref();
+  const handleFileExceed = (files) => {
+    courseContentUpload.value!.clearFiles(); // 清空文件列表
+    const file = files[0];
+    if (!beforeUpload(file)) {
+      return;
+    }
+    courseContentUpload.value!.handleStart(file); // 手动触发上传
+  };
+    // 课程内容文件上传
+  const handleFileChange = async (file, fileList) => {
+
+    if (!allowedTypes.includes(file.raw.type)) {
+      ElMessage.error('不支持的文件格式');
+      return;
+    }
+
+    if (file.raw.size > 20 * 1024 * 1024) {
+      ElMessage.error('文件大小不能超过20MB');
+      return;
+    }
+    
+    if(file.raw){
+        let res = await formatAttachment(file.raw)
+        console.log(res, '文件上传')
+        // form.uploadAttach = 
+        
+        if(res){
+            ElMessage.success('上传成功')
+        }
+    }
+    // 直接替换为新文件
+    fileList.value = [file.raw]; 
+    form.uploadAttach = file; // 更新表单数据
+  };
+  const saveSummary = async()=>{
+    try {
+      await updateEducationTrainingPlanCourseSummary(form);
+      ElMessage.success('更新小结成功');
+      getTableData();
+    } catch (e) {
+      ElMessage.error('更新小结失败');
+    }
+  }
+   const handleSummary = async (row) => {
+    dialogVisible.value = true
+    form.id = row.id
+  };
   const handleSizeChange = (value: number) => {
     pagination.pageSize = value;
     tableQuery.pageSize = value;
@@ -169,7 +314,7 @@
         pagination.total = res.totalRow;
       }
     } catch (e) {
-      console.error('获取物品库存列表失败:', e);
+      console.error('获取列表失败:', e);
       tableData.value = [];
       pagination.total = 0;
     } finally {
@@ -200,6 +345,8 @@
     handleSearch();
   };
 
+
+
   // 批量导入
   const batchImportVisible = ref(false);
   const { urlPrefix } = useGlobSetting();