iHRM人力资源 - 角色管理
文章目录
一、搭建页面结构
如下图所示,角色管理模块的内容
主要就是table表格+分页组件
可以将代码放在这里
1.1 页面结构
<template>
<div class="container">
<div class="app-container">
<!--角色管理的内容-->
<div class="role-operate">
<el-button size="mini" type="primary">添加角色</el-button>
</div>
<!--放置表格组件-->
<el-table>
<!--放置4列,align="center"表示居中对齐,"角色"列与"启用"列固定宽度-->
<el-table-column align="center" width="200" label="角色"></el-table-column>
<el-table-column align="center" width="200" label="启用"></el-table-column>
<el-table-column align="center" label="描述"></el-table-column>
<el-table-column align="center" label="操作"></el-table-column>
</el-table>
<!--放置分页组件-->
<!--使用flex的方式对齐,然后再使用justify="end"表示尾部对齐-->
<!--align="middle"表示垂直居中-->
<el-row type="flex" justify="end" style="height: 60px" align="middle">
<!--放置分页组件,并且在右下方-->
<!--layout表示分页插件中显示的内容,并且用逗号分隔,"prev,pager,next"分别表示左箭头、数字面板、右箭头-->
<el-pagination layout="prev,pager,next">
</el-pagination>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: 'Role'
}
</script>
<style scoped>
.role-operate {
padding: 10px
}
</style>
效果图如下所示
1.2 获取角色数据
这一步我们要显示数据,如下图所示
可以按照如下所示的步骤
请求参数
/**
* 获取角色列表
*/
export function getRoleList(params) {
return request({
url: '/sys/role',
// 这个接口需要param形式的参数,其中param的值就是页码和每页的大小
params: params // 查询参数
})
}
- 页面初始化时调用函数并赋值给数据
import { getRoleList } from '@/api/role'
export default {
name: 'Role',
data() {
return {
// 存储分页的数据
list: []
}
},
created() {
this.getRoleList()
},
methods: {
async getRoleList() {
// 将返回结构中的rows内容提取出来
const { rows } = await getRoleList()
this.list = rows
}
}
}
- 绑定表格
<!--放置表格组件-->
<!--:data="list" 绑定数据-->
<el-table :data="list">
<!--放置4列,align="center"表示居中对齐,"角色"列与"启用"列固定宽度-->
<el-table-column prop="name" align="center" width="200" label="角色"></el-table-column>
<el-table-column prop="state" align="center" width="200" label="启用"></el-table-column>
<el-table-column prop="description" align="center" label="描述"></el-table-column>
<el-table-column align="center" label="操作"></el-table-column>
</el-table>
- 效果图
发现两个问题,
问题1:“启用”列是数字,不是文字
问题2:“操作”列没有操作
所以我们需要自定义一下表格的列
1.3 自定义表格列
接下来我们要展示出如下图所示的结构
element-ui的表格里面可以传入我们的插槽的内容
我们根据数字1或者0展示文字的问题,我们可以使用作用域插槽
下图中,row表示行数据,column表示列数据,我们要行数据row就可以了
如下所示
<!--放置表格组件-->
<!--:data="list" 绑定数据-->
<el-table :data="list">
<!--放置4列,align="center"表示居中对齐,"角色"列与"启用"列固定宽度-->
<el-table-column prop="name" align="center" width="200" label="角色"></el-table-column>
<el-table-column prop="state" align="center" width="200" label="启用">
<!--插槽内传入我们自定义的列结构-->
<!--是对象里面有row,所以我们{ row }解构一下数据-->
<template v-slot="{ row }">
<!-- 我们可以使用插值语法{{row}}看一下内容内容:{ "id": 1, "name": "系统管理员", "description": "管理整合平台,可以操作企业所有功能", "state": 1 }-->
<span>{{ row.state === 1 ? '已启用' : row.state === 0 ? '未启用' : '无' }}</span>
</template>
</el-table-column>
<el-table-column prop="description" align="center" label="描述"></el-table-column>
<el-table-column align="center" label="操作">
<!--在操作中放置三个按钮-->
<template>
<!--type = "text"是将按钮形态转换成链接形态-->
<el-button size="mini" type="text">分配权限</el-button>
<el-button size="mini" type="text">编辑</el-button>
<el-button size="mini" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
效果图如下所示
1.4 分页功能
虽然我们之前写了分页组件,但是我们并没有写真正的页面,现在来完善一下
我们要做的分页形式,如下图所示
流程如下图所示
- 绑定分页信息
<!--放置分页组件-->
<!--使用flex的方式对齐,然后再使用justify="end"表示尾部对齐-->
<!--align="middle"表示垂直居中-->
<el-row type="flex" justify="end" style="height: 60px" align="middle">
<!--放置分页组件,并且在右下方-->
<!--layout表示分页插件中显示的内容,并且用逗号分隔,"prev,pager,next"分别表示左箭头、数字面板、右箭头-->
<!--page-size、current-page、total三个是分页数据-->
<el-pagination
:page-size="pageParams.pagesize"
:current-page="pageParams.page"
:total="pageParams.total"
layout="prev,pager,next"
>
</el-pagination>
</el-row>
分页数据
data() {
return {
// 存储分页的数据
list: [],
// 将分页信息放到对象中,方便管理
pageParams: {
// 当前页默认是1
page: 1,
// 每页多少数据
pagesize: 5,
// 总数
total: 0
}
}
}
- 初始化时请求第一页
created() {
this.getRoleList()
},
methods: {
async getRoleList() {
// 将返回结构中的rows内容提取出来,也提取出total总数字段
const { rows, total } = await getRoleList(this.pageParams)
this.list = rows
this.pageParams.total = total
}
}
- 切换分页时请求对应数据
分页组件增加 @current-change事件,并调用changePage方法
<!--放置分页组件,并且在右下方-->
<!--layout表示分页插件中显示的内容,并且用逗号分隔,"prev,pager,next"分别表示左箭头、数字面板、右箭头-->
<!--page-size、current-page、total三个是分页数据-->
<!--事件@current-change触发时,会给我们传一个是哪一页的参数-->
<el-pagination
:page-size="pageParams.pagesize"
:current-page="pageParams.page"
:total="pageParams.total"
@current-change="changePage"
layout="prev,pager,next"
>
</el-pagination>
// 会给我们传一个是哪一页的参数,在这里就是newPage
changePage(newPage) {
this.pageParams.page = newPage
this.getRoleList()
}
二、添加角色
效果及流程如下图所示
2.1 角色弹出层
按钮
<div class="role-operate">
<!--添加点击事件-->
<el-button @click="showDialog=true" size="mini" type="primary">添加角色</el-button>
</div>
数据
data() {
return {
// 控制添加弹层
showDialog: false
........
}
},
弹出层
<!--弹层-->
<!--sync的目的:当我们点击×号的时候,能把showDialog值改为false-->
<el-dialog width="500px" title="新增角色" :visible.sync="showDialog">
<el-form label-width="120px">
<el-form-item label="角色名称">
<el-input style="width: 300px" size="mini"></el-input>
</el-form-item>
<el-form-item label="启用">
<!--开关-->
<el-switch size="mini"></el-switch>
</el-form-item>
<el-form-item label="角色描述">
<!--文本域,行数为3行-->
<el-input type="textarea" :rows="3" style="width: 300px" size="mini"></el-input>
</el-form-item>
<el-form-item>
<!--为了让按钮居中,我们可以采用row和col组件来实现-->
<el-row type="flex" justify="center">
<el-col :span="12">
<el-button type="primary" size="mini">确定</el-button>
<el-button size="mini">取消</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</el-dialog>
效果图
2.2 表单校验
实现如下图所示的内容
绑定属性及校验规则
// 角色表单
roleForm: {
// 名称
name: '',
// 描述
description: '',
// 是否启用,默认未启用状态
state: 0
},
rules: {
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
description: [{ required: true, message: '角色描述不能为空', trigger: 'blur' }]
}
<!--弹层-->
<!--sync的目的:当我们点击×号的时候,能把showDialog值改为false-->
<el-dialog width="500px" title="新增角色" :visible.sync="showDialog">
<el-form ref="roleForm" :model="roleForm" label-width="120px" :rules="rules">
<el-form-item prop="name" label="角色名称">
<el-input v-model="roleForm.name" style="width: 300px" size="mini"></el-input>
</el-form-item>
<el-form-item prop="state" label="启用">
<!--开关,此处不需要校验-->
<!--开和关的默认值是true和false,但是我们需要的是1或者0,所以我们自定义一下 active-value="1" inactive-value="0",表示开为1,关为0-->
<el-switch v-model="roleForm.state" :active-value="1" :inactive-value="0" size="mini"></el-switch>
</el-form-item>
<el-form-item prop="description" label="角色描述">
<!--文本域,行数为3行-->
<el-input v-model="roleForm.description" type="textarea" :rows="3" style="width: 300px" size="mini"></el-input>
</el-form-item>
<el-form-item>
<!--为了让按钮居中,我们可以采用row和col组件来实现-->
<el-row type="flex" justify="center">
<el-col :span="12">
<el-button type="primary" size="mini">确定</el-button>
<el-button size="mini">取消</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</el-dialog>
效果图
2.3 确认取消
流程如下所示
API请求
/**
* 新增角色
*/
export function addRole(data) {
return request({
url: '/sys/role',
// 这个接口需要param形式的参数,其中param的值就是页码和每页的大小
method: 'post',
data: data
})
}
按钮
<el-button @click="btnOK" type="primary" size="mini">确定</el-button>
<el-button @click="btnCancel" size="mini">取消</el-button>
方法
// 确定按钮
btnOK() {
this.$refs.roleForm.validate(async isOK => {
if (isOK) {
await addRole(this.roleForm)
this.$message.success('新增角色成功')
// 重新获取数据
this.getRoleList()
this.btnCancel()
}
})
},
// 取消按钮
btnCancel() {
// 重置表单
this.$refs.roleForm.resetFields()
// 关闭弹层
this.showDialog = false
}
但是现在有一个问题
我们在使用 this.$refs.roleForm.resetFields()重置表单的时候,并没有把roleForm.state属性值重置为0,原因是我们表单中的state并没用使用prop绑定到roleForm.state值上
所以我们仍然需要在“启用”上加上prop字段
简单的说,如果我们需要使用resetFields()方法重置表单的时候,也需要在el-form-item标签上添加prop属性
<el-form-item prop="state" label="启用">
<!--开关,此处不需要校验-->
<!--开和关的默认值是true和false,但是我们需要的是1或者0,所以我们自定义一下 active-value="1" inactive-value="0",表示开为1,关为0-->
<el-switch v-model="roleForm.state" :active-value="1" :inactive-value="0" size="mini"></el-switch>
</el-form-item>
并且我们发现点击叉号关闭弹层后不会将表单重置
因为visible.sync只会把showDialog属性值变成false
所以我们在标签el-dialog上添加@close事件
<el-dialog @close="btnCancel" width="500px" title="新增角色" :visible.sync="showDialog">
三、行内编辑
3.1 思路分析
如下图所示,不需要弹层进行编辑,而是直接在此数据行修改数据
并且还能实现多行同时编辑
如下四功能
- 要实现表格的行内表单,也就是说要把表单嵌入在表格的列里面
- 可以多行同时编辑
- 确定是保存数据
- 当我们修改完数据后,突然不想修改,仍能保持原数据
实现思路
-
首先在行数据里面定义一个"编辑"的标记。
也就是说看哪一行进入了编辑状态
-
点击某一行的“编辑”后,此时的“编辑”状态就会发生变化
-
在列里面利用自定义插槽自定义我们的编辑表单
3.2 行内编辑
- 行数据定义编辑标记
async getRoleList() {
// 将返回结构中的rows内容提取出来,也提取出total总数字段
const { rows, total } = await getRoleList(this.pageParams)
this.list = rows
this.pageParams.total = total
// 针对每一行数据添加一个编辑标记,为了实现行内编辑
this.list.formEach(item => {
// 添加一个新字段isEdit,并且值为false
item.isEdit = false
})
},
效果如下图所示
- 点击行编辑,标记状态
也就是让isEdit字段的值为true
按钮
<!--在操作中放置三个按钮-->
<!--是对象里面有row,所以我们{ row }解构一下数据-->
<template v-slot="{ row }">
<!--type = "text"是将按钮形态转换成链接形态-->
<el-button @click="btnEditRow(row)" size="mini" type="text">编辑</el-button>
......
</template>
执行方法btnEditRow
btnEditRow(row) {
// 改变行的编辑状态
row.isEdit = true
}
- 插槽根据标记渲染表单
<!--放置表格组件-->
<!--:data="list" 绑定数据-->
<el-table :data="list">
<!--放置4列,align="center"表示居中对齐,"角色"列与"启用"列固定宽度-->
<el-table-column prop="name" align="center" width="200" label="角色">
<!--行内编辑时的表单-->
<!--利用插槽解构出row-->
<template v-slot="{ row }">
<!--条件判断,当isEdit为true时,就展示input框-->
<el-input size="mini" v-if="row.isEdit"></el-input>
<span v-else>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="state" align="center" width="200" label="启用">
<!--插槽内传入我们自定义的列结构-->
<!--是对象里面有row,所以我们{ row }解构一下数据-->
<template v-slot="{ row }">
<el-switch v-if="row.isEdit" size="mini"></el-switch>
<!-- 我们可以使用插值语法{{row}}看一下内容内容:{ "id": 1, "name": "系统管理员", "description": "管理整合平台,可以操作企业所有功能", "state": 1 }-->
<span v-else>{{ row.state === 1 ? '已启用' : row.state === 0 ? '未启用' : '无' }}</span>
</template>
</el-table-column>
<el-table-column prop="description" align="center" label="描述">
<template v-slot="{ row }">
<el-input v-if="row.isEdit" type="textarea" size="mini"></el-input>
<span v-else>{{ row.description }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<!--在操作中放置三个按钮-->
<!--是对象里面有row,所以我们{ row }解构一下数据-->
<template v-slot="{ row }">
<!--编辑状态-->
<template v-if="row.isEdit">
<el-button type="primary" size="mini">确定</el-button>
<el-button size="mini">取消</el-button>
</template>
<!--非编辑状态-->
<template v-else>
<!--下面三个标签在编辑情况下是不展示的-->
<!--type = "text"是将按钮形态转换成链接形态-->
<el-button size="mini" type="text">分配权限</el-button>
<el-button @click="btnEditRow(row)" size="mini" type="text">编辑</el-button>
<el-button size="mini" type="text">删除</el-button>
</template>
</template>
</el-table-column>
</el-table>
修改bug
我们点击“编辑”,发现并不管用,原因如下
async getRoleList() {
// 将返回结构中的rows内容提取出来,也提取出total总数字段
const { rows, total } = await getRoleList(this.pageParams)
this.list = rows
this.pageParams.total = total
// 针对每一行数据添加一个编辑标记,为了实现行内编辑
this.list.forEach(item => {
// 添加一个新字段isEdit,并且值为false
// item.isEdit = false
// 数据响应式的问题:数据变化,视图更新 是针对已经有的属性
// 添加的动态属性,是不具备响应式特点,因为并没有监控到isEdit字段值的变化,所以我们点击“编辑”后,并不会出现输入框
// 所以我们应该像下面一样添加属性
// this.$set(目标对象,属性名称,初始值)可以针对目标对象添加的属性添加响应式
this.$set(item, 'isEdit', false)
// 这样的话就会触发视图的更新
})
}
效果图
当时我们发现修改时,没有数据,我们接下来就写上
3.3 行内数据缓存
3.3.1 分析
我们要实现点击“编辑”时,数据能够回显上去
我们编辑的数据并不是真实的列表数据,而是行内数据里面的editRow的缓存数据
这样就确保了原来的数据不受任何的影响,所以我们需要在每一行里加一个“editRow”对象
不仅初始化的时候要更新editRow,而且我们每次点击“编辑”的时候,都要重新更新editRow缓存中内容
3.3.2 实现
- 初始化时缓存数据
async getRoleList() {
// 将返回结构中的rows内容提取出来,也提取出total总数字段
const { rows, total } = await getRoleList(this.pageParams)
this.list = rows
this.pageParams.total = total
// 针对每一行数据添加一个编辑标记,为了实现行内编辑
this.list.forEach(item => {
// 添加一个新字段isEdit,并且值为false
// item.isEdit = false
// 数据响应式的问题:数据变化,视图更新 是针对已经有的属性
// 添加的动态属性,是不具备响应式特点,因为并没有监控到isEdit字段值的变化,所以我们点击“编辑”后,并不会出现输入框
// 所以我们应该像下面一样添加属性
// this.$set(目标对象,属性名称,初始值)可以针对目标对象添加的属性添加响应式
// 这样的话就会触发视图的更新
this.$set(item, 'isEdit', false)
// 行内数据缓存
this.$set(item, 'editRow', { name: item.name, state: item.state, description: item.description })
})
-
将数据绑定到编辑形态上的表单
其实就在column标签中的编辑形态下的input框加一个v-model双向绑定
<!--放置4列,align="center"表示居中对齐,"角色"列与"启用"列固定宽度-->
<el-table-column prop="name" align="center" width="200" label="角色">
<!--行内编辑时的表单-->
<!--利用插槽解构出row-->
<template v-slot="{ row }">
<!--条件判断,当isEdit为true时,就展示input框-->
<el-input v-model="row.editRow.name" size="mini" v-if="row.isEdit"></el-input>
<span v-else>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="state" align="center" width="200" label="启用">
<!--插槽内传入我们自定义的列结构-->
<!--是对象里面有row,所以我们{ row }解构一下数据-->
<template v-slot="{ row }">
<el-switch v-model="row.editRow.state" :active-value="1" :inactive-value="0" v-if="row.isEdit" size="mini"></el-switch>
<!-- 我们可以使用插值语法{{row}}看一下内容内容:{ "id": 1, "name": "系统管理员", "description": "管理整合平台,可以操作企业所有功能", "state": 1 }-->
<span v-else>{{ row.state === 1 ? '已启用' : row.state === 0 ? '未启用' : '无' }}</span>
</template>
</el-table-column>
<el-table-column prop="description" align="center" label="描述">
<template v-slot="{ row }">
<el-input v-model="row.editRow.description" v-if="row.isEdit" type="textarea" size="mini"></el-input>
<span v-else>{{ row.description }}</span>
</template>
</el-table-column>
- 点击“编辑”按钮的时候,更新我们的缓存数据
btnEditRow(row) {
// 改变行的编辑状态
row.isEdit = true
// 更新缓存数据
row.editRow.name = row.name
row.editRow.state = row.state
row.editRow.description = row.description
}
3.4 确定与取消按钮
我们在表格里面没法使用form表单,所以我们就需要手动检查必填项
api请求
/**
* 修改角色
*/
export function updateRole(data) {
return request({
url: `/sys/role/${data.id}`,
// 这个接口需要param形式的参数,其中param的值就是页码和每页的大小
method: 'put',
data: data
})
}
确定与取消按钮
<!--编辑状态-->
<template v-if="row.isEdit">
<el-button @click="btnEditOK(row)" type="primary" size="mini">确定</el-button>
<!--退出编辑模式-->
<el-button @click="row.isEdit = false" size="mini">取消</el-button>
</template>
方法
// 编辑功能
async btnEditOK(row) {
// 我们要发送请求的数据其实是row.editRow
// 检查必填项
if (row.editRow.name && row.editRow.description) {
// 说明这两项已经填写了
// ...row.editRow表示将row.editRow里面的字段拷贝了一份
// id表示新增的字段, row.id表示将其值赋值给id字段
await updateRole({ ...row.editRow, id: row.id })
// 更新成功
this.$message.success('更新角色成功')
// 更新显示数据、退出编辑状态
// row.name = row.editRow.name // 这个地方虽然有报错,但是是eslint解析的误判
// Object.assign(target,source) 我们可以把来源的数据赋值给target中的数据
// 下面的值也包括退出编辑模式
Object.assign(row, {
name: row.editRow.name,
description: row.editRow.description,
state: row.editRow.state,
isEdit: false
})
// 还有一个写法就是使用延展运算符 Object.assign(row, { ...row.editRow,isEdit: false })
} else {
this.$message.warning('角色和描述不能为空')
}
}
四、删除角色
点击删除的时候,会出现一个气泡框
流程如下所示
删除的时候要判断是否删除的是最后一个数据
api请求
/**
* 删除角色
*/
export function delRole(id) {
return request({
url: `/sys/role/${id}`,
// 这个接口需要param形式的参数,其中param的值就是页码和每页的大小
method: 'delete'
})
}
按钮
<!--非编辑状态-->
<template v-else>
<!--type = "text"是将按钮形态转换成链接形态-->
..........
<!--点击确认后会触发@onConfirm事件-->
<el-popconfirm @onConfirm="confirmDel(row.id)" title="您确定要删除吗?">
<el-button slot="reference" style="margin-left: 10px" size="mini" type="text">删除</el-button>
</el-popconfirm>
</template>
方法
// 删除角色
async confirmDel(id) {
await delRole(id)
this.$message.success('删除角色成功')
// 判断删除的是不是数据中最后一个
if (this.list.length === 1) {
// 说明此时删除的数据是最后一个数据
this.pageParams.page = this.pageParams.page - 1
}
// 重新加载数据
this.getRoleList()
}