Forráskód Böngészése

Merge branch 'feat/production-safety' of http://192.168.6.110/product-group-fe/sfy-safety-group/sfy-safety into feat/production-safety

sunqijun 2 hónapja
szülő
commit
1d8a4df6d4
23 módosított fájl, 484 hozzáadás és 104 törlés
  1. 27 1
      src/api/drawLessons/index.ts
  2. 1 0
      src/api/production-safety/special-equipment.ts
  3. 38 1
      src/api/safety-culture/index.ts
  4. 21 0
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/configs/tables.ts
  5. 8 8
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/oneByOneManagement.vue
  6. 10 8
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/components/oneByOneManagementDeptDetail.vue
  7. 1 1
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/configs/tables.ts
  8. 20 4
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/oneByOneManagementDept.vue
  9. 1 1
      src/views/production-safety/productionSafetySystem/lawManagement/lawManagement.vue
  10. 2 2
      src/views/production-safety/risk-identification-and-control/labor-products-manage/list.vue
  11. 14 14
      src/views/production-safety/risk-identification-and-control/special-equipment-manage/configs/form.ts
  12. 4 3
      src/views/production-safety/risk-identification-and-control/special-equipment-manage/edit.vue
  13. 12 11
      src/views/production-safety/safety-culture/accidentCaseManagement/accidentCaseManagement.vue
  14. 94 6
      src/views/production-safety/safety-culture/accidentCaseManagement/components/accidentCaseManagementDetail.vue
  15. 7 0
      src/views/production-safety/safety-culture/accidentCaseManagement/configs/form.ts
  16. 7 7
      src/views/production-safety/safety-culture/accidentCaseManagement/configs/tables.ts
  17. 10 10
      src/views/production-safety/safety-culture/safetyCultureActivityManagement/components/activityRegistrationManagement.vue
  18. 6 6
      src/views/production-safety/safety-culture/safetyCultureActivityManagement/configs/activityTargetTables.ts
  19. 13 13
      src/views/production-safety/safety-culture/safetyCultureActivityManagement/configs/tables.ts
  20. 185 6
      src/views/production-safety/safety-culture/safetyCultureActivityManagement/safetyCultureActivityManagement.vue
  21. 1 0
      src/views/production-safety/safety-culture/safetyCultureMaterialManagement/components/safetyCultureMaterialManagementDetail.vue
  22. 1 1
      src/views/production-safety/safety-culture/safetyCultureMaterialManagement/configs/form.ts
  23. 1 1
      src/views/production-safety/safety-culture/safetyCultureMaterialManagement/safetyCultureMaterialManagement.vue

+ 27 - 1
src/api/drawLessons/index.ts

@@ -64,6 +64,20 @@ export function queryDrawLessonsAdminPage(
   });
 }
 
+/**
+ * 2.1 分页查询举一反三
+ * POST /api/drawLessons/admin/queryPage
+ */
+export function queryDrawLessonsAdminDeptPage(
+  query: QueryPageRequest<DrawLessonsQueryParam>,
+) {
+  return http.request<QueryPageResponse<DrawLessonsItem>>({
+    url: '/drawLessons/dept/queryPage',
+    method: 'post',
+    data: query,
+  });
+}
+
 /**
  * 2.2 查询举一反三详情
  * GET /api/drawLessons/admin/queryDetail?id=xxx
@@ -76,6 +90,18 @@ export function getDrawLessonsAdminDetail(id: number) {
   });
 }
 
+/**
+ * 2.2 查询举一反三详情
+ * GET /api/drawLessons/admin/queryDetail?id=xxx
+ */
+export function getDrawLessonsAdminDeptDetail(id: number) {
+  return http.request<DrawLessonsItem>({
+    url: '/drawLessons/dept/queryDetail',
+    method: 'get',
+    params: { id },
+  });
+}
+
 /**
  * 2.3 新增举一反三
  * POST /api/drawLessons/admin/save
@@ -154,7 +180,7 @@ export interface IssueDrawLessonsRequest {
   /** 计划开始日期(下发弹窗) */
   planStartDate?: string;
   /** 计划结束时间(下发弹窗) */
-  planEndTime?: string;
+  planEndDate?: string;
 }
 
 export function issueDrawLessons(data: IssueDrawLessonsRequest) {

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

@@ -54,6 +54,7 @@ export interface SpecialEquipment {
     url: string;
   }[]; // 附件列表
   [key: string]: any;
+  useDepartmentPath?: any; // 使用部门路径(部门ID列表)
 }
 
 /** 分页查询特种设备设施列表 */

+ 38 - 1
src/api/safety-culture/index.ts

