Jelajahi Sumber

feat:角色管理

刘恒 1 tahun lalu
induk
melakukan
1bee467777

+ 1 - 1
.env.development

@@ -4,7 +4,7 @@ VITE_APP_SETTING = true
 VITE_APP_TITLE = 掌柜运营平台
 # 接口请求地址,会设置到 axios 的 baseURL 参数上
 # VITE_APP_API_BASEURL = https://test.baoxianzhanggui.com/web-api #默认地址
-VITE_APP_API_BASEURL = http://192.168.5.148:8001  #田智
+VITE_APP_API_BASEURL = http://192.168.0.253:8001  #田智
 
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
 VITE_APP_DEBUG_TOOL =

+ 11 - 1
src/api/modules/role.ts

@@ -5,7 +5,7 @@ interface roleData {
   system: string,
   permsId: number[],
   perms: string[],
-  [key:string]:any,
+  [property:string]:any,
 }
 const roleApi = (axiosInstance: ApiInstance) => ({
   // 新增角色
@@ -16,6 +16,16 @@ const roleApi = (axiosInstance: ApiInstance) => ({
 
   //查询角色信息
   roleInfo:(params: roleData) => axiosInstance.get('userRole/roleGet', {params}),
+
+  //编辑角色信息
+  roleUpdate:(data: roleData) => axiosInstance.post('userRole/roleUpdate', data,{}),
+ 
+  //角色删除检测
+  roleDeleteValid:(data: roleData) => axiosInstance.post('userRole/roleDeleteValid', data,{}),
+
+   //角色删除检测
+   roleDelete:(data: roleData) => axiosInstance.post('userRole/roleDelete', data,{}),
+ 
 })
 
 export default roleApi

+ 5 - 4
src/utils/composables/routeUtils.ts

@@ -26,11 +26,11 @@ export interface TreeNode {
   [key: string]: any; // 允许任意其他属性
 }
 
-let idCounter = 0;
+let idCounter = ref(0);
 
 // 生成唯一 ID
 const generateId = (): number => {
-  return ++idCounter;
+  return ++idCounter.value;
 };
 
 // 预处理函数:确保 children 和 auth 类型正确
@@ -73,7 +73,8 @@ const parseAuthToChildren = (authList: string[]): TreeNode[] => {
 };
 
 // 将 RouteItem 转换为 TreeNode
-export const convertToTreeData = (routes: RouteItem[]): TreeNode[] => {
+export const convertToTreeData = (id:number,routes: RouteItem[]): TreeNode[] => {
+  idCounter.value=id;
   return routes.map(route => {
     const node: TreeNode = {
       id: generateId(),
@@ -83,7 +84,7 @@ export const convertToTreeData = (routes: RouteItem[]): TreeNode[] => {
     };
 
     if (route.children && route.children.length > 0) {
-      node.children = convertToTreeData(route.children);
+      node.children = convertToTreeData(idCounter.value,route.children);
     }
 
     if (route.meta?.auth) {

+ 113 - 96
src/views/personnel/memberManagement.vue

@@ -7,17 +7,23 @@
         placeholder="姓名,手机号,工号查询"
         :prefix-icon="Search"
       />
-
+      <div style="display: flex;justify-content: space-between;margin: 10px 0;">
+        <el-button type="primary" link @click="$router.push({path:'/personnel/institution'})">机构管理</el-button>
+        <el-button type="primary" link @click="expandTree">
+        
+          {{ isExpand?'全部收起':'全部展开' }}
+        </el-button>
+      </div>
       <el-tree
         ref="treeRef"
         style="max-width: 600px"
         class="filter-tree"
-        :data="data"
+        :data="deptTreeData"
         :props="defaultProps"
-        default-expand-all
+        :default-expand-all="isExpand"
         :filter-node-method="filterNode"
         :expand-on-click-node="false"
-      >
+        >
         <template #default="{ node, data }">
           <span class="custom-tree-node">
             <span>{{ node.label }}</span>
@@ -40,40 +46,7 @@
       </el-tree>
     </div>
     <div class="member-main">
-      <!-- <el-form label-position="left" label-width="auto">
-        <el-row :gutter="16">
-          <el-col :span="6">
-            <el-form-item label="成员信息">
-              <el-input
-                v-model="formAccessibility.firstName"
-                placeholder="输入姓名,手机号,工号查询"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="6">
-            <el-form-item label="状态">
-              <el-select
-                v-model="value"
-                placeholder="成员状态"
-                style="width: 240px"
-              >
-                <el-option
-                  v-for="item in options"
-                  :key="item.value"
-                  :label="item.label"
-                  :value="item.value"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="">
-              <el-button type="primary">查询</el-button>
-              <el-button type="primary" plain @click="reset">重置</el-button>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form> -->
+      
 
       <Table
         :tableData="tableData"
@@ -147,6 +120,7 @@
                 v-model.trim="form.name"
                 maxlength="20"
                 placeholder="输入人员姓名"
+                clearable
               />
             </el-form-item>
           </el-col>
@@ -156,12 +130,30 @@
                 v-model.number.trim="form.phone"
                 maxlength="11"
                 placeholder="输入人员手机号"
+                clearable
               />
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="角色" prop="role">
+              <el-select
+                v-model="form.role"
+                placeholder="选择角色"
+                clearable
+              >
+                <el-option
+                  v-for="item in options"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
           <el-col :span="12">
             <el-form-item label="登录密码" prop="password">
               <el-input
+              clearable
                 v-model.trim="form.password"
                 maxlength="20"
                 placeholder="输入登录密码(8-20位,包含字母和数字)"
@@ -169,11 +161,11 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="角色" prop="role">
+            <el-form-item label="所在机构" prop="role">
               <el-select
                 v-model="form.role"
-                placeholder="选择角色"
-                style="width: 240px"
+                placeholder="选择机构"
+                clearable
               >
                 <el-option
                   v-for="item in options"
@@ -184,6 +176,38 @@
               </el-select>
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="证件号" prop="password">
+              <el-input
+                v-model.trim="form.password"
+                maxlength="18"
+                placeholder="输入身份证号码"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="证件有效期始" prop="password">
+              <el-date-picker
+                v-model="value1"
+                type="date"
+                placeholder="请选择证件有效期始"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="证件有效期止" prop="password">
+              <el-date-picker
+                class="date-picker-custom"
+                v-model="value1"
+                type="date"
+                placeholder="请选择证件有效期止"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+         
         </el-row>
       </el-form>
       <template #footer>
@@ -199,14 +223,39 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watch } from "vue";
+import { ref, reactive, watch,onMounted } from "vue";
 import type { ComponentSize, FormInstance, FormRules } from "element-plus";
 import { Plus, Search } from "@element-plus/icons-vue";
 
 import type Node from "element-plus/es/components/tree/src/model/node";
 // import universalTable from "../"
 import { ElTree } from "element-plus";
+import api from "@/api";
+onMounted(() => {
+  // 对 asyncRoutes 进行预处理并转换为树形结构
+
+
+  getInstitutionList();
+});
+//机构列表
+type tableType = {
+  id: string;
+  name: string;
+};
+//获取机构列表
+const deptTreeData = ref<tableType[]>([]);
 
+const getInstitutionList = () => {
+  // pageInfo.pageNum=item.pageNum
+  api.institutionApi.institutionList({}).then((res) => {
+    // if(res.code===200){
+
+    // }
+    deptTreeData.value = [...res.data];
+    // deptTreeData.value = JSON.parse(JSON.stringify(res.data));
+  });
+  // pageInfo.pageSize=item.pageSize
+};
 interface Tree {
   [key: string]: any;
 }
@@ -216,7 +265,7 @@ const treeRef = ref<InstanceType<typeof ElTree>>();
 
 const defaultProps = {
   children: "children",
-  label: "label",
+  label: "name",
 };
 
 watch(filterText, (val) => {
@@ -224,59 +273,23 @@ watch(filterText, (val) => {
 });
 
 const filterNode = (value: string, data: Tree) => {
+  // console.log(data)
   if (!value) return true;
-  return data.label.includes(value);
+  return data.name.includes(value);
 };
-const data: Tree[] = [
-  {
-    id: 1,
-    label: "Level one 1",
-    children: [
-      {
-        id: 4,
-        label: "Level two 1-1",
-        children: [
-          {
-            id: 9,
-            label: "Level three 1-1-1",
-          },
-          {
-            id: 10,
-            label: "Level three 1-1-2",
-          },
-        ],
-      },
-    ],
-  },
-  {
-    id: 2,
-    label: "Level one 2",
-    children: [
-      {
-        id: 5,
-        label: "Level two 2-1",
-      },
-      {
-        id: 6,
-        label: "Level two 2-2",
-      },
-    ],
-  },
-  {
-    id: 3,
-    label: "Level one 3",
-    children: [
-      {
-        id: 7,
-        label: "Level two 3-1",
-      },
-      {
-        id: 8,
-        label: "Level two 3-2",
-      },
-    ],
-  },
-];
+//全部展示机构树,或全部收起
+const isExpand=ref(false)
+function expandTree(){
+  // console.log(treeRef.value)
+  
+
+  isExpand.value=!isExpand.value
+  // treeRef.value!.defaultExpandAll=!isExpand.value;
+  const nodes = treeRef.value?.store._getAllNodes();
+  if (nodes) {
+    nodes.forEach((item: any) => item.expanded = isExpand.value);
+  }
+}
 
 const formAccessibility = reactive({
   fullName: "",
@@ -433,7 +446,11 @@ const reset = (item: any) => {
   // ElMessage({})
 };
 </script>
-<style lang="scss" scoped>
+<style lang="scss" scoped>、
+:deep(.date-picker-custom.el-input__inner){
+  display: flex;
+  flex-direction: row-reverse;
+}
 :deep(.table-title-btn){
   display: flex;
   align-items: center;
@@ -451,7 +468,7 @@ const reset = (item: any) => {
   display: flex;
   .member-tree {
     flex: none;
-    width: 20%;
+    width: 18%;
     padding: 20px;
     box-sizing: border-box;
     border-right: 1px solid #ccc;

+ 193 - 35
src/views/personnel/roleManagement.vue

@@ -47,7 +47,7 @@
               <span class="strong">角色管理</span>
               <div>
                 <ElButton type="primary" @click="roleSubmit">提交</ElButton>
-                <ElButton type="danger" @click="roleSubmit">删除</ElButton>
+                <ElButton type="danger" @click="deleteRole">删除</ElButton>
               </div>
             </div>
           </template>
@@ -76,6 +76,7 @@
                 v-model="roleInfo.deptId"
                 :options="tableData"
                 :props="tableProps"
+                @change="deptChange"
               />
             </ElFormItem>
             <ElFormItem prop="desc" label="角色描述">
@@ -109,12 +110,15 @@
                   show-checkbox
                   node-key="id"
                   :default-expand-all="true"
-                  :default-checked-keys="[6]"
+                  :default-checked-keys="checkedKeys"
                   @check="handleCheckChange"
                 />
               </el-tab-pane>
               <el-tab-pane label="组织架构权限">
-                <el-radio-group v-model="roleInfo.dataMark">
+                <el-radio-group
+                  v-model="roleInfo.dataMark"
+                  @change="dataMarkChange"
+                >
                   <el-radio value="1" size="large">可见本机构下的数据</el-radio>
                   <el-radio value="2" size="large"
                     >可见本机构及下级机构数据</el-radio
@@ -123,13 +127,15 @@
                 </el-radio-group>
                 <ElTree
                   style="max-height: 500px; overflow-y: auto"
-                  :data="tableData"
+                  :data="deptTreeData"
                   :props="deptProps"
                   show-checkbox
                   node-key="id"
+                  :check-strictly="true"
                   :default-expand-all="true"
-                  :default-checked-keys="[6]"
+                  :default-checked-keys="roleInfo.deptIds"
                   @check="handleDeptCheckChange"
+                  ref="deptTree"
                 />
               </el-tab-pane>
             </el-tabs>
@@ -142,8 +148,13 @@
 
 <script lang="ts" setup>
 import { asyncRoutes } from "@/router/routes"; // 请根据实际路径调整
-import { ref, reactive, onMounted } from "vue";
-import type { FormRules } from "element-plus";
+import { ref, reactive, onMounted, onBeforeMount, } from "vue";
+import {
+  type FormRules,
+  type ElTree,
+  ElMessage,
+  ElMessageBox,
+} from "element-plus";
 import {
   preprocessRoutes,
   convertToTreeData,
@@ -157,9 +168,9 @@ let roleInfo = reactive<{
   desc: string;
   system: string;
   permsId: number[];
-  perms: string[];
+  perms: any[];
   deptId: string;
-  deptIds: [];
+  deptIds: any[];
   dataMark: string;
 }>({
   name: "", //角色
@@ -201,12 +212,9 @@ const handleCheckChange = (
   // console.log(checkedInfo.checkedNodes);
   roleInfo.permsId = checkedInfo.checkedKeys;
   roleInfo.perms = [];
-  checkedInfo.checkedNodes.forEach((val) => {
-    if (val.type === "2") {
-      roleInfo.perms.push(val.auth);
-    }
-  });
+  roleInfo.perms = checkedInfo.checkedNodes;
 };
+
 const handleDeptCheckChange = (
   _checkedNodes: TreeNode[],
   checkedInfo: {
@@ -218,7 +226,9 @@ const handleDeptCheckChange = (
 ) => {
   // console.log('Checked keys:', checkedInfo.checkedKeys);
   // console.log(checkedInfo.checkedNodes);
+
   roleInfo.deptIds = checkedInfo.checkedKeys;
+
   // roleInfo.perms = [];
   // checkedInfo.checkedNodes.forEach((val) => {
   //   if (val.type === "2") {
@@ -248,18 +258,24 @@ const info = ref<institutionData>({
   system: "",
 });
 //获取机构列表
+const deptTreeData = ref<tableType[]>([]);
 const getInstitutionList = () => {
   // pageInfo.pageNum=item.pageNum
   api.institutionApi.institutionList(info.value).then((res) => {
     // if(res.code===200){
 
     // }
-    tableData.value = res.data;
+    tableData.value = [...res.data];
+    deptTreeData.value = JSON.parse(JSON.stringify(res.data));
   });
   // pageInfo.pageSize=item.pageSize
 };
 
 const showRole = ref(false);
+interface Perm {
+  id: string; // 根据实际情况,id 可能是数字或字符串
+}
+const checkedKeys = ref<string[]>([]);
 //选择角色
 function chooseRole(item: any) {
   roleId.value = item.id;
@@ -273,10 +289,23 @@ function chooseRole(item: any) {
       perms: [],
     })
     .then((res) => {
-      console.log(res);
+      // console.log(res);
       roleInfo.name = res.data.name;
+
       roleInfo.desc = res.data.remark;
       roleInfo.deptId = res.data.deptId;
+      roleInfo.permsId = res.data.perms.map((val: Perm) => val.id);
+      roleInfo.perms = res.data.perms;
+      checkedKeys.value = res.data.perms.map((val: Perm) => val.id);
+      if(res.data.dataScope?.deptIds){
+        deptTree.value!.setCheckedKeys(res.data.dataScope?.deptIds, true);
+        roleInfo.dataMark = res.data.dataScope?.dataMark;
+      }else{
+        deptTree.value!.setCheckedKeys([], false);
+        roleInfo.dataMark = '1';
+      }
+      
+      roleInfo.deptIds = res.data.dataScope?.deptIds;
     });
   operationType.value = "edit";
   // roleInfo.name=item.name
@@ -295,29 +324,153 @@ function addRole() {
   showRole.value = true;
   operationType.value = "add";
 }
-
+//提交角色信息
 function roleSubmit() {
-  let role = { ...roleInfo };
-
   if (operationType.value == "add") {
-    api.roleApi.roleAdd({ 
-      // roleId: roleInfo.id,
-      name: roleInfo.name,
-      desc: roleInfo.desc,
-      system: localStorage.getItem("login_system") || '',
-      permsId: roleInfo.permsId,
-      perms: roleInfo.perms,
-      deptId:roleInfo.deptId[roleInfo.deptId.length-1],
-      dataScope:{
-        deptIds:roleInfo.deptIds,
-        dataMark:roleInfo.dataMark,
-
-      }
-     }).then((res) => {
+    api.roleApi
+      .roleAdd({
+        name: roleInfo.name,
+        desc: roleInfo.desc,
+        system: localStorage.getItem("login_system") || "",
+        permsId: roleInfo.permsId,
+        perms: roleInfo.perms,
+        deptId: roleInfo.deptId,
+        dataScope: {
+          deptIds: roleInfo.deptIds,
+          dataMark: roleInfo.dataMark,
+        },
+      })
+      .then((res) => {
+        // console.log(res);
+        ElMessage.success("操作成功");
+        getRoleList();
+      });
+  } else {
+    api.roleApi
+      .roleUpdate({
+        // roleId: roleInfo.id,
+        id: roleId.value,
+        name: roleInfo.name,
+        desc: roleInfo.desc,
+        system: localStorage.getItem("login_system") || "",
+        permsId: roleInfo.permsId,
+        perms: roleInfo.perms,
+        deptId: roleInfo.deptId,
+        dataScope: {
+          deptIds: roleInfo.deptIds,
+          dataMark: roleInfo.dataMark,
+        },
+      })
+      .then((res) => {
+        // console.log(res);
+        ElMessage.success("操作成功");
+        getRoleList();
+      });
+  }
+}
+//删除角色
+function deleteRole() {
+  api.roleApi
+    .roleDeleteValid({
+      id: roleId.value,
+    })
+    .then((res) => {
       console.log(res);
+      if (res.code == 200) {
+        ElMessageBox.confirm("确认要删除当前数据吗?", "提示", {
+          confirmButtonText: "确认",
+          cancelButtonText: "取消",
+          type: "warning",
+        })
+          .then(() => {
+            api.roleApi
+              .roleDelete({
+                id: roleId.value,
+              })
+              .then((res) => {
+                ElMessage.success("操作成功");
+                getRoleList();
+              });
+          })
+          .catch(() => {
+            ElMessage.warning("用户取消操作");
+          });
+      }
     });
+}
+// const deptIds=re
+//机构单选切换
+const dataMarkChange = () => {
+  deptIds.value = [];
+  disabledTree(deptTreeData.value, roleInfo.deptId);
+  roleInfo.deptIds = deptIds.value;
+  if (deptIds.value.length == 0) {
+    deptTree.value!.setCheckedKeys([], false);
   } else {
+    deptTree.value!.setCheckedKeys(deptIds.value, true);
+  }
+};
+
+//根据所属id 判断组织架构权限是否可选
+function deptChange(e:any) {
+  // console.log(e)
+  if (e != undefined) {
+    let deptId = e[e?.length - 1];
+    roleInfo.deptId = deptId;
+    // if(roleInfo.dataMark=='1'){
+    // let arr=[...deptTreeData.value]
+    deptIds.value=[]
+    disabledTree(deptTreeData.value, deptId);
+    deptTree.value!.setCheckedKeys(deptIds.value, true);
+    roleInfo.deptIds = deptIds.value;
+  }else{
+    roleInfo.deptId=''
+  }
+
+  // deptTree.value.defaultCheckedKeys=roleInfo.deptIds
+  // }
+}
+const deptIds = ref([]);
+const deptTree = ref<InstanceType<typeof ElTree>>();
+//禁用机构选择
+function disabledTree(list: any[], id: string) {
+  for (let i = 0; i < list.length; i++) {
+    const node = list[i];
+
+    if (roleInfo.dataMark == "1") {
+      if (id) {
+        if (list[i].id != id) {
+          list[i].disabled = true;
+        } else {
+          deptIds.value.push(list[i].id);
+        }
+      } else {
+        list[i].disabled = false;
+      }
+    } else if (roleInfo.dataMark == "2") {
+      if (id) {
+        if (list[i].id != id && list[i].parentId != id) {
+          list[i].disabled = true;
+        } else {
+          list[i].disabled = false;
+          deptIds.value.push(list[i].id);
+        }
+        // if (list[i].disabled == false) {
+        //   deptIds.value.push(list[i].id);
+        // }
+      } else {
+        list[i].disabled = false;
+      }
+
+      //
+    } else {
+      list[i].disabled = false;
+    }
+    // console.log(list[i].disabled)
 
+    if (node.children && node.children.length > 0) {
+      disabledTree(node.children, id);
+    }
   }
 }
 
@@ -341,10 +494,15 @@ function getRoleList() {
       // roleId.value = res.data[0].id;
     });
 }
-onMounted(() => {
+onBeforeMount(() => {
   // 对 asyncRoutes 进行预处理并转换为树形结构
   const processedRoutes = preprocessRoutes(asyncRoutes);
-  treeData.value = convertToTreeData(processedRoutes);
+
+  treeData.value = convertToTreeData(0, processedRoutes);
+});
+onMounted(() => {
+  // 对 asyncRoutes 进行预处理并转换为树形结构
+
   getRoleList();
   getInstitutionList();
 });