Explorar o código

feat:增加安全组织架构功能

sunqijun hai 1 mes
pai
achega
1cae80cc65

+ 15 - 0
src/router/routers/production-safety-router/productionSafetySystem.ts

@@ -225,6 +225,21 @@ const productionSafetySystemRoutes: RouteComponent[] = [{
         noCache: false,
       },
     },
+    {
+      id: 90030,
+      parentId: 9002,
+      name: 'securityOrganizationalStructure',
+      path: 'security-organizational-structure',
+      component: '/production-safety/productionSafetySystem/securityOrganizationalStructure/securityOrganizationalStructure',
+      meta: {
+        title: '安全组织体系架构',
+        icon: 'OverviewIcon',
+        activeMenu: '/work-safety/productionSafetySystem/securityOrganizationalStructure',
+        isRoot: false,
+        hidden: false,
+        noCache: false,
+      },
+    },
     {
       id: 90027,
       parentId: 9002,

+ 53 - 1
src/views/production-safety/productionSafetySystem/safetyOrganizationSystemManagement/safetyOrganizationSystemManagement.vue

@@ -75,6 +75,7 @@
         <div style="margin-bottom:20px">
             <el-button type="primary" :icon="Plus"  @click="handleCreate"> 添加 </el-button>
             <el-button plain  @click="handleImport">导入</el-button>
+            <!-- <el-button plain  @click="handlerEdit">架构</el-button> -->
         </div>
         <header>
           <div class="act-search">
@@ -144,6 +145,7 @@
             </template>
           </BasicTable>
         </div>
+        <OrgChart :treeData="treeData" @node-click="handleNodeClick" />
       </div>
     </main>
     <BatchImport
@@ -187,7 +189,7 @@
   Edit,
   Plus,
 } from '@element-plus/icons-vue'
-
+  import OrgChart from '@/views/emergency/components/OrgChart.vue';
 const position = ref('left')
 
   const router = useRouter();
@@ -216,6 +218,34 @@ const position = ref('left')
     },
   });
 
+  type OrganizationTreeType = {
+    id: string;
+    data: { name: string };
+    children?: OrganizationTreeType[];
+  };
+
+  const treeData = ref<OrganizationTreeType>({
+    id: 'root',
+    data: { name: '应急领导小组' },
+    children: [
+      {
+        id: 'group1',
+        data: { name: '应急指挥小组' },
+      },
+      {
+        id: 'group2',
+        data: { name: '应急响应小组' },
+      },
+      {
+        id: 'group3',
+        data: { name: '应急支援小组' },
+      },
+    ],
+  });
+
+  const handlerEdit = ()=>{
+    router.push({name: 'securityOrganizationalStructure'})
+  }
   const handleSizeChange = (value: number) => {
     pagination.pageSize = value;
     tableQuery.pageSize = value;
@@ -227,7 +257,11 @@ const position = ref('left')
     tableQuery.pageNumber = value;
     getTableData();
   };
+  const handleNodeClick = (nodeData: any) => {
+    // selectedTeamId.value = Number(nodeData.id);
 
+    // teamDetailDrawerRef.value?.drawerShow();
+  };
   async function getTableData() {
     tableConfig.loading = true;
     try {
@@ -269,11 +303,29 @@ const position = ref('left')
     };
     addSafetySystemVisible.value = true;
   };
