危险

为之则易,不为则难

0%

08_人资后台

✔ 掌握员工头像上传的业务逻辑。

✔ 掌握给用户分配角色,给角色分配权限的操作(RBAC)。

员工详情-封装员工头像组件

  1. 创建 image-upload 组件,src/views/employee/components/image-upload.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template>
<el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="beforeAvatarUpload">
<!-- (自动上传)action是上传地址 人资项目不需要 人资项目(手动上传) -->
<!-- show-file-list不展示列表 -->
<img v-if="value" :src="value" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</template>

<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
methods: {
// 检查函数 判断文件的类型还有大小 return true(继续上传)/false(停止上传)
beforeAvatarUpload(file) {
// jpeg png gif bmp

const isJPG = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp'].includes(file.type)
const isLt5M = file.size / 1024 / 1024 < 5 // 5M

if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG PNG GIF BMP 格式!')
}
if (!isLt5M) {
this.$message.error('上传头像图片大小不能超过 5MB!')
}
return isJPG && isLt5M
}
}
}
</script>

<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}

.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}

.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}

.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
  1. 在父组件中应用,src/views/employee/detail.vue
1
<image-upload v-model="userInfo.staffPhoto" />

员工详情-上传图片-创建腾讯云存储桶

  1. 注册腾讯云账号(课前完成 https://cloud.tencent.com/login)。

  2. 创建腾讯云存储桶。

  3. 得到应用密钥和应用标识。

  • 创建存储桶

先点击头像左边的【控制台】。

获取存储桶相关信息

将存储桶和所属地域拷贝下来,备用。获取应用标识 https://console.cloud.tencent.com/cam/capi

将 SecretId 和 SecretKey 拷贝下来,备用。

员工详情-使用cos-sdk完成上传

  1. 安装腾讯云 js-sdk。
1
npm i cos-js-sdk-v5 
  1. 使用 el-upload 自定义上传,src/views/employee/components/image-upload.vue
1
2
3
4
5
6
7
8
<template>
<el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="beforeAvatarUpload" :http-request="uploadImage">
<!-- (自动上传)action是上传地址 人资项目不需要 人资项目(手动上传) -->
<!-- show-file-list不展示列表 -->
<img v-if="value" :src="value" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</template>
  1. 实现上传方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import COS from 'cos-js-sdk-v5'
// 选择图片上传
uploadImage(params) {
console.log(params.file)
const cos = new COS({
SecretId: 'AKIDQmu0HrSQcZeU3cqA4iCcmgYidIhrQr4jy7',
SecretKey: 'jDRPmTx8rrdBTWXINPltwjKvJRmqfEYl'
}) // 完成 COS 对象的初始化
cos.putObject({
Bucket: 'ifer-1253924894', // 存储桶名称
Region: 'ap-nanjing', // 地域名称
Key: params.file.name, // 文件名称
StorageClass: 'STANDARD', // 固定值
Body: params.file // 文件对象
}, (err, data) => {
if (data.statusCode === 200 && data.Location) {
// 拿到了腾讯云返回的地址
// 通过 input 自定义事件将地址传出去
this.$emit('input', 'http://' + data.Location) // 将地址返回了
} else {
this.$message.error(err.Message) // 上传失败提示消息
}
})
}

权限管理-搭建权限页面

实现权限管理的页面结构, src/views/permission/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div class="container">
<div class="app-container">
<el-card>
<el-button class="btn-add" size="mini" type="primary">添加权限</el-button>
<el-table>
<el-table-column label="名称" />
<el-table-column label="标识" />
<el-table-column label="描述" />
<el-table-column label="操作">
<el-button size="mini" type="text">添加</el-button>
<el-button size="mini" type="text">编辑</el-button>
<el-button size="mini" type="text">删除</el-button>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: 'Permission'
}
</script>
<style>
.btn-add {
margin: 10px;
}
</style>

权限管理-获取数据转化树形

  1. 封装获取权限 API,src/api/permission.js
1
2
3
4
5
6
7
import request from '@/utils/request'

export function getPermissionList() {
return request({
url: '/sys/permission'
})
}
  1. 获取数据-转化树形-控制二级权限不显示添加按钮,src/views/permission/index.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div class="container">