@@ -54,7 +54,7 @@ export interface safetyCultureFilePageQuery {
  * 导出参数
  */
 export interface safetyCultureFileExportQuery extends safetyCultureFileQuery { }
-
+export interface ProductionSafetyFileExportQuery extends safetyCultureFileExportQuery { }
 
 /**
  * 新增事故案例参数
@@ -108,6 +108,15 @@ export interface ActivityRegistrationTargetItem {
   subtractScore: number; // 减分项分数
 }
 
+
+export interface IssueHiddenDangerRequest {
+  id: number;
+  specificDeptId: string;
+  specificPersonId: string;
+  startTime: string;
+  endTime: string;
+}
+
 // =============================== 事故案例管理 ============================
 
 /**
@@ -453,3 +462,31 @@ export function deleteSafetyPublicityBoardPage(id: number) {
     },
   });
 }
+
+/**
+ * 导出事故案例管理文件
+ */
+export function exportAccidentCaseManagementFile(params?: ProductionSafetyFileExportQuery, classifyName?: string) {
+  return http.request({
+    url: '/accidentCase/export',
+    method: 'post',
+    data: params,
+    params: classifyName ? { classifyName } : undefined,
+    responseType: 'blob',
+  }, {
+    isTransformResponse: false,
+  });
+}
+
+/**
+ * 安全文化活动下发
+ * PUT /api/safetyCulture/activity/assign
+ */
+
+export function activityDistribution(data: IssueHiddenDangerRequest) {
+  return http.request({
+    url: '/safetyCulture/activity/assign',
+    method: 'put',
+    data,
+  });
+}

+ 21 - 0
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/configs/tables.ts

@@ -58,6 +58,27 @@ export const DRAW_LESSONS_TABLE_COLUMNS: TableColumnProps[] = [
     minWidth: '140px',
     showOverflowTooltip: true,
   },
+  {
+    label: '下发数',
+    prop: 'issueCount',
+    slot: 'issueCount',
+    align: 'center',
+    width: '120px',
+  },
+  {
+    label: '反馈人数',
+    prop: 'feedbackCount',
+    slot: 'feedbackCount',
+    align: 'center',
+    width: '120px',
+  },
+  {
+    label: '反馈比例',
+    prop: 'feedbackRatio',
+    slot: 'feedbackRatio',
+    align: 'center',
+    width: '120px',
+  },
   {
     label: '计划完成时间',
     prop: 'associationOtTimeLimit',

+ 8 - 8
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/oneByOneManagement.vue

@@ -165,9 +165,9 @@
             style="width: 100%"
           />
         </el-form-item>
-        <el-form-item label="计划结束时间" prop="planEndTime" required>
+        <el-form-item label="计划结束时间" prop="planEndDate" required>
           <el-date-picker
-            v-model="issueForm.planEndTime"
+            v-model="issueForm.planEndDate"
             type="date"
             value-format="YYYY-MM-DD"
             placeholder="选择计划结束日期"
@@ -394,12 +394,12 @@
     groupDeptId: undefined as number | undefined,
     groupDeptName: '',
     planStartDate: '',
-    planEndTime: '',
+    planEndDate: '',
   });
   const issueRules: FormRules = {
     groupDeptId: [{ required: true, message: '请选择分组名称', trigger: 'change' }],
     planStartDate: [{ required: true, message: '请选择计划开始日期', trigger: 'change' }],
-    planEndTime: [{ required: true, message: '请选择计划结束时间', trigger: 'change' }],
+    planEndDate: [{ required: true, message: '请选择计划结束时间', trigger: 'change' }],
   };
   const groupOptions = ref<Array<{ id: number; deptName: string }>>([]);
 
@@ -453,14 +453,14 @@
       groupDeptId: undefined,
       groupDeptName: '',
       planStartDate: '',
-      planEndTime: '',
+      planEndDate: '',
     };
     currentIssueRow.value = null;
   }
 
   function handleSend(row: { id: number; dangerId?: number; associationOneThree?: string }) {
     currentIssueRow.value = { id: row.id, dangerId: row.dangerId, associationOneThree: row.associationOneThree };
-    issueForm.value = { groupDeptId: undefined, groupDeptName: '', planStartDate: '', planEndTime: '' };
+    issueForm.value = { groupDeptId: undefined, groupDeptName: '', planStartDate: '', planEndDate: '' };
     showIssueDialog.value = true;
   }
 
@@ -474,9 +474,9 @@
         executGroupIds: issueForm.value.groupDeptId,
         associationOtObligationDeptName: issueForm.value.groupDeptName,
         associationOneThree: currentIssueRow.value.associationOneThree,
-        associationOtTimeLimit: issueForm.value.planEndTime || undefined,
+        associationOtTimeLimit: issueForm.value.planEndDate || undefined,
         planStartDate: issueForm.value.planStartDate || undefined,
-        planEndTime: issueForm.value.planEndTime || undefined,
+        planEndDate: issueForm.value.planEndDate || undefined,
       });
       ElMessage.success('下发成功');
       showIssueDialog.value = false;

+ 10 - 8
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/components/oneByOneManagementDeptDetail.vue

@@ -66,7 +66,7 @@
           <span>是否存在问题</span>
         </h4>
         <div class="detail-ct detail-ct--radio">
-          <el-radio-group v-model="hasProblem" class="has-problem-radio">
+          <el-radio-group v-model="hasProblem" :disabled="isViewMode" class="has-problem-radio">
             <el-radio :label="false">否</el-radio>
             <el-radio :label="true">是</el-radio>
           </el-radio-group>
@@ -151,7 +151,7 @@
     </main>
     <footer class="safety-platform-container__footer">
       <el-button @click="router.back()">返回</el-button>
-      <el-button type="primary" @click="handleSubmit">
+      <el-button type="primary" @click="handleSubmit" v-if="!isViewMode">
         提交
       </el-button>
     </footer>
@@ -204,7 +204,7 @@
   import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
   import type { FileItem } from '@/components/UploadFiles/types';
   import { formatAttachmentList } from '@/components/UploadFiles/utils';
-  import { getDrawLessonsAdminDetail } from '@/api/drawLessons';
+  import { getDrawLessonsAdminDeptDetail } from '@/api/drawLessons';
   import { submitDrawLessonsDeptFeedback, type DeptFeedbackRequest } from '@/api/drawLessons';
   import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
   import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
@@ -214,6 +214,8 @@
   const route = useRoute();
 
   const currentId = computed(() => Number(route.query.id));
+  const isViewMode = computed(() => route.query.operate === 'one-by-one-dept-view');
+
 
   /** 是否存在问题:否/是,为是时材料上传显示选择附件 */
   const hasProblem = ref(false);
@@ -325,7 +327,8 @@
         attachments,
       });
       ElMessage.success('反馈提交成功');