+
+// id: 'root',
+//     data: { name: '应急领导小组' },
+//     children: [
+//       {
+//         id: 'group1',
+//         data: { name: '应急指挥小组' },
+//       },
+//       {
+//         id: 'group2',
+//         data: { name: '应急响应小组' },
+//       },
+//       {
+//         id: 'group3',
+//         data: { name: '应急支援小组' },
+//       },
+//     ],
   // 获取组织列表
   const fetchSafetyOrganizationTeamList = async () => {
     try {
       const res = await getSafetySystemList();
       fetchSafetyOrganizationList.value = res;
+    //   treeData.value = res.map(li=>({id:li.orgId,data:{name: li.orgName}}))
     } catch (error) {
       ElMessage.error('获取组织列表失败');
     }

+ 76 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/components/addSafetySystem.vue

@@ -0,0 +1,76 @@
+<!--
+ * @Author: liuJie
+ * @Date: 2026-02-05 10:20:24
+ * @LastEditors: liuJie
+ * @LastEditTime: 2026-02-05 20:25:10
+ * @Describe: 添加组织
+-->
+<script lang="ts" setup>
+  import { ref, watch } from 'vue';
+
+  const props = withDefaults(
+    defineProps<{
+      visible?: boolean;
+      data?: any;
+    }>(),
+    {
+      visible: false,
+      data: () => ({}),
+    },
+  );
+  const team = ref('');
+  const emit = defineEmits(['update:visible', 'confirmAddSafetySystem']);
+  const dialogVisible = ref(props.visible);
+
+  const title = ref('添加组织');
+
+  watch(
+    () => props.visible,
+    (val) => {
+      dialogVisible.value = val;
+      if (val) {
+        if (props.data.type === 'parent' && props.data.action === 'add') {
+          title.value = '添加组织';
+        } else if (props.data.type === 'parent' && props.data.action === 'edit') {
+          title.value = '编辑组织';
+          team.value = props.data.orgName;
+        } else if (props.data.type === 'children' && props.data.action === 'add') {
+          title.value = '添加子组织';
+        } else {
+          title.value = '编辑子组织';
+          team.value = props.data.orgName;
+        }
+      }
+    },
+  );
+
+  watch(dialogVisible, (val) => {
+    emit('update:visible', val);
+  });
+
+  const confirmSave = () => {
+    emit('confirmAddSafetySystem', { value: team.value, ...props.data });
+    handleClose();
+  };
+  function handleClose() {
+    dialogVisible.value = false;
+    team.value = '';
+  }
+</script>
+<template>
+  <div class="content">
+    <el-dialog v-model="dialogVisible" :title="title" width="500" :before-close="handleClose">
+      <div class="flex items-center">
+        <span style="width: 120px">组织名称:</span>
+        <el-input v-model="team"></el-input>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="handleClose">取消</el-button>
+          <el-button type="primary" @click="confirmSave"> 确认 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<style lang="scss" scoped></style>

+ 224 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/components/safetyOrganizationSystemManagementDetail.vue

@@ -0,0 +1,224 @@
+<template>
+  <main class="safety-platform-container__main">
+    <el-form
+      ref="formRef"
+      :model="form"
+      :rules="rules"
+      label-width="100px"
+      style="max-width: 600px"
+      label-position="left"
+    >
+      <el-form-item label="员工工号:" prop="employeeId" required>
+        <el-input v-model="form.employeeId" placeholder="请输入员工工号" :disabled="isViewMode" />
+      </el-form-item>
+
+      <el-form-item label="员工姓名:" prop="employeeName" required>
+        <el-input v-model="form.employeeName" placeholder="请输入员工姓名" :disabled="isViewMode" />
+      </el-form-item>
+
+      <el-form-item label="组织名称:" prop="orgId" required>
+        <el-tree-select
+          placeholder="请选择组织名称"
+          v-model="form.orgId"
+          :data="organizations"
+          check-strictly
+          :render-after-expand="false"
+          @change="handleOrgChange"
+          :disabled="isViewMode"
+        >
+        </el-tree-select>
+      </el-form-item>
+
+      <el-form-item label="岗位职责:" prop="jobResp" required>
+        <el-input
+          v-model="form.jobResp"
+          type="textarea"
+          :rows="4"
+          :maxlength="300"
+          placeholder="请填写岗位职责"
+          :disabled="isViewMode"
+        />
+      </el-form-item>
+
+      <!-- 状态 -->
+      <el-form-item label="状态:" prop="status" required>
+        <el-radio-group v-model="form.status" :disabled="isViewMode">
+          <el-radio :value="1">启用</el-radio>
+          <el-radio :value="2">禁用</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+  </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 { FORM_RULES } from '../configs/form';
+  import {
+    getSafetySystemList,
+    addEmployee,
+    updateEmployee,
+    employeeDetail,
+  } from '@/api/safety-organization-management';
+
+  const router = useRouter();
+  const route = useRoute();
+
+  const operate = computed(() => (route.query.operate as string) || 'employee-create');
+  const currentId = computed(() => Number(route.query.id));
+
+  const isCreateMode = computed(() => operate.value === 'employee-create');
+  const isEditMode = computed(() => operate.value === 'employee-edit');
+  const isViewMode = computed(() => operate.value === 'employee-view');
+
+  interface FormDataType {
+    id?: number;
+    orgId?: string;
+    employeeId: string;
+    employeeName: string;
+    jobResp: string;
+    status: string|number;
+    selectedOrg?: any;
+  }
+
+  const form = ref<FormDataType>({
+    id: undefined,
+    orgId: '',
+    employeeId: '',
+    employeeName: '',
+    jobResp: '',
+    status: 1,
+  });
+  const selectedOrg = ref({});
+  // 表单验证规则
+  const rules = ref(FORM_RULES);
+  // 组织名称选项
+  const organizations = ref([]);
+  // 获取组织列表
+  const fetchSafetyOrganizationTeamList = async () => {
+    try {
+      const res = await getSafetySystemList();
+      organizations.value = formatTreeData(res);
+    } catch (error) {
+      ElMessage.error('获取组织列表失败');
+    }
+  };
+  // 组织名称剔除空的children
+  const formatTreeData = (data) => {
+    return data
+      .map((node) => {
+        const children = node.children?.length ? formatTreeData(node.children) : undefined;
+        return {
+          value: node.orgId,
+          label: node.orgName,
+          orgId: node.orgId,
+          orgName: node.orgName,
+          children,
+        };
+      })
+      .filter((node) => node); // 过滤掉无效节点
+  };
+  // 在树数据中根据 value 查找节点
+  const findNodeByValue = (tree, value) => {
+    if (!tree || !Array.isArray(tree)) {
+      return null;
+    }
+
+    for (const node of tree) {
+      // 如果当前节点的value匹配,返回该节点
+      if (node.value === value || node.orgId === value) {
+        return node;
+      }
+
+      // 如果有子节点,递归查找
+      if (node.children && node.children.length > 0) {
+        const found = findNodeByValue(node.children, value);
+        if (found) {
+          return found;
+        }
+      }
+    }
+    return null;
+  };
+
+  // 选中组织变化时的回调
+  const handleOrgChange = (value) => {
+    // 通过 value 在 organizations 树中查找对应的节点
+    const selectedNode = findNodeByValue(organizations.value, value);
+
+    if (selectedNode) {
+      // 存储当前选中的组织信息
+      form.value.orgId = selectedNode.orgId;
+      selectedOrg.value = selectedNode;
+    }
+  };
+
+  const formRef = ref();
+
+  const handleValidate = async () => {
+    if (!formRef.value) return;
+    const res = await formRef.value.validateField();
+    return res;
+  };
+
+  const getDetail = async () => {
+    if (!currentId.value) return;
+    try {
+      const res = await employeeDetail(currentId.value);
+      if (res) {
+        // 详情页面(映射接口字段到表单字段)
+        Object.assign(form.value, res);
+      }
+    } catch (e) {
+      console.error('获取详情失败:', e);
+      ElMessage.error('获取详情失败');
+    }
+  };
+
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      console.log(res, form.value);
+
+      if (isCreateMode.value) {
+        await addEmployee(form.value);
+        ElMessage.success('创建成功');
+      } else if (isEditMode.value && currentId.value) {
+        await updateEmployee({
+          id: currentId.value,
+          ...form.value,
+        });
+        ElMessage.success('保存成功');
+      }
+
+      router.back();
+    } catch (e) {
+      console.error('保存失败:', e);
+      ElMessage.error('保存失败,请重试');
+    }
+  };
+
+  onMounted(() => {
+    fetchSafetyOrganizationTeamList();
+    if (isEditMode.value || isViewMode.value) {
+      getDetail();
+    }
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+
+  .el-form-item {
+    margin-bottom: 30px;
+  }
+</style>

+ 76 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/configs/form.ts

@@ -0,0 +1,76 @@
+/*
+ * @Author: liuJie
+ * @Date: 2026-01-27 16:29:28
+ * @LastEditors: liuJie
+ * @LastEditTime: 2026-02-06 13:35:38
+ * @Describe: file describe
+ */
+import { FormConfig } from '@/types/basic-form';
+
+export const INVENTORY_FORM_CONFIG: FormConfig[] = [
+  {
+    prop: 'itemName',
+    label: '员工工号:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入员工工号',
+    },
+  },
+  {
+    prop: 'itemName',
+    label: '员工姓名:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入员工姓名',
+    },
+  },
+  {
+    prop: 'itemQuantity',
+    label: '组织名称:',
+    component: 'ElSelect',
+    componentProps: {
+      placeholder: '请选择组织',
+    },
+  },
+  {
+    label: '岗位职责:',
+    prop: 'remarks',
+    component: 'ElInput',
+    componentProps: {
+      type: 'textarea',
+      rows: 5,
+      placeholder: '请填写岗位职责',
+    },
+  },
+  {
+    prop: 'status',
+    label: '状态:',
+    slot: 'status',
+  },
+];
+
+export const INVENTORY_FORM_DATA = {
+  itemName: '',
+  warehouseDate: '',
+  itemQuantity: 1, // 最小值为1
+  remarks: '',
+  orgId: '',
+  jobResp: '',
+};
+// 表单验证规则
+export const FORM_RULES = {
+  employeeId: [
+    { required: true, message: '请输入员工工号', trigger: 'blur' },
+    { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' },
+  ],
+  employeeName: [
+    { required: true, message: '请输入员工姓名', trigger: 'blur' },
+    { min: 1, max: 10, message: '长度在 1 到 10 个字符', trigger: 'blur' },
+  ],
+  orgId: [{ required: true, message: '请选择组织名称', trigger: 'change' }],
+  jobResp: [
+    { required: true, message: '请填写岗位职责', trigger: 'blur' },
+    { min: 1, max: 300, message: '最大字数300字', trigger: 'blur' },
+  ],
+  status: [{ required: true, message: '请选择状态', trigger: 'blur' }],
+};

+ 70 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/configs/tables.ts

@@ -0,0 +1,70 @@
+/*
+ * @Author: liuJie
+ * @Date: 2026-01-27 16:29:28
+ * @LastEditors: liuJie
+ * @LastEditTime: 2026-03-07 20:34:07
+ * @Describe: file describe
+ */
+import type { TableColumnProps } from '@/types/basic-table';
+
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  maxHeight: 'calc(70vh - 150px)',
+};
+
+export const TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+  },
+  {
+    label: '员工工号',
+    prop: 'employeeId',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '员工姓名',
+    prop: 'employeeName',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '状态',
+    prop: 'status',
+    slot: 'status',
+    align: 'left',
+    minWidth: '120px',
+  },
+
+  {
+    label: '组织名称',
+    prop: 'orgName',
+    align: 'left',
+    minWidth: '140px',
+  },
+  {
+    label: '岗位职责',
+    prop: 'jobResp',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '添加时间',
+    prop: 'createdAt',
+    align: 'center',
+    minWidth: '140px',
+  },
+  {
+    label: '操作',
+    prop: 'action',
+    slot: 'action',
+    fixed: 'right',
+    width: '180px',
+    align: 'left',
+  },
+];