<div class="app-container">
<el-card>
<el-button class="btn-add" size="mini" type="primary">添加权限</el-button>
<el-table default-expand-all :data="list" row-key="id">
<el-table-column prop="name" label="名称" />
<el-table-column prop="code" label="标识" />
<el-table-column prop="description" label="描述" />
<el-table-column label="操作">
<template v-slot="{ row }">
<el-button v-if="row.type === 1" 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>
</el-card>
</div>
</div>
</template>
<script>
import {
getPermissionList
} from '@/api/permission'
import {
transListToTreeData
} from '@/utils'
export default {
name: 'Permission',
data() {
return {
list: []
}
},
created() {
this.getPermissionList()
},
methods: {
async getPermissionList() {
this.list = transListToTreeData(await getPermissionList(), 0) // 将列表数据转化成树形结构
}
}
}
</script>
<style>
.btn-add {
margin: 10px;
}
</style>

权限管理-作业

基于权限接口和线上效果,完成权限点的新增、删除、编辑功能。

作业要求如下。

  • 实现新增-删除-编辑功能。

  • 编辑模式不做限制-弹层-行内-跳转都可。

  • 注意:二级权限和一级权限的区别为 type,当 type 为 1 时,为一级权限,当 type 为 2 时,为二级权限。

  • 二级权限应该添加在一级权限下,二者是通过 id 和 pid 进行关联的,其类似于组织架构中的添加子部门。

  1. 封装权限管理的 API 接口,src/api/permission.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 新增权限
export function addPermission(data) {
return request({
url: '/sys/permission',
method: 'post',
data
})
}

// 更新权限
export function updatePermission(data) {
return request({
url: `/sys/permission/${data.id}`,
method: 'put',
data
})
}

// 删除权限
export function delPermission(id) {
return request({
url: `/sys/permission/${id}`,
method: 'delete'
})
}
// 获取权限详情
export function getPermissionDetail(id) {
return request({
url: `/sys/permission/${id}`
})
}
  1. 新增编辑权限的弹层,src/views/permission/index.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 放置一个弹层 用来编辑新增节点 -->
<el-dialog :title="`${showText}权限点`" :visible="showDialog" @close="btnCancel">
<!-- 表单 -->
<el-form ref="perForm" :model="formData" :rules="rules" label-width="120px">
<el-form-item label="权限名称" prop="name">
<el-input v-model="formData.name" style="width:90%" />
</el-form-item>
<el-form-item label="权限标识" prop="code">
<el-input v-model="formData.code" style="width:90%" />
</el-form-item>
<el-form-item label="权限描述">
<el-input v-model="formData.description" style="width:90%" />
</el-form-item>
<el-form-item label="开启">
<el-switch v-model="formData.enVisible" active-value="1" inactive-value="0" />
</el-form-item>
</el-form>
<el-row slot="footer" type="flex" justify="center">
<el-col :span="6">
<el-button size="small" type="primary" @click="btnOK">确定</el-button>
<el-button size="small" @click="btnCancel">取消</el-button>
</el-col>
</el-row>
</el-dialog>
  1. 声明控制弹层需要的变量、表单数据和校验规则,src/views/permission/index.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import {
getPermissionList,
updatePermission,
addPermission,
getPermissionDetail,
delPermission
} from '@/api/permission'
import {
transListToTreeData
} from '@/utils'
export default {
data() {
return {
list: [],
formData: {
name: '', // 名称
code: '', // 标识
description: '', // 描述
type: '', // 类型,该类型不需要显示,点击添加的时候直接传递接口(路由级别的权限为 1,按钮级别的为 2)
pid: '', // 因为做的是树,需要知道添加到哪个节点下了
enVisible: "0" // 默认关闭
},
rules: {
name: [{
required: true,
message: '权限名称不能为空',
trigger: 'blur'
}],
code: [{
required: true,
message: '权限标识不能为空',
trigger: 'blur'
}]
},
showDialog: false
}
},
computed: {
showText() {
return this.formData.id ? '编辑' : '新增'
}
},
created() {
this.getPermissionList()
},
methods: {
async getPermissionList() {
this.list = transListToTreeData(await getPermissionList(), 0)
},
btnOK() {},
btnCancel() {}
}
}
  1. 实现一级权限新增-二级权限新增, src/views/permission/index.vue

注意:一级添加的 type 为 1, 二级添加的 type 为 2。

1
<el-button class="btn-add" size="mini" type="primary" @click="addPermission(0, 1)">添加权限</el-button>
1
2
3
4
5
<el-table-column label="操作">
<template v-slot="{ row }">
<el-button v-if="row.type === 1" size="mini" type="text" @click="addPermission(row.id, 2)">添加</el-button>
</template>
</el-table-column>
  1. 点击添加按钮显示弹框,并记录 pid 和 type。
