feat(xumu): 新增应用角色权限管理功能
- 添加 AppRoleRightsManager 组件,实现角色管理、权限分配等功能 - 新增 rightsAdd 组件,用于添加和编辑应用角色 - 新增 rightsGraph 组件,用于展示权限关系图 - 集成 ECharts 实现复杂的权限关系可视化
This commit is contained in:
		
							
								
								
									
										416
									
								
								project/xumu/AppRoleRightsManager/AppRoleRightsManager.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								project/xumu/AppRoleRightsManager/AppRoleRightsManager.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,416 @@ | ||||
| <template> | ||||
|   <section class="AppRoleRightsManager"> | ||||
|     <ai-list v-if="!showDetail"> | ||||
|       <ai-title slot="title" title="角色管理" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button size="small" type="primary" icon="iconfont iconAdd" | ||||
|                        @click="$router.push({hash:'#add'})" | ||||
|                        v-if="$permissions('admin_sysapprole_add')"> | ||||
|               添加 | ||||
|             </el-button> | ||||
|             <el-button | ||||
|                 size="small" | ||||
|                 icon="iconfont iconDelete" | ||||
|                 :disabled="!multipleSelection.length" | ||||
|                 class="del-btn-list" | ||||
|                 @click="deleteApp('all')" | ||||
|                 v-if="$permissions('admin_sysapprole_del')" | ||||
|             >删除 | ||||
|             </el-button> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input | ||||
|                 size="small" | ||||
|                 v-model="search.roleName" | ||||
|                 placeholder="角色名称" | ||||
|                 clearable | ||||
|                 @change="searchList()" | ||||
|                 suffix-icon="iconfont iconSearch"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="adminList" :colConfigs="colConfigs" :total="total" :current.sync="page.pageNum" | ||||
|                   :size.sync="page.pageSize" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict" | ||||
|                   @selection-change="v=>multipleSelection=v"> | ||||
|           <el-table-column label="角色用户" slot="users" align="center"> | ||||
|             <template slot-scope="scope"> | ||||
|               <el-tooltip | ||||
|                   effect="light" | ||||
|                   placement="top" | ||||
|                   :disabled="scope.row.users.length <= 2" | ||||
|                   content="更多角色用户请点击详情按钮"> | ||||
|               <span v-if="scope.row.users.length"> | ||||
|                 {{ | ||||
|                   scope.row.users | ||||
|                       .slice(0, 2) | ||||
|                       .map((e) => e.name + "(" + e.phone + ")") | ||||
|                       .join(";") | ||||
|                 }} | ||||
|                 <span v-if="scope.row.users.length > 2">...</span> | ||||
|               </span> | ||||
|                 <span v-else>-</span> | ||||
|               </el-tooltip> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" label="操作" fixed="right" align="center"> | ||||
|             <template slot-scope="{row}"> | ||||
|               <el-button type="text" @click="beforeCopy(row)" v-if="$permissions('admin_sysapprole_edit')">复制 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="viewApp(row)" v-if="$permissions('admin_sysapprole_detail')">详情 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="openRightsGraph(row)" v-if="$permissions('admin_sysapprole_detail')">关系图 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="toAddAppRole(row)" v-if="$permissions('admin_sysapprole_edit')">编辑 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="deleteApp(row)" v-if="$permissions('admin_sysapprole_del')">删除</el-button> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|         <ai-dialog | ||||
|             title="应用角色详情" | ||||
|             :visible.sync="viewShow" | ||||
|             width="600px" | ||||
|             customFooter> | ||||
|           <ai-card title="基本信息"> | ||||
|             <template #content> | ||||
|               <ai-wrapper> | ||||
|                 <ai-info-item label="应用角色名称" :value="viewInfo.name" isLine/> | ||||
|               </ai-wrapper> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card title="权限信息"> | ||||
|             <template #content> | ||||
|               <div style="margin-bottom: 16px" v-text="roleList.map(e => e.name).join('、')"/> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card title="角色账号"> | ||||
|             <template #right> | ||||
|               <span style="text-align: right; color: #999"> | ||||
|                 共<span style="color: #26f" v-text="userList.length"/>个账号 | ||||
|               </span> | ||||
|             </template> | ||||
|             <template #content> | ||||
|               <div class="datail-table-body" v-if="userList.length"> | ||||
|                 <div class="datail-item" v-for="(item, index) in userList" :key="index"> | ||||
|                   <span class="item-name">{{ item.name }}</span> | ||||
|                   <span style="color: #999">{{ item.phone }}</span> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <template #footer> | ||||
|             <el-button | ||||
|                 type="primary" | ||||
|                 @click="toAddAppRole(viewInfo)" | ||||
|                 v-if="$permissions('admin_sysapprole_edit')" | ||||
|             >编辑角色 | ||||
|             </el-button> | ||||
|           </template> | ||||
|         </ai-dialog> | ||||
|         <ai-dialog title="权限关系图" :visible.sync="rightsGraph" class="rightsGraphDialog" customFooter> | ||||
|           <rights-graph :instance="instance" :dict="dict" :app="selectApp"/> | ||||
|           <el-button slot="footer" @click="rightsGraph=false">关闭</el-button> | ||||
|         </ai-dialog> | ||||
|         <!--复制角色--> | ||||
|         <el-dialog | ||||
|             class="editStyle" | ||||
|             :visible.sync="copyDialog" | ||||
|             width="520px" | ||||
|             @close="dataInit()" | ||||
|             title="复制角色"> | ||||
|           <el-form :model="form" label-width="80px"> | ||||
|             <el-form-item label="角色名" :rules="[{ required: true, message: '', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                   v-model="editName" | ||||
|                   placeholder="请输入..." | ||||
|                   size="small" | ||||
|                   clearable | ||||
|               /> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|           <div slot="footer" style="text-align: center"> | ||||
|             <el-button | ||||
|                 style="width: 92px" | ||||
|                 size="small" | ||||
|                 @click="copyDialog = false" | ||||
|             >取消 | ||||
|             </el-button | ||||
|             > | ||||
|             <el-button | ||||
|                 style="width: 92px" | ||||
|                 size="small" | ||||
|                 type="primary" | ||||
|                 @click="copyFn()" | ||||
|                 :disabled="!editName" | ||||
|             > | ||||
|               确认 | ||||
|             </el-button> | ||||
|           </div> | ||||
|         </el-dialog> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <rights-add v-else :instance="instance" :dict="dict" :permissions="permissions"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import RightsAdd from "./rightsAdd"; | ||||
| import RightsGraph from "./rightsGraph"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppRoleRightsManager", | ||||
|   components: {RightsGraph, RightsAdd}, | ||||
|   label: "角色管理", | ||||
|   provide() { | ||||
|     return { | ||||
|       top: this | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|     actions: { | ||||
|       default: () => ({ | ||||
|         list: '/admin/role/page', | ||||
|         apps: '/admin/role/list-all', | ||||
|         delete: '/admin/role/del', | ||||
|         detail: '/admin/role/queryById-checked', | ||||
|         modify: '/admin/role/modify', | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {type: "selection"}, | ||||
|         {label: "角色名", prop: "name", width: '100px'}, | ||||
|         {label: "所属端", prop: "type", width: '100px', dict: "roleType"}, | ||||
|         {label: "用户数量", prop: "roleCount", align: 'center', width: '80px'}, | ||||
|         {slot: "users"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     showDetail() { | ||||
|       return this.$route.hash == "#add" | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       page: {pageNum: 1, pageSize: 10}, | ||||
|       search: {roleName: ''}, | ||||
|       adminList: [], //列表数据 | ||||
|       total: 0, | ||||
|       multipleSelection: [], | ||||
|       delShow: false, | ||||
|       delParams: "", | ||||
|       delIds: [], | ||||
|       viewShow: false, | ||||
|       viewInfo: {}, | ||||
|       roleList: [], //详情权限列表 | ||||
|       row: {}, | ||||
|       copyDialog: false, | ||||
|       titleDel: "", | ||||
|       form: {}, | ||||
|       editName: "", | ||||
|       userList: [], | ||||
|       rightsGraph: false, | ||||
|       selectApp: {} | ||||
|     }; | ||||
|   }, | ||||
|   created() { | ||||
|     this.getTableData(); | ||||
|     this.dict.load('roleType') | ||||
|   }, | ||||
|   methods: { | ||||
|     //查询table列表 | ||||
|     getTableData() { | ||||
|       this.adminList = []; | ||||
|       this.instance.post(this.actions.list, null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.total = res.data.total; | ||||
|           this.adminList = res.data.records; | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //查询 | ||||
|     searchList() { | ||||
|       this.page.pageNum = 1; | ||||
|       this.getTableData(); | ||||
|     }, | ||||
|     //添加按钮 | ||||
|     toAddAppRole(item) { | ||||
|       this.$router.push({ | ||||
|         hash: "#add", | ||||
|         query: { | ||||
|           id: item.id, | ||||
|           name: item.name | ||||
|         }, | ||||
|       }); | ||||
|     }, | ||||
|     //删除 | ||||
|     deleteApp(e) { | ||||
|       if (e == "all") { | ||||
|         this.multipleSelection.map((item) => { | ||||
|           this.delIds.push(item.id); | ||||
|         }); | ||||
|         this.delParams = `ids=${this.delIds}`; | ||||
|         this.titleDel = "确定要执行删除操作吗?"; | ||||
|       } else { | ||||
|         this.delParams = `ids=${e.id}`; | ||||
|         this.titleDel = "确定需要删除该角色吗?"; | ||||
|       } | ||||
|       this.$confirm(this.titleDel, { | ||||
|         type: "error", | ||||
|       }).then(() => { | ||||
|         this.instance.post(`${this.actions.delete}?${this.delParams}`).then(res => { | ||||
|           if (res?.msg == "success") { | ||||
|             this.getTableData(); | ||||
|           } else { | ||||
|             this.$message.error(res.msg); | ||||
|           } | ||||
|         }); | ||||
|       }).catch(() => 0); | ||||
|     }, | ||||
|     //查看信息 | ||||
|     viewApp(e) { | ||||
|       this.userList = e.users; | ||||
|       this.viewInfo = e; | ||||
|       this.viewShow = true; | ||||
|       this.getRowInfo(this.viewInfo.appId, this.viewInfo.id); | ||||
|     }, | ||||
|     //查询 row 信息 | ||||
|     getRowInfo(appId, id) { | ||||
|       this.roleList = []; | ||||
|       this.instance.post(`${this.actions.detail}?id=${appId}&roleId=${id}`) | ||||
|           .then(res => { | ||||
|             if (res?.data) { | ||||
|               this.roleList = res.data.filter(e => e.checked) | ||||
|             } | ||||
|           }) | ||||
|     }, | ||||
|     //复制 | ||||
|     beforeCopy(row) { | ||||
|       this.row = row; | ||||
|       this.copyDialog = true; | ||||
|       this.getRowInfo(this.row.appId, this.row.id); | ||||
|     }, | ||||
|     //确认复制 | ||||
|     copyFn() { | ||||
|       let crr = []; | ||||
|       let appRoleList = this.roleList; | ||||
|       for (let i = 0; i < appRoleList.length; i++) { | ||||
|         if (appRoleList[i].checked) { | ||||
|           crr.push(appRoleList[i].id); | ||||
|           if (appRoleList[i].list.length) { | ||||
|             for (let j = 0; j < appRoleList[i].list.length; j++) { | ||||
|               if (appRoleList[i].list[j].checked) { | ||||
|                 crr.push(appRoleList[i].list[j].id); | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       this.instance.post(`${this.actions.modify}?menus=${crr}`, null, { | ||||
|         params: { | ||||
|           roleName: this.editName, | ||||
|           appId: this.row.appId, | ||||
|         }, | ||||
|       }) | ||||
|           .then((res) => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message({message: "复制成功", type: "success"}); | ||||
|               this.copyDialog = false; | ||||
|               this.searchList() | ||||
|             } | ||||
|           }); | ||||
|     }, | ||||
|     dataInit() { | ||||
|       this.multipleSelection = []; | ||||
|       this.row = {}; | ||||
|     }, | ||||
|     openRightsGraph(row) { | ||||
|       this.rightsGraph = true | ||||
|       this.selectApp = row | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppRoleRightsManager { | ||||
|   height: 100%; | ||||
|  | ||||
|  | ||||
|   :deep( .ai-dialog ) { | ||||
|     .ai-card { | ||||
|       box-shadow: none; | ||||
|       border: 1px solid #eee; | ||||
|  | ||||
|       .aibar { | ||||
|         height: 40px; | ||||
|         background: #f3f6f9; | ||||
|       } | ||||
|  | ||||
|       .ai-card__body { | ||||
|         padding: 0 16px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .rightsGraphDialog ) { | ||||
|     .el-dialog__body { | ||||
|       padding: 0; | ||||
|     } | ||||
|  | ||||
|     .ai-dialog__content { | ||||
|       padding-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .datail-table-body ) { | ||||
|     width: 100%; | ||||
|     height: auto; | ||||
|     margin-bottom: 16px; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|  | ||||
|     .datail-item { | ||||
|       flex-shrink: 0; | ||||
|       width: 50%; | ||||
|       height: 24px; | ||||
|       line-height: 24px; | ||||
|  | ||||
|       span { | ||||
|         display: inline-block; | ||||
|         font-size: 12px; | ||||
|       } | ||||
|  | ||||
|       .item-name { | ||||
|         width: 102px; | ||||
|         padding-left: 16px; | ||||
|         color: #333; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .datail-item:nth-of-type(2n - 1) { | ||||
|       border-right: 1px solid rgba(208, 212, 220, 1); | ||||
|       width: calc(50% - 1px); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .padd-l0 { | ||||
|     padding-left: 0 !important; | ||||
|   } | ||||
|  | ||||
|   .pad-l16 { | ||||
|     padding-left: 16px; | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										196
									
								
								project/xumu/AppRoleRightsManager/rightsAdd.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								project/xumu/AppRoleRightsManager/rightsAdd.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| <template> | ||||
|   <ai-detail class="rightsAdd"> | ||||
|     <ai-title :title="addTitle" slot="title" isShowBottomBorder isShowBack @onBackClick="back"/> | ||||
|     <template #content> | ||||
|       <el-form size="small" ref="rightsForm" :model="form" label-width="120px" :rules="rules"> | ||||
|         <ai-card title="基本信息"> | ||||
|           <template #content> | ||||
|             <div class="grid"> | ||||
|               <el-form-item label="应用角色名称" prop="roleName"> | ||||
|                 <el-input v-model="form.roleName" placeholder="请输入应用角色名称" clearable/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="角色标记" prop="type"> | ||||
|                 <ai-select v-model="form.type" dict="roleType" placeholder="请选择角色标记" clearable/> | ||||
|               </el-form-item> | ||||
|             </div> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="权限信息"> | ||||
|           <template #content> | ||||
|             <el-form-item label="权限列表" prop="menus"> | ||||
|               <div class="roleList"> | ||||
|                 <el-input v-model="filterText" placeholder="请输入..." clearable suffix-icon="iconfont iconSearch" | ||||
|                           @change="$refs.tree.filter(filterText)" :validate-event="false"/> | ||||
|                 <div class="tree_list"> | ||||
|                   <el-tree class="filter-tree" ref="roleTree" | ||||
|                            :data="roleList" | ||||
|                            show-checkbox | ||||
|                            :props="defaultProps" | ||||
|                            default-expand-all | ||||
|                            :check-strictly="false" | ||||
|                            node-key="id" | ||||
|                            :default-checked-keys="form.menus" | ||||
|                            :filter-node-method="filterNode" | ||||
|                            @check="handleMenusSelect"/> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </el-form-item> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </el-form> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="back()">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">保存</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|   name: "rightsAdd", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {}, | ||||
|       roleName: '', | ||||
|       id: '', | ||||
|       appList: [], | ||||
|       roleList: [], | ||||
|       defaultProps: { | ||||
|         children: 'list', | ||||
|         label: 'name' | ||||
|       }, | ||||
|       treeList: [], | ||||
|       filterText: '', | ||||
|       msgTitle: '添加' | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.isEdit) { | ||||
|       let {id, name: roleName} = this.$route.query | ||||
|       this.form = {menus: [], id, roleName} | ||||
|       this.msgTitle = '编辑' | ||||
|     } | ||||
|     this.getPermissions() | ||||
|   }, | ||||
|   computed: { | ||||
|     isEdit() { | ||||
|       return this.$route.query.id | ||||
|     }, | ||||
|     addTitle() { | ||||
|       return this.isEdit ? '编辑应用角色' : '新增应用角色' | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         roleName: {required: true, message: '请输入应用角色名称'}, | ||||
|         roleType: {required: true, message: '请选择角色标记'}, | ||||
|         menus: {required: true, message: '请选择权限列表内容'}, | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     filterNode(value, data) { | ||||
|       if (!value) return true; | ||||
|       return data.name.indexOf(value) !== -1; | ||||
|     }, | ||||
|     //应用名称选择 获取权限列表 | ||||
|     getId(data) { | ||||
|       if (data.list.length) { | ||||
|         data.list.forEach(item => { | ||||
|           this.getId(item) | ||||
|         }) | ||||
|       } else { | ||||
|         if (data.checked) { | ||||
|           this.form.menus?.push(data.id) | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     getPermissions() { | ||||
|       this.filterText = '' | ||||
|       let {id: roleId} = this.form | ||||
|       this.instance.post(this.top.actions.detail, null, { | ||||
|         params: {roleId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.roleList = res.data; | ||||
|           if (this.isEdit) { | ||||
|             this.roleList.forEach(e => this.getId(e)) | ||||
|           } | ||||
|           this.roleList = this.roleList.filter(item => !(item.component && item.isApp == 0 && item.isMenu == 0)) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleMenusSelect(node, selected) { | ||||
|       this.$set(this.form, 'menus', [...selected?.checkedKeys]) | ||||
|       this.$refs.rightsForm.validateField('menus') | ||||
|     }, | ||||
|     //保存提交 | ||||
|     confirm() { | ||||
|       this.$refs.rightsForm.validate(v => { | ||||
|         if (v) { | ||||
|           let menus = [this.$refs.roleTree?.getHalfCheckedKeys(), this.$refs.roleTree?.getCheckedKeys()]?.flat()?.toString() | ||||
|           this.instance.post(this.top.actions.modify, null, { | ||||
|             params: {...this.form, menus} | ||||
|           }).then(res => { | ||||
|             if (res?.msg == "success") { | ||||
|               this.$message.success(`${this.msgTitle}应用角色成功`) | ||||
|               this.back() | ||||
|               this.top.searchList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //取消 返回 | ||||
|     back() { | ||||
|       this.$router.push({}) | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .rightsAdd { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   position: relative; | ||||
|  | ||||
|   .el-form-item { | ||||
|     .el-select { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     &.is-error { | ||||
|       .roleList { | ||||
|         border-color: #f46; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .roleList { | ||||
|     background-color: #fcfcfc; | ||||
|     border-radius: 2px; | ||||
|     border: solid 1px #d0d4dc; | ||||
|     padding: 8px; | ||||
|  | ||||
|     .input { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       padding: 5px; | ||||
|       margin: 0; | ||||
|     } | ||||
|  | ||||
|     .tree_list { | ||||
|       padding: 5px; | ||||
|       height: 370px; | ||||
|       overflow: auto; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										192
									
								
								project/xumu/AppRoleRightsManager/rightsGraph.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								project/xumu/AppRoleRightsManager/rightsGraph.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| <template> | ||||
|   <section class="rightsGraph"> | ||||
|     <div id="RightGraph"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| ; | ||||
|  | ||||
| export default { | ||||
|   name: "rightsGraph", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     app: Object | ||||
|   }, | ||||
|   computed: { | ||||
|     graphData() { | ||||
|       let data = [...this.users, ...this.nodes].map(e => { | ||||
|         if (e.x) { | ||||
|           return e | ||||
|         } else return {...e, ...this.renderPosition(e)} | ||||
|       }) | ||||
|       return [ | ||||
|         { | ||||
|           data, | ||||
|           links: this.links, | ||||
|           categories: data.map(e => e.category).flat().map(name => ({name})) | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       graph: null, | ||||
|       nodes: [], | ||||
|       links: [], | ||||
|       users: [] | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     graphData: { | ||||
|       deep: true, handler() { | ||||
|         this.refreshGraph() | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     initGraph() { | ||||
|       let dom = document.querySelector("#RightGraph") | ||||
|       if (dom) { | ||||
|         this.graph = echarts.init(dom) | ||||
|         this.graph.setOption({ | ||||
|           tooltip: {}, | ||||
|           series: [ | ||||
|             { | ||||
|               type: 'graph', | ||||
|               layout: 'none', | ||||
|               roam: true, | ||||
|               label: { | ||||
|                 show: true, | ||||
|                 position: 'right', | ||||
|                 formatter: '{b}' | ||||
|               }, | ||||
|               labelLayout: { | ||||
|                 hideOverlap: true, | ||||
|               }, | ||||
|               scaleLimit: { | ||||
|                 min: 0.4, | ||||
|                 max: 4 | ||||
|               }, | ||||
|               lineStyle: { | ||||
|                 color: 'target', | ||||
|                 curveness: 0.1 | ||||
|               }, | ||||
|               emphasis: { | ||||
|                 focus: 'adjacency', | ||||
|                 lineStyle: { | ||||
|                   width: 5 | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }) | ||||
|         this.graph.on('click', this.handleNodeClick) | ||||
|       } | ||||
|     }, | ||||
|     refreshGraph() { | ||||
|       this.graph?.setOption({ | ||||
|         series: this.graphData | ||||
|       }) | ||||
|     }, | ||||
|     getAppRoles(role) { | ||||
|       if (role) { | ||||
|         this.nodes.push({...role, category: '应用角色', value: "应用角色", symbolSize: 15}) | ||||
|       } else this.instance.post(this.top.actions.list, null, { | ||||
|         params: {pageSize: 999} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           res.data.records.map(e => { | ||||
|             this.getUsers(e.users, e.id) | ||||
|             this.nodes.push({...e, category: '应用角色', value: "应用角色"}) | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getUsers(pending, source) { | ||||
|       pending?.map(e => { | ||||
|         if (!this.users.some(u => u.id == e.phone)) { | ||||
|           this.users.push({id: e.phone, name: e.name, symbolSize: 5, category: '用户', value: "用户"}) | ||||
|         } | ||||
|         this.links.push({source, target: e.phone}) | ||||
|       }) | ||||
|     }, | ||||
|     getPermissions({id: roleId, name: category, dataIndex}) { | ||||
|       const addNodes = (list, source, category, pos) => { | ||||
|         list?.map(e => { | ||||
|           let node = { | ||||
|             ...e, | ||||
|             symbolSize: 5, | ||||
|             category, | ||||
|             value: e.list?.length > 0 ? "应用" : "权限", | ||||
|           } | ||||
|           node = {...node, ...this.renderPosition(pos || node)} | ||||
|           this.nodes.splice(dataIndex, 0, node) | ||||
|           if (e.checked == 1) { | ||||
|             this.links.push({source, target: e.id}) | ||||
|           } | ||||
|           addNodes(e.list, e.id, e.label, node) | ||||
|         }) | ||||
|       } | ||||
|       roleId && this.instance.post(this.top.actions.detail, null, { | ||||
|         params: {roleId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           addNodes(res.data, roleId, category) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleNodeClick(v) { | ||||
|       let {data: role, dataIndex} = v | ||||
|       if (!this.nodes.some(e => e.category == role?.name)) { | ||||
|         role && this.getPermissions({...role, dataIndex}) | ||||
|       } | ||||
|     }, | ||||
|     renderPosition(node) { | ||||
|       node = JSON.parse(JSON.stringify(node)) | ||||
|       let pos = {x: 0, y: 0} | ||||
|       if (node?.x) { | ||||
|         pos.x = Math.max(this.graph?.getWidth() / 3 * 2, node.x) + 100 + Math.random() * 50 | ||||
|         pos.y = node.y + Math.random() * 100 - 50 | ||||
|       } else if (node.value == '应用角色') { | ||||
|         pos.x = this.graph?.getWidth() / 3 - 200 + Math.random() * 200 | ||||
|         pos.y = this.graph?.getHeight() / 2 - 100 + Math.random() * 200 | ||||
|       } else if (node.value == '应用') { | ||||
|         pos.x = this.graph?.getWidth() / 3 * 2 - 100 + Math.random() * 100 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } else if (node.value == '用户') { | ||||
|         pos.x = Math.random() * 50 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } else { | ||||
|         pos.x = this.graph?.getWidth() - 100 + Math.random() * 100 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } | ||||
|       return pos | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getAppRoles(this.app) | ||||
|     this.getUsers(this.app.users, this.app.id) | ||||
|     this.getPermissions(this.app) | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$nextTick(() => { | ||||
|       this.initGraph() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .rightsGraph { | ||||
|   height: 100%; | ||||
|  | ||||
|   :deep( #RightGraph ){ | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     min-height: 500px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
		Reference in New Issue
	
	Block a user