第五节:基础组件(Table、Pagination、Tree、Tag) 之 角色/权限管理
一. 基础组件
1. Table表格+Pagination分页
(1). 表格
最基本的表格数据结构也很简单,返回一个数组,然后和属性名对应即可。
<el-table :data="userlist" border stripe> <el-table-column type="index"></el-table-column> <el-table-column label="姓名" prop="username"></el-table-column> <el-table-column label="邮箱" prop="email"></el-table-column> <el-table-column label="电话" prop="mobile"></el-table-column> <el-table-column label="角色" prop="role_name"></el-table-column> <el-table-column label="时间" prop="create_time"> <template slot-scope="scope"> {{scope.row.create_time | dateFormat}} </template> </el-table-column> <el-table-column label="状态"> <template slot-scope="scope"> <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"> </el-switch> </template> </el-table-column> <el-table-column label="操作" width="180px"> <template slot-scope="scope"> <!-- 修改按钮 --> <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button> <!-- 删除按钮 --> <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"> </el-button> <!-- 分配角色按钮 --> <el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false"> <el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button> </el-tooltip> </template> </el-table-column> </el-table>
(2). 分页
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination>
剖析:
① size-change方法:监控页数变化 、 current-change方法:监控当前页数的改变
② page-size属性:监控当前一页的条数、current-page属性:监控当前页码、total属性:表示总条数
大致业务逻辑分享:
// 1.加载表格事件
async getUserList() {
const {
data: res
} = await this.$http.get('users', {
params: this.queryInfo
})
if (res.meta.status !== 200) {
return this.$message.error('获取用户列表失败!')
}
this.userlist = res.data.users
this.total = res.data.total
console.log(res)
},
// 2.监听 pagesize 改变的事件
handleSizeChange(newSize) {
this.queryInfo.pagesize = newSize
this.getUserList()
},
// 3.监听 页码值 改变的事件
handleCurrentChange(newPage) {
console.log(newPage)
this.queryInfo.pagenum = newPage
this.getUserList()
},
(3). 表格中如何自定义处理列内容
(如何拿该行内容)
需要在该行中增加一个<template slot-scope="scope"></template>标签,然后铜鼓scope.row就可以拿到改行的内容,比如拿id,则 scope.row.id .
部分样例参考:
<el-table-column label="操作" width="180px"> <template slot-scope="scope"> <!-- 修改按钮 --> <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button> <!-- 删除按钮 --> <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"> </el-button> <!-- 分配角色按钮 --> <el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false"> <el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button> </el-tooltip> </template> </el-table-column>
<el-table-column label="时间" prop="create_time"> <template slot-scope="scope"> {{scope.row.create_time | dateFormat}} </template> </el-table-column>
(4). 展开行
在表格所有列的最前面添加一列
<el-table-column type="expand"> <template slot-scope="scope"></template> </el-table-column>
完整代码:
<template> <el-table :data="tableData" style="width: 100%"> <el-table-column type="expand"> <template slot-scope="props"> <el-form label-position="left" inline class="demo-table-expand"> <el-form-item label="商品名称"> <span>{{ props.row.name }}</span> </el-form-item> <el-form-item label="所属店铺"> <span>{{ props.row.shop }}</span> </el-form-item> <el-form-item label="商品 ID"> <span>{{ props.row.id }}</span> </el-form-item> <el-form-item label="店铺 ID"> <span>{{ props.row.shopId }}</span> </el-form-item> <el-form-item label="商品分类"> <span>{{ props.row.category }}</span> </el-form-item> <el-form-item label="店铺地址"> <span>{{ props.row.address }}</span> </el-form-item> <el-form-item label="商品描述"> <span>{{ props.row.desc }}</span> </el-form-item> </el-form> </template> </el-table-column> <el-table-column label="商品 ID" prop="id"> </el-table-column> <el-table-column label="商品名称" prop="name"> </el-table-column> <el-table-column label="描述" prop="desc"> </el-table-column> </el-table> </template> <style> .demo-table-expand { font-size: 0; } .demo-table-expand label { width: 90px; color: #99a9bf; } .demo-table-expand .el-form-item { margin-right: 0; margin-bottom: 0; width: 50%; } </style> <script> export default { data() { return { tableData: [{ id: '12987122', name: '好滋好味鸡蛋仔', category: '江浙小吃、小吃零食', desc: '荷兰优质淡奶,奶香浓而不腻', address: '上海市普陀区真北路', shop: '王小虎夫妻店', shopId: '10333' }, { id: '12987123', name: '好滋好味鸡蛋仔', category: '江浙小吃、小吃零食', desc: '荷兰优质淡奶,奶香浓而不腻', address: '上海市普陀区真北路', shop: '王小虎夫妻店', shopId: '10333' }, { id: '12987125', name: '好滋好味鸡蛋仔', category: '江浙小吃、小吃零食', desc: '荷兰优质淡奶,奶香浓而不腻', address: '上海市普陀区真北路', shop: '王小虎夫妻店', shopId: '10333' }, { id: '12987126', name: '好滋好味鸡蛋仔', category: '江浙小吃、小吃零食', desc: '荷兰优质淡奶,奶香浓而不腻', address: '上海市普陀区真北路', shop: '王小虎夫妻店', shopId: '10333' }] } } } </script>
(5). 如何设置表格自适应
目标:表格数据少的时候,有几行数据,表格就占多少,当表格数据多的时候,表格最多占满屏幕。
思路:
①:封装两个自定义指令,代码如下:
// 设置表格高度 const doResize = async (el, binding, vnode) => { // 获取表格Dom对象 const { componentInstance: $table } = await vnode // 获取调用传递过来的数据 const { value } = binding if (!$table.height) { throw new Error('el-$table must set the height. Such as height=\'100px\'') } // 获取距底部距离(用于展示页码等信息) const bottomOffset = (value && value.bottomOffset) || 90 if (!$table) return // 计算列表高度并设置 const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset // 计算Table的Header和Body的高度和 var autoHeight = 0 $table.$children.forEach((item) => { var offsetHeight = item.$el.offsetHeight autoHeight += offsetHeight }) if (autoHeight < height) { autoHeight += 1 $table.layout.setHeight(autoHeight) } else { $table.layout.setHeight(height) } $table.doLayout() } export default { // 初始化设置 bind (el, binding, vnode) { // 设置resize监听方法 el.resizeListener = async () => { await doResize(el, binding, vnode) } // 绑定监听方法到addResizeListener // addResizeListener(window.document.body, el.resizeListener) window.addEventListener('resize', el.resizeListener) }, update (el, binding, vnode) { doResize(el, binding, vnode) }, // 绑定默认高度 async inserted (el, binding, vnode) { await doResize(el, binding, vnode) }, // 销毁时设置 unbind (el) { // 移除resize监听 // removeResizeListener(el, el.resizeListener) window.removeEventListener('resize', el.resizeListener) } }
index.js
import adaptive from './adaptive' const install = function (Vue) { // 绑定v-adaptive指令 Vue.directive('adaptive', adaptive) } if (window.Vue) { window.adaptive = adaptive Vue.use(install) } adaptive.install = install export default adaptive
②:导入到main.js中
// 导入表格自适应方法 import adaptive from './utils/table-adapter'
Vue.use(adaptive)
③:使用
2. Tree树形控件
(1). 数据结构
下面给出结构是标准的数据结构:
const data = [{
id: 1,
label: '一级 1',
children: [{
id: 4,
label: '二级 1-1',
children: [{
id: 9,
label: '三级 1-1-1'
}, {
id: 10,
label: '三级 1-1-2'
}]
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 5,
label: '二级 2-1'
}, {
id: 6,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 7,
label: '二级 3-1'
}, {
id: 8,
label: '二级 3-2'
}]
其中的label和children两个名称可以通过props属性进行修改成自定义名称。
(2). 基础用法
代码分享
<el-tree :data="rightslist" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree>
剖析:
① data属性:绑定数据
② props属性:用来配置tree的配置选项,比如:
// 树形控件的属性绑定对象 (用label和children映射接口中的自定义属性名)
treeProps: {
label: 'authName',
children: 'children'
}
③ show-checkbox 表示可选框 、default-expand-all 表示默认展开所有节点、 ref表示获取该标签自定义名。
④ node-key:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的。 这里用id标示。
⑤ default-checked-keys:默认勾选的节点的 key 的数组。 注:这里只包含选中的子节点,不包含父节点。
(3). 获取选中节点
通过下面的方式,可以获取到所有选中的节点,包含半选中的父节点,最终的keys数组,就是我们所需要的id值。
// getCheckedKeys 获取的选中节点,参数值默认为false,代表获取选中节点,如果父节点的子节点全部被选中,那么此时能获取父节点,参数值为true的时候,永远获取的都是子节点 // 注意,如果该节的子节点有没被选中的,那么该节点称作半选中状态,需要通过下面的getHalfCheckedKeys获取 // getHalfCheckedKeys获取的是半选中节点(指子节点未被全部选中的父节点!!) // 下面的keys利用剩余参数合并了两个数组(不去重),正好获取了所有节点 const keys = [ ...this.$refs.treeRef.getCheckedKeys(), ...this.$refs.treeRef.getHalfCheckedKeys() ]
最终效果:
3. Tag标签
这里使用了Tag的基本用法,如下图:
代码分享
<el-table-column label="权限等级" prop="level"> <template slot-scope="scope"> <el-tag v-if="scope.row.level === '0'">一级</el-tag> <el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag> <el-tag type="warning" v-else>三级</el-tag> </template> </el-table-column>
二. 角色/权限管理
1. 效果图
2. 代码分享
(1) 角色管理
<template> <div> <!-- 一. 面包屑导航区域 --> <el-breadcrumb separator-class="el-icon-arrow-right"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>权限管理</el-breadcrumb-item> <el-breadcrumb-item>角色列表</el-breadcrumb-item> </el-breadcrumb> <!--二. 卡片视图 --> <el-card> <!-- 1.添加角色按钮区域 --> <el-row> <el-col> <el-button type="primary" size="small">添加角色</el-button> </el-col> </el-row> <!-- 2.角色列表区域 --> <el-table :data="rolelist" border stripe> <!-- 2.1 展开列 --> <el-table-column type="expand"> <template slot-scope="scope"> <el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id"> <!-- 渲染一级权限 --> <el-col :span="5"> <el-tag closable @close="removeRightById(scope.row, item1.id)">{{item1.authName}}</el-tag> <i class="el-icon-caret-right"></i> </el-col> <!-- 渲染二级和三级权限 --> <el-col :span="19"> <!-- 通过 for 循环 嵌套渲染二级权限 --> <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id"> <el-col :span="6"> <el-tag type="success" closable @close="removeRightById(scope.row, item2.id)">{{item2.authName}} </el-tag> <i class="el-icon-caret-right"></i> </el-col> <el-col :span="18"> <el-tag type="warning" v-for="(item3, i3) in item2.children" :key="item3.id" closable @close="removeRightById(scope.row, item3.id)">{{item3.authName}}</el-tag> </el-col> </el-row> </el-col> </el-row> </template> </el-table-column> <!-- 2.2 索引列 --> <el-table-column type="index"></el-table-column> <!-- 2.3 其它列 --> <el-table-column label="角色名称" prop="roleName"></el-table-column> <el-table-column label="角色描述" prop="roleDesc"></el-table-column> <el-table-column label="操作" width="300px"> <template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> <el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限 </el-button> </template> </el-table-column> </el-table> </el-card> <!-- 三. 分配权限的对话框 --> <el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="500px" @close="setRightDialogClosed"> <div style="height: 400px; overflow: auto;"> <!-- 树形控件 --> <el-tree :data="rightslist" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree> </div> <span slot="footer" class="dialog-footer"> <el-button @click="setRightDialogVisible = false">取 消</el-button> <el-button type="primary" @click="allotRights">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { data () { return { // 所有角色列表数据 rolelist: [], // 控制分配权限对话框的显示与隐藏 setRightDialogVisible: false, // 所有权限的数据 rightslist: [], // 树形控件的属性绑定对象 treeProps: { label: 'authName', children: 'children' }, // 默认选中的节点Id值数组 defKeys: [], // 当前即将分配权限的角色id roleId: '' } }, created () { this.getRolesList() }, methods: { // 1.获取所有角色的列表 async getRolesList () { const { data: res } = await this.$http.get('roles') if (res.meta.status !== 200) { return this.$message.error('获取角色列表失败!') } this.rolelist = res.data console.log(this.rolelist) }, // 2.根据Id删除对应的权限 async removeRightById (role, rightId) { // 弹框提示用户是否要删除 const confirmResult = await this.$confirm('此操作将永久删除该权限吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).catch(err => err) if (confirmResult !== 'confirm') { // return this.$message.info('取消了删除!'); return } const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${rightId}`) if (res.meta.status !== 200) { return this.$message.error('删除权限失败!') } // 更新最新权限 role.children = res.data }, // 3.打开分配权限的对话框+显示对应权限 async showSetRightDialog (role) { this.roleId = role.id // 获取所有权限的数据 const { data: res } = await this.$http.get('rights/tree') if (res.meta.status !== 200) { return this.$message.error('获取权限数据失败!') } // 把获取到的权限数据保存到 rightslist 中 this.rightslist = res.data console.log(this.rightslist) // 递归获取三级节点的Id this.getLeafKeys(role, this.defKeys) this.setRightDialogVisible = true console.log(`选中节点${this.defKeys}`); }, // 4.通过递归的形式,获取角色下所有三级权限的id,并保存到 defKeys 数组中 getLeafKeys (node, arr) { // 如果当前 node 节点不包含 children 属性,则是三级节点 if (!node.children) { return arr.push(node.id) } node.children.forEach(item => this.getLeafKeys(item, arr)) }, // 5.监听分配权限对话框的关闭事件 setRightDialogClosed () { // 清空默认选中的节点Id值数组 this.defKeys = [] }, // 6.点击为角色分配权限 async allotRights () { // getCheckedKeys 获取的选中节点,参数值默认为false,代表获取选中节点,如果父节点的子节点全部被选中,那么此时能获取父节点,参数值为true的时候,永远获取的都是子节点 // 注意,如果该节的子节点有没被选中的,那么该节点称作半选中状态,需要通过下面的getHalfCheckedKeys获取 // getHalfCheckedKeys获取的是半选中节点(指子节点未被全部选中的父节点!!) // 下面的keys利用剩余参数合并了两个数组(不去重),正好获取了所有节点 const keys = [ ...this.$refs.treeRef.getCheckedKeys(), ...this.$refs.treeRef.getHalfCheckedKeys() ] const idStr = keys.join(',') const { data: res } = await this.$http.post(`roles/${this.roleId}/rights`, { rids: idStr }) if (res.meta.status !== 200) { return this.$message.error('分配权限失败!') } this.$message.success('分配权限成功!') this.getRolesList() this.setRightDialogVisible = false } } } </script> <style lang="less" scoped> .el-tag { margin: 7px; } .bdtop { border-top: 1px solid #eee; } .bdbottom { border-bottom: 1px solid #eee; } .vcenter { display: flex; align-items: center; } </style>
(2) 权限管理
<template> <div> <!-- 一. 面包屑导航区域 --> <el-breadcrumb separator-class="el-icon-arrow-right"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>权限管理</el-breadcrumb-item> <el-breadcrumb-item>权限列表</el-breadcrumb-item> </el-breadcrumb> <!--二.卡片视图 --> <el-card> <el-table :data="rightsList" border stripe height="500"> <el-table-column type="index"></el-table-column> <el-table-column label="权限名称" prop="authName"></el-table-column> <el-table-column label="路径" prop="path"></el-table-column> <el-table-column label="权限等级" prop="level"> <template slot-scope="scope"> <el-tag v-if="scope.row.level === '0'">一级</el-tag> <el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag> <el-tag type="warning" v-else>三级</el-tag> </template> </el-table-column> </el-table> </el-card> </div> </template> <script> export default { data () { return { // 权限列表 rightsList: [] } }, created () { // 获取所有的权限 this.getRightsList() }, methods: { // 1. 获取权限列表 async getRightsList () { const { data: res } = await this.$http.get('rights/list') if (res.meta.status !== 200) { return this.$message.error('获取权限列表失败!') } this.rightsList = res.data console.log(this.rightsList) } } } </script> <style lang="less" scoped> </style>
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。