1
2
3
4
5
addPermission(pid, type) {
this.formData.pid = pid
this.formData.type = type
this.showDialog = true
},
  1. 点击确定按钮调用添加接口、取消按钮操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
export default {
name: 'Permission',
// ...
methods: {
// ...
btnOK() {
this.$refs.perForm.validate().then(() => {
if (this.formData.id) {
return updatePermission(this.formData)
}
return addPermission(this.formData)
}).then(() => {
// 提示消息
this.$message.success('新增成功')
this.getPermissionList()
this.showDialog = false
})
},
btnCancel() {
this.formData = {
name: '', // 名称
code: '', // 标识
description: '', // 描述
type: '', // 类型 该类型 不需要显示 因为点击添加的时候已经知道类型了
pid: '', // 因为做的是树 需要知道添加到哪个节点下了
enVisible: '0' // 默认关闭
}
this.$refs.perForm.resetFields()
this.showDialog = false
}
}
}
  1. 实现编辑功能。
1
<el-button type="text" size="mini" @click="editPermission(row.id)">编辑</el-button>
1
2
3
4
5
6
7
8
9
10
11
12
export default {
name: 'Permission',
// ...
methods: {
// ...
async editPermission(id) {
// 根据获取id获取详情
this.formData = await getPermissionDetail(id)
this.showDialog = true
}
}
}
  1. 实现删除功能。
1
<el-button type="text" size="mini" @click="delPermission(row.id)"> 删除</el-button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export default {
name: 'Permission',
// ...
methods: {
// ...
async delPermission(id) {
try {
await this.$confirm('确定要删除该数据吗')
await delPermission(id)
this.getPermissionList()
this.$message.success('删除成功')
} catch (error) {
console.log(error)
}
},
}
}

权限应用-权限概念

权限是通过角色这个中间人实现,首先员工拥有角色,角色拥有权限,那么员工也就自动拥有了角色所对应的权限。

所以接下来,我们需要实现给员工分配角色,给角色分配权限。

给员工分角色,views/employee/index.vue

给角色分权限,views/role/index.vue

权限应用-员工分配角色-弹出层

  1. 封装获取所有角色的 API, src/api/employee.js
1
2
3
4
5
6
7
8
9
/**
* 获取可用的角色
* **/

export function getEnableRoleList() {
return request({
url: '/sys/role/list/enabled'
})
}
  1. 声明相关变量, src/views/employee/index.vue
1
2
3
4
5
6
7
data() {
return {
showRoleDialog: false, // 用来控制角色弹层的显示
roleList: [], // 接收角色列表
roleIds: [] // 用来双向绑定数据的
}
}
  1. 点击角色按钮,获取角色列表, src/views/employee/index.vue
1
2
3
4
5
6
import { getEnableRoleList } from '@/api/employee'
// 点击角色按钮弹出层
async btnRole() {
this.showRoleDialog = true
this.roleList = await getEnableRoleList()
}
  1. 放置弹层,绑定变量和渲染 checkbox, src/views/employee/index.vue
1
2
3
4
5
6
7
8
<el-dialog :visible.sync="showRoleDialog" title="分配角色">
<!-- 弹层内容 -->
<!-- checkbox -->
<el-checkbox-group v-model="roleIds">
<!-- 放置n个的checkbox 要执行checkbox的存储值 item.id-->
<el-checkbox v-for="item in roleList" :key="item.id" :label="item.id">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-dialog>

权限应用-员工分配角色-回显数据并提交

  1. 封装给员工分配角色的接口, src/api/employee.js
1
2
3
4
5
6
7
8
9
10
11
/**
* 分配员工角色
* ***/

export function assignRole(data) {
return request({
url: '/sys/user/assignRoles',
method: 'put',
data
})
}
  1. 点击角色按钮时,录当前点击的用户 id,并获取员工已经拥有的角色, src/views/employee/index.vue
1
2
3
4
5
data() {
return {
currentUserId: null // 用来记录当前点击的用户id
}
}
1
2
3
4
5
6
7
8
9
10
async btnRole(id) {
this.roleList = await getEnableRoleList()
// 记录当前点击的id 因为后边 确定取消要存取给对应的用户
this.currentUserId = id
const {
roleIds
} = await getEmployeeDetail(id)
this.roleIds = roleIds
this.showRoleDialog = true // 调整了顺序
},
  1. 点击确定实现给用户分配角色, src/views/employee/index.vue
