Răsfoiți Sursa

feat:角色管理及机构管理

刘恒 1 an în urmă
părinte
comite
d974a357eb

+ 4 - 0
src/api/index.ts

@@ -8,6 +8,8 @@ import Message from 'vue-m-message'
 import appApi from './modules/app'
 import appApi from './modules/app'
 import userApi from './modules/user'
 import userApi from './modules/user'
 import roleApi from './modules/role'
 import roleApi from './modules/role'
+import institutionApi from "./modules/institution"
+import areaApi from "./modules/area"
 
 
 const axiosInstance:ApiInstance = axios.create({
 const axiosInstance:ApiInstance = axios.create({
   baseURL: (import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY === 'true') ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
   baseURL: (import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY === 'true') ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
@@ -87,5 +89,7 @@ const api = {
   appApi: appApi(axiosInstance),
   appApi: appApi(axiosInstance),
   userApi: userApi(axiosInstance),
   userApi: userApi(axiosInstance),
   roleApi: roleApi(axiosInstance),
   roleApi: roleApi(axiosInstance),
+  institutionApi:institutionApi(axiosInstance),
+  areaApi:areaApi(axiosInstance)
 }
 }
 export default api
 export default api

+ 11 - 0
src/api/modules/area.ts

@@ -0,0 +1,11 @@
+import { ApiInstance } from '../type'
+
+const areaApi = (axiosInstance: ApiInstance) => ({
+  // 后端获取路由数据
+  areaList: (data:any) => axiosInstance.post('area/queryTree',data,{}),
+
+  systemList: (params:any) => axiosInstance.get('dict/get',{ params }),
+  
+})
+
+export default areaApi

+ 22 - 0
src/api/modules/institution.ts

@@ -0,0 +1,22 @@
+import { ApiInstance } from '../type'
+interface institutionData {
+  name: string;
+  system: string,
+  [key: string]: any; // 允许其他任意字段
+}
+const institutionApi = (axiosInstance: ApiInstance) => ({
+  // 新增机构
+  institutionAdd: (data: institutionData) => axiosInstance.post('/dept/add', data, {}),
+
+  //机构树形列表
+  institutionList:(data: institutionData) => axiosInstance.post('/dept/getDeptTree', data, {}),
+
+  //编辑
+  institutionEdit: (data: institutionData) => axiosInstance.post('/dept/update', data, {}),
+  //删除
+  institutionDel: (data: institutionData) => axiosInstance.post('/dept/delete', data, {}),
+
+
+})
+
+export default institutionApi

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

@@ -4,11 +4,18 @@ interface roleData {
   desc: string;
   desc: string;
   system: string,
   system: string,
   permsId: number[],
   permsId: number[],
-  perms: string[]
+  perms: string[],
+  [key:string]:any,
 }
 }
 const roleApi = (axiosInstance: ApiInstance) => ({
 const roleApi = (axiosInstance: ApiInstance) => ({
   // 新增角色
   // 新增角色
   roleAdd: (data: roleData) => axiosInstance.post('userRole/roleAdd', data, {}),
   roleAdd: (data: roleData) => axiosInstance.post('userRole/roleAdd', data, {}),
+
+  //角色列表
+  roleList:(data: roleData) => axiosInstance.post('userRole/roleList', data, {}),
+
+  //查询角色信息
+  roleInfo:(params: roleData) => axiosInstance.get('userRole/roleGet', {params}),
 })
 })
 
 
 export default roleApi
 export default roleApi

+ 6 - 4
src/components/LoginForm/index.vue

@@ -40,11 +40,11 @@ const form = ref({
   captcha: "",
   captcha: "",
   system: "PLATFORM",
   system: "PLATFORM",
   captchaId: 0,
   captchaId: 0,
-  systemCode: "",
+  system: "",
 });
 });
 const rules = ref<FormRules>({
 const rules = ref<FormRules>({
   userName: [{ required: true, trigger: "blur", message: "请输入用户名" }],
   userName: [{ required: true, trigger: "blur", message: "请输入用户名" }],
-  systemCode: [{ required: true, trigger: "blur", message: "请输入租户编码" }],
+  system: [{ required: true, trigger: "blur", message: "请输入租户编码" }],
   passWord: [
   passWord: [
     { required: true, trigger: "blur", message: "请输入密码" },
     { required: true, trigger: "blur", message: "请输入密码" },
     { min: 6, max: 18, trigger: "blur", message: "密码长度为6到18位" },
     { min: 6, max: 18, trigger: "blur", message: "密码长度为6到18位" },
@@ -86,9 +86,11 @@ function handleLogin() {
         .then(() => {
         .then(() => {
           if (remember) {
           if (remember) {
             localStorage.setItem("login_account", form.value.userName);
             localStorage.setItem("login_account", form.value.userName);
+
           } else {
           } else {
             localStorage.removeItem("login_account");
             localStorage.removeItem("login_account");
           }
           }
+          localStorage.setItem("login_system", form.value.system);
           emits("onLogin", form.value.userName);
           emits("onLogin", form.value.userName);
         })
         })
         .finally(() => {
         .finally(() => {
@@ -165,9 +167,9 @@ function handleLogin() {
         <div class="tag"></div>
         <div class="tag"></div>
       </div>
       </div>
       <div>
       <div>
-        <ElFormItem prop="systemCode">
+        <ElFormItem prop="system">
           <ElInput
           <ElInput
-            v-model="form.systemCode"
+            v-model="form.system"
             placeholder="输入租户编码"
             placeholder="输入租户编码"
             type="text"
             type="text"
             tabindex="1"
             tabindex="1"

+ 168 - 171
src/components/Table/index.vue

@@ -1,191 +1,188 @@
-
 <template>
 <template>
-	<div class="container">
-		<div class="table_box">
+  <div class="container">
+    <div class="table_box">
       <div class="table-title-btn">
       <div class="table-title-btn">
         <slot name="table-title-btn"></slot>
         <slot name="table-title-btn"></slot>
       </div>
       </div>
-			<el-table ref="multipleTableRef"  :header-cell-style="{'background-color': '#f2f2f2'}" :data="props.tableData" style="width: 100%">
-				<el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" />
-				
-				<el-table-column v-if="showIndex" type="index" label="序号" width="55" />
-				<el-table-column
-					v-for="column in columns"
-					:key="column.prop"
-					:prop="column.prop"
-					:label="column.label"
-					:sortable="column.sortable ? 'custom' : false"
-					:width="column.width"
-				>
-					<template #header>
-						<slot name="search" :slotData="column"></slot>
-					</template>
-					<template #default="{ row }">
-						<template v-if="row._edit">
-							<el-input
-								v-if="
-									column.prop === 'Date' || column.prop === 'Name' || column.prop === 'Organization'
-								"
-								v-model="row[column.prop]"
-							/>
-							<el-select v-if="column.prop === 'Tag'" v-model="Tagvalue">
-								<el-option
-									v-for="(type, label) in tagTypes"
-									:key="label"
-									:label="label"
-									:value="label"
-								/>
-							</el-select>
-						</template>
-						<template v-else>
-							<span v-if="column.prop !== 'Tag'">{{ row[column.prop] }}</span>
-							<el-tag v-if="column.prop === 'Tag'" :type="getTagType(row[column.prop])">{{
-								row[column.prop]
-							}}</el-tag>
-						</template>
-					</template>
-				</el-table-column>
-				<el-table-column label="操作">
-                    <template #default="scope">
-                        <slot name="operation" :operationData="scope"></slot>
-                    </template>
-					<!-- <template #default="scope">
+      <el-table
+        ref="multipleTableRef"
+        :header-cell-style="{ 'background-color': '#f2f2f2' }"
+        :data="props.tableData"
+        style="width: 100%"
+        row-key="id"
+        :tree-props="props.treeProps"
+      >
+        <el-table-column
+          type="selection"
+          v-if="showSelect"
+          :selectable="selectable"
+          width="55"
+        />
+
+        <el-table-column
+          v-if="showIndex"
+          type="index"
+          label="序号"
+          width="55"
+        />
+        <el-table-column
+          v-for="column in columns"
+          :key="column.prop"
+          :prop="column.prop"
+          :label="column.label"
+          :sortable="column.sortable ? 'custom' : false"
+          :width="column.width"
+        >
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+          <template #default="scope">
+            
+          
+              
+              <component v-if="column.formatter" :is="column.formatter(scope.row, column, scope.row[column.prop], scope.$index)" class="routerlick" />
+              <span v-else>{{ scope.row[column.prop] }}</span>
+            
+            
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template #default="scope">
+            <slot name="operation" :operationData="scope"></slot>
+          </template>
+          <!-- <template #default="scope">
 						<div v-if="scope.row._edit">
 						<div v-if="scope.row._edit">
 							<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
 							<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
 							<el-button @click="handleCancle(scope)"> 取消 </el-button>
 							<el-button @click="handleCancle(scope)"> 取消 </el-button>
 						</div>
 						</div>
 						<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
 						<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
 					</template> -->
 					</template> -->
-				</el-table-column>
-			</el-table>
-		</div>
-		<div class="page_box">
-			<el-pagination
-                v-model:current-page="props.pageInfo.pageNum"
-                v-model:page-size="props.pageInfo.pageSize"
-                :page-sizes="props.pageInfo.sizes"
-                layout="total, sizes, prev, pager, next, jumper"
-                :total="props.pageInfo.total"
-                @size-change="handleSizeChange"
-                @current-change="handleCurrentChange"
-            />
-		</div>
-	</div>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="page_box">
+      <el-pagination
+        v-model:current-page="props.pageInfo.pageNum"
+        v-model:page-size="props.pageInfo.pageSize"
+        :page-sizes="props.pageInfo.sizes"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="props.pageInfo.total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
 </template>
 </template>
- 
-<script lang="ts" setup>
-	import { defineProps, ref, onMounted,defineEmits  } from "vue";
-	import type { TableInstance } from "element-plus";
- 
-	interface TableRow {
-		[key: string]: any; 
-	}
-    interface TagTypes{
-        [key:string]:string;
-    }
-    const emit = defineEmits(['getList']);
-	const props = defineProps<{
-		tableData: Array<TableRow>;
-		columns: {
-			prop: string;
-			label: string;
-			sortable?: boolean;
-			width?: string;
-			style?: string;
-		}[];
-        pageInfo:{
-            pageNum: number;
-            pageSize: number;
-            sizes: number[];
-            total: number;
-        };
-        showSelect:boolean;
-        showIndex:boolean;
 
 
-	}>();
-	const multipleTableRef = ref<TableInstance>();
-	const TableData = ref<TableRow[]>();
-	onMounted(() => {
-		let counter = 1;
-		TableData.value = props.tableData.map((row) => ({
-			...row,
-			_edit: false, // 设置默认非编辑状态
-			counter: counter++
-		}));
-	});
-	const selectable = () => true;
-	const Tagvalue = ref("");
-	const tagTypes:TagTypes = {
-		'公司': "primary",
-		'甲方': "success",
-		'合作伙伴': "primary"
-	};
-    const handleSizeChange = (val: number) => {
-        
-        props.pageInfo.pageSize=val
-      
-        emit('getList', props.pageInfo);
-    }
-    const handleCurrentChange = (val: number) => {
-   
-        props.pageInfo.pageNum=val
-        emit('getList', props.pageInfo);
+<script lang="ts" setup>
+import { defineProps, ref, onMounted, defineEmits } from "vue";
+import type { TableInstance } from "element-plus";
 
 
+interface TableRow {
+  [key: string]: any;
+}
+interface TagTypes {
+  [key: string]: string;
+}
+const emit = defineEmits(["getList"]);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    formatter?: (row: any, column: any, cellValue: any, index: number) => any;
+  }[];
+  pageInfo: {
+    pageNum: number;
+    pageSize: number;
+    sizes: number[];
+    total: number;
+  };
+  showSelect: boolean;
+  showIndex: boolean;
+  treeProps:{}
+}>();
+const multipleTableRef = ref<TableInstance>();
+const TableData = ref<TableRow[]>();
+onMounted(() => {
+  let counter = 1;
+  TableData.value = props.tableData.map((row) => ({
+    ...row,
+    _edit: false, // 设置默认非编辑状态
+    counter: counter++,
+  }));
+});
+const selectable = () => true;
+const Tagvalue = ref("");
+const tagTypes: TagTypes = {
+  公司: "primary",
+  甲方: "success",
+  合作伙伴: "primary",
+};
+const handleSizeChange = (val: number) => {
+  props.pageInfo.pageSize = val;
 
 
-    }
-	const getTagType = (tag: string) => {
-		return tagTypes[tag] || tagTypes["公司"];
-	};
-	const handleEdit = (scope: { row: TableRow }) => {
-		Tagvalue.value = scope.row.Tag || "";
-		scope.row._edit = true;
-	};
-	const handleSave = (scope: { row: TableRow }) => {
-		scope.row.Tag = Tagvalue.value;
-		scope.row._edit = false;
-	};
-	const handleCancle = (scope: { row: TableRow }) => {
-		scope.row._edit = false;
-	};
-  const headClass= (scope: { row: TableRow }) =>  {
-      // return 'background:#F8F8F9'
-    }
+  emit("getList", props.pageInfo);
+};
+const handleCurrentChange = (val: number) => {
+  props.pageInfo.pageNum = val;
+  emit("getList", props.pageInfo);
+};
+const getTagType = (tag: string) => {
+  return tagTypes[tag] || tagTypes["公司"];
+};
+const handleEdit = (scope: { row: TableRow }) => {
+  Tagvalue.value = scope.row.Tag || "";
+  scope.row._edit = true;
+};
+const handleSave = (scope: { row: TableRow }) => {
+  scope.row.Tag = Tagvalue.value;
+  scope.row._edit = false;
+};
+const handleCancle = (scope: { row: TableRow }) => {
+  scope.row._edit = false;
+};
+const headClass = (scope: { row: TableRow }) => {
+  // return 'background:#F8F8F9'
+};
 </script>
 </script>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-	.container {
-		width: 100%;
-		height: auto;
-		margin: 5px;
-		display: flex;
-		flex-direction: column;
-	}
- .table_box {
-		flex: 1;
-		margin-bottom: 10px;
-		overflow-y: auto;
-		.table_item_container {
-			margin-left: 20px;
-			padding: 0;
-			.table_item {
-				margin: 5px 0;
-			}
-		}
-    :deep(.cell){
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-    .table-title-btn {
-      margin-bottom: 20px;
-      :deep(.el-button){
-        margin-right: 20px;
-      }
+.container {
+  width: 100%;
+  height: auto;
+  margin: 5px;
+  display: flex;
+  flex-direction: column;
+}
+.table_box {
+  flex: 1;
+  margin-bottom: 10px;
+  overflow-y: auto;
+  .table_item_container {
+    margin-left: 20px;
+    padding: 0;
+    .table_item {
+      margin: 5px 0;
     }
     }
-    
-	}
-  .page_box{
+  }
+  :deep(.cell) {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
-    justify-content: flex-end;
+    justify-content: center;
+  }
+  .table-title-btn {
+    margin-bottom: 20px;
+    :deep(.el-button) {
+      margin-right: 20px;
+    }
   }
   }
-</style>
+}
+.page_box {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 11 - 1
src/router/modules/personnel.ts

@@ -8,7 +8,7 @@ const routes: RouteRecordRaw = {
   redirect: '/personnel/member',
   redirect: '/personnel/member',
   name: 'Personnel',
   name: 'Personnel',
   meta: {
   meta: {
-    title: '成员管理',
+    title: '组织架构',
   },
   },
   children: [
   children: [
     {
     {
@@ -19,6 +19,16 @@ const routes: RouteRecordRaw = {
         title: '成员管理',
         title: '成员管理',
 
 
 
 
+      },
+    },
+    {
+      path: 'institution',
+      name: 'personnelInstitution',
+      component: () => import('@/views/personnel/institutionManage.vue'),
+      meta: {
+        title: '机构管理',
+
+
       },
       },
     },
     },
     {
     {

+ 9 - 9
src/router/routes.ts

@@ -61,16 +61,16 @@ const systemRoutes: RouteRecordRaw[] = [
 
 
 // 动态路由(异步路由、导航栏路由)
 // 动态路由(异步路由、导航栏路由)
 const asyncRoutes: Route.recordMainRaw[] = [
 const asyncRoutes: Route.recordMainRaw[] = [
-  {
-    meta: {
-      title: '演示',
-      icon: 'i-uim:box',
-    },
-    children: [
-      MultilevelMenuExample,
+  // {
+  //   meta: {
+  //     title: '演示',
+  //     icon: 'i-uim:box',
+  //   },
+  //   children: [
+  //     MultilevelMenuExample,
 
 
-    ],
-  },
+  //   ],
+  // },
   {
   {
     meta: {
     meta: {
       title: '组织架构',
       title: '组织架构',

+ 628 - 0
src/views/personnel/institutionManage.vue

@@ -0,0 +1,628 @@
+<template>
+  <div class="institution">
+    <div class="institution-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="输入机构名称查找"
+                :prefix-icon="Search"
+              />
+            </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"
+        :columns="columns"
+        :showIndex="false"
+        :showSelect="true"
+        :pageInfo="pageInfo"
+        @getList="getList"
+        :treeProps="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+        <template v-slot:table-title-btn>
+          <div>
+            <el-button type="primary" @click="add(ruleFormRef)">
+              <el-icon><Plus /></el-icon>添加机构</el-button
+            >
+            <el-dropdown placement="bottom-start">
+              <el-button>
+                批量操作<el-icon class="el-icon--right">
+                  <arrow-down />
+                </el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item>删除</el-dropdown-item>
+                  <el-dropdown-item>设置负责人</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </div>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button
+            type="primary"
+            link
+            @click="addChild(scope.operationData.row)"
+          >
+            添加子机构
+          </el-button>
+          <el-dropdown placement="bottom-start">
+            <span class="el-dropdown-link"> 更多 </span>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item v-on:click="editor(scope.operationData.row)"
+                  >编辑</el-dropdown-item
+                >
+                <el-dropdown-item v-on:click="del(scope.operationData.row)">
+                  删除
+                </el-dropdown-item>
+                <el-dropdown-item command="set">设置负责人</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:search="scope">
+          {{ scope.slotData.label }}
+        </template>
+      </Table>
+    </div>
+
+    <el-dialog v-model="dialogVisible" title="基本信息" width="800" draggable>
+      <el-form
+        label-position="top"
+        label-width="auto"
+        :rules="rules"
+        :model="form"
+        ref="ruleFormRef"
+      >
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form-item label="机构名称" prop="name">
+              <el-input
+                v-model.trim="form.name"
+                maxlength="50"
+                placeholder="输入机构名称"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上级机构" prop="parentId">
+              <el-cascader
+                clearable
+                style="width: 100%"
+                v-model="form.parentId"
+                :options="tableData"
+                :props="tableProps"
+                @change="handleChange"
+              />
+              <!-- <el-select v-model="form.parentId" placeholder="选择上级机构" clearable>
+                <el-option
+                  v-for="item in tableData"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+
+              </el-select> -->
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="机构类型" prop="comType">
+              <el-select
+                v-model="form.comType"
+                placeholder="选择机构类型"
+                clearable
+              >
+                <el-option
+                  v-for="item in comTypeList"
+                  :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="area">
+              <el-cascader
+                clearable
+                style="width: 100%"
+                v-model="form.area"
+                :options="areaOptins"
+                :props="areaProps"
+                @change="handleChange"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="展业范围" prop="exhibitionScope">
+              <el-select
+                v-model="form.exhibitionScope"
+                placeholder="选择展业范围"
+                multiple
+                clearable
+              >
+                <el-option
+                  v-for="item in exhibitionScopeTypeList"
+                  :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="address">
+              <el-input
+                v-model.trim="form.address"
+                maxlength="50"
+                placeholder="请输入机构地址"
+                type="textarea"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="机构负责人" prop="leaderId">
+              <el-select
+                v-model="form.leaderId"
+                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-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="confirm(ruleFormRef)">
+            保 存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+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 { ElMessage, ElMessageBox, ElPopconfirm, ElTree } from "element-plus";
+import api from "@/api";
+import { h } from "vue";
+
+interface Tree {
+  [key: string]: any;
+}
+
+const filterText = ref("");
+const treeRef = ref<InstanceType<typeof ElTree>>();
+
+const defaultProps = {
+  children: "children",
+  label: "label",
+};
+
+watch(filterText, (val) => {
+  treeRef.value!.filter(val);
+});
+
+const formAccessibility = reactive({
+  fullName: "",
+  firstName: "",
+  lastName: "",
+  phone: "",
+});
+const ruleFormRef = ref<FormInstance>();
+let form = reactive({
+  name: "", //机构名称
+  parentId: "", //上级机构id
+  comType: "", //机构类型
+  exhibitionScope: "", //展业范围
+  address: "", //地址
+  leaderId: "", //负责人id
+  area: [], //地区
+});
+
+interface RuleForm {
+  name: string; //机构名称
+  parentId: string; //上级机构id
+  comType: string; //机构类型
+  exhibitionScope: string[]; //展业范围
+  address: string; //地址
+  leaderId: string; //负责人id
+  area: string[]; //地区
+  id: string;
+}
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+const value = ref("");
+const dialogVisible = ref(false);
+const options = [
+  {
+    value: "Option1",
+    label: "Option1",
+  },
+  {
+    value: "Option2",
+    label: "Option2",
+  },
+  {
+    value: "Option3",
+    label: "Option3",
+  },
+  {
+    value: "Option4",
+    label: "Option4",
+  },
+  {
+    value: "Option5",
+    label: "Option5",
+  },
+];
+
+const rules = reactive<FormRules<RuleForm>>({
+  name: [
+    { required: true, message: "请输入机构名称", trigger: "blur" },
+    { min: 1, max: 50, message: "请输入机构名称", trigger: "blur" },
+  ],
+  parentId: [{ required: true, message: "请选择上级机构", trigger: "change" }],
+  comType: [{ required: true, message: "请选择机构类型", trigger: "change" }],
+  exhibitionScope: [
+    { required: true, message: "请选择展业范围", trigger: "change" },
+  ],
+  address: [
+    { required: true, message: "请输入机构地址", trigger: "blur" },
+    { min: 1, max: 50, message: "请输入机构地址", trigger: "blur" },
+  ],
+  area: [{ required: true, message: "请选择机构所在地区", trigger: "change" }],
+});
+// import Select from "@/components/select/selectMain.vue";
+// import TestTable from "@/components/Table/index.vue";
+//机构列表
+type tableType = {
+  id: string;
+  name: string;
+};
+const tableData = ref<tableType[]>([]);
+const typeformatter = (row: any, column: any, cellValue: any) => {
+  let comType = "";
+  comTypeList.value.map((ele) => {
+    if (ele.value == row.comType) {
+      comType = ele.label;
+    }
+  });
+
+  return () => h("div", {}, comType);
+};
+const columns = [
+  { prop: "name", label: "机构名称" },
+  { prop: "Phone", label: "人数" },
+  { prop: "comType", label: "机构类型", formatter: typeformatter },
+  {
+    prop: "comAttribute",
+    label: "展业范围",
+    formatter: (row: any, column: any, cellValue: any) => {
+      let comType = "";
+      exhibitionScopeTypeList.value.map((ele) => {
+        if (ele.value == row.exhibitionScope) {
+          comType = ele.label;
+        }
+      });
+
+      return () => h("div", {}, comType);
+    },
+  },
+  { prop: "address", label: "所属地区" },
+  { prop: "status", label: "负责人" },
+];
+
+const pageInfo = {
+  pageNum: 1,
+  pageSize: 10,
+  sizes: [10, 20, 30, 40, 50],
+  total: 100,
+};
+//分页列表功能
+const getList = (item: PageInfo) => {
+  pageInfo.pageNum = item.pageNum;
+  pageInfo.pageSize = item.pageSize;
+};
+//添加子机构
+
+const isDisabledId = ref("");
+const addChild = (item: any) => {
+  // form.id=item.id
+  form.parentId = item.id;
+  isDisabledId.value = item.id;
+  dialogVisible.value = true;
+  operationType.value = "add";
+  disabledTree(tableData.value);
+  // pageInfo.pageNum=item.pageNum
+  // pageInfo.pageSize=item.pageSize
+};
+const editor = (item: any) => {
+  console.log(item);
+  // form={...item}
+  for (let key in item) {
+    form[key] = item[key];
+  }
+  isDisabledId.value=''
+  // form.area=[item.province,item.city,item.area]
+  dialogVisible.value = true;
+  operationType.value = "edit";
+  disabledTree(tableData.value)
+
+  // pageInfo.pageNum=item.pageNum
+  // pageInfo.pageSize=item.pageSize
+};
+const del = (item: any) => {
+  // ElPopconfirm('是否确认')
+  ElMessageBox.confirm("确认要删除当前数据吗?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      console.log(123);
+      api.institutionApi
+        .institutionDel({
+          id: item.id,
+        })
+        .then((res) => {
+          ElMessage.success("操作成功");
+          getInstitutionList(); //机构列表
+        });
+    })
+    .catch(() => {
+      ElMessage.warning("用户取消操作");
+    });
+};
+
+//添加的弹框
+let operationType = ref("");
+const add = (formEl: FormInstance | undefined) => {
+  dialogVisible.value = true;
+  operationType.value = "add";
+
+  if (!formEl) return;
+  formEl.resetFields();
+  // pageInfo.pageNum=item.pageNum
+  // pageInfo.pageSize=item.pageSize
+};
+
+//保存功能
+
+interface institutionData {
+  name: string;
+  system: string;
+}
+const info = ref<institutionData>({
+  name: "",
+  system: "",
+});
+//获取全国省市区级联数据
+const areaOptins = ref([]);
+const areaProps = ref({
+  value: "areaCode",
+  label: "areaCname",
+  children: "child",
+});
+const tableProps = ref({
+  value: "id",
+  label: "name",
+  children: "children",
+  checkStrictly: true,
+});
+
+const getAreaList = () => {
+  api.areaApi.areaList({}).then((res) => {
+    areaOptins.value = res.data;
+  });
+};
+
+//获取机构类型字典值
+type comType = {
+  value: string;
+  label: string;
+};
+let comTypeList = ref<comType[]>([]);
+const getSystemList = () => {
+  api.areaApi
+    .systemList({
+      dictCode: "com_type",
+    })
+    .then((res) => {
+      comTypeList.value = res.data;
+    });
+};
+
+//展业范围
+type exhibitionScopeType = {
+  value: string;
+  label: string;
+};
+let exhibitionScopeTypeList = ref<exhibitionScopeType[]>([]);
+const getExhibitionScopeSystemList = () => {
+  api.areaApi
+    .systemList({
+      dictCode: "exhibition_scope",
+    })
+    .then((res) => {
+      exhibitionScopeTypeList.value = res.data;
+    });
+};
+const handleChange = (e: any) => {
+  console.log(e);
+};
+
+//禁用机构选择
+function disabledTree(list: tableType) {
+  for (let i = 0; i < list.length; i++) {
+    const node = list[i];
+    if (isDisabledId.value) {
+      if (list[i].id != isDisabledId.value) {
+        list[i].disabled = true;
+      }
+    } else {
+      list[i].disabled = false;
+    }
+
+    if (node.children && node.children.length > 0) {
+      disabledTree(node.children);
+    }
+  }
+}
+
+//获取机构列表
+const getInstitutionList = () => {
+  // pageInfo.pageNum=item.pageNum
+  api.institutionApi.institutionList(info.value).then((res) => {
+    // if(res.code===200){
+
+    // }
+    tableData.value = res.data;
+    console.log(tableData.value);
+  });
+  // pageInfo.pageSize=item.pageSize
+};
+
+const confirm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      const obj = { ...form };
+      delete obj.area;
+
+      if (operationType.value == "add") {
+        api.institutionApi
+          .institutionAdd({
+            ...obj,
+            system: localStorage.getItem("login_system") || "",
+            province: form.area[0],
+            city: form.area[1],
+            area: form.area[2],
+          })
+          .then((res) => {
+            if (res.code === 200) {
+              ElMessage.success("操作成功");
+              dialogVisible.value = false;
+              getInstitutionList(); //机构列表
+            }
+          });
+      } else {
+        api.institutionApi
+          .institutionEdit({
+            ...obj,
+            system: localStorage.getItem("login_system") || "",
+            province: form.area[0],
+            city: form.area[1],
+            area: form.area[2],
+          })
+          .then((res) => {
+            console.log(res);
+            if (res.code === 200) {
+              ElMessage.success("操作成功");
+              dialogVisible.value = false;
+            }
+          });
+      }
+    }
+  });
+};
+
+const remove = (node: Node, data: Tree) => {
+  const parent = node.parent;
+  const children: Tree[] = parent.data.children || parent.data;
+  const index = children.findIndex((d) => d.id === data.id);
+  children.splice(index, 1);
+  // dataSource.value = [...dataSource.value]
+};
+const reset = (item: any) => {
+  // ElMessage({})
+};
+
+onMounted(() => {
+  // 对 asyncRoutes 进行预处理并转换为树形结构
+  getInstitutionList(); //机构列表
+  getAreaList(); //省市区级联数据
+  getSystemList(); //机构类型字典
+  getExhibitionScopeSystemList(); //展业范围字段
+});
+</script>
+<style lang="scss" scoped>
+:deep(.table-title-btn) {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  > div {
+    display: flex;
+  }
+}
+.institution {
+  background: #fff;
+  width: 100%;
+  // height: 100%;
+  // padding: 20px;
+  box-sizing: border-box;
+  display: flex;
+  .institution-tree {
+    flex: none;
+    width: 20%;
+    padding: 20px;
+    box-sizing: border-box;
+    border-right: 1px solid #ccc;
+  }
+  .institution-main {
+    flex: 1;
+    // width: 80%;
+    padding: 20px;
+    box-sizing: border-box;
+  }
+}
+:deep(.el-dropdown-link) {
+  cursor: pointer;
+  color: var(--el-color-primary);
+  display: flex;
+  align-items: center;
+  margin-left: 15px;
+}
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+</style>

+ 307 - 39
src/views/personnel/roleManagement.vue

@@ -4,18 +4,40 @@
       <ElCol :md="6">
       <ElCol :md="6">
         <PageMain class="vh100">
         <PageMain class="vh100">
           <ElFormItem prop="roleName">
           <ElFormItem prop="roleName">
-            <ElCol :md="17">
-              <ElInput v-model="roleName" placeholder="角色名称查找" type="text" tabindex="1">
+            <ElCol :md="20">
+              <ElInput
+                v-model="roleName"
+                placeholder="角色名称查找"
+                type="text"
+                tabindex="1"
+              >
                 <template #prefix>
                 <template #prefix>
                   <SvgIcon name="i-ri:user-3-fill" />
                   <SvgIcon name="i-ri:user-3-fill" />
                 </template>
                 </template>
               </ElInput>
               </ElInput>
             </ElCol>
             </ElCol>
-            <ElCol :md="6">
+            <ElCol :md="20" style="margin-top: 20px">
+              <ElButton type="primary" @click="addRole">
+                <el-icon><Plus /></el-icon>
+                添加</ElButton
+              >
+
               <ElButton type="primary">查找</ElButton>
               <ElButton type="primary">查找</ElButton>
             </ElCol>
             </ElCol>
+            <ElCol :md="24">
+              <div
+                v-for="item in roleList"
+                :key="item.id"
+                :class="roleId == item.id ? 'on' : 'role-item'"
+                @click="chooseRole(item)"
+              >
+                <p>{{ item.name }}</p>
+                <p>
+                  <span>{{ item.remark || "--" }}</span>
+                </p>
+              </div>
+            </ElCol>
           </ElFormItem>
           </ElFormItem>
-
         </PageMain>
         </PageMain>
       </ElCol>
       </ElCol>
       <ElCol :md="18">
       <ElCol :md="18">
@@ -25,65 +47,146 @@
               <span class="strong">角色管理</span>
               <span class="strong">角色管理</span>
               <div>
               <div>
                 <ElButton type="primary" @click="roleSubmit">提交</ElButton>
                 <ElButton type="primary" @click="roleSubmit">提交</ElButton>
+                <ElButton type="danger" @click="roleSubmit">删除</ElButton>
               </div>
               </div>
             </div>
             </div>
           </template>
           </template>
           <h5>基本信息</h5>
           <h5>基本信息</h5>
-          <ElCol :md="6"></ElCol>
-          <ElForm :model="roleInfo" :rules="roleRules" label-width="auto" size="" style="max-width: 500px;">
+          <!-- <ElCol :md="6"></ElCol> -->
+          <ElForm
+            :model="roleInfo"
+            :rules="roleRules"
+            label-width="auto"
+            size=""
+            style="max-width: 500px"
+          >
             <ElFormItem prop="name" label="角色名称">
             <ElFormItem prop="name" label="角色名称">
-              <ElInput v-model="roleInfo.name" placeholder="输入20个字以内的角色名称" type="text" tabindex="1">
+              <ElInput
+                v-model="roleInfo.name"
+                placeholder="输入20个字以内的角色名称"
+                type="text"
+                tabindex="1"
+              >
               </ElInput>
               </ElInput>
             </ElFormItem>
             </ElFormItem>
+            <ElFormItem prop="deptId" label="所属机构">
+              <el-cascader
+                clearable
+                style="width: 100%"
+                v-model="roleInfo.deptId"
+                :options="tableData"
+                :props="tableProps"
+              />
+            </ElFormItem>
             <ElFormItem prop="desc" label="角色描述">
             <ElFormItem prop="desc" label="角色描述">
-              <ElInput v-model="roleInfo.desc" placeholder="请输入" type="textarea" tabindex="2">
+              <ElInput
+                v-model="roleInfo.desc"
+                placeholder="请输入"
+                type="textarea"
+                tabindex="2"
+              >
               </ElInput>
               </ElInput>
             </ElFormItem>
             </ElFormItem>
           </ElForm>
           </ElForm>
           <ElDivider></ElDivider>
           <ElDivider></ElDivider>
-          <div>
-            <h5>角色权限</h5>
-            <ElTree style="max-height: 350px;overflow-y: auto;" :data="treeData" :props="defaultProps" show-checkbox
-              node-key="id" :default-expand-all="true" :default-checked-keys="[6]" @check="handleCheckChange" />
+          <div style="min-height: 500px">
+            <h5>
+              角色权限<span
+                style="color: #ccc; font-size: 12px; font-weight: 400"
+                >(该角色享有的后台模块功能权限剩余数据权限)</span
+              >
+            </h5>
+            <el-tabs
+              :tab-position="'left'"
+              style="height: 500px"
+              class="demo-tabs"
+            >
+              <el-tab-pane label="菜单/操作权限">
+                <ElTree
+                  style="max-height: 500px; overflow-y: auto"
+                  :data="treeData"
+                  :props="defaultProps"
+                  show-checkbox
+                  node-key="id"
+                  :default-expand-all="true"
+                  :default-checked-keys="[6]"
+                  @check="handleCheckChange"
+                />
+              </el-tab-pane>
+              <el-tab-pane label="组织架构权限">
+                <el-radio-group v-model="roleInfo.dataMark">
+                  <el-radio value="1" size="large">可见本机构下的数据</el-radio>
+                  <el-radio value="2" size="large"
+                    >可见本机构及下级机构数据</el-radio
+                  >
+                  <el-radio value="3" size="large">指定机构</el-radio>
+                </el-radio-group>
+                <ElTree
+                  style="max-height: 500px; overflow-y: auto"
+                  :data="tableData"
+                  :props="deptProps"
+                  show-checkbox
+                  node-key="id"
+                  :default-expand-all="true"
+                  :default-checked-keys="[6]"
+                  @check="handleDeptCheckChange"
+                />
+              </el-tab-pane>
+            </el-tabs>
           </div>
           </div>
         </PageMain>
         </PageMain>
       </ElCol>
       </ElCol>
     </ElRow>
     </ElRow>
-
   </div>
   </div>
 </template>
 </template>
 
 
-<script lang='ts' setup>
-import { asyncRoutes } from '@/router/routes' // 请根据实际路径调整
-import { ref, reactive, onMounted } from 'vue';
-import type { FormRules } from 'element-plus';
-import { preprocessRoutes, convertToTreeData, TreeNode } from '@/utils/composables/routeUtils'; // 导入工具方法
-import api from '@/api'
+<script lang="ts" setup>
+import { asyncRoutes } from "@/router/routes"; // 请根据实际路径调整
+import { ref, reactive, onMounted } from "vue";
+import type { FormRules } from "element-plus";
+import {
+  preprocessRoutes,
+  convertToTreeData,
+  TreeNode,
+} from "@/utils/composables/routeUtils"; // 导入工具方法
+import api from "@/api";
 const roleName = ref<string>("");
 const roleName = ref<string>("");
 
 
-const roleInfo = reactive<{
+let roleInfo = reactive<{
   name: string;
   name: string;
   desc: string;
   desc: string;
-  system: string,
-  permsId: number[],
-  perms: string[]
+  system: string;
+  permsId: number[];
+  perms: string[];
+  deptId: string;
+  deptIds: [];
+  dataMark: string;
 }>({
 }>({
-  name: "",//角色
-  desc: "",//描述
-  system: "PLATFORM",//系统
-  permsId: [],//权限id
-  perms: [],//权限标识
+  name: "", //角色
+  desc: "", //描述
+  system: "PLATFORM", //系统
+  permsId: [], //权限id
+  perms: [], //权限标识
+  deptId: "",
+  deptIds: [],
+  dataMark: "1",
 });
 });
 const roleRules = ref<FormRules>({
 const roleRules = ref<FormRules>({
   name: [
   name: [
-    { required: true, trigger: 'blur', message: '请输入角色名称' },
+    { required: true, trigger: "blur", message: "请输入角色名称" },
+    { min: 1, max: 20, message: "请输入机构名称", trigger: "blur" },
   ],
   ],
-
+  deptId: [{ required: true, message: "请选择机构", trigger: "change" }],
 });
 });
 const treeData = ref<TreeNode[]>([]);
 const treeData = ref<TreeNode[]>([]);
 const defaultProps = {
 const defaultProps = {
-  children: 'children',
-  label: 'title',
+  children: "children",
+  label: "title",
+};
+
+const deptProps = {
+  children: "children",
+  label: "name",
 };
 };
 const handleCheckChange = (
 const handleCheckChange = (
   _checkedNodes: TreeNode[],
   _checkedNodes: TreeNode[],
@@ -98,24 +201,153 @@ const handleCheckChange = (
   // console.log(checkedInfo.checkedNodes);
   // console.log(checkedInfo.checkedNodes);
   roleInfo.permsId = checkedInfo.checkedKeys;
   roleInfo.permsId = checkedInfo.checkedKeys;
   roleInfo.perms = [];
   roleInfo.perms = [];
-  checkedInfo.checkedNodes.forEach(val => {
-    if (val.type === '2' ) {
+  checkedInfo.checkedNodes.forEach((val) => {
+    if (val.type === "2") {
       roleInfo.perms.push(val.auth);
       roleInfo.perms.push(val.auth);
     }
     }
   });
   });
 };
 };
-   function roleSubmit() {
-    api.roleApi.roleAdd(roleInfo).then((res)=>{
-      console.log(res)
+const handleDeptCheckChange = (
+  _checkedNodes: TreeNode[],
+  checkedInfo: {
+    checkedKeys: number[];
+    checkedNodes: TreeNode[];
+    halfCheckedKeys: number[];
+    halfCheckedNodes: TreeNode[];
+  }
+) => {
+  // console.log('Checked keys:', checkedInfo.checkedKeys);
+  // console.log(checkedInfo.checkedNodes);
+  roleInfo.deptIds = checkedInfo.checkedKeys;
+  // roleInfo.perms = [];
+  // checkedInfo.checkedNodes.forEach((val) => {
+  //   if (val.type === "2") {
+  //     roleInfo.perms.push(val.auth);
+  //   }
+  // });
+};
+
+//机构列表
+type tableType = {
+  id: string;
+  name: string;
+};
+const tableData = ref<tableType[]>([]);
+const tableProps = ref({
+  value: "id",
+  label: "name",
+  children: "children",
+  checkStrictly: true,
+});
+interface institutionData {
+  name: string;
+  system: string;
+}
+const info = ref<institutionData>({
+  name: "",
+  system: "",
+});
+//获取机构列表
+const getInstitutionList = () => {
+  // pageInfo.pageNum=item.pageNum
+  api.institutionApi.institutionList(info.value).then((res) => {
+    // if(res.code===200){
+
+    // }
+    tableData.value = res.data;
+  });
+  // pageInfo.pageSize=item.pageSize
+};
+
+const showRole = ref(false);
+//选择角色
+function chooseRole(item: any) {
+  roleId.value = item.id;
+  api.roleApi
+    .roleInfo({
+      roleId: item.id,
+      name: "",
+      desc: "",
+      system: "",
+      permsId: [],
+      perms: [],
     })
     })
+    .then((res) => {
+      console.log(res);
+      roleInfo.name = res.data.name;
+      roleInfo.desc = res.data.remark;
+      roleInfo.deptId = res.data.deptId;
+    });
+  operationType.value = "edit";
+  // roleInfo.name=item.name
+  // roleInfo.desc=item.ramark
+  // roleInfo.desc=item.desc
+  // roleInfo={...item}
+  // for(let key in roleInfo){
+  //   roleInfo[key]=item[key]
+  // }
+  // console.log(roleInfo)
+}
+
+//添加角色页面及配置权限
+const operationType = ref("add");
+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) => {
+      console.log(res);
+    });
+  } else {
+
   }
   }
+}
+
+//角色列表
+interface roleitem {
+  [key: string]: any;
+}
+const roleId = ref("");
+const roleList = ref<roleitem>([]);
+function getRoleList() {
+  api.roleApi
+    .roleList({
+      system: localStorage.getItem("login_system") || "",
+      name: "",
+      desc: "",
+      permsId: [],
+      perms: [],
+    })
+    .then((res) => {
+      roleList.value = res.data;
+      // roleId.value = res.data[0].id;
+    });
+}
 onMounted(() => {
 onMounted(() => {
   // 对 asyncRoutes 进行预处理并转换为树形结构
   // 对 asyncRoutes 进行预处理并转换为树形结构
   const processedRoutes = preprocessRoutes(asyncRoutes);
   const processedRoutes = preprocessRoutes(asyncRoutes);
   treeData.value = convertToTreeData(processedRoutes);
   treeData.value = convertToTreeData(processedRoutes);
-
+  getRoleList();
+  getInstitutionList();
 });
 });
-
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -133,4 +365,40 @@ onMounted(() => {
 .active {
 .active {
   background: #b4b2b2;
   background: #b4b2b2;
 }
 }
+p {
+  margin: 0;
+}
+.role-item {
+  padding: 0 10px;
+  box-sizing: border-box;
+  cursor: pointer;
+  margin-top: 10px;
+
+  > p {
+    span {
+      color: #b4b2b2;
+      font-size: 10px;
+    }
+  }
+  // >p:last-child{
+  //   // margin: 5px 0;
+
+  // }
+}
+.on {
+  background: rgb(242, 242, 242);
+  padding: 0 10px;
+  box-sizing: border-box;
+  margin-top: 10px;
+  cursor: pointer;
+  > p {
+    span {
+      color: #b4b2b2;
+      font-size: 10px;
+    }
+  }
+}
+:deep(.p-5) {
+  padding: 0.2rem !important;
+}
 </style>
 </style>