前言
隨着Vue的爆火(很大部分是我們國內開發者功勞),自開源以來,在GitHub上已經斬獲176k票star,位居前三,所以餓了么推出的基於Vue的前端框架ElementUI以簡潔的UI與實用的組件等普及度越來越高。
下面是一個完整的el-tree實例,包含了我所遇到的坑。
Template代碼
<el-tree style="height:100%;" :data="data" @check="handleCheckChange" show-checkbox node-key="menuid" ref="authTree" highlight-current :props="defaultProps"> </el-tree>
- data:樹結構綁定的數據源,后台借口傳過來的數據
- check函數:當復選框被點擊的時候觸發
- show-checkbox:顯示選項的多選框
- node-key="menuid":什么意思呢,官網很模糊只是說一些功能都必須要提供這個屬性(比如選中等等),它其實就是我們數據源中的唯一標識,比如數據庫中你的權限表主鍵為id,這里就是填id
- ref=""authTree":創建引用到此組件的類似指針的功能
- :props:"defaultProps":這個屬性就是定義你要綁定的子節點和展示標題相關,官網有四個屬性可選值,比較常用的就這兩個,其他的可以移步官網:
- label:定義你要綁定的標簽屬性
- children:定義子樹的屬性值
script代碼
只用語言是描述不了的,我直接在代碼中解釋:
data: function() { return { page: 1,//分頁 rows: 10,//行數 total: 0,//總記錄數 data: [],//我們后台接口訪問到的展示數據 defaultProps: {//這里就是props屬性綁定的值 children: 'permissions', //填寫你的數據格式里面的子集,我這里是permissions label: 'menuname' //填寫你要展示的標題名稱,我這是menuname }, //權限Tree選中的節點 moduleids: [], //獲取table當前選中行row對象 curRow: null }; },
我后台的數據結構:
這個結構看懂了關系就一目了然,node-key屬性就是我們選中節點的值,我們將這個值發送到后端進行操作,后台只需要根據逗號分割就可以得到所有選中的權限節點,也就能夠理解為什么官方說這個屬性是必須要指定的。
然后是方法部分的代碼,比較重要的參數傳遞和綁值的問題:
let params = { roleId: this.curRow.roleId, sysPermission: this.moduleids.toString()//傳遞參數時直接toString也更省事,后台只需切割一下。 }
巨坑(查詢角色的權限后父節點的子節點全部選中了)
明明查詢到的是很正常的數組,但是父節點的所有子節點都被選中了,這時候只能用一個方法 setChecked() ,另外兩個方法( setCheckedKeys() setCheckedNodes() )會造成子節點選中的情況。
例如:A 是 父節點, B C D 分別是 子節點,勾選了 B節點,checkedId
僅包含B,沒有問題,如果checkedId包含了A和B,A節點下的節點都被選中。
后台必須返回節點的id數組,利用循環綁定node-key。
返回一個id數組:
查詢角色對應權限並且綁定選擇框:
queryRoleModuleId: function(roleId) { //先清空,再查權限 this.$refs['authTree'].setCheckedKeys([]); var url = this.axios.urls.PERMISSION_QUERYPERMISSIONBYROLEID; this.axios.post(url, { roleId: roleId }).then(resp => { this.moduleids = resp.data.data; console.log(this.moduleids); if (this.moduleids) { this.$nextTick(() => { this.moduleids.forEach(value=>{//真的大坑,我自己摸索好久!!! this.$refs.authTree.setChecked(value, true,false) //給樹節點賦值 }); this.checkStrictly = false //重點: 賦值完成后 設置為false }) } }).catch(); },
獲取選中節點的坑
默認的選中是不包含父節點的,所以我們需要拼接半選中的節點:
handleCheckChange: function(data, checked) { //checked.checkedKeys 選中的節點id數組z //checked.halfCheckedKeys 半選中節點id數組 this.moduleids = checked.halfCheckedKeys.concat(checked.checkedKeys); //選中節點和半選中節點所有的id }
整個組件源碼
<template> <el-container class="auth-container"> <el-header style="width:100%;margin:5px 0px;padding:0px 15px;"> <el-form :inline="true"> <el-form-item> <el-input v-model="roleName" size="small" placeholder="角色名稱"></el-input> </el-form-item> <el-form-item> <el-button type="primary" style="background-color: #42B983;" size="small" @click="query(1)"><i class="el-icon-search"></i>搜索</el-button> </el-form-item> <el-form-item> <el-button size="small" @click="refresh()"><i class="el-icon-refresh"></i>刷新</el-button> </el-form-item> <el-form-item> <el-button type="primary" size="small" icon="el-icon-edit-outline" @click="saveAuth">保存角色權限</el-button> </el-form-item> </el-form> </el-header> <el-container class="auth-container"> <el-aside class="auth-aside"> <el-tree style="height:100%;" :data="data" :check-strictly="checkStrictly" @check="handleCheckChange" show-checkbox node-key="menuid" ref="authTree" highlight-current :props="defaultProps"> </el-tree> </el-aside> <el-main class="auth-main"> <!--數據表格--> <el-table size="medium" :loading="true" :header-cell-style="{background:'#eef1f6',color:'#606266'}" ref="singleTable" :highlight-current-row="true" @row-click="rowClick" :data="result" style="width: 100%"> <el-table-column type="index" label="序號" width="50" align="center" :index="indexMethod"></el-table-column> </el-table-column> <el-table-column prop="roleName" label="角色名稱" align="center"> <template slot-scope="scope"> <el-button type="text" @click="queryRoleModuleId(scope.row.roleId)">{{scope.row.roleName}}</el-button> </template> </el-table-column> <el-table-column prop="roleTime" label="上次更新時間" :formatter="formatter" align="center"> </el-table-column> </el-table> <!--分頁組件--> <div class="paginationClass"> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page" :page-sizes="[10, 20, 30, 40]" :page-size="rows" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> </el-main> </el-container> </el-container> </template> <script> import date from '../../api/date.js' export default { name: 'BookList', data: function() { return { result: [], roleName: '', page: 1, rows: 10, total: 0, data: [], checkStrictly: false, defaultProps: { children: 'permissions', //填寫你的數據格式里面的子集 label: 'menuname' //填寫你要展示的標題名稱 }, //權限Tree選中的節點 moduleids: [], //獲取table當前選中行row對象 curRow: null }; }, methods: { refresh() { this.roleName = ''; this.page = 1; this.rows = 10; this.curRow = null; this.$refs.authTree.setCheckedKeys([]); //折疊 let nodes = this.$refs.authTree.store._getAllNodes(); for (let i = 0; i < nodes.length; i++) { nodes[i].expanded = false } this.$forceUpdate(); this.query(1); }, formatter(row, column) { return date(row.roleTime); }, //自定義索引 indexMethod: function(index) { return (this.page - 1) * this.rows + (index + 1); }, //分頁 handleSizeChange: function(rows) { this.page = 1; this.rows = rows; this.query(1); }, handleCurrentChange: function(page) { this.page = page; this.query(page); }, //數據查詢 query: function(num) { var params = { roleName: this.roleName, page: num !== null ? num : this.page, rows: this.rows, } var url = this.axios.urls.ROLE_QUERYROLEPAGER; this.axios.post(url, params).then(resp => { this.result = resp.data.data; this.total = resp.data.total; }).catch(); }, //表格行的單擊事件 rowClick: function(row, column, event) { this.curRow = row; }, //保存權限 saveAuth: function() { if (this.moduleids.length <= 0) { this.$message({ showClose: true, message: '請選擇權限分配給指定角色!', type: 'error' }); return false; } if (null == this.curRow) { this.$message({ showClose: true, message: '請選中你要分配權限的角色!', type: 'error' }); return false; } if (this.curRow.roleId == 1) { return this.$message({ type: 'error', message: '管理員不允許操作哦!' }); } let params = { roleId: this.curRow.roleId, sysPermission: this.moduleids.toString() //傳遞參數時直接toString也更省事,后台只需切割一下。 } //保存角色權限 var url = this.axios.urls.PERMISSION_SAVEATUTH; this.axios.post(url, params).then(resp => { this.$message({ showClose: true, message: resp.status == 200 ? resp.data.msg : '授予權限失敗!', type: resp.status == 200 ? 'success' : 'error' }); //保存成功,刷新列表和清空數據信息 this.curRow = null; this.moduleids = []; this.query(1); this.$refs['authTree'].setCheckedKeys([]); }).catch(); }, queryRoleModuleId: function(roleId) { //先清空,再查權限 this.$refs['authTree'].setCheckedKeys([]); var url = this.axios.urls.PERMISSION_QUERYPERMISSIONBYROLEID; this.axios.post(url, { roleId: roleId }).then(resp => { this.moduleids = resp.data.data; console.log(this.moduleids); if (this.moduleids) { this.$nextTick(() => { this.moduleids.forEach(value=>{//真的大坑,我自己摸索好久!!! this.$refs.authTree.setChecked(value, true,false) //給樹節點賦值 }); this.checkStrictly = false //重點: 賦值完成后 設置為false }) } }).catch(); }, handleCheckChange: function(data, checked) { //checked.checkedKeys 選中的節點id數組z //checked.halfCheckedKeys 半選中節點id數組 this.moduleids = checked.halfCheckedKeys.concat(checked.checkedKeys); //選中節點和半選中節點所有的id }, }, created: function() { //加載權限樹 if (this.$store.getters.getTreeList.length <= 0) { this.axios.post(this.axios.urls.PERMISSION_QUERYPERMISSIONPAGER, { pagination: false }).then(res => { this.$store.commit('setTreeList', res.data.data); }).catch(err => console.log(err)); } //得到所有節點 this.query(1); this.data = this.$store.getters.getTreeList; } } </script> <style scoped> .paginationClass { margin-top: 15px; bottom: 0; right: 0; float: right; } .auth-container { height: 100%; width: 100%; display: flex; object-fit: fill; } .auth-aside { width: 200px !important; padding: 0px 10px; height: 100%; object-fit: fill; margin-left: 5px; } .auth-main { padding: 0px; height: 100%; margin-right: 15px; } .el-row-bg { padding-top: 10px; padding-left: 10px; color: #000000; font-weight: bold; height: 60px; background: #f4f4f5; } </style>