1
2
3
4
5
6
<el-row slot="footer" type="flex" justify="center">
<el-col :span="6">
<el-button type="primary" size="mini" @click="btnRoleOK">确定</el-button>
<el-button size="mini" @click="showRoleDialog = false">取消</el-button>
</el-col>
</el-row>
1
2
3
4
5
6
7
8
9
// 点击角色的确定
async btnRoleOK() {
await assignRole({
id: this.currentUserId,
roleIds: this.roleIds
})
this.$message.success('分配用户角色成功')
this.showRoleDialog = false
}

权限应用-给角色分配权限-弹出层

  1. 声明控制弹层显示和接收权限数据的变量, src/views/role/index.vue
1
2
3
4
5
6
data() {
return {
showPermissionDialog: false,
permissionData: []
}
}
  1. 点击分配权限-弹出层-获取数据并转化树形, src/views/role/index.vue
1
<el-button size="mini" type="text" @click="btnPermission">分配权限</el-button>
1
2
3
4
5
6
import { transListToTreeData } from '@/utils'
import { getPermissionList } from '@/api/permission'
async btnPermission() {
this.showPermissionDialog = true
this.permissionData = transListToTreeData(await getPermissionList(), 0)
}
  1. 放置弹层和树组件, src/views/role/index.vue
1
2
3
4
5
<!-- 放置权限弹层 -->
<el-dialog :visible.sync="showPermissionDialog" title="分配权限">
<!-- 放置权限数据 -->
<el-tree :data="permissionData" :props="{ label: 'name' }" show-checkbox default-expand-all />
</el-dialog>

权限应用-角色分配权限-显示已有权限数据

  1. 获取当前角色的权限, src/api/role.js
1
2
3
4
5
export function getRoleDetail(id) {
return request({
url: `/sys/role/${id}`
})
}
  1. 声明变量,记录当前点击的角色 id 和角色所拥有的权限数据, src/views/role/index.vue
1
2
3
4
5
6
data() {
return {
currentRoleId: null,
permIds: []
}
}
  1. 点击分配权限按钮时传递角色 id,并根据 id 获取该角色所拥有的权限, src/views/role/index.vue
1
<el-button size="mini" type="text" @click="btnPermission(row.id)">分配权限</el-button>
1
2
3
4
5
6
7
8
9
async btnPermission(id) {
this.currentRoleId = id
const {
permIds
} = await getRoleDetail(id)
this.permIds = permIds
this.permissionData = transListToTreeData(await getPermissionList(), 0)
this.showPermissionDialog = true
}
  1. 设置 el-tree 组件的属性 node-key 和当前选中数据, src/views/role/index.vue
1
<el-tree check-strictly node-key="id" :data="permissionData" :props="{ label: 'name' }" show-checkbox default-expand-all :default-checked-keys="permIds" />

default-checked-keys 的属性是设置当前选中的节点,但是必须配合 node-key 属性,因为 permIds 变量中存储的都是 id,必须让 el-tree 组件知道 key 是哪个字段,所以设置 node-key="id"

check-strictly 表示父子不关联。

权限应用-角色分配权限-确定提交

  1. 封装分配权限的接口 API,src/api/role.js
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 给角色分配权限
*
* ***/

export function assignPerm(data) {
return request({
url: '/sys/role/assignPrem',
method: 'put',
data
})
}
  1. 确定和取消事件, src/views/role/index.vue
1
2
3
4
5
6
<el-row slot="footer" type="flex" justify="center">
<el-col :span="6">
<el-button type="primary" size="mini" @click="btnPermissionOK">确定</el-button>
<el-button size="mini" @click="cancelPermDialog">取消</el-button>
</el-col>
</el-row>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 点击确定时触发
async btnPermissionOK() {
await assignPerm({
id: this.currentRoleId,
// 记得给 el-tree 组件添加 ref="permTree"
permIds: this.$refs.permTree.getCheckedKeys()
})
this.$message.success('角色分配权限成功')
this.showPermissionDialog = false
},
cancelPermDialog() {
this.showPermissionDialog = false
this.permIds = []
}
1
2
<!-- 绑定了 @close 事件,.sync 修饰符没有要了 -->
<el-dialog :visible="showPermissionDialog" title="分配权限" @close="cancelPermDialog"></el-dialog>