wuzh пре 1 недеља
родитељ
комит
2a86cd2861

+ 43 - 0
src/api/trainingMaterials/index.js

@@ -0,0 +1,43 @@
+import request from '@/utils/request';
+
+export async function getList(params) {
+  const res = await request.get('/ehs/trainingMaterial/page', { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+export async function getById(id) {
+  const res = await request.get(`/ehs/trainingMaterial/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+export async function save(data) {
+  const res = await request.post('/ehs/trainingMaterial/save', data);
+  if (res.data.code == 0) {
+    return res.data.data || res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+export async function update(data) {
+  const res = await request.put('/ehs/trainingMaterial/update', data);
+  if (res.data.code == 0) {
+    return res.data.data || res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+export async function remove(ids) {
+  const res = await request.delete('/ehs/trainingMaterial/delete', {
+    data: ids
+  });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 4 - 2
src/enum/dict.js

@@ -44,7 +44,8 @@ export default {
   记录规则报工类型: 'record_rules_report_work_type',
 
   试题专业类别: 'esh_professional_type',
-  试题类型: 'esh_question_type'
+  试题类型: 'esh_question_type',
+  培训资料专业类别: 'esh_material_professional_type'
 };
 //主数据分类关联对应常量
 export const treeClassifyCodeEnum = {
@@ -58,7 +59,8 @@ export const numberList = [
   'emergency_plan_type',
   'drill_type',
   'drill_level',
-  'esh_professional_type'
+  'esh_professional_type',
+  'esh_material_professional_type'
 ];
 
 //审核枚举

+ 48 - 36
src/views/safetyTraining/components/learningPreview.vue

@@ -23,8 +23,12 @@
           @node-click="handleNodeClick"
         >
           <span slot-scope="{ node, data }" class="custom-tree-node">
-            <span :class="{ 'not-learned': data.isLeaf }">{{ node.label }}</span>
-            <span v-if="data.duration" class="node-duration">{{ data.duration }}</span>
+            <span :class="{ 'not-learned': data.isLeaf }">{{
+              node.label
+            }}</span>
+            <span v-if="data.duration" class="node-duration">{{
+              data.duration
+            }}</span>
           </span>
         </el-tree>
       </div>
@@ -33,13 +37,14 @@
       <div class="right-content">
         <div class="content-title">{{ currentTitle }}</div>
         <div class="iframe-wrapper">
-          <iframe
-            v-if="currentSrc"
-            :src="currentSrc"
-            frameborder="0"
-            class="preview-iframe"
-          />
-          <div v-else class="iframe-placeholder">暂无预览地址</div>
+          <div v-if="!showOverlay" class="iframe-placeholder">
+            <fileMain
+              v-if="currentAttachment.length"
+              v-model="currentAttachment"
+              type="view"
+            />
+            <span v-else>暂无附件</span>
+          </div>
 
           <!-- 开始学习遮罩 -->
           <div v-if="showOverlay" class="start-overlay">
@@ -62,57 +67,64 @@
     data() {
       return {
         visible: false,
-        treeTitle: '安全生产法律法规(共73张PPT)',
-        currentTitle: '安全生产法律法规(共73张PPT)',
-        currentSrc: '', // iframe 地址自行调整
+        treeTitle: '',
+        currentTitle: '',
+        currentAttachment: [],
         showOverlay: true,
         defaultProps: {
           children: 'children',
           label: 'label'
         },
-        treeData: [
-          {
-            id: '1',
-            label: '安全生产法律法规(共73张PPT)',
-            children: [
-              {
-                id: '1-1',
-                label: '第1集 安全生产法律法规(共73张PPT)',
-                duration: '总时长: 6 分钟',
-                isLeaf: true
-              }
-            ]
-          }
-        ]
+        treeData: []
       };
     },
     methods: {
       open(row) {
         this.visible = true;
-        if (row && row.name) {
-          this.treeTitle = row.name;
-          this.currentTitle = row.name;
-        }
+        const resources = (row && row.trainingResourceList) || [];
+        const materialName = (row && row.materialName) || '培训资料';
+        const children = resources.map((item, index) => ({
+          id: item.id || `resource-${index}`,
+          label: item.resourceName || `培训资源${index + 1}`,
+          duration: item.suggestDuration
+            ? `总时长: ${item.suggestDuration} 分钟`
+            : '',
+          attachment: item.attachment
+            ? String(item.attachment)
+                .split(',')
+                .filter((id) => id)
+            : [],
+          isLeaf: true
+        }));
+        this.treeTitle = materialName;
+        this.currentTitle = children.length ? children[0].label : materialName;
+        this.currentAttachment = children.length ? children[0].attachment : [];
+        this.treeData = [
+          {
+            id: row.id || 'material',
+            label: materialName,
+            children
+          }
+        ];
         this.showOverlay = true;
         this.$nextTick(() => {
-          this.$refs.treeRef.setCurrentKey('1-1');
+          if (children.length) {
+            this.$refs.treeRef.setCurrentKey(children[0].id);
+          }
         });
       },
       cancel() {
         this.visible = false;
         this.showOverlay = true;
-        this.currentSrc = '';
+        this.currentAttachment = [];
       },
       handleNodeClick(data) {
         this.currentTitle = data.label;
-        // 切换左边文件时默认改成未学习状态,需要重新点击开始学习
+        this.currentAttachment = data.attachment || [];
         this.showOverlay = true;
-        this.currentSrc = '';
       },
       handleStartLearn() {
         this.showOverlay = false;
-        // TODO: 自行设置 iframe 地址
-        // this.currentSrc = 'xxx';
       }
     }
   };

+ 105 - 20
src/views/safetyTraining/trainingMaterials/add.vue

@@ -14,6 +14,7 @@
       :rules="rules"
       label-width="120px"
       class="el-form-box"
+      :disabled="isView"
     >
       <div class="section-title">资料信息</div>
 
@@ -130,7 +131,10 @@
               class="table-form-item"
               label-width="0"
             >
-              <fileMain v-model="scope.row.attachment" type="add" />
+              <fileMain
+                v-model="scope.row.attachment"
+                :type="isView ? 'view' : 'add'"
+              />
             </el-form-item>
           </template>
         </el-table-column>
@@ -150,18 +154,29 @@
     </el-form>
 
     <template v-slot:footer>
-      <el-button type="primary" @click="save()" :loading="loading"
+      <el-button
+        v-if="!isView"
+        type="primary"
+        @click="save()"
+        :loading="loading"
         >保存</el-button
       >
-      <el-button @click="cancel">取消</el-button>
+      <el-button @click="cancel">{{ isView ? '关闭' : '取消' }}</el-button>
     </template>
   </ele-modal>
 </template>
 
 <script>
   import DeptSelect from '@/components/CommomSelect/dept-select.vue';
+  import dictMixins from '@/mixins/dictMixins';
+  import {
+    getById,
+    save as saveTrainingMaterial,
+    update as updateTrainingMaterial
+  } from '@/api/trainingMaterials/index.js';
 
   const defResource = {
+    id: '',
     resourceName: '',
     duration: '',
     teacher: '',
@@ -169,6 +184,7 @@
   };
 
   const defForm = {
+    id: '',
     name: '',
     category: '',
     deptId: '',
@@ -182,18 +198,13 @@
     components: {
       DeptSelect
     },
+    mixins: [dictMixins],
     data() {
       return {
         visible: false,
         loading: false,
         dialogType: 'add',
         form: JSON.parse(JSON.stringify(defForm)),
-        categoryOptions: [
-          { label: '安全生产', value: '1' },
-          { label: '通用企业培训课件', value: '2' },
-          { label: '职业健康', value: '3' },
-          { label: '环境保护', value: '4' }
-        ],
         rules: {
           name: [
             { required: true, message: '请输入资料名称', trigger: 'blur' }
@@ -229,14 +240,35 @@
           view: '查看培训资料'
         };
         return titleMap[this.dialogType] || '培训资料';
+      },
+      isView() {
+        return this.dialogType === 'view';
+      },
+      categoryOptions() {
+        return (this.dict[this.dictEnum['培训资料专业类别']] || []).map(
+          (item) => ({
+            label: item.dictValue,
+            value: String(item.dictCode)
+          })
+        );
       }
     },
     methods: {
-      open(row, type = 'add') {
+      async open(row, type = 'add') {
         this.visible = true;
         this.dialogType = type;
+        this.requestDict('培训资料专业类别');
         if (row && row.id) {
-          this.form = JSON.parse(JSON.stringify({ ...defForm, ...row }));
+          this.loading = true;
+          try {
+            const detail = await getById(row.id);
+            this.form = this.mapDetailToForm(detail);
+          } catch (error) {
+            this.$message.error(error.message || '获取培训资料失败');
+            this.cancel();
+          } finally {
+            this.loading = false;
+          }
         } else {
           this.form = JSON.parse(JSON.stringify(defForm));
         }
@@ -252,6 +284,56 @@
       onDeptChange(deptId, node) {
         this.form.deptName = node ? node.name : '';
       },
+      attachmentToArray(attachment) {
+        if (Array.isArray(attachment)) return attachment;
+        return attachment
+          ? String(attachment)
+              .split(',')
+              .filter((item) => item)
+          : [];
+      },
+      mapDetailToForm(detail) {
+        const resources = (detail.trainingResourceList || []).map((item) => ({
+          id: item.id || '',
+          resourceName: item.resourceName || '',
+          duration: item.suggestDuration || '',
+          teacher: item.teacherName || '',
+          attachment: this.attachmentToArray(item.attachment)
+        }));
+        return {
+          id: detail.id || '',
+          name: detail.materialName || '',
+          category:
+            detail.professionalType === undefined ||
+            detail.professionalType === null
+              ? ''
+              : String(detail.professionalType),
+          deptId: detail.departmentId || '',
+          deptName: detail.departmentName || '',
+          position: detail.applicablePositions || '',
+          intro: detail.description || '',
+          resources: resources.length ? resources : [{ ...defResource }]
+        };
+      },
+      buildPayload() {
+        return {
+          id: this.form.id || undefined,
+          materialName: this.form.name,
+          professionalType:
+            this.form.category === '' ? undefined : Number(this.form.category),
+          departmentId: this.form.deptId,
+          departmentName: this.form.deptName,
+          applicablePositions: this.form.position,
+          description: this.form.intro,
+          trainingResourceList: this.form.resources.map((item) => ({
+            id: item.id || undefined,
+            resourceName: item.resourceName,
+            suggestDuration: item.duration,
+            teacherName: item.teacher,
+            attachment: (item.attachment || []).join(',')
+          }))
+        };
+      },
       handleAddResource() {
         this.form.resources.push({ ...defResource });
       },
@@ -266,19 +348,22 @@
         this.form.resources.splice(index, 1);
       },
       save() {
-        this.$refs.form.validate((valid) => {
+        this.$refs.form.validate(async (valid) => {
           if (valid) {
             this.loading = true;
-            setTimeout(() => {
-              this.loading = false;
-              const messageMap = {
-                add: '添加成功',
-                edit: '编辑成功'
-              };
-              this.$message.success(messageMap[this.dialogType] || '保存成功');
+            try {
+              const request = this.form.id
+                ? updateTrainingMaterial
+                : saveTrainingMaterial;
+              await request(this.buildPayload());
+              this.$message.success(this.form.id ? '编辑成功' : '添加成功');
               this.cancel();
               this.$emit('reload');
-            }, 500);
+            } catch (error) {
+              this.$message.error(error.message || '保存失败');
+            } finally {
+              this.loading = false;
+            }
           }
         });
       }

+ 37 - 24
src/views/safetyTraining/trainingMaterials/index.vue

@@ -70,11 +70,18 @@
   import add from './add.vue';
   import tabMixins from '@/mixins/tableColumnsMixin';
   import learningPreview from '../components/learningPreview.vue';
+  import dictMixins from '@/mixins/dictMixins';
+  import { getById, getList, remove } from '@/api/trainingMaterials/index.js';
   export default {
     components: {
-      add,learningPreview
+      add,
+      learningPreview
+    },
+    mixins: [tabMixins, dictMixins],
+
+    created() {
+      this.requestDict('培训资料专业类别');
     },
-    mixins: [tabMixins],
 
     data() {
       return {
@@ -87,33 +94,35 @@
             align: 'center'
           },
           {
-            prop: 'name',
+            prop: 'materialName',
             label: '资料名称',
             align: 'center',
             minWidth: 180,
             showOverflowTooltip: true
           },
           {
-            prop: 'categoryName',
+            prop: 'professionalType',
             label: '专业类别',
             align: 'center',
-            minWidth: 140
+            minWidth: 140,
+            formatter: (row) =>
+              this.getDictValue('培训资料专业类别', row.professionalType)
           },
           {
-            prop: 'intro',
+            prop: 'description',
             label: '资料简介',
             align: 'center',
             minWidth: 200,
             showOverflowTooltip: true
           },
           {
-            prop: 'deptName',
+            prop: 'departmentName',
             label: '所属部门',
             align: 'center',
             minWidth: 150
           },
           {
-            prop: 'position',
+            prop: 'applicablePositions',
             label: '适用岗位',
             align: 'center',
             minWidth: 140
@@ -140,27 +149,27 @@
         return [
           {
             label: '资料名称:',
-            value: 'name',
+            value: 'materialName',
             type: 'input',
             width: 220,
             placeholder: '请输入资料名称'
           },
           {
             label: '专业类别:',
-            value: 'category',
+            value: 'professionalType',
             type: 'select',
             width: 220,
             placeholder: '点击选择',
-            planList: [
-              { label: '安全生产', value: '1' },
-              { label: '通用企业培训课件', value: '2' },
-              { label: '职业健康', value: '3' },
-              { label: '环境保护', value: '4' }
-            ]
+            planList: (this.dict[this.dictEnum['培训资料专业类别']] || []).map(
+              (item) => ({
+                label: item.dictValue,
+                value: item.dictCode
+              })
+            )
           },
           {
             label: '资料简介:',
-            value: 'intro',
+            value: 'description',
             type: 'input',
             width: 220,
             placeholder: '请输入资料简介'
@@ -178,10 +187,13 @@
         this.$refs.dialogRef.open(row, 'edit');
       },
       // 预览
-      handlePreview(row) {
-        // TODO: 预览逻辑
-        this.$refs.learningPreviewRef.open()
-        console.log('预览', row);
+      async handlePreview(row) {
+        try {
+          const detail = await getById(row.id);
+          this.$refs.learningPreviewRef.open(detail);
+        } catch (error) {
+          this.$message.error(error.message || '获取培训资料失败');
+        }
       },
       // 删除
       handleDelete(row) {
@@ -190,8 +202,9 @@
           cancelButtonText: '取消',
           type: 'warning'
         })
-          .then(() => {
-            this.$message.success('删除成功');
+          .then(() => remove([row.id]))
+          .then((message) => {
+            this.$message.success(message || '删除成功');
             this.$refs.table.reload();
           })
           .catch(() => {});
@@ -208,7 +221,7 @@
           pageNum: page,
           size: limit
         };
-        return [{}];
+        return getList(body);
       }
     }
   };