Ver código fonte

Merge branch 'feat/production-safety-evaluation' into feat/production-safety

xiaweibo 2 meses atrás
pai
commit
53242a3a86

+ 60 - 1
src/router/routers/production-safety-router/risk-identification-and-control.ts

@@ -549,8 +549,67 @@ import { RouteComponent } from "vue-router";
             hidden: false,
             noCache: false,
           }
+        },    
+        //劳防用品采购申请
+        {
+          id: 93030,
+          parentId: 90014,
+          name: 'laborProductsPurchaseApplyManage',
+          path: 'labor-products-purchase-apply-manage',
+          component: '/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/list',
+          meta: {
+            title: '劳防用品采购申请',
+            icon: 'OverviewIcon',
+            isRoot: false,
+            hidden: false,
+            noCache: false,
+          }    
+        },
+        {
+          id: 93031,
+          parentId: 90014,
+          name: 'laborProductsPurchaseApplyManageItem',
+          path: 'labor-products-purchase-apply-manage-item',
+          component: '/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/item',
+          meta: {
+            title: '劳防用品采购申请详情',
+            activeMenu: '/work-safety/risk-identification-and-control/labor-products-purchase-apply-manage',
+            icon: 'OverviewIcon',
+            isRoot: false,
+            hidden: false,
+            noCache: false,
+          }
+        },
+        //工伤认定申请
+        {
+          id: 93032,
+          parentId: 90014,
+          name: 'workInjuryApplyManage',
+          path: 'work-injury-apply-manage',
+          component: '/production-safety/risk-identification-and-control/work-injury-apply-manage/list',
+          meta: {
+            title: '工伤认定申请',
+            icon: 'OverviewIcon',
+            isRoot: false,
+            hidden: false,
+            noCache: false,
+          }
+        },
+        {
+          id: 93033,
+          parentId: 90014,
+          name: 'workInjuryApplyManageItem',
+          path: 'work-injury-apply-manage-item',
+          component: '/production-safety/risk-identification-and-control/work-injury-apply-manage/item',
+          meta: {
+            title: '工伤认定申请详情',
+            activeMenu: '/work-safety/risk-identification-and-control/work-injury-apply-manage',
+            icon: 'OverviewIcon',
+            isRoot: false,
+            hidden: false,
+            noCache: false,
+          }
         },
-
       ],
     }];
      

+ 44 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/Item.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>
+    <Detail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import Detail from './components/detail.vue';
+
+  const route = useRoute();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'inventory-create':
+        return '新增劳防用品采购申请';
+      case 'inventory-edit':
+        return '编辑劳防用品采购申请';
+      case 'inventory-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>
+

+ 136 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/components/detail.vue

@@ -0,0 +1,136 @@
+<template>
+  <main class="safety-platform-container__main">
+    <BasicForm
+      ref="basicFormRef"
+      :formData="ruleFormData"
+      :formRules="isViewMode ? undefined : formRules"
+      :formConfig="computedFormConfig"
+    >
+      <template #status>
+        <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
+          <el-radio label="ENABLE">启用</el-radio>
+          <el-radio label="DISABLE">禁用</el-radio>
+        </el-radio-group>
+      </template>
+    </BasicForm>
+  </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.status = res.status ? 'ENABLE' : 'DISABLE'; // 状态
+        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,
+        status: ruleFormData.status === 'ENABLE',
+        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>
+

+ 68 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/configs/form.ts

@@ -0,0 +1,68 @@
+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: '请输入备注',
+    },
+  },
+  {
+    prop: 'status',
+    label: '状态:',
+    slot: 'status',
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  status: 'ENABLE',
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+};
+
+export const INVENTORY_FORM_RULES = {
+  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+  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' },
+  ],
+  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+};

+ 62 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/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/risk-identification-and-control/labor-products-purchase-apply-manage/list.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: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        operate: 'inventory-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        id,
+        operate: 'inventory-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: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        id,
+        operate: 'inventory-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/risk-identification-and-control/work-injury-apply-manage/Item.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>
+    <Detail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import Detail from './components/detail.vue';
+
+  const route = useRoute();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'inventory-create':
+        return '新增工伤认定申请';
+      case 'inventory-edit':
+        return '编辑工伤认定申请';
+      case 'inventory-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>
+

+ 136 - 0
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/components/detail.vue

@@ -0,0 +1,136 @@
+<template>
+  <main class="safety-platform-container__main">
+    <BasicForm
+      ref="basicFormRef"
+      :formData="ruleFormData"
+      :formRules="isViewMode ? undefined : formRules"
+      :formConfig="computedFormConfig"
+    >
+      <template #status>
+        <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
+          <el-radio label="ENABLE">启用</el-radio>
+          <el-radio label="DISABLE">禁用</el-radio>
+        </el-radio-group>
+      </template>
+    </BasicForm>
+  </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.status = res.status ? 'ENABLE' : 'DISABLE'; // 状态
+        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,
+        status: ruleFormData.status === 'ENABLE',
+        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>
+

+ 68 - 0
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/form.ts

@@ -0,0 +1,68 @@
+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: '请输入备注',
+    },
+  },
+  {
+    prop: 'status',
+    label: '状态:',
+    slot: 'status',
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  status: 'ENABLE',
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+};
+
+export const INVENTORY_FORM_RULES = {
+  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+  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' },
+  ],
+  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+};

+ 62 - 0
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/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/risk-identification-and-control/work-injury-apply-manage/list.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: 'workInjuryApplyManageItem',
+      query: {
+        operate: 'inventory-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'workInjuryApplyManageItem',
+      query: {
+        id,
+        operate: 'inventory-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: 'workInjuryApplyManageItem',
+      query: {
+        id,
+        operate: 'inventory-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>