wuguojie 1 سال پیش
والد
کامیت
2d422e3f1e

+ 4 - 0
src/api/index.ts

@@ -8,6 +8,8 @@ import Message from 'vue-m-message'
 import appApi from './modules/app'
 import userApi from './modules/user'
 import roleApi from './modules/role'
+import institutionApi from './modules/institution'
+import areaApi from "./modules/area"
 
 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,
@@ -87,5 +89,7 @@ const api = {
   appApi: appApi(axiosInstance),
   userApi: userApi(axiosInstance),
   roleApi: roleApi(axiosInstance),
+  institutionApi:institutionApi(axiosInstance),
+  areaApi:areaApi(axiosInstance)
 }
 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;
   system: string,
   permsId: number[],
-  perms: string[]
+  perms: string[],
+  [key:string]:any,
 }
 const roleApi = (axiosInstance: ApiInstance) => ({
   // 新增角色
   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

+ 22 - 0
src/components/LoginForm/index.vue

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

+ 10 - 16
src/components/Table/index.vue

@@ -1,3 +1,4 @@
+<<<<<<< HEAD
 
 <template>
 	<div class="container">
@@ -7,7 +8,7 @@
       </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"
@@ -49,13 +50,6 @@
                     <template #default="scope">
                         <slot name="operation" :operationData="scope"></slot>
                     </template>
-					<!-- <template #default="scope">
-						<div v-if="scope.row._edit">
-							<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
-							<el-button @click="handleCancle(scope)"> 取消 </el-button>
-						</div>
-						<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
-					</template> -->
 				</el-table-column>
 			</el-table>
 		</div>
@@ -72,13 +66,13 @@
 		</div>
 	</div>
 </template>
- 
+
 <script lang="ts" setup>
 	import { defineProps, ref, onMounted,defineEmits  } from "vue";
 	import type { TableInstance } from "element-plus";
- 
+
 	interface TableRow {
-		[key: string]: any; 
+		[key: string]: any;
 	}
     interface TagTypes{
         [key:string]:string;
@@ -121,13 +115,13 @@
 		'合作伙伴': "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);
 
@@ -181,11 +175,11 @@
         margin-right: 20px;
       }
     }
-    
+
 	}
   .page_box{
     display: flex;
     align-items: center;
     justify-content: flex-end;
   }
-</style>
+</style>

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

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

+ 10 - 0
src/router/routes.ts

@@ -72,6 +72,16 @@ const asyncRoutes: Route.recordMainRaw[] = [
 
     ],
   },
