浏览代码

feat开发

xiaweibo 3 月之前
父节点
当前提交
4d9ff70563
共有 19 个文件被更改,包括 1870 次插入43 次删除
  1. 80 43
      src/api/production-safety-system/index.ts
  2. 104 0
      src/router/routers/production-safety-router/safetyTrainingAndEducation.ts
  3. 2 0
      src/router/routers/production-safety.ts
  4. 13 0
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/configs/types.ts
  5. 127 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/components/educationTrainingPlanManagementDetail.vue
  6. 60 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/configs/form.ts
  7. 62 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/configs/tables.ts
  8. 264 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagement.vue
  9. 44 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagementItem.vue
  10. 127 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/components/educationTrainingPlanManagementDeptDetail.vue
  11. 60 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/configs/form.ts
  12. 62 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/configs/tables.ts
  13. 264 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDept.vue
  14. 44 0
      src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDeptItem.vue
  15. 127 0
      src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/components/employeeTrainingRecordCardManagementDetail.vue
  16. 60 0
      src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/configs/form.ts
  17. 62 0
      src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/configs/tables.ts
  18. 264 0
      src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagement.vue
  19. 44 0
      src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagementItem.vue

+ 80 - 43
src/api/production-safety-system/index.ts

@@ -756,41 +756,44 @@ const LABEL_TO_FREQUENCY: Record<string, number> = {
 
 /** 后端 API 返回的区域检查计划记录格式(与接口文档 DTO 对齐:*Name/*Code) */
 interface AreaCheckPlanManageApiRecord {
-  id?: number;
-  areaCheckPlanName?: string;
-  checkPlace?: string;
-  checkPlaceCategory?: string;
-  checkCategory?: string;
-  status?: number;
-  responsibleDeptName?: string;
-  responsibleDeptCode?: string;
-  selfCheckFrequency?: number | string;
-  responsibleDeptExecGroupName?: string;
-  responsibleDeptExecGroupCode?: string;
-  responsibleDeptPersonName?: string;
-  responsibleDeptPersonCode?: string;
-  safetyEmergencyDeptName?: string;
-  safetyEmergencyDeptCode?: string;
-  safetyEmergencyCheckFrequency?: number | string;
-  safetyEmergencyExecGroupName?: string;
-  safetyEmergencyExecGroupCode?: string;
-  safetyEmergencyPersonName?: string;
-  safetyEmergencyPersonCode?: string;
-  hospitalLeaderDeptName?: string;
-  hospitalLeaderDeptCode?: string;
-  hospitalLeaderCheckFrequency?: number | string;
-  hospitalLeaderExecGroupName?: string;
-  hospitalLeaderExecGroupCode?: string;
-  hospitalLeaderPersonName?: string;
-  hospitalLeaderPersonCode?: string;
-  checklistCategoryName?: string;
-  checklistCategoryCode?: string;
-  checklistTemplateName?: string;
-  checkKeyContent?: string;
-  needOverallDesc?: number | string;
-  needSigneeSign?: number | string;
-  planStartTime?: string;
-  planEndTime?: string;
+  id?: number; // 主键ID
+  areaCheckPlanName?: string; // 区域检查计划名称
+  checkPlace?: string; // 检查场所
+  checkPlaceCategory?: string; // 检查场所所属类别
+  checkCategory?: string; // 检查类别
+  status?: number | string; // 状态:0=未开始 1=进行中 2=已完成 3=已终止
+  responsibleDeptName?: string; // 主责部门名称
+  responsibleDeptCode?: string; // 主责部门code
+  selfCheckFrequency?: number | string; // 自查频次:0=每日 1=每周 2=每月 3=每季度 4=每半年 5=每年
+  responsibleDeptExecGroupName?: string; // 主责部门执行人所属分组名称
+  responsibleDeptExecGroupCode?: string; // 主责部门执行人所属分组code
+  responsibleDeptPersonName?: string; // 主责部门责任人名称
+  responsibleDeptPersonCode?: string; // 主责部门责任人code
+  safetyEmergencyDeptName?: string; // 安全应急部门名称
+  safetyEmergencyDeptCode?: string; // 安全应急部门code
+  safetyEmergencyCheckFrequency?: number | string; // 安全应急部检查频次
+  safetyEmergencyExecGroupName?: string; // 安全应急部执行人所属分组名称
+  safetyEmergencyExecGroupCode?: string; // 安全应急部执行人所属分组code
+  safetyEmergencyPersonName?: string; // 安全应急部责任人名称
+  safetyEmergencyPersonCode?: string; // 安全应急部责任人code
+  hospitalLeaderDeptName?: string; // 院领导部门名称
+  hospitalLeaderDeptCode?: string; // 院领导部门code
+  hospitalLeaderCheckFrequency?: number | string; // 院领导检查频次
+  hospitalLeaderExecGroupName?: string; // 院领导执行人所属分组名称
+  hospitalLeaderExecGroupCode?: string; // 院领导执行人所属分组code
+  hospitalLeaderPersonName?: string; // 院领导责任人名称
+  hospitalLeaderPersonCode?: string; // 院领导责任人code
+  checklistCategoryName?: string; // 检查单所属类别名称/检查单模版类别
+  checklistCategoryCode?: string; // 检查单模板类别code
+  checklistTemplateName?: string; // 检查单模版名称
+  checkKeyContent?: string; // 检查重点内容
+  needOverallDesc?: number | string; // 是否需要整体检查情况描述:0=否 1=是
+  needSigneeSign?: number | string; // 是否需要被检查人签字:0=否 1=是
+  planStartTime?: string; // 计划开始时间
+  planEndTime?: string; // 计划结束时间
+  isDeleted?: number | string; // 软删除:0=未删除,大于0(时间戳)=已删除
+  createdAt?: string; // 创建时间
+  updatedAt?: string; // 更新时间
   /** 兼容旧字段名 */
   responsibleDept?: string;
   responsibleDeptExecGroup?: string;
@@ -812,24 +815,37 @@ export interface AreaCheckPlanRecord {
   venueCategoryName?: string;
   planName?: string;
   mainDeptName?: string;
+  mainDeptCode?: string;
   selfCheckFrequency?: string;
   mainDeptExecutorGroupName?: string;
+  mainDeptExecGroupCode?: string;
   mainDeptResponsiblePerson?: string;
+  mainDeptResponsiblePersonCode?: string;
   safetyEmergencyDeptName?: string;
+  safetyEmergencyDeptCode?: string;
   safetyEmergencyCheckFrequency?: string;
   safetyEmergencyExecutorGroupName?: string;
+  safetyEmergencyExecGroupCode?: string;
   safetyEmergencyResponsiblePerson?: string;
+  safetyEmergencyPersonCode?: string;
   hospitalLeaderDeptName?: string;
+  hospitalLeaderDeptCode?: string;
   hospitalLeaderCheckFrequency?: string;
   hospitalLeaderExecutorGroupName?: string;
+  hospitalLeaderExecGroupCode?: string;
   hospitalLeaderResponsiblePerson?: string;
+  hospitalLeaderPersonCode?: string;
   checkKeyContent?: string;
   checklistCategoryName?: string;
+  checklistCategoryCode?: string;
   checklistTemplateName?: string;
   needOverallDesc?: boolean;
   needInspectedSign?: boolean;
   planStartTime?: string;
   planEndTime?: string;
+  isDeleted?: number | string;
+  createdAt?: string;
+  updatedAt?: string;
   [key: string]: unknown;
 }
 
@@ -849,24 +865,37 @@ export function mapAreaCheckPlanApiRecordToUi(api: AreaCheckPlanManageApiRecord
     venueCategoryName: api.checkPlaceCategory ?? api.checkCategory,
     status: api.status as AreaCheckPlanStatus | undefined,
     mainDeptName: api.responsibleDeptName ?? api.responsibleDept,
+    mainDeptCode: api.responsibleDeptCode,
     selfCheckFrequency: toFreq(api.selfCheckFrequency),
     mainDeptExecutorGroupName: api.responsibleDeptExecGroupName ?? api.responsibleDeptExecGroup,
+    mainDeptExecGroupCode: api.responsibleDeptExecGroupCode,
     mainDeptResponsiblePerson: api.responsibleDeptPersonName ?? api.responsibleDeptPerson,
+    mainDeptResponsiblePersonCode: api.responsibleDeptPersonCode,
     safetyEmergencyDeptName: api.safetyEmergencyDeptName ?? api.safetyEmergencyDept,
+    safetyEmergencyDeptCode: api.safetyEmergencyDeptCode,
     safetyEmergencyCheckFrequency: toFreq(api.safetyEmergencyCheckFrequency),
     safetyEmergencyExecutorGroupName: api.safetyEmergencyExecGroupName ?? api.safetyEmergencyExecGroup,
     safetyEmergencyResponsiblePerson: api.safetyEmergencyPersonName ?? api.safetyEmergencyPerson,
+    safetyEmergencyExecGroupCode: api.safetyEmergencyExecGroupCode,
+    safetyEmergencyPersonCode: api.safetyEmergencyPersonCode,
     hospitalLeaderDeptName: api.hospitalLeaderDeptName ?? api.hospitalLeaderDept,
+    hospitalLeaderDeptCode: api.hospitalLeaderDeptCode,
     hospitalLeaderCheckFrequency: toFreq(api.hospitalLeaderCheckFrequency),
     hospitalLeaderExecutorGroupName: api.hospitalLeaderExecGroupName ?? api.hospitalLeaderExecGroup,
     hospitalLeaderResponsiblePerson: api.hospitalLeaderPersonName ?? api.hospitalLeaderPerson,
+    hospitalLeaderExecGroupCode: api.hospitalLeaderExecGroupCode,
+    hospitalLeaderPersonCode: api.hospitalLeaderPersonCode,
     checklistCategoryName: api.checklistCategoryName,
+    checklistCategoryCode: api.checklistCategoryCode,
     checklistTemplateName: api.checklistTemplateName,
     checkKeyContent: api.checkKeyContent,
     needOverallDesc: toBool(api.needOverallDesc),
     needInspectedSign: toBool(api.needSigneeSign),
     planStartTime: api.planStartTime,
     planEndTime: api.planEndTime,
+    isDeleted: api.isDeleted,
+    createdAt: api.createdAt,
+    updatedAt: api.updatedAt,
   };
 }
 
@@ -880,21 +909,27 @@ function uiRecordToApi(ui: AreaCheckPlanRecord & { id?: number }): AreaCheckPlan
     checkPlace: ui.checkVenue,
     checkPlaceCategory: ui.venueCategoryName,
     responsibleDeptName: ui.mainDeptName,
-    responsibleDeptCode: code.mainDeptCode as string | undefined,
+    // 若 mainDeptCode 为空,则回退使用 mainDeptId 作为 code
+    responsibleDeptCode: (code.mainDeptCode as string | undefined)
+      ?? (code.mainDeptId != null ? String(code.mainDeptId) : undefined),
     selfCheckFrequency: toFreqNum(ui.selfCheckFrequency),
     responsibleDeptExecGroupName: ui.mainDeptExecutorGroupName,
     responsibleDeptExecGroupCode: code.mainDeptExecGroupCode as string | undefined,
     responsibleDeptPersonName: ui.mainDeptResponsiblePerson,
     responsibleDeptPersonCode: code.mainDeptResponsiblePersonCode as string | undefined,
     safetyEmergencyDeptName: ui.safetyEmergencyDeptName,
-    safetyEmergencyDeptCode: code.safetyEmergencyDeptCode as string | undefined,
+    // 若 safetyEmergencyDeptCode 为空,则回退使用 safetyEmergencyDeptId 作为 code
+    safetyEmergencyDeptCode: (code.safetyEmergencyDeptCode as string | undefined)
+      ?? (code.safetyEmergencyDeptId != null ? String(code.safetyEmergencyDeptId) : undefined),
     safetyEmergencyCheckFrequency: toFreqNum(ui.safetyEmergencyCheckFrequency),
     safetyEmergencyExecGroupName: ui.safetyEmergencyExecutorGroupName,
     safetyEmergencyExecGroupCode: code.safetyEmergencyExecGroupCode as string | undefined,
     safetyEmergencyPersonName: ui.safetyEmergencyResponsiblePerson,
     safetyEmergencyPersonCode: code.safetyEmergencyPersonCode as string | undefined,
     hospitalLeaderDeptName: ui.hospitalLeaderDeptName,
-    hospitalLeaderDeptCode: code.hospitalLeaderDeptCode as string | undefined,
+    // 若 hospitalLeaderDeptCode 为空,则回退使用 hospitalLeaderDeptId 作为 code
+    hospitalLeaderDeptCode: (code.hospitalLeaderDeptCode as string | undefined)
+      ?? (code.hospitalLeaderDeptId != null ? String(code.hospitalLeaderDeptId) : undefined),
     hospitalLeaderCheckFrequency: toFreqNum(ui.hospitalLeaderCheckFrequency),
     hospitalLeaderExecGroupName: ui.hospitalLeaderExecutorGroupName,
     hospitalLeaderExecGroupCode: code.hospitalLeaderExecGroupCode as string | undefined,
@@ -944,11 +979,13 @@ interface AreaCheckPlanManagePageQuery {
 }
 
 interface AreaCheckPlanManagePageRes {
-  records?: AreaCheckPlanManageApiRecord[];
-  totalRow?: number;
-  totalPage?: number;
-  pageNumber?: number;
-  pageSize?: number;
+  records?: AreaCheckPlanManageApiRecord[]; // 当前页记录列表
+  pageNumber?: number; // 页号,默认值为1
+  pageSize?: number; // 每页数量
+  maxPageSize?: number; // 最大每页数量
+  totalPage?: number; // 总页数
+  totalRow?: number; // 总行数
+  optimizeCountQuery?: boolean; // 是否优化计数查询
 }
 
 function buildPageQuery(uiQuery: { pageNumber: number; pageSize: number; queryParam?: AreaCheckPlanQuery }): AreaCheckPlanManagePageQuery {

+ 104 - 0
src/router/routers/production-safety-router/safetyTrainingAndEducation.ts

@@ -0,0 +1,104 @@
+// === 安全教育培训 ===
+import { RouteComponent } from "vue-router";
+const safetyTrainingAndEducationRoutes: RouteComponent[] = [{
+  id: 9006,
+  parentId: 9000,
+  name: 'safetyTrainingAndEducation',
+  path: 'safety-training-and-education',
+  redirect: '/work-safety/safety-training-and-education/education-training-plan-management',
+  meta: {
+    title: '安全教育培训',
+    icon: 'OverviewIcon',
+    isRoot: false,
+    hidden: false,
+    noCache: false,
+  },
+  children: [
+    {
+      id: 900601,
+      parentId: 9006,
+      name: 'educationTrainingPlanManagement',
+      path: 'education-training-plan-management',
+      component: '/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagement',
+      meta: {
+        title: '教育培训计划管理(管理员)',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: false,
+        noCache: false,
+      },
+    },
+    {
+      id: 90060101,
+      parentId: 900601,
+      name: 'educationTrainingPlanManagementItem',
+      path: 'education-training-plan-management-item',
+      component: '/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagementItem',
+      meta: {
+        title: '教育培训计划管理(管理员)详情',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: true,
+        noCache: false,
+      },
+    },
+    {
+      id: 900602,
+      parentId: 9006,
+        name: 'educationTrainingPlanManagementDept',
+      path: 'education-training-plan-management-dept',
+      component: '/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDept',
+      meta: {
+        title: '教育培训计划管理(部门)',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: false,
+        noCache: false,
+      },
+    },
+    {
+      id: 90060201,
+      parentId: 900602,
+      name: 'educationTrainingPlanManagementDeptItem',
+      path: 'education-training-plan-management-dept-item',
+      component: '/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDeptItem',
+      meta: {
+        title: '教育培训计划管理(部门)详情',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: true,
+        noCache: false,
+      },
+    },
+    {
+      id: 90060202,
+      parentId: 9006,
+      name: 'employeeTrainingRecordCardManagement',
+      path: 'employee-training-record-card-management',
+      component: '/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagement',
+      meta: {
+        title: '员工培训记录卡管理',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: false,
+        noCache: false,
+      },
+    },
+    {
+      id: 90060203,
+      parentId: 90060202,
+      name: 'employeeTrainingRecordCardManagementItem',
+      path: 'employee-training-record-card-management-item',
+      component: '/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagementItem',
+      meta: {
+        title: '员工培训记录卡管理详情',
+        icon: 'OverviewIcon',
+        isRoot: false,
+        hidden: true,
+        noCache: false,
+      },
+    },
+  ],
+}];
+
+export default safetyTrainingAndEducationRoutes;

+ 2 - 0
src/router/routers/production-safety.ts

@@ -5,6 +5,7 @@ import safetyAssessmentRoutes from './production-safety-router/safetyAssessment'
 import productionSafetySystemRoutes from './production-safety-router/productionSafetySystem';
 import hiddenTroubleInvestigationAndGovernanceRoutes from './production-safety-router/hiddenTroubleInvestigationAndGovernance';
 import safetyCultureRoutes from './production-safety-router/safetyCulture';
+import safetyTrainingAndEducationRoutes from './production-safety-router/safetyTrainingAndEducation';
 const productionSafetyRoutes = {
   id: 9000,
   parentId: -1,
@@ -25,6 +26,7 @@ const productionSafetyRoutes = {
     ...productionSafetySystemRoutes,
     ...hiddenTroubleInvestigationAndGovernanceRoutes,
     ...safetyCultureRoutes,
+    ...safetyTrainingAndEducationRoutes,
   ],
   
 };

+ 13 - 0
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/configs/types.ts

@@ -8,24 +8,37 @@ export interface AreaCheckPlanRecord {
   venueCategoryName?: string;
   planName?: string;
   mainDeptName?: string;
+  mainDeptCode?: string;
   selfCheckFrequency?: string;
   mainDeptExecutorGroupName?: string;
+  mainDeptExecGroupCode?: string;
   mainDeptResponsiblePerson?: string;
+  mainDeptResponsiblePersonCode?: string;
   safetyEmergencyDeptName?: string;
+  safetyEmergencyDeptCode?: string;
   safetyEmergencyCheckFrequency?: string;
   safetyEmergencyExecutorGroupName?: string;
+  safetyEmergencyExecGroupCode?: string;
   safetyEmergencyResponsiblePerson?: string;
+  safetyEmergencyPersonCode?: string;
   hospitalLeaderDeptName?: string;
+  hospitalLeaderDeptCode?: string;
   hospitalLeaderCheckFrequency?: string;
   hospitalLeaderExecutorGroupName?: string;
+  hospitalLeaderExecGroupCode?: string;
   hospitalLeaderResponsiblePerson?: string;
+  hospitalLeaderPersonCode?: string;
   checkKeyContent?: string;
   checklistCategoryName?: string;
+  checklistCategoryCode?: string;
   checklistTemplateName?: string;
   needOverallDesc?: boolean;
   needInspectedSign?: boolean;
   planStartTime?: string;
   planEndTime?: string;
+  isDeleted?: number | string;
+  createdAt?: string;
+  updatedAt?: string;
   [key: string]: unknown;
 }
 

+ 127 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/components/educationTrainingPlanManagementDetail.vue

@@ -0,0 +1,127 @@
+<template>
+  <main class="safety-platform-container__main">
+    <BasicForm
+      ref="basicFormRef"
+      :formData="ruleFormData"
+      :formRules="isViewMode ? undefined : formRules"
+      :formConfig="computedFormConfig"
+    />
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">返回</el-button>
+    <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
+      {{ isCreateMode ? '提交' : '保存' }}
+    </el-button>
+  </footer>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, ref } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import BasicForm from '@/components/BasicForm.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES } from '../configs/form';
+  import { queryInventoryDetail, saveInventory, updateInventory } from '@/api/inventory';
+
+  const router = useRouter();
+  const route = useRoute();
+
+  const operate = computed(() => (route.query.operate as string) || 'inventory-create');
+  const currentId = computed(() => Number(route.query.id));
+
+  const isCreateMode = computed(() => operate.value === 'inventory-create');
+  const isEditMode = computed(() => operate.value === 'inventory-edit');
+  const isViewMode = computed(() => operate.value === 'inventory-view');
+
+  const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
+    useFormConfigHook(INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES);
+
+  // 查看模式下,所有字段设为只读
+  const viewFormConfig = ref(
+    INVENTORY_FORM_CONFIG.map((item) => ({
+      ...item,
+      componentProps: {
+        ...item.componentProps,
+        disabled: true,
+      },
+    })),
+  );
+
+  const computedFormConfig = computed(() => {
+    if (isViewMode.value) {
+      return viewFormConfig.value;
+    }
+    return ruleFormConfig.value;
+  });
+
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+
+  const handleValidate = async () => {
+    if (!basicFormRef.value) return;
+    const res = await basicFormRef.value.validateForm();
+    return res;
+  };
+
+  const getDetail = async () => {
+    if (!currentId.value) return;
+    try {
+      const res = await queryInventoryDetail(currentId.value);
+      if (res) {
+        // 映射接口字段到表单字段
+        ruleFormData.itemName = res.stuffName; // 物品名称
+        ruleFormData.warehouseDate = res.inStoreTime ? res.inStoreTime.split('T')[0] : ''; // 入库日期(YYYY-MM-DD)
+        ruleFormData.itemQuantity = res.stuffQty; // 物品数量
+        ruleFormData.remarks = res.remark || ''; // 备注
+      }
+      cloneRuleFormData();
+    } catch (e) {
+      console.error('获取物品库存详情失败:', e);
+      ElMessage.error('获取详情失败');
+    }
+  };
+
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      const basePayload = {
+        stuffName: ruleFormData.itemName,
+        inStoreTime: ruleFormData.warehouseDate
+          ? new Date(ruleFormData.warehouseDate).toISOString()
+          : '',
+        stuffQty: ruleFormData.itemQuantity,
+        remark: ruleFormData.remarks || '',
+      };
+
+      if (isCreateMode.value) {
+        await saveInventory(basePayload);
+        ElMessage.success('创建成功');
+      } else if (isEditMode.value && currentId.value) {
+        await updateInventory({
+          id: currentId.value,
+          ...basePayload,
+        });
+        ElMessage.success('保存成功');
+      }
+
+      router.back();
+    } catch (e) {
+      console.error('保存物品库存失败:', e);
+      ElMessage.error('保存失败,请重试');
+    }
+  };
+
+  onMounted(() => {
+    cloneRuleFormData();
+    beforeRouteLeave();
+    if (isEditMode.value || isViewMode.value) {
+      getDetail();
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+</style>
+

+ 60 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/configs/form.ts

@@ -0,0 +1,60 @@
+import { FormConfig } from '@/types/basic-form';
+
+export const INVENTORY_FORM_CONFIG: FormConfig[] = [
+  {
+    prop: 'itemName',
+    label: '物品名称:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入物品名称',
+    },
+  },
+  {
+    prop: 'warehouseDate',
+    label: '入库日期:',
+    component: 'ElDatePicker',
+    componentProps: {
+      type: 'date',
+      placeholder: '请选择入库日期',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    prop: 'itemQuantity',
+    label: '物品数量:',
+    component: 'ElInputNumber',
+    componentProps: {
+      min: 1,
+      max: 99999,
+      precision: 0, // 不允许小数点,只能输入整数
+      placeholder: '请输入物品数量',
+    },
+  },
+  {
+    label: '备注:',
+    prop: 'remarks',
+    component: 'ElInput',
+    componentProps: {
+      type: 'textarea',
+      rows: 5,
+      placeholder: '请输入备注',
+    },
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+};
+
+export const INVENTORY_FORM_RULES = {
+  itemName: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
+  warehouseDate: [{ required: true, message: '请选择入库日期', trigger: 'change' }],
+  itemQuantity: [
+    { required: true, message: '请输入物品数量', trigger: 'blur' },
+    { type: 'number', min: 1, message: '物品数量不能小于1', trigger: 'blur' },
+    { type: 'number', max: 99999, message: '物品数量不能大于99999', trigger: 'blur' },
+  ],
+};

+ 62 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/configs/tables.ts

@@ -0,0 +1,62 @@
+import type { TableColumnProps } from '@/types/basic-table';
+
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  maxHeight: 'calc(70vh - 150px)',
+};
+
+export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+  },
+  {
+    label: '物品名称',
+    prop: 'itemName',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '入库日期',
+    prop: 'warehouseDate',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '物品数量',
+    prop: 'itemQuantity',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '经办人',
+    prop: 'handler',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '备注',
+    prop: 'remarks',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '状态',
+    prop: 'status',
+    slot: 'status',
+    align: 'center',
+    minWidth: '100px',
+  },
+  {
+    label: '操作',
+    prop: 'action',
+    slot: 'action',
+    fixed: 'right',
+    width: '180px',
+    align: 'left',
+  },
+];

+ 264 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagement.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 物品库存管理 </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-table-container">
+        <header>
+          <div style="position: relative">
+            <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>
+            <el-button plain class="search-table-container--button" @click="handleDownload">
+              导出
+            </el-button>
+          </div>
+
+          <div class="act-search">
+            <section class="select-box">
+              <div class="select-box--item">
+                <span>物品名称:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.stuffName"
+                  placeholder="搜索物品名称"
+                  class="act-search-input"
+                />
+              </div>
+              <div class="select-box--item">
+                <span>状态:</span>
+                <el-select
+                  v-model="tableQuery.queryParam.status"
+                  placeholder="请选择状态"
+                  clearable
+                >
+                  <el-option label="启用" :value="true" />
+                  <el-option label="禁用" :value="false" />
+                </el-select>
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</el-button>
+            </section>
+          </div>
+        </header>
+
+        <div class="batch-table">
+          <BasicTable
+            ref="basicTableRef"
+            :tableData="tableData"
+            :tableConfig="tableConfig"
+            @update:pageSize="handleSizeChange"
+            @update:pageNumber="handleCurrentChange"
+          >
+            <template #status="scope">
+              <span>
+                {{ scope.row.statusName || (scope.row.status === true || scope.row.status === 'true' ? '启用' : scope.row.status === false || scope.row.status === 'false' ? '禁用' : '-') }}
+              </span>
+            </template>
+            <template #action="scope">
+              <div class="action-container--div" style="justify-content: left">
+                <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
+                <ActionButton
+                  text="删除"
+                  :popconfirm="{
+                    title: '确定要删除?',
+                  }"
+                  @confirm="handleDelete(scope.row.id)"
+                />
+                <ActionButton text="查看" @click="handleView(scope.row.id)" />
+              </div>
+            </template>
+          </BasicTable>
+        </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>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { TABLE_OPTIONS, INVENTORY_TABLE_COLUMNS } from './configs/tables';
+  import { useRouter } from 'vue-router';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import { queryInventoryManage, deleteInventory, exportInventory } from '@/api/inventory';
+  import { downloadByData } from '@/utils/file/download';
+  import BatchImport from '@/components/batch-import/BatchImport.vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
+
+  const router = useRouter();
+
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(INVENTORY_TABLE_COLUMNS, TABLE_OPTIONS);
+
+  const tableData = ref<any[]>([]);
+
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      stuffName: '', // 物品名称
+      status: true, // 状态,默认启用
+      ids: [], // 选择数据的ID
+    },
+  });
+
+  const handleSizeChange = (value: number) => {
+    pagination.pageSize = value;
+    tableQuery.pageSize = value;
+    getTableData();
+  };
+
+  const handleCurrentChange = (value: number) => {
+    pagination.pageNumber = value;
+    tableQuery.pageNumber = value;
+    getTableData();
+  };
+
+
+  async function getTableData() {
+    tableConfig.loading = true;
+    try {
+      const res = await queryInventoryManage(tableQuery);
+      if (res) {
+        // 映射返回数据字段到表格字段
+        tableData.value = res.records.map((item) => ({
+          id: item.id,
+          itemName: item.stuffName, // 物品名称
+          warehouseDate: item.inStoreTime, // 入库日期
+          itemQuantity: item.stuffQty, // 物品数量
+          handler: item.createdUserName, // 经办人
+          remarks: item.remark, // 备注
+          status: item.status, // 状态:true-启用,false-禁用
+          statusName: item.statusName, // 状态名称
+        }));
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取物品库存列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    tableQuery.queryParam.stuffName = '';
+    tableQuery.queryParam.status = true; // 重置为默认启用状态
+    tableQuery.queryParam.ids = [];
+    handleSearch();
+  };
+
+  // 批量导入
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/inventory/importInventory'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-inventory-template.xlsx');
+
+  const handleImport = () => {
+    batchImportVisible.value = true;
+  };
+
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    getTableData();
+  };
+
+  const handleDownload = async () => {
+    try {
+      const exportParams = {
+        stuffName: tableQuery.queryParam.stuffName || undefined,
+        status: tableQuery.queryParam.status,
+        ids: tableQuery.queryParam.ids.length > 0 ? tableQuery.queryParam.ids : undefined,
+      };
+      const response = await exportInventory(exportParams);
+      if (response) {
+        const fileName = `物品库存管理_${new Date().toISOString().split('T')[0]}.xlsx`;
+        downloadByData(response, fileName);
+        ElMessage.success('导出成功');
+      }
+    } catch (e) {
+      console.error('导出物品库存失败:', e);
+      ElMessage.error('导出失败,请重试');
+    }
+  };
+
+  const handleCreate = () => {
+    router.push({
+      name: 'educationTrainingPlanManagementItem',
+      query: {
+        operate: 'education-training-plan-management-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'educationTrainingPlanManagementItem',
+      query: {
+        id,
+        operate: 'education-training-plan-management-edit',
+      },
+    });
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await deleteInventory(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e) {
+      console.error('删除物品库存失败:', e);
+      ElMessage.error('删除失败,请重试');
+    }
+  };
+
+  const handleView = (id: number) => {
+    router.push({
+      name: 'educationTrainingPlanManagementItem',
+      query: {
+        id,
+        operate: 'education-training-plan-management-view',
+      },
+    });
+  };
+
+  onMounted(() => {
+    getTableData();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  @use '@/views/traffic/violation/style/act-search-table.scss' as *;
+</style>

+ 44 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagement/educationTrainingPlanManagementItem.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <BreadcrumbBack />
+      <span class="breadcrumb-title">{{ headerTitle }}</span>
+    </header>
+    <EducationTrainingPlanManagementDetail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import EducationTrainingPlanManagementDetail from './components/educationTrainingPlanManagementDetail.vue';
+
+  const route = useRoute();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'education-training-plan-management-create':
+        return '新增物品';
+      case 'education-training-plan-management-edit':
+        return '编辑物品';
+      case 'education-training-plan-management-view':
+        return '查看物品';
+      default:
+        return '未知操作';
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+
+  .safety-platform-container__header {
+    flex-direction: row !important;
+    justify-content: flex-start !important;
+    gap: 8px !important;
+  }
+</style>
+

+ 127 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/components/educationTrainingPlanManagementDeptDetail.vue

@@ -0,0 +1,127 @@
+<template>
+  <main class="safety-platform-container__main">
+    <BasicForm
+      ref="basicFormRef"
+      :formData="ruleFormData"
+      :formRules="isViewMode ? undefined : formRules"
+      :formConfig="computedFormConfig"
+    />
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">返回</el-button>
+    <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
+      {{ isCreateMode ? '提交' : '保存' }}
+    </el-button>
+  </footer>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, ref } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import BasicForm from '@/components/BasicForm.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES } from '../configs/form';
+  import { queryInventoryDetail, saveInventory, updateInventory } from '@/api/inventory';
+
+  const router = useRouter();
+  const route = useRoute();
+
+  const operate = computed(() => (route.query.operate as string) || 'inventory-create');
+  const currentId = computed(() => Number(route.query.id));
+
+  const isCreateMode = computed(() => operate.value === 'inventory-create');
+  const isEditMode = computed(() => operate.value === 'inventory-edit');
+  const isViewMode = computed(() => operate.value === 'inventory-view');
+
+  const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
+    useFormConfigHook(INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES);
+
+  // 查看模式下,所有字段设为只读
+  const viewFormConfig = ref(
+    INVENTORY_FORM_CONFIG.map((item) => ({
+      ...item,
+      componentProps: {
+        ...item.componentProps,
+        disabled: true,
+      },
+    })),
+  );
+
+  const computedFormConfig = computed(() => {
+    if (isViewMode.value) {
+      return viewFormConfig.value;
+    }
+    return ruleFormConfig.value;
+  });
+
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+
+  const handleValidate = async () => {
+    if (!basicFormRef.value) return;
+    const res = await basicFormRef.value.validateForm();
+    return res;
+  };
+
+  const getDetail = async () => {
+    if (!currentId.value) return;
+    try {
+      const res = await queryInventoryDetail(currentId.value);
+      if (res) {
+        // 映射接口字段到表单字段
+        ruleFormData.itemName = res.stuffName; // 物品名称
+        ruleFormData.warehouseDate = res.inStoreTime ? res.inStoreTime.split('T')[0] : ''; // 入库日期(YYYY-MM-DD)
+        ruleFormData.itemQuantity = res.stuffQty; // 物品数量
+        ruleFormData.remarks = res.remark || ''; // 备注
+      }
+      cloneRuleFormData();
+    } catch (e) {
+      console.error('获取物品库存详情失败:', e);
+      ElMessage.error('获取详情失败');
+    }
+  };
+
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      const basePayload = {
+        stuffName: ruleFormData.itemName,
+        inStoreTime: ruleFormData.warehouseDate
+          ? new Date(ruleFormData.warehouseDate).toISOString()
+          : '',
+        stuffQty: ruleFormData.itemQuantity,
+        remark: ruleFormData.remarks || '',
+      };
+
+      if (isCreateMode.value) {
+        await saveInventory(basePayload);
+        ElMessage.success('创建成功');
+      } else if (isEditMode.value && currentId.value) {
+        await updateInventory({
+          id: currentId.value,
+          ...basePayload,
+        });
+        ElMessage.success('保存成功');
+      }
+
+      router.back();
+    } catch (e) {
+      console.error('保存物品库存失败:', e);
+      ElMessage.error('保存失败,请重试');
+    }
+  };
+
+  onMounted(() => {
+    cloneRuleFormData();
+    beforeRouteLeave();
+    if (isEditMode.value || isViewMode.value) {
+      getDetail();
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+</style>
+

+ 60 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/configs/form.ts

@@ -0,0 +1,60 @@
+import { FormConfig } from '@/types/basic-form';
+
+export const INVENTORY_FORM_CONFIG: FormConfig[] = [
+  {
+    prop: 'itemName',
+    label: '物品名称:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入物品名称',
+    },
+  },
+  {
+    prop: 'warehouseDate',
+    label: '入库日期:',
+    component: 'ElDatePicker',
+    componentProps: {
+      type: 'date',
+      placeholder: '请选择入库日期',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    prop: 'itemQuantity',
+    label: '物品数量:',
+    component: 'ElInputNumber',
+    componentProps: {
+      min: 1,
+      max: 99999,
+      precision: 0, // 不允许小数点,只能输入整数
+      placeholder: '请输入物品数量',
+    },
+  },
+  {
+    label: '备注:',
+    prop: 'remarks',
+    component: 'ElInput',
+    componentProps: {
+      type: 'textarea',
+      rows: 5,
+      placeholder: '请输入备注',
+    },
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+};
+
+export const INVENTORY_FORM_RULES = {
+  itemName: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
+  warehouseDate: [{ required: true, message: '请选择入库日期', trigger: 'change' }],
+  itemQuantity: [
+    { required: true, message: '请输入物品数量', trigger: 'blur' },
+    { type: 'number', min: 1, message: '物品数量不能小于1', trigger: 'blur' },
+    { type: 'number', max: 99999, message: '物品数量不能大于99999', trigger: 'blur' },
+  ],
+};

+ 62 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/configs/tables.ts

@@ -0,0 +1,62 @@
+import type { TableColumnProps } from '@/types/basic-table';
+
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  maxHeight: 'calc(70vh - 150px)',
+};
+
+export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+  },
+  {
+    label: '物品名称',
+    prop: 'itemName',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '入库日期',
+    prop: 'warehouseDate',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '物品数量',
+    prop: 'itemQuantity',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '经办人',
+    prop: 'handler',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '备注',
+    prop: 'remarks',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '状态',
+    prop: 'status',
+    slot: 'status',
+    align: 'center',
+    minWidth: '100px',
+  },
+  {
+    label: '操作',
+    prop: 'action',
+    slot: 'action',
+    fixed: 'right',
+    width: '180px',
+    align: 'left',
+  },
+];

+ 264 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDept.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 物品库存管理 </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-table-container">
+        <header>
+          <div style="position: relative">
+            <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>
+            <el-button plain class="search-table-container--button" @click="handleDownload">
+              导出
+            </el-button>
+          </div>
+
+          <div class="act-search">
+            <section class="select-box">
+              <div class="select-box--item">
+                <span>物品名称:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.stuffName"
+                  placeholder="搜索物品名称"
+                  class="act-search-input"
+                />
+              </div>
+              <div class="select-box--item">
+                <span>状态:</span>
+                <el-select
+                  v-model="tableQuery.queryParam.status"
+                  placeholder="请选择状态"
+                  clearable
+                >
+                  <el-option label="启用" :value="true" />
+                  <el-option label="禁用" :value="false" />
+                </el-select>
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</el-button>
+            </section>
+          </div>
+        </header>
+
+        <div class="batch-table">
+          <BasicTable
+            ref="basicTableRef"
+            :tableData="tableData"
+            :tableConfig="tableConfig"
+            @update:pageSize="handleSizeChange"
+            @update:pageNumber="handleCurrentChange"
+          >
+            <template #status="scope">
+              <span>
+                {{ scope.row.statusName || (scope.row.status === true || scope.row.status === 'true' ? '启用' : scope.row.status === false || scope.row.status === 'false' ? '禁用' : '-') }}
+              </span>
+            </template>
+            <template #action="scope">
+              <div class="action-container--div" style="justify-content: left">
+                <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
+                <ActionButton
+                  text="删除"
+                  :popconfirm="{
+                    title: '确定要删除?',
+                  }"
+                  @confirm="handleDelete(scope.row.id)"
+                />
+                <ActionButton text="查看" @click="handleView(scope.row.id)" />
+              </div>
+            </template>
+          </BasicTable>
+        </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>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { TABLE_OPTIONS, INVENTORY_TABLE_COLUMNS } from './configs/tables';
+  import { useRouter } from 'vue-router';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import { queryInventoryManage, deleteInventory, exportInventory } from '@/api/inventory';
+  import { downloadByData } from '@/utils/file/download';
+  import BatchImport from '@/components/batch-import/BatchImport.vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
+
+  const router = useRouter();
+
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(INVENTORY_TABLE_COLUMNS, TABLE_OPTIONS);
+
+  const tableData = ref<any[]>([]);
+
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      stuffName: '', // 物品名称
+      status: true, // 状态,默认启用
+      ids: [], // 选择数据的ID
+    },
+  });
+
+  const handleSizeChange = (value: number) => {
+    pagination.pageSize = value;
+    tableQuery.pageSize = value;
+    getTableData();
+  };
+
+  const handleCurrentChange = (value: number) => {
+    pagination.pageNumber = value;
+    tableQuery.pageNumber = value;
+    getTableData();
+  };
+
+
+  async function getTableData() {
+    tableConfig.loading = true;
+    try {
+      const res = await queryInventoryManage(tableQuery);
+      if (res) {
+        // 映射返回数据字段到表格字段
+        tableData.value = res.records.map((item) => ({
+          id: item.id,
+          itemName: item.stuffName, // 物品名称
+          warehouseDate: item.inStoreTime, // 入库日期
+          itemQuantity: item.stuffQty, // 物品数量
+          handler: item.createdUserName, // 经办人
+          remarks: item.remark, // 备注
+          status: item.status, // 状态:true-启用,false-禁用
+          statusName: item.statusName, // 状态名称
+        }));
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取物品库存列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    tableQuery.queryParam.stuffName = '';
+    tableQuery.queryParam.status = true; // 重置为默认启用状态
+    tableQuery.queryParam.ids = [];
+    handleSearch();
+  };
+
+  // 批量导入
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/inventory/importInventory'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-inventory-template.xlsx');
+
+  const handleImport = () => {
+    batchImportVisible.value = true;
+  };
+
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    getTableData();
+  };
+
+  const handleDownload = async () => {
+    try {
+      const exportParams = {
+        stuffName: tableQuery.queryParam.stuffName || undefined,
+        status: tableQuery.queryParam.status,
+        ids: tableQuery.queryParam.ids.length > 0 ? tableQuery.queryParam.ids : undefined,
+      };
+      const response = await exportInventory(exportParams);
+      if (response) {
+        const fileName = `物品库存管理_${new Date().toISOString().split('T')[0]}.xlsx`;
+        downloadByData(response, fileName);
+        ElMessage.success('导出成功');
+      }
+    } catch (e) {
+      console.error('导出物品库存失败:', e);
+      ElMessage.error('导出失败,请重试');
+    }
+  };
+
+  const handleCreate = () => {
+    router.push({
+      name: 'educationTrainingPlanManagementDeptItem',
+      query: {
+        operate: 'education-training-plan-management-dept-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'educationTrainingPlanManagementDeptItem',
+      query: {
+        id,
+        operate: 'education-training-plan-management-dept-edit',
+      },
+    });
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await deleteInventory(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e) {
+      console.error('删除物品库存失败:', e);
+      ElMessage.error('删除失败,请重试');
+    }
+  };
+
+  const handleView = (id: number) => {
+    router.push({
+      name: 'educationTrainingPlanManagementDeptItem',
+      query: {
+        id,
+        operate: 'education-training-plan-management-dept-view',
+      },
+    });
+  };
+
+  onMounted(() => {
+    getTableData();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  @use '@/views/traffic/violation/style/act-search-table.scss' as *;
+</style>

+ 44 - 0
src/views/production-safety/safetyTrainingAndEducation/educationTrainingPlanManagementDept/educationTrainingPlanManagementDeptItem.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <BreadcrumbBack />
+      <span class="breadcrumb-title">{{ headerTitle }}</span>
+    </header>
+    <EducationTrainingPlanManagementDeptDetail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import EducationTrainingPlanManagementDeptDetail from './components/educationTrainingPlanManagementDeptDetail.vue';
+
+  const route = useRoute();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'education-training-plan-management-dept-create':
+        return '新增物品';
+      case 'education-training-plan-management-dept-edit':
+        return '编辑物品';
+      case 'education-training-plan-management-dept-view':
+        return '查看物品';
+      default:
+        return '未知操作';
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+
+  .safety-platform-container__header {
+    flex-direction: row !important;
+    justify-content: flex-start !important;
+    gap: 8px !important;
+  }
+</style>
+

+ 127 - 0
src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/components/employeeTrainingRecordCardManagementDetail.vue

@@ -0,0 +1,127 @@
+<template>
+  <main class="safety-platform-container__main">
+    <BasicForm
+      ref="basicFormRef"
+      :formData="ruleFormData"
+      :formRules="isViewMode ? undefined : formRules"
+      :formConfig="computedFormConfig"
+    />
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">返回</el-button>
+    <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
+      {{ isCreateMode ? '提交' : '保存' }}
+    </el-button>
+  </footer>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, ref } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import BasicForm from '@/components/BasicForm.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES } from '../configs/form';
+  import { queryInventoryDetail, saveInventory, updateInventory } from '@/api/inventory';
+
+  const router = useRouter();
+  const route = useRoute();
+
+  const operate = computed(() => (route.query.operate as string) || 'inventory-create');
+  const currentId = computed(() => Number(route.query.id));
+
+  const isCreateMode = computed(() => operate.value === 'inventory-create');
+  const isEditMode = computed(() => operate.value === 'inventory-edit');
+  const isViewMode = computed(() => operate.value === 'inventory-view');
+
+  const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
+    useFormConfigHook(INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES);
+
+  // 查看模式下,所有字段设为只读
+  const viewFormConfig = ref(
+    INVENTORY_FORM_CONFIG.map((item) => ({
+      ...item,
+      componentProps: {
+        ...item.componentProps,
+        disabled: true,
+      },
+    })),
+  );
+
+  const computedFormConfig = computed(() => {
+    if (isViewMode.value) {
+      return viewFormConfig.value;
+    }
+    return ruleFormConfig.value;
+  });
+
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+
+  const handleValidate = async () => {
+    if (!basicFormRef.value) return;
+    const res = await basicFormRef.value.validateForm();
+    return res;
+  };
+
+  const getDetail = async () => {
+    if (!currentId.value) return;
+    try {
+      const res = await queryInventoryDetail(currentId.value);
+      if (res) {
+        // 映射接口字段到表单字段
+        ruleFormData.itemName = res.stuffName; // 物品名称
+        ruleFormData.warehouseDate = res.inStoreTime ? res.inStoreTime.split('T')[0] : ''; // 入库日期(YYYY-MM-DD)
+        ruleFormData.itemQuantity = res.stuffQty; // 物品数量
+        ruleFormData.remarks = res.remark || ''; // 备注
+      }
+      cloneRuleFormData();
+    } catch (e) {
+      console.error('获取物品库存详情失败:', e);
+      ElMessage.error('获取详情失败');
+    }
+  };
+
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      const basePayload = {
+        stuffName: ruleFormData.itemName,
+        inStoreTime: ruleFormData.warehouseDate
+          ? new Date(ruleFormData.warehouseDate).toISOString()
+          : '',
+        stuffQty: ruleFormData.itemQuantity,
+        remark: ruleFormData.remarks || '',
+      };
+
+      if (isCreateMode.value) {
+        await saveInventory(basePayload);
+        ElMessage.success('创建成功');
+      } else if (isEditMode.value && currentId.value) {
+        await updateInventory({
+          id: currentId.value,
+          ...basePayload,
+        });
+        ElMessage.success('保存成功');
+      }
+
+      router.back();
+    } catch (e) {
+      console.error('保存物品库存失败:', e);
+      ElMessage.error('保存失败,请重试');
+    }
+  };
+
+  onMounted(() => {
+    cloneRuleFormData();
+    beforeRouteLeave();
+    if (isEditMode.value || isViewMode.value) {
+      getDetail();
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+</style>
+

+ 60 - 0
src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/configs/form.ts

@@ -0,0 +1,60 @@
+import { FormConfig } from '@/types/basic-form';
+
+export const INVENTORY_FORM_CONFIG: FormConfig[] = [
+  {
+    prop: 'itemName',
+    label: '物品名称:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入物品名称',
+    },
+  },
+  {
+    prop: 'warehouseDate',
+    label: '入库日期:',
+    component: 'ElDatePicker',
+    componentProps: {
+      type: 'date',
+      placeholder: '请选择入库日期',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    prop: 'itemQuantity',
+    label: '物品数量:',
+    component: 'ElInputNumber',
+    componentProps: {
+      min: 1,
+      max: 99999,
+      precision: 0, // 不允许小数点,只能输入整数
+      placeholder: '请输入物品数量',
+    },
+  },
+  {
+    label: '备注:',
+    prop: 'remarks',
+    component: 'ElInput',
+    componentProps: {
+      type: 'textarea',
+      rows: 5,
+      placeholder: '请输入备注',
+    },
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+};
+
+export const INVENTORY_FORM_RULES = {
+  itemName: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
+  warehouseDate: [{ required: true, message: '请选择入库日期', trigger: 'change' }],
+  itemQuantity: [
+    { required: true, message: '请输入物品数量', trigger: 'blur' },
+    { type: 'number', min: 1, message: '物品数量不能小于1', trigger: 'blur' },
+    { type: 'number', max: 99999, message: '物品数量不能大于99999', trigger: 'blur' },
+  ],
+};

+ 62 - 0
src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/configs/tables.ts

@@ -0,0 +1,62 @@
+import type { TableColumnProps } from '@/types/basic-table';
+
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  maxHeight: 'calc(70vh - 150px)',
+};
+
+export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+  },
+  {
+    label: '物品名称',
+    prop: 'itemName',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '入库日期',
+    prop: 'warehouseDate',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '物品数量',
+    prop: 'itemQuantity',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '经办人',
+    prop: 'handler',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '备注',
+    prop: 'remarks',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '状态',
+    prop: 'status',
+    slot: 'status',
+    align: 'center',
+    minWidth: '100px',
+  },
+  {
+    label: '操作',
+    prop: 'action',
+    slot: 'action',
+    fixed: 'right',
+    width: '180px',
+    align: 'left',
+  },
+];

+ 264 - 0
src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagement.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 物品库存管理 </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-table-container">
+        <header>
+          <div style="position: relative">
+            <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>
+            <el-button plain class="search-table-container--button" @click="handleDownload">
+              导出
+            </el-button>
+          </div>
+
+          <div class="act-search">
+            <section class="select-box">
+              <div class="select-box--item">
+                <span>物品名称:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.stuffName"
+                  placeholder="搜索物品名称"
+                  class="act-search-input"
+                />
+              </div>
+              <div class="select-box--item">
+                <span>状态:</span>
+                <el-select
+                  v-model="tableQuery.queryParam.status"
+                  placeholder="请选择状态"
+                  clearable
+                >
+                  <el-option label="启用" :value="true" />
+                  <el-option label="禁用" :value="false" />
+                </el-select>
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</el-button>
+            </section>
+          </div>
+        </header>
+
+        <div class="batch-table">
+          <BasicTable
+            ref="basicTableRef"
+            :tableData="tableData"
+            :tableConfig="tableConfig"
+            @update:pageSize="handleSizeChange"
+            @update:pageNumber="handleCurrentChange"
+          >
+            <template #status="scope">
+              <span>
+                {{ scope.row.statusName || (scope.row.status === true || scope.row.status === 'true' ? '启用' : scope.row.status === false || scope.row.status === 'false' ? '禁用' : '-') }}
+              </span>
+            </template>
+            <template #action="scope">
+              <div class="action-container--div" style="justify-content: left">
+                <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
+                <ActionButton
+                  text="删除"
+                  :popconfirm="{
+                    title: '确定要删除?',
+                  }"
+                  @confirm="handleDelete(scope.row.id)"
+                />
+                <ActionButton text="查看" @click="handleView(scope.row.id)" />
+              </div>
+            </template>
+          </BasicTable>
+        </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>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { TABLE_OPTIONS, INVENTORY_TABLE_COLUMNS } from './configs/tables';
+  import { useRouter } from 'vue-router';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import { queryInventoryManage, deleteInventory, exportInventory } from '@/api/inventory';
+  import { downloadByData } from '@/utils/file/download';
+  import BatchImport from '@/components/batch-import/BatchImport.vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
+
+  const router = useRouter();
+
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(INVENTORY_TABLE_COLUMNS, TABLE_OPTIONS);
+
+  const tableData = ref<any[]>([]);
+
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      stuffName: '', // 物品名称
+      status: true, // 状态,默认启用
+      ids: [], // 选择数据的ID
+    },
+  });
+
+  const handleSizeChange = (value: number) => {
+    pagination.pageSize = value;
+    tableQuery.pageSize = value;
+    getTableData();
+  };
+
+  const handleCurrentChange = (value: number) => {
+    pagination.pageNumber = value;
+    tableQuery.pageNumber = value;
+    getTableData();
+  };
+
+
+  async function getTableData() {
+    tableConfig.loading = true;
+    try {
+      const res = await queryInventoryManage(tableQuery);
+      if (res) {
+        // 映射返回数据字段到表格字段
+        tableData.value = res.records.map((item) => ({
+          id: item.id,
+          itemName: item.stuffName, // 物品名称
+          warehouseDate: item.inStoreTime, // 入库日期
+          itemQuantity: item.stuffQty, // 物品数量
+          handler: item.createdUserName, // 经办人
+          remarks: item.remark, // 备注
+          status: item.status, // 状态:true-启用,false-禁用
+          statusName: item.statusName, // 状态名称
+        }));
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取物品库存列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    tableQuery.queryParam.stuffName = '';
+    tableQuery.queryParam.status = true; // 重置为默认启用状态
+    tableQuery.queryParam.ids = [];
+    handleSearch();
+  };
+
+  // 批量导入
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/inventory/importInventory'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-inventory-template.xlsx');
+
+  const handleImport = () => {
+    batchImportVisible.value = true;
+  };
+
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    getTableData();
+  };
+
+  const handleDownload = async () => {
+    try {
+      const exportParams = {
+        stuffName: tableQuery.queryParam.stuffName || undefined,
+        status: tableQuery.queryParam.status,
+        ids: tableQuery.queryParam.ids.length > 0 ? tableQuery.queryParam.ids : undefined,
+      };
+      const response = await exportInventory(exportParams);
+      if (response) {
+        const fileName = `物品库存管理_${new Date().toISOString().split('T')[0]}.xlsx`;
+        downloadByData(response, fileName);
+        ElMessage.success('导出成功');
+      }
+    } catch (e) {
+      console.error('导出物品库存失败:', e);
+      ElMessage.error('导出失败,请重试');
+    }
+  };
+
+  const handleCreate = () => {
+    router.push({
+      name: 'employeeTrainingRecordCardManagementItem',
+      query: {
+        operate: 'employee-training-record-card-management-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'employeeTrainingRecordCardManagementItem',
+      query: {
+        id,
+        operate: 'employee-training-record-card-management-edit',
+      },
+    });
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await deleteInventory(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e) {
+      console.error('删除物品库存失败:', e);
+      ElMessage.error('删除失败,请重试');
+    }
+  };
+
+  const handleView = (id: number) => {
+    router.push({
+      name: 'employeeTrainingRecordCardManagementItem',
+      query: {
+        id,
+        operate: 'employee-training-record-card-management-view',
+      },
+    });
+  };
+
+  onMounted(() => {
+    getTableData();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  @use '@/views/traffic/violation/style/act-search-table.scss' as *;
+</style>

+ 44 - 0
src/views/production-safety/safetyTrainingAndEducation/employeeTrainingRecordCardManagement/employeeTrainingRecordCardManagementItem.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <BreadcrumbBack />
+      <span class="breadcrumb-title">{{ headerTitle }}</span>
+    </header>
+    <EmployeeTrainingRecordCardManagementDetail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import EmployeeTrainingRecordCardManagementDetail from './components/employeeTrainingRecordCardManagementDetail.vue';
+
+  const route = useRoute();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'employee-training-record-card-management-create':
+        return '新增物品';
+      case 'employee-training-record-card-management-edit':
+        return '编辑物品';
+      case 'employee-training-record-card-management-view':
+        return '查看物品';
+      default:
+        return '未知操作';
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+
+  .safety-platform-container__header {
+    flex-direction: row !important;
+    justify-content: flex-start !important;
+    gap: 8px !important;
+  }
+</style>
+