+ 533 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/securityOrganizationalStructure.vue

@@ -0,0 +1,533 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 安全组织体系架构 </div>
+    </header>
+    <main class="safety-platform-container__main flex">
+      <div class="nav">
+        <el-button type="primary" :icon="Plus" @click="addTeam('parent')"> 添加组织 </el-button>
+        
+        <div class="collapse-wrapper">
+            <!-- expand-icon-position="left" 版本小了,不支持 -->
+          <el-collapse accordion v-model="activeName" v-if="fetchSafetyOrganizationList.length > 0">
+            <el-collapse-item :name="item.orgId" v-for="item in fetchSafetyOrganizationList">
+              <template #title="{ isActive }">
+                <div :class="['title-wrapper', { 'is-active': isActive }]">
+                  <span @click.stop="querySafetyTeamData('parent', item)">
+                    {{ item.orgName }}
+                  </span>
+                  <div class="handler">
+                    <el-button :icon="Plus" size="small" type="default" dashed @click.stop="handleCreateSafetySystem('children', item)"
+                      ></el-button
+                    >
+                    <el-button size="small" type="primary" link @click.stop="handleEditSafetySystem('parent', item, '')"
+                      >编辑</el-button
+                    >
+                    <el-button  size="small" type="danger" link @click.stop="handleDelSafetySystem('parent', item)"
+                      >删除</el-button
+                    >
+                  </div>
+                </div>
+              </template>
+              <div class="collapse-item-content">
+                <div v-if="item.children.length > 0">
+                  <ul>
+                    <li class="flex" v-for="children in item.children" :key="children.orgId">
+                      <span @click="querySafetyTeamData('children', children)">
+                        {{ children.orgName }}
+                      </span>
+                      <div class="handler">
+                        <el-button
+                          link
+                          size="small"
+                          type="primary"
+                          @click.stop="handleEditSafetySystem('children', children, item.orgId)"
+                          >编辑</el-button
+                        >
+                        <el-button
+                          link
+                          size="small"
+                          type="danger"
+                          @click.stop="handleDelSafetySystem('children', children)"
+                          >删除</el-button
+                        >
+                      </div>
+                    </li>
+                  </ul>
+                </div>
+                <div v-else>
+                  <el-empty description="未添加子组织" :image-size="40" />
+                </div>
+              </div>
+            </el-collapse-item>
+          </el-collapse>
+          <div v-else>
+            <el-empty description="未添加组织" />
+          </div>
+        </div>
+        <AddSafetySystem
+          v-model:visible="addSafetySystemVisible"
+          :data="addSafetyOrganizationSystemFormData"
+          @confirmAddSafetySystem="confirmAddSafetySystemCallback"
+        />
+      </div>
+      <div class="search-table-container table-content">
+        <div style="margin-bottom:20px">
+            <el-button type="primary" :icon="Plus"  @click="handleCreate"> 添加 </el-button>
+            <el-button plain  @click="handleImport">导入</el-button>
+        </div>
+        <header>
+          <div class="act-search">
+            <section class="select-box">
+              <div class="select-box--item">
+                <span>搜索工号/姓名:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.keyword"
+                  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="1" />
+                  <el-option label="禁用" :value="2" />
+                </el-select>
+              </div>
+              <div class="select-box--item">
+                <span>日期范围:</span>
+                <el-date-picker
+                  v-model="dateRange"
+                  @change="onchangeDateRange"
+                  type="daterange"
+                  range-separator="至"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  value-format="YYYY-MM-DD"
+                  format="YYYY-MM-DD"
+                />
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</el-button>
+              <el-button plain  @click="handleDownload">导出</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.status === 1 ? '启用' : scope.row.status === 2 ? '禁用' : '-' }}
+              </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="true"
+      @close="batchImportVisible = false"
+      @update="handleUpdate"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage, ElMessageBox } from 'element-plus';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { TABLE_OPTIONS, TABLE_COLUMNS } from './configs/tables';
+  import { useRouter } from 'vue-router';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import {
+    getSafetySystemList,
+    addSafetySystem,
+    updateSafetySystem,
+    deleteSafetySystem,
+    fetchTableList,
+    delEmployee,
+    exportSafetyOrganizationSystemManagement
+  } from '@/api/safety-organization-management';
+  import { downloadByData } from '@/utils/file/download';
+  import BatchImport from '@/components/batch-import/BatchImport.vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
+  import AddSafetySystem from './components/addSafetySystem.vue';
+  import {
+  Delete,
+  Edit,
+  Plus,
+} from '@element-plus/icons-vue'
+
+const position = ref('left')
+
+  const router = useRouter();
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(TABLE_COLUMNS, TABLE_OPTIONS);
+
+  const tableData = ref<any[]>([]);
+
+  const fetchSafetyOrganizationList = ref<any[]>([]);
+
+  const activeName = ref('');
+  // 日期范围(用于日期选择器)
+  const dateRange = ref<[string, string] | string>('');
+
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      classifyName: '',
+      keyword: '',
+      status: '',
+      startTime: '',
+      endTime: '',
+    },
+  });
+
+  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 fetchTableList(tableQuery);
+      if (res) {
+        tableData.value = res.records
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  interface addSafetyOrganizationSystemFormDataType {
+    type: String;
+    orgName?: String;
+    orgId?: String | number;
+    action?: String;
+    parentid?: String | number;
+  }
+
+  const addSafetySystemVisible = ref(false);
+  const addSafetyOrganizationSystemFormData = ref<addSafetyOrganizationSystemFormDataType>({
+    type: '',
+    orgName: '',
+    orgId: '',
+    action: '',
+    parentid: '',
+  });
+  // 一级新增
+  const addTeam = (type) => {
+    addSafetyOrganizationSystemFormData.value = {
+      type,
+      action: 'add',
+    };
+    addSafetySystemVisible.value = true;
+  };
+  // 获取组织列表
+  const fetchSafetyOrganizationTeamList = async () => {
+    try {
+      const res = await getSafetySystemList();
+      fetchSafetyOrganizationList.value = res;
+    } catch (error) {
+      ElMessage.error('获取组织列表失败');
+    }
+  };
+  // 定义组织数据类型
+  interface SafetySystemFormData {
+    value: string; // 输入的组织名称
+    action: 'add' | 'edit'; // 操作类型:新增或编辑
+    orgId?: string | number; // 组织ID(编辑时必传)
+    parentid?: string | number; // 父组织ID(新增子组织时必传)
+    type?: 'children' | 'parent'; // 组织类型(子组织或根组织)
+  }
+
+  // 保存弹窗回调
+  const confirmAddSafetySystemCallback = async (formData: SafetySystemFormData) => {
+    try {
+      if (!formData.value?.trim()) {
+        ElMessage.warning('请输入有效的组织名称!');
+        return;
+      }
+      const requestData = {
+        orgName: formData.value.trim(),
+        id: formData.action === 'edit' ? formData.orgId : undefined,
+        parentid: formData.action === 'add' ? formData.parentid : undefined,
+      };
+      // console.log(formData, 'formData')
+      if (formData.action === 'add') {
+        if (formData.type === 'children' && formData.orgId) {
+          requestData.parentid = formData.orgId;
+        }
+        await addSafetySystem(requestData);
+        ElMessage.success('新增组织成功!');
+      } else {
+        // 如果是子类,补充父级ID
+        if (formData.type === 'children' && formData.parentid) {
+          requestData.parentid = formData.parentid;
+        }
+        await updateSafetySystem(requestData);
+        ElMessage.success('编辑组织成功!');
+      }
+      // 刷新列表
+      fetchSafetyOrganizationTeamList();
+    } catch (error) {
+      console.error('操作失败:', error);
+      ElMessage.error(formData.action === 'add' ? '新增组织失败!' : '编辑组织失败!');
+    }
+  };
+  // 二级新增
+  const handleCreateSafetySystem = async (type, value) => {
+    addSafetyOrganizationSystemFormData.value = {
+      type,
+      action: 'add',
+      orgName: value.orgName,
+      orgId: value.orgId,
+    };
+    addSafetySystemVisible.value = true;
+    // 打开某一个
+    activeName.value = value.orgId;
+  };
+  // 编辑
+  const handleEditSafetySystem = (type, value, parentid) => {
+    // console.log(value)
+    addSafetySystemVisible.value = true;
+    addSafetyOrganizationSystemFormData.value = {
+      type,
+      action: 'edit',
+      orgName: value.orgName,
+      orgId: value.orgId,
+      parentid,
+    };
+  };
+  // 删除
+  const handleDelSafetySystem = async (type, value) => {
+    // console.log('删除', type, value)
+    ElMessageBox.confirm('确认删除该组织吗?', '警告', { type: 'warning' }).then(async () => {
+      try {
+        await deleteSafetySystem(value.orgId);
+
+        ElMessage.success('删除成功');
+        // 刷新列表
+        fetchSafetyOrganizationTeamList();
+        handleReset();
+      } catch (error) {
+        ElMessage.error(error || '删除失败');
+      }
+    });
+  };
+  // 查询
+  const querySafetyTeamData = (type, value) => {
+    console.log(type, '查询', value);
+    tableQuery.queryParam.classifyName = value.orgId;
+    getTableData();
+  };
+  // 时间查询
+  const onchangeDateRange = () => {
+    if (dateRange.value && Array.isArray(dateRange.value) && dateRange.value.length === 2) {
+      tableQuery.queryParam.startTime = dateRange.value[0] || '';
+      tableQuery.queryParam.endTime = dateRange.value[1] || '';
+    } else {
+      tableQuery.queryParam.startTime = '';
+      tableQuery.queryParam.endTime = '';
+    }
+    getTableData();
+  };
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    pagination.pageNumber = 1;
+    tableQuery.queryParam = {
+      classifyName: '',
+      keyword: '',
+      status: '', // 重置为默认启用状态
+      startTime: '',
+      endTime: '',
+    };
+    dateRange.value = '';
+    handleSearch();
+  };
+
+  // 批量导入
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/safetyorguser/importSafetyOrgUser'));
+  const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/安全组织体系管理导入模版.xlsx');
+
+  const handleImport = () => {
+    batchImportVisible.value = true;
+  };
+
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    getTableData();
+  };
+
+  const handleDownload = async () => {
+    try {
+      const response = await exportSafetyOrganizationSystemManagement(tableQuery.queryParam);
+      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: 'SafetyOrganizationSystemManagementItem',
+      query: {
+        operate: 'employee-create',
+      },
+    });
+  };
+
+  const handleEdit = (id: number) => {
+    router.push({
+      name: 'SafetyOrganizationSystemManagementItem',
+      query: {
+        id,
+        operate: 'employee-edit',
+      },
+    });
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await delEmployee(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e) {
+      console.error('删除员工失败:', e);
+      ElMessage.error('删除失败,请重试');
+    }
+  };
+
+  const handleView = (id: number) => {
+    router.push({
+      name: 'SafetyOrganizationSystemManagementItem',
+      query: {
+        id,
+        operate: 'employee-view',
+      },
+    });
+  };
+
+  onMounted(() => {
+    fetchSafetyOrganizationTeamList();
+    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 *;
+  .table-content {
+  }
+  .nav {
+    flex: 0 0 300px;
+    margin-right: 15px;
+    padding-right: 15px;
+    border-right: 1px solid #eee;
+    :deep(.collapse-title) {
+        flex: 1 0 90%;
+        order: 1;
+        .el-collapse-item__header {
+            flex: 1 0 auto;
+            order: -1;
+        }
+    }
+    .collapse-wrapper {
+      margin-top: 10px;
+
+      .title-wrapper {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        width: 100%;
+
+        .handler {
+          flex: 1;
+          display: flex;
+          justify-content: flex-end;
+          align-items: center;
+          padding-right: 15px;
+        }
+      }
+
+      .collapse-item-content {
+        ul {
+          padding-left: 40px;
+
+          li {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            width: 100%;
+            padding: 6px 0;
+            border-bottom: 1px solid #eeeeeed1;
+
+            span {
+              cursor: pointer;
+            }
+          }
+        }
+      }
+    }
+  }
+</style>

+ 51 - 0
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/securityOrganizationalStructureItme.vue

@@ -0,0 +1,51 @@
+<!--
+ * @Author: liuJie
+ * @Date: 2026-01-27 16:29:28
+ * @LastEditors: liuJie
+ * @LastEditTime: 2026-02-06 09:35:00
+ * @Describe: file describe
+-->
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <BreadcrumbBack />
+      <span class="breadcrumb-title">{{ headerTitle }}</span>
+    </header>
+    <SafetyOrganizationSystemManagementDetail />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
+  import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
+  import SafetyOrganizationSystemManagementDetail from './components/safetyOrganizationSystemManagementDetail.vue';
+
+  const route = useRoute();
+  const router = useRouter();
+  const operate = route.query.operate as string;
+
+  const headerTitle = computed(() => {
+    switch (operate) {
+      case 'employee-create':
+        return '新增员工';
+      case 'employee-edit':
+        return '编辑员工';
+      case 'employee-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>

+ 3 - 1
src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationSystemAdvancedPerson.vue

@@ -13,6 +13,9 @@
     <main class="safety-platform-container__main">
       <div class="search-table-container">
         <header>
+            <div style="margin-bottom:20px">
+               <el-button type="primary" @click="openAddDialog"> 添加 </el-button>
+            </div>
           <div class="act-search">
             <section class="select-box">
               <div class="select-box--item">
@@ -53,7 +56,6 @@
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
-              <el-button type="primary" @click="openAddDialog"> 添加 </el-button>
               <el-button plain @click="handleExport"> 导出 </el-button>
             </section>
           </div>