+  // {
+  //   meta: {
+  //     title: '演示',
+  //     icon: 'i-uim:box',
+  //   },
+  //   children: [
+  //     MultilevelMenuExample,
+
+  //   ],
+  // },
   {
     meta: {
       title: '组织架构',

+ 0 - 18
src/views/login.vue

@@ -4,7 +4,6 @@
     constant: true
     layout: false
   </route>
-
   <script setup lang="ts">
   import { ref } from 'vue'
   import { useRoute, useRouter } from 'vue-router'
@@ -26,7 +25,6 @@
   <template>
     <div class="bg-banner" />
     <img class="bgback" src=".././assets/images/loginback1.png" alt="">
-
     <div class="login-box">
       <!-- <div class="login-banner">
         <img
@@ -35,7 +33,6 @@
         >
         <img src="@/assets/images/login-banner.png" class="banner">
       </div> -->
-
       <div class="login-form flex-col-center">
         <Transition name="fade" mode="out-in">
           <LoginForm
@@ -55,7 +52,6 @@
       </div>
     </div>
   </template>
-
   <style lang="scss" scoped>
   .bg-banner {
     position: fixed;
@@ -66,7 +62,6 @@
     background-image: url(".././assets/images/loginback.png");
     background-size: cover;
   }
-
   .bgback {
     position: fixed;
     top: 58%;
@@ -74,18 +69,15 @@
     z-index: 0;
     transform: translateX(-50%) translateY(-50%);
   }
-
   [data-mode="mobile"] {
     .login-box {
       position: relative;
       flex-direction: column;
       justify-content: start;
       width: 100%;
-
       .login-banner {
         width: 100%;
         padding: 20px 0;
-
         .banner {
           position: relative;
           top: inherit;
@@ -97,23 +89,19 @@
           transform: translateY(0);
         }
       }
-
       .login-form {
         width: 100%;
       }
     }
-
     .copyright {
       position: relative;
     }
   }
-
   .login-box {
     position: absolute;
     display: flex;
     overflow: hidden;
     background-color: var(--g-container-bg);
-
     [data-mode="pc"] & {
       top: 50%;
       left: 70%;
@@ -121,13 +109,11 @@
       box-shadow: var(--el-box-shadow);
       transform: translateX(-50%) translateY(-50%);
     }
-
     .login-banner {
       position: relative;
       width: 450px;
       overflow: hidden;
       background-color: var(--g-bg);
-
       .banner {
         position: absolute;
         top: 50%;
@@ -135,13 +121,11 @@
         transform: translateY(-50%);
       }
     }
-
     .login-form {
       width: 500px;
       transition: height 0.15s ease;
     }
   }
-
   .copyright {
     position: absolute;
     bottom: 0;
@@ -149,12 +133,10 @@
     padding: 20px;
     margin: 0;
   }
-
   .fade-enter-active,
   .fade-leave-active {
     transition: opacity 0.15s ease;
   }
-
   .fade-enter-from,
   .fade-leave-to {
     opacity: 0;

+ 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 - 40
src/views/personnel/roleManagement.vue

@@ -4,18 +4,40 @@
       <ElCol :md="6">
         <PageMain class="vh100">
           <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>
                   <SvgIcon name="i-ri:user-3-fill" />
                 </template>
               </ElInput>
             </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>
             </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>
-
         </PageMain>
       </ElCol>
       <ElCol :md="18">
@@ -24,66 +46,146 @@
             <div class="flex j-s a-c">
               <span class="strong">角色管理</span>
               <div>
-                <ElButton type="primary" @click="roleSubmit">提交</ElButton>
+                <ElButton type="danger" @click="roleSubmit">删除</ElButton>
               </div>
             </div>
           </template>
           <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="角色名称">
-              <ElInput v-model="roleInfo.name" placeholder="输入20个字以内的角色名称" type="text" tabindex="1">
+              <ElInput
+                v-model="roleInfo.name"
+                placeholder="输入20个字以内的角色名称"
+                type="text"
+                tabindex="1"
+              >
               </ElInput>
             </ElFormItem>
+            <ElFormItem prop="deptId" label="所属机构">
+              <el-cascader
+                clearable
+                style="width: 100%"
+                v-model="roleInfo.deptId"
+                :options="tableData"
+                :props="tableProps"
+              />
+            </ElFormItem>
             <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>
             </ElFormItem>
           </ElForm>
           <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>
         </PageMain>
       </ElCol>
     </ElRow>
-
   </div>
 </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 roleInfo = reactive<{
+let roleInfo = reactive<{
   name: 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>({
   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 defaultProps = {
-  children: 'children',
-  label: 'title',
+  children: "children",
+  label: "title",
+};
+
+const deptProps = {
+  children: "children",
+  label: "name",
 };
 const handleCheckChange = (
   _checkedNodes: TreeNode[],
@@ -98,24 +200,153 @@ const handleCheckChange = (
   // console.log(checkedInfo.checkedNodes);
   roleInfo.permsId = checkedInfo.checkedKeys;
   roleInfo.perms = [];
-  checkedInfo.checkedNodes.forEach(val => {
-    if (val.type === '2' ) {
+  checkedInfo.checkedNodes.forEach((val) => {
+    if (val.type === "2") {
       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(() => {
   // 对 asyncRoutes 进行预处理并转换为树形结构
   const processedRoutes = preprocessRoutes(asyncRoutes);
   treeData.value = convertToTreeData(processedRoutes);
-
+  getRoleList();
+  getInstitutionList();
 });
-
 </script>
 
 <style lang="scss" scoped>
@@ -133,4 +364,40 @@ onMounted(() => {
 .active {
   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>

+ 1 - 1
vite.config.ts

@@ -21,7 +21,7 @@ export default defineConfig(({ mode, command }) => {
     server: {
       open: true,
       host: '0.0.0.0', // 监听所有接口
-      port: 9000,
+      port: 9001,
       proxy: {
         '/proxy': {
           target: env.VITE_APP_API_BASEURL,