-      getDetail();
+      // getDetail();
+      router.back();
     } catch (e) {
       console.error('反馈提交失败:', e);
       ElMessage.error(e?.message || e?.data || '反馈提交失败,请重试');
@@ -335,10 +338,11 @@
   const getDetail = async () => {
     if (!currentId.value) return;
     try {
-      const res = await getDrawLessonsAdminDetail(currentId.value);
+      const res = await getDrawLessonsAdminDeptDetail(currentId.value);
       const data = (res as any)?.data ?? res;
       if (data && typeof data === 'object') {
         detailData.value = data;
+        hasProblem.value = Number(data.feedbackHasIssue) === 1 ? true : false;
       }
     } catch (e) {
       console.error('获取举一反三详情失败:', e);
@@ -400,9 +404,7 @@
       await submitDrawLessonsDeptFeedback(feedbackForm.value);
       ElMessage.success('反馈提交成功');
       showFeedbackDialog.value = false;
-      router.push({
-        path: '/production-safety/hidden-trouble-investigation-and-governance/one-by-one-management-dept',
-      });
+      router.back();
     } catch (e) {
       console.error('反馈提交失败:', e);
       ElMessage.error(e?.message || e?.data || '反馈提交失败,请重试');

+ 1 - 1
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/configs/tables.ts

@@ -59,7 +59,7 @@ export const DRAW_LESSONS_DEPT_TABLE_COLUMNS: TableColumnProps[] = [
     prop: 'action',
     slot: 'action',
     fixed: 'right',
-    width: '100px',
+    width: '140px',
     align: 'left',
   },
 ];

+ 20 - 4
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/oneByOneManagementDept.vue

@@ -72,7 +72,11 @@
                 <ActionButton
                   v-if="scope.row.statusId === 3"
                   text="反馈"
-                  @click="handleFeedback(scope.row.id)"
+                  @click="handleFeedback(scope.row.associationOtId)"
+                />
+                <ActionButton
+                  text="查看"
+                  @click="handleView(scope.row.associationOtId)"
                 />
               </div>
             </template>
@@ -103,7 +107,7 @@
   import { useRouter } from 'vue-router';
   import type { QueryPageRequest } from '@/types/basic-query';
   import {
-    queryDrawLessonsAdminPage,
+    queryDrawLessonsAdminDeptPage,
     type DrawLessonsQueryParam,
   } from '@/api/drawLessons';
   import { downloadByData } from '@/utils/file/download';
@@ -159,7 +163,7 @@
     applyPlanDateRange();
     tableConfig.loading = true;
     try {
-      const res = await queryDrawLessonsAdminPage(tableQuery);
+      const res = await queryDrawLessonsAdminDeptPage(tableQuery);
       if (res) {
         // 这里暂时共用管理员侧分页接口,后端如有部门侧接口再调整
         tableData.value = res.records.map((item: any) => ({
@@ -170,6 +174,7 @@
           associationOtObligationDeptName: item.associationOtObligationDeptName,
           associationOneThree: item.associationOneThree,
           associationOtTimeLimit: item.associationOtTimeLimit,
+          associationOtId: item.associationOtId,
           planCompleteTime: item.planCompleteTime ?? item.associationOtTimeLimit,
         }));
         pagination.total = (res as any).totalRow ?? (res as any).total ?? 0;
@@ -204,7 +209,7 @@
   const handleDownload = async () => {
     try {
       // 后端如有专门导出接口,可在此替换
-      await queryDrawLessonsAdminPage(tableQuery);
+      await queryDrawLessonsAdminDeptPage(tableQuery);
       ElMessage.success('导出逻辑待实现');
     } catch (e) {
       console.error('导出举一反三失败:', e);
@@ -232,6 +237,17 @@
     });
   };
 
+  /** 查看:跳转详情页,操作仅根据状态显示 */
+  const handleView = (id: number) => {
+    router.push({
+      name: 'oneByOneManagementDeptItem',
+      query: {
+        id: String(id),
+        operate: 'one-by-one-dept-view',
+      },
+    });
+  };
+
   onMounted(() => {
     getTableData();
   });

+ 1 - 1
src/views/production-safety/productionSafetySystem/lawManagement/lawManagement.vue

@@ -231,7 +231,7 @@
   // 批量导入
   const batchImportVisible = ref(false);
   const { urlPrefix } = useGlobSetting();
-  const importApiUrl = ref(urlJoin(urlPrefix, '/admin/prod/lawRegulation/importLawRegulation'));
+  const importApiUrl = ref(urlJoin(urlPrefix, '/productionSafety/lawRegulation/import'));
   const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-law-regulation-template.xlsx');
 
   const handleImport = () => {

+ 2 - 2
src/views/production-safety/risk-identification-and-control/labor-products-manage/list.vue

@@ -6,10 +6,10 @@
     <main class="safety-platform-container__main">
       <div class="search-form">
         <el-form :inline="true">
-          <el-form-item label="标题">
+          <el-form-item label="物品名称">
             <el-input
               v-model="queryParams.queryParam.name"
-              placeholder="请输入标题"
+              placeholder="请输入物品名称"
               clearable
               style="width: 170px"
             />

+ 14 - 14
src/views/production-safety/risk-identification-and-control/special-equipment-manage/configs/form.ts

@@ -190,16 +190,16 @@ export const SPECIAL_EQUIPMENT_FORM_CONFIG: FormConfig[] = [
     label: '设备状态:',
     slot: 'deviceStatus',
   },
-  {
-    prop: 'nextInspectionDate',
-    label: '下次检测时间:',
-    component: 'ElDatePicker',
-    componentProps: {
-      type: 'date',
-      valueFormat: 'YYYY-MM-DD',
-      placeholder: '请选择下次检测时间',
-    },
-  },
+  // {
+  //   prop: 'nextInspectionDate',
+  //   label: '下次检测时间:',
+  //   component: 'ElDatePicker',
+  //   componentProps: {
+  //     type: 'date',
+  //     valueFormat: 'YYYY-MM-DD',
+  //     placeholder: '请选择下次检测时间',
+  //   },
+  // },
 ];
 
 export const SPECIAL_EQUIPMENT_FORM_DATA = {
@@ -236,20 +236,20 @@ export const SPECIAL_EQUIPMENT_FORM_RULES = {
   typeId: [{ required: true, message: '请选择设备种类', trigger: 'change' }],
   registerCode: [{ required: true, message: '注册代码不能为空', trigger: 'blur' }],
   licenseNo: [{ required: true, message: '使用证号不能为空', trigger: 'blur' }],
-  deviceCode: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
+  // deviceCode: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
   safeLocation: [{ required: true, message: '安全地点不能为空', trigger: 'blur' }],
   useDepartment: [{ required: true, message: '请选择使用部门', trigger: 'change' }],
   responsibilityDeptId: [{ required: true, message: '请选择责任部门', trigger: 'change' }],
   responsiblePerson: [{ required: true, message: '责任人不能为空', trigger: 'blur' }],
   jobNo: [{ required: true, message: '工号不能为空', trigger: 'blur' }],
-  factoryNo: [{ required: true, message: '出厂编号不能为空', trigger: 'blur' }],
-  productionDate: [{ required: true, message: '请选择生产日期', trigger: 'change' }],
+  // factoryNo: [{ required: true, message: '出厂编号不能为空', trigger: 'blur' }],
+  // productionDate: [{ required: true, message: '请选择生产日期', trigger: 'change' }],
   startUseDate: [{ required: true, message: '请选择启用日期', trigger: 'change' }],
   inspectionCycle: [{ required: true, message: '检测周期不能为空', trigger: 'blur' }],
   useYears: [{ required: true, message: '使用年限不能为空', trigger: 'blur' }],
   inspectionTime: [{ required: true, message: '请选择检测时间', trigger: 'change' }],
   deviceStatus: [{ required: true, message: '请选择设备状态', trigger: 'change' }],
-  nextInspectionDate: [{ required: true, message: '请选择下次检测时间', trigger: 'change' }],
+  // nextInspectionDate: [{ required: true, message: '请选择下次检测时间', trigger: 'change' }],
 };
 
 

+ 4 - 3
src/views/production-safety/risk-identification-and-control/special-equipment-manage/edit.vue

@@ -161,8 +161,8 @@
   const handleDeptChange = (val: number[]) => {
     if (Array.isArray(val) && val.length) {
       (formData as any).responsibilityDeptId = val[val.length - 1];
-      (formData as any).useDepartment = getDeptNameByPath(deptOptions.value, val);
-      useDepartmentPath.value = [...val];
+      // (formData as any).useDepartment = getDeptNameByPath(deptOptions.value, val);
+      // useDepartmentPath.value = [...val];
     } else {
       (formData as any).responsibilityDeptId = undefined;
     }
@@ -193,7 +193,7 @@
         (formData as any).typeId = res.typeId;
         (formData as any).registerCode = res.registerCode ?? '';
         (formData as any).deviceCode = res.deviceCode ?? '';
-        (formData as any).useDepartment = res.useDepartment ?? '';
+        (formData as any).useDepartment = res.isUseDepartment ?? '';
         (formData as any).responsiblePerson = res.responsiblePerson ?? '';
         (formData as any).factoryNo = res.factoryNo ?? '';
         formData.startUseDate = res.startUseDate ?? '';
@@ -209,6 +209,7 @@
         formData.deviceStatus = res.deviceStatus ?? 1;
         formData.nextInspectionDate = res.nextInspectionDate ?? '';
         formData.remark = res.remark ?? '';
+        useDepartmentPath.value = res.isUseDepartment ?? [];
       }
     } catch (e) {
       console.error('获取特种设备设施详情失败:', e);

+ 12 - 11
src/views/production-safety/safety-culture/accidentCaseManagement/accidentCaseManagement.vue

@@ -10,12 +10,12 @@
             <el-button type="primary" class="search-table-container--button" @click="handleCreate">
               新增
             </el-button>
-            <!-- <el-button plain class="search-table-container--button" @click="handleImport">
+            <el-button plain class="search-table-container--button" @click="handleImport">
               导入
             </el-button>
             <el-button plain class="search-table-container--button" @click="handleDownload">
               导出
-            </el-button> -->
+            </el-button>
           </div>
 
           <div class="act-search">
@@ -28,7 +28,7 @@
                 <span>状态:</span>
                 <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
                   <el-option label="启用" :value="1" />
-                  <el-option label="禁用" :value="0" />
+                  <el-option label="禁用" :value="2" />
                 </el-select>
               </div>
               <div class="select-box--item">
@@ -56,7 +56,7 @@
             @update:pageSize="handleSizeChange" @update:pageNumber="handleCurrentChange">
             <template #status="scope">
               <span>
-                {{ scope.row.status === 1 ? '启用' : scope.row.status === 0 ? '禁用' : '-' }}
+                {{ scope.row.status === 1 ? '启用' : scope.row.status === 2 ? '禁用' : '-' }}
               </span>
             </template>
             <template #action="scope">
@@ -90,6 +90,7 @@ import { useRouter } from 'vue-router';
 import {
   accidentCaseManagementFilePage,
   deleteAccidentCase,
+  exportAccidentCaseManagementFile,
   type safetyCultureFileQuery,
   type safetyCultureFilePageQuery,
 } from '@/api/safety-culture';
@@ -187,7 +188,7 @@ const handleReset = () => {
 // 批量导入
 const batchImportVisible = ref(false);
 const { urlPrefix } = useGlobSetting();
-const importApiUrl = ref(urlJoin(urlPrefix, '/productionSafety/academyFile/import'));
+const importApiUrl = ref(urlJoin(urlPrefix, '/accidentCase/import'));
 const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-academy-file-template.xlsx');
 
 const handleImport = () => {
@@ -208,12 +209,12 @@ const handleDownload = async () => {
       startDate: queryParams.startDate || undefined,
       endDate: queryParams.endDate || undefined,
     };
-    // const response = await exportAcademyFile(exportParams, queryParams.classifyName || undefined);
-    // if (response) {
-    //   const fileName = `院级文件管理_${new Date().toISOString().split('T')[0]}.xlsx`;
-    //   downloadByData(response, fileName);
-    //   ElMessage.success('导出成功');
-    // }
+    const response = await exportAccidentCaseManagementFile(exportParams, queryParams.classifyName || undefined);
+    if (response) {
+      const fileName = `事故案例管理_${new Date().toISOString().split('T')[0]}.xlsx`;
+      downloadByData(response, fileName);
+      ElMessage.success('导出成功');
+    }
   } catch (e) {
     console.error('导出院级文件失败:', e);
     ElMessage.error('导出失败,请重试');

+ 94 - 6
src/views/production-safety/safety-culture/accidentCaseManagement/components/accidentCaseManagementDetail.vue

@@ -28,6 +28,36 @@
             </div>
           </div>
       </template>
+      <template #imageFileUrl>
+        <el-upload
+          :key="approvalUploadKey"
+          ref="approvalUploadRef"
+          v-model:file-list="approvalImageFileList"
+          :auto-upload="false"
+          list-type="picture-card"
+          :limit="1"
+          :disabled="isViewMode"
+          :on-change="handleApprovalImageChange"
+          :on-exceed="handleApprovalImageExceed"
+          :on-preview="handleApprovalPictureCardPreview"
+          :on-remove="handleApprovalImageRemove"
+        >
+          <el-icon><Plus /></el-icon>
+          <template #file="{ file }"> 
+            <div>
+              <img class="el-upload-list__item-thumbnail" :src="file.fileUrl" alt="" />
+              <span class="el-upload-list__item-actions" v-if="!isViewMode">
+                <span class="el-upload-list__item-preview" @click="handleApprovalPictureCardPreview(file)">
+                  <el-icon><ZoomIn /></el-icon>
+                </span>
+                <span class="el-upload-list__item-delete" @click.stop="handleApprovalDeleteClick()">
+                  <el-icon><Delete /></el-icon>
+                </span>
+              </span>
+            </div>
+          </template>
+        </el-upload>
+      </template>
       <template #content>
         <div class="editor-container">
           <Toolbar style="border-bottom: 1px solid #dcdfe6" :editor="editorRef" />
@@ -38,7 +68,7 @@
       <template #status>
         <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode"> 
           <el-radio :value="1">启用</el-radio>
-          <el-radio :value="0">禁用</el-radio>
+          <el-radio :value="2">禁用</el-radio>
         </el-radio-group>
       </template>
     </BasicForm>
@@ -55,7 +85,7 @@
 <script setup lang="ts">
 import { computed, onMounted, ref, shallowRef, onBeforeUnmount } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
-import { ElMessage } from 'element-plus';
+import { ElMessage , ElIcon, genFileId, type UploadProps, type UploadUserFile, type UploadRawFile, type UploadInstance } from 'element-plus';
 import BasicForm from '@/components/BasicForm.vue';
 import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
 import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
@@ -71,18 +101,19 @@ import {
 import type { FileItem } from '@/components/UploadFiles/types';
 import { formatAttachmentList } from '@/components/UploadFiles/utils';
 import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
-
+import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
 const router = useRouter();
 const route = useRoute();
-
+const approvalUploadKey = ref(0);
 const operate = computed(() => (route.query.operate as string) || 'safety-culture-material-create');
 const currentId = computed(() => Number(route.query.id));
-
+const approvalImageFileList = ref<UploadUserFile[]>([]);
 const isCreateMode = computed(() => operate.value === 'safety-culture-material-create');
 const isEditMode = computed(() => operate.value === 'safety-culture-material-edit');
 const isViewMode = computed(() => operate.value === 'safety-culture-material-view');
-
+const approvalUploadRef = ref<UploadInstance>();
 const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+import { Plus, Delete, ZoomIn } from '@element-plus/icons-vue';
 
 const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
   useFormConfigHook(ACADEMY_FILE_FORM_CONFIG, ACADEMY_FILE_FORM_DATA, ACADEMY_FILE_FORM_RULES);
@@ -180,6 +211,8 @@ const getDetail = async () => {
       ruleFormData.status = res.status ?? 1;
       ruleFormData.description = res.description || '';
       ruleFormData.attachmentUrl = JSON.parse(res.attachmentUrl || res.fileUrl || '');
+      approvalImageFileList.value = JSON.parse(res.imageUrl || '[]');
+      ruleFormData.imageUrl = res.imageUrl || '';
     }
     cloneRuleFormData();
   } catch (e) {
@@ -208,6 +241,7 @@ const handleSubmit = async () => {
       description: ruleFormData.description,
       attachmentUrl:JSON.stringify(uploadedFileList),
       status: ruleFormData.status,
+      imageUrl: JSON.stringify(approvalImageFileList.value),
     };
     if (isCreateMode.value) {
       await saveAccidentCase(basePayload);
@@ -233,6 +267,60 @@ const previewOnline = (url: string | undefined, type) => {
   }
 };
 
+const handleApprovalImageChange = (uploadFile: UploadUserFile) => {
+  if (!uploadFile.raw) return;
+
+  uploadFileApi({
+    file: uploadFile.raw,
+    bizType: UPLOAD_BIZ_TYPE['DICTIONARY'],
+    fileName: uploadFile.raw.name,
+  })
+    .then((res) => {
+      ruleFormData.imageUrl = [res.url];
+      approvalImageFileList.value = [
+        {
+          fileName: uploadFile.name || res.url.split('/').pop() || 'image',
+          fileUrl: res.url,
+          fileId: res.uid
+        },
+      ];
+    })
+    .catch(() => {
+      ElMessage.error('图片上传失败,请重试');
+    });
+};
+
+
+const handleApprovalImageExceed: UploadProps['onExceed'] = (files) => {
+  const uploadInstance = approvalUploadRef.value;
+  if (!uploadInstance) return;
+
+  uploadInstance.clearFiles();
+  const file = files[0] as UploadRawFile;
+  file.uid = genFileId();
+  uploadInstance.handleStart(file);
+};
+
+const handleApprovalPictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
+  // dialogImageUrl.value = uploadFile.url || '';
+  // dialogVisible.value = true;
+};
+
+const resetApprovalImageUpload = () => {
+  approvalUploadRef.value?.clearFiles();
+  ruleFormData.imageUrl = [];
+  approvalImageFileList.value = [];
+  approvalUploadKey.value += 1;
+};
+
+const handleApprovalDeleteClick = () => {
+  resetApprovalImageUpload();
+};
+
+const handleApprovalImageRemove: UploadProps['onRemove'] = () => {
+  resetApprovalImageUpload();
+};
+
 onMounted(() => {
   cloneRuleFormData();
   // beforeRouteLeave();

+ 7 - 0
src/views/production-safety/safety-culture/accidentCaseManagement/configs/form.ts

@@ -37,6 +37,11 @@ export const ACADEMY_FILE_FORM_CONFIG: FormConfig[] = [
     label: '状态:',
     slot: 'status',
   },
+  {
+    prop: 'imageUrl',
+    label: '图片上传:',
+    slot: 'imageFileUrl',
+  },
 ];
 
 export const ACADEMY_FILE_FORM_DATA = {
@@ -45,6 +50,7 @@ export const ACADEMY_FILE_FORM_DATA = {
   attachmentUrl: [] as FileItem[],
   description: '',
   status: 1,
+  imageUrl: [] as FileItem[],
 };
 
 export const ACADEMY_FILE_FORM_RULES = {
@@ -53,4 +59,5 @@ export const ACADEMY_FILE_FORM_RULES = {
   // attachmentUrl: [{ required: true, message: '请选择文档上传', trigger: 'change' }],
   description: [{ required: true, message: '请输入案例描述', trigger: 'blur' }],
   status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+  imageUrl: [{ required: true, message: '请选择图片上传', trigger: 'change' }],
 };

+ 7 - 7
src/views/production-safety/safety-culture/accidentCaseManagement/configs/tables.ts

@@ -34,13 +34,13 @@ export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
     align: 'left',
     minWidth: '120px',
   },
-  {
-    label: '案例描述',
-    prop: 'description',
-    align: 'left',
-    minWidth: '120px',
-    showOverflowTooltip: true,
-  },
+  // {
+  //   label: '案例描述',
+  //   prop: 'description',
+  //   align: 'left',
+  //   minWidth: '120px',
+  //   showOverflowTooltip: true,
+  // },
   {
     label: '上传时间',
     prop: 'createdAt',

+ 10 - 10
src/views/production-safety/safety-culture/safetyCultureActivityManagement/components/activityRegistrationManagement.vue

@@ -17,7 +17,7 @@
             <section class="select-box">
               <div class="select-box--item">
                 <span>员工工号/名称:</span>
-                <el-input v-model="tableQuery.queryParam.userName" placeholder="搜索员工工号或名称" class="act-search-input" />
+                <el-input v-model="tableQuery.queryParam.keyword" placeholder="搜索员工工号或名称" class="act-search-input" />
               </div>
               <div class="select-box--item">
                 <span>报名日期:</span>
@@ -31,9 +31,9 @@
               <el-button type="primary" style="margin:0 10px;" @click="selectDeptType">
                 添加
               </el-button>
-              <el-button plain @click="handleExport">
+              <!-- <el-button plain @click="handleExport">
                 导出
-              </el-button>
+              </el-button> -->
             </section>
           </div>
         </header>
@@ -60,7 +60,7 @@
         <!-- 普通部门表单 -->
         <el-form ref="normalFormRef" :model="normalForm" :rules="normalFormRules" label-width="140px">
           <el-form-item label="部门名称" prop="employeeDeptName">
-            <el-cascader style="width: 50%" size="large" :ref="(el) => (cascaderRef['employeeDeptName'] = el)"
+            <el-cascader style="width: 100%" size="large" :ref="(el) => (cascaderRef['employeeDeptName'] = el)"
               :options="firstLevelDepts" :props="cascaderProp" :show-all-levels="false" placeholder="请选择安全责任部门"
               filterable v-model="normalForm.deptId" @change="(val) => handleChangeDept(val, 'employeeDeptName')" />
           </el-form-item>
@@ -131,7 +131,7 @@ const tableQuery = reactive<QueryPageRequest<any>>({
   pageNumber: pagination.pageNumber,
   pageSize: pagination.pageSize,
   queryParam: {
-    userName: '',
+    keyword: '',
     dateRange: null as any,
     startTime: '',
     endTime: '',
@@ -166,7 +166,7 @@ const normalForm = reactive({
 const firstLevelDepts = ref<any[]>([]);
 
 const normalFormRules: FormRules = {
-  deptId: [
+  employeeDeptName: [
     { required: true, message: '请选择部门名称', trigger: 'blur' },
   ],
   employeeCode: [
@@ -214,9 +214,9 @@ async function getTableData() {
       pageNumber: tableQuery.pageNumber,
       pageSize: tableQuery.pageSize,
       queryParam: {
-        employeeKeyword: tableQuery.queryParam.userName,
-        registrationDateStart: tableQuery.queryParam.startTime,
-        registrationDateEnd: tableQuery.queryParam.endTime,
+        keyword: tableQuery.queryParam.keyword,
+        startDate: tableQuery.queryParam.startTime,
+        endDate: tableQuery.queryParam.endTime,
       },
     };
 
@@ -247,7 +247,7 @@ function handleSearch() {
 }
 
 const handleReset = () => {
-  tableQuery.queryParam.userName = '';
+  tableQuery.queryParam.keyword = '';
   tableQuery.queryParam.dateRange = null;
   handleSearch();
 };

+ 6 - 6
src/views/production-safety/safety-culture/safetyCultureActivityManagement/configs/activityTargetTables.ts

@@ -153,12 +153,12 @@ export const ACTIVITY_REGISTRATION_ADVANCED_PERSON_TABLE_COLUMNS: TableColumnPro
     align: 'left',
     minWidth: '150px',
   },
-  {
-    label: '部门负责人',
-    prop: 'departmentLeader',
-    align: 'left',
-    minWidth: '140px',
-  },
+  // {
+  //   label: '部门负责人',
+  //   prop: 'departmentLeader',
+  //   align: 'left',
+  //   minWidth: '140px',
+  // },
   {
     label: '先进个人描述',
     prop: 'remark',

+ 13 - 13
src/views/production-safety/safety-culture/safetyCultureActivityManagement/configs/tables.ts

@@ -65,30 +65,30 @@ export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
     align: 'left',
     minWidth: '160px',
   },
-  {
-    label: '完成形式',
-    prop: 'createdAt',
-    align: 'left',
-    minWidth: '160px',
-  },
+  // {
+  //   label: '完成形式',
+  //   prop: 'createdAt',
+  //   align: 'left',
+  //   minWidth: '160px',
+  // },
   {
     label: '具体负责人',
     prop: 'specificPersonName',
     align: 'left',
     minWidth: '160px',
   },
-  {
-    label: '工作规划/进展',
-    prop: 'createdAt',
-    align: 'left',
-    minWidth: '160px',
-  },
+  // {
+  //   label: '工作规划/进展',
+  //   prop: 'createdAt',
+  //   align: 'left',
+  //   minWidth: '160px',
+  // },
   {
     label: '操作',
     prop: 'action',
     slot: 'action',
     fixed: 'right',
-    width: '180px',
+    width: '250px',
     align: 'left',
   },
 ];

+ 185 - 6
src/views/production-safety/safety-culture/safetyCultureActivityManagement/safetyCultureActivityManagement.vue

@@ -40,7 +40,7 @@
                 </el-select>
               </div>
               <div class="select-box--item">
-                <span>上传日期范围:</span>
+                <span>计划日期范围:</span>
                 <el-date-picker v-model="uploadDateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
                   end-placeholder="结束日期" value-format="YYYY-MM-DD" format="YYYY-MM-DD" />
               </div>
@@ -55,23 +55,102 @@
         <div class="batch-table">
           <BasicTable ref="basicTableRef" :tableData="tableData" :tableConfig="tableConfig"
             @update:pageSize="handleSizeChange" @update:pageNumber="handleCurrentChange">
-            <template #actionContent="scope">
+            <!-- <template #actionContent="scope">
               <span class="action-content" @click="activityRegistration(scope.row.id)">
                 {{ scope.row.actionContent || '-'}}
               </span>
-            </template>
+            </template> -->
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
-                <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
-                <ActionButton text="删除" :popconfirm="{
+                <ActionButton text="编辑" v-if="scope.row.status === 1" @click="handleEdit(scope.row.id)" />
+                <ActionButton text="删除" v-if="scope.row.status === 1" :popconfirm="{
                   title: '确定要删除?',
                 }" @confirm="handleDelete(scope.row.id)" />
                 <ActionButton text="查看" @click="handleView(scope.row.id)" />
+                <ActionButton text="下发" @click="handleDispatch(scope.row.id)" v-if="scope.row.status === 1"/>
+                <ActionButton text="活动报名" @click="activityRegistration(scope.row.id)" v-if="scope.row.status !== 1"/>
               </div>
             </template>
           </BasicTable>
         </div>
       </div>
+      
+      <el-dialog
+        v-model="showIssueDialog"
+        title="安全文化活动下发"
+        width="500px"
+        destroy-on-close
+        @open="onIssueDialogOpen"
+      >
+        <el-form ref="issueFormRef" :model="issueForm" :rules="issueRules" label-width="140px">
+          <el-form-item label="具体负责人部门" prop="rectificationDepartmentId">
+            <el-cascader
+              v-model="issueForm.rectificationDepartmentId"
+              :options="issueDeptTree"
+              :props="cascaderDeptProp"
+              :show-all-levels="false"
+              placeholder="请选择具体负责人部门"
+              filterable
+              clearable
+              style="width: 100%"
+              @change="onIssueDeptChange"
+            />
+          </el-form-item>
+          <el-form-item label="具体负责人" prop="rectificationResponsibleUserId">
+            <el-select
+              v-model="issueForm.rectificationResponsibleUserId"
+              placeholder="选择具体负责人"
+              filterable
+              clearable
+              style="width: 100%"
+            >
+              <el-option
+                v-for="user in issueUserList"
+                :key="user.id"
+                :label="user.realname ?? user.username"
+                :value="user.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="计划开始日期:" prop="startDate">
+            <el-date-picker
+              v-model="issueForm.startDate"
+              type="date"
+              placeholder="请选择计划开始日期"
+              value-format="YYYY-MM-DD"
+              format="YYYY-MM-DD"
+              style="width: 100%"
+              :disabled-date="(date: Date) => {
+                if (issueForm.endDate) {
+                  return date > new Date(issueForm.endDate);
+                }
+                return false;
+              }"
+            />
+          </el-form-item>
+          <el-form-item label="计划结束日期:" prop="endDate">
+            <el-date-picker
+              v-model="issueForm.endDate"
+              type="date"
+              placeholder="请选择计划结束日期"
+              value-format="YYYY-MM-DD"
+              format="YYYY-MM-DD"
+              style="width: 100%"
+              :disabled-date="(date: Date) => {
+                if (issueForm.startDate) {
+                  return date < new Date(issueForm.startDate);
+                }
+                return false;
+              }"
+            />
+          </el-form-item>
+        </el-form>
+        <template #footer>
+          <el-button @click="showIssueDialog = false">取消</el-button>
+          <el-button type="primary" @click="handleIssueSave">保存</el-button>
+        </template>
+      </el-dialog>
+
     </main>
     <BatchImport v-if="batchImportVisible" :visible="batchImportVisible" :import-api-url="importApiUrl"
       :template-url="templateUrl" template-name="下载模板" :show-template="false" @close="batchImportVisible = false"
@@ -91,6 +170,7 @@ import {
   safetyCultureActivityManagementFilePage,
   deleteSafetyCultureActivityManagement,
   getAllDepartments,
+  activityDistribution,
   type safetyCultureFileQuery,
   type safetyCultureFilePageQuery,
 } from '@/api/safety-culture';
@@ -100,10 +180,38 @@ import BatchImport from '@/components/batch-import/BatchImport.vue';
 import { useGlobSetting } from '@/hooks/setting';
 import urlJoin from 'url-join';
 import { http } from '@/utils/http/axios';
-
+import { queryAvailableUserList } from '@/api/production-safety/responsibility-implementation';
 
 const router = useRouter();
 
+/** 下发弹窗:点击下发打开弹窗,弹窗内部门用 queryAllDeptTree、负责人用 queryAvailableUserList,点击保存才触发下发接口 */
+const showIssueDialog = ref(false);
+const distributionId = ref<number>(0);
+const issueFormRef = ref();
+const issueForm = ref({
+  rectificationDepartmentId: undefined as number | undefined,
+  rectificationResponsibleUserId: undefined as number | undefined,
+  rectificationResponsiblePersonName: '' as string,
+  startDate: '' as string,
+  endDate: '' as string,
+});
+const issueRules = {
+  rectificationDepartmentId: [{ required: true, message: '请选择整改责任部门', trigger: 'change' }],
+  rectificationResponsibleUserId: [{ required: true, message: '请选择整改负责人', trigger: 'change' }],
+  startDate: [{ required: true, message: '请选择计划开始日期', trigger: 'change' }],
+  endDate: [{ required: true, message: '请选择计划结束日期', trigger: 'change' }],
+};
+/** 下发弹窗部门树,与新增隐患台账复查人员所属部门一致(getAllDepartments 取第一级 children) */
+const cascaderDeptProp = {
+  checkStrictly: true,
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'deptName',
+  emitPath: false,
+};
+const issueDeptTree = ref<DeptTree[]>([]);
+const issueUserList = ref<Array<{ id: number; realname?: string; username?: string }>>([]);
+
 // 表格
 const basicTableRef = ref<InstanceType<typeof BasicTable>>();
 
@@ -388,6 +496,77 @@ const activityRegistration = async (id: number) => {
   });
 };
 
+const onIssueDialogOpen = async () => {
+  try {
+    const [deptRes, userRes] = await Promise.all([
+      getAllDepartments(),
+      queryAvailableUserList({ pageNumber: 1, pageSize: 9999, queryParam: {} }),
+    ]);
+    const fullTree = (deptRes as DeptTree[]) ?? [];
+    issueDeptTree.value = Array.isArray(fullTree) && fullTree[0]?.children ? fullTree[0].children : [];
+    issueUserList.value = (userRes as any)?.records ?? [];
+  } catch (e) {
+    console.error('获取部门/用户列表失败:', e);
+    ElMessage.error(e?.message || e?.data || '加载部门或负责人列表失败');
+    issueDeptTree.value = [];
+    issueUserList.value = [];
+  }
+};
+
+const onIssueDeptChange = () => {
+  issueForm.value.rectificationResponsibleUserId = undefined;
+  issueForm.value.rectificationResponsiblePersonName = '';
+};
+
+const handleDispatch = (id: number) => {
+  distributionId.value = id;
+  showIssueDialog.value = true;
+};
+
+/** 从部门树中根据 id 查找部门名称 */
+function findDeptNameById(nodes: DeptTree[] | undefined, id: number): string {
+  if (!nodes?.length) return '';
+  for (const n of nodes) {
+    if (n.id === id) return n.deptName ?? '';
+    if (n.children?.length) {
+      const found = findDeptNameById(n.children, id);
+      if (found) return found;
+    }
+  }
+  return '';
+};
+
+const handleIssueSave = async () => {
+  await issueFormRef.value?.validate?.().catch(() => {});
+  const { rectificationDepartmentId, rectificationResponsibleUserId } = issueForm.value;
+  if (rectificationDepartmentId == null || rectificationResponsibleUserId == null) {
+    ElMessage.warning('请选择整改责任部门和整改负责人');
+    return;
+  }
+  if (!issueForm.value.startDate || !issueForm.value.endDate) {
+    ElMessage.warning('请选择计划开始日期和计划结束日期');
+    return;
+  }
+  const selectedUser = issueUserList.value.find((u) => u.id === rectificationResponsibleUserId);
+  const personName = selectedUser?.realname ?? selectedUser?.username ?? '';
+  const deptName = findDeptNameById(issueDeptTree.value, rectificationDepartmentId);
+  try {
+    await activityDistribution({
+      id: distributionId.value,
+      specificDeptId: String(rectificationDepartmentId),
+      specificPersonId: String(rectificationResponsibleUserId),
+      startTime: issueForm.value.startDate,
+      endTime: issueForm.value.endDate,
+    });
+    ElMessage.success('下发成功');
+    showIssueDialog.value = false;
+    getTableData();
+  } catch (e) {
+    console.error('下发失败:', e);
+    ElMessage.error(e?.message || e?.data || '下发失败,请重试');
+  }
+};
+
 onMounted(() => {
   loadDeptNameMap().finally(() => {
     getTableData();

+ 1 - 0
src/views/production-safety/safety-culture/safetyCultureMaterialManagement/components/safetyCultureMaterialManagementDetail.vue

@@ -188,6 +188,7 @@
   // 文件上传
   const handleUploadSuccess = (files: FileItem[]) => {
     ruleFormData.attachmentUrl = files;
+    ruleFormData.fileUrl = JSON.stringify(files);
   };
 
   const handleApprovalPictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {

+ 1 - 1
src/views/production-safety/safety-culture/safetyCultureMaterialManagement/configs/form.ts

@@ -97,7 +97,7 @@ export const ACADEMY_FILE_FORM_RULES = {
   fileFormat: [{ required: true, message: '请选择文件格式', trigger: 'change' }],
   publishDate: [{ required: true, message: '请选择发布日期', trigger: 'change' }],
   status: [{ required: true, message: '请选择状态', trigger: 'change' }],
-  // fileUrl: [{ required: true, message: '请选择文档上传', trigger: 'change' }],
+  fileUrl: [{ required: true, message: '请选择文档上传', trigger: 'change' }],
   content: [{ required: true, message: '请输入文档内容', trigger: 'blur' }],
   // imageFileUrl: [{ required: true, message: '请选择图片上传', trigger: 'change' }],
 };

+ 1 - 1
src/views/production-safety/safety-culture/safetyCultureMaterialManagement/safetyCultureMaterialManagement.vue

@@ -39,7 +39,7 @@
                 </el-select>
               </div>
               <div class="select-box--item">
-                <span>上传日期范围:</span>
+                <span>计划日期范围:</span>
                 <el-date-picker v-model="uploadDateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
                   end-placeholder="结束日期" value-format="YYYY-MM-DD" format="YYYY-MM-DD" />
               </div>