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