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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
<template>
<div class="role-manage">
<div class="query-form">
<el-form ref="form" :inline="true" :model="queryForm">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="queryForm.roleName" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getRoleList">查询</el-button>
<el-button @click="handleReset('form')">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="base-table">
<div class="action">
<el-button type="primary" @click="handleAdd(1)">创建</el-button>
</div>
<!-- 必须加入key才可以变成树形结构 -->
<!-- tree-prop是可以将json数据中的孩子字段进行改名,如果不是叫children的话 -->
<el-table :data="roleList">
<el-table-column
v-for="item in columns"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:width="item.width"
:formatter="item.formatter"
>
</el-table-column>
<el-table-column label="操作" width="260">
<template #default="scope">
<el-button @click="handleEdit(scope.row)" size="mini"
>编辑</el-button
>
<el-button
@click="handleOpenPermission(scope.row)"
size="mini"
type="primary"
>设置权限</el-button
>
<el-button
type="danger"
size="mini"
@click="handleDel(scope.row._id)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination
background
layout="prev, pager, next"
:total="pager.total"
:page-size="pager.pageSize"
class="pagination"
@current-change="handleCurrentChange"
/>
<!-- 占位div -->
<!-- <div class="bottom"></div> -->
</div>

<!-- 弹出框,新增修改 -->
<el-dialog title="用户新增" v-model="showModal">
<el-form
ref="dialogForm"
:model="roleForm"
label-width="100px"
:rules="rules"
>
<el-form-item label="角色名称" prop="roleName">
<el-input
v-model="roleForm.roleName"
placeholder="请输入角色名称"
></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
type="textarea"
:rows="2"
v-model="roleForm.remark"
placeholder="请输入备注"
></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 权限弹出框 -->
<el-dialog title="权限设置" v-model="showPermission">
<el-form label-width="100px">
<el-form-item label="角色名称">
{{ curRoleName }}
</el-form-item>
<el-form-item label="选择权限">
<el-tree
ref="tree"
:data="menuList"
show-checkbox
node-key="_id"
default-expand-all
:props="{ label: 'menuName' }"
></el-tree>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showPermission = false">取消</el-button>
<el-button type="primary" @click="handlePermissionSubmit"
>确定</el-button
>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import utils from "../utils/utils";
export default {
name: "Role",
data() {
return {
queryForm: {
roleName: "",
},
columns: [
{
label: "角色名称",
prop: "roleName",
},
{
label: "备注",
prop: "remark",
},
{
label: "权限列表",
prop: "permissionList",
width: 200,
formatter:(row, column, value)=> {
let names = [];
let list = value.halfCheckedKeys || [];
list.map((key) => {
let name = this.actionMap[key];
if (name) names.push(name);
});
return names;
},
},
{
label: "更新时间",
prop: "updateTime",
formatter(row, column, value) {
return utils.formateDate(new Date(value));
},
},
{
label: "创建时间",
prop: "createTime",
formatter(row, column, value) {
return utils.formateDate(new Date(value));
},
},
],
roleList: [],
pager: {
total: 0,
pageNum: 1,
pageSize: 10,
},
// 显示弹出框
showModal: false,
action: "create",
roleForm: {},
rules: {
roleName: [
{
required: true,
message: "请输入角色名称",
trigger: "blur",
},
],
},
showPermission: false,
curRoleId: "",
curRoleName: "",
menuList: [],
actionMap: {},
};
},
mounted() {
this.getRoleList();
this.getMenuList();
},
methods: {
// 菜单列表初始化
async getRoleList() {
try {
let { list, page } = await this.$api.getRoleList({
...this.queryForm,
...this.pager,
});
this.roleList = list;
this.pager.total = page.total;
} catch (e) {
throw new Error(e);
}
},
// 菜单列表初始化
async getMenuList() {
try {
let list = await this.$api.getMenuList();
// console.log(list);
this.menuList = list;
this.getActionMap(list);
} catch (e) {
throw new Error(e);
}
},
getActionMap(list) {
let actionMap = {};
const deep = (arr) => {
while (arr.length) {
let item = arr.pop();
// 如果满足条件说明子菜单就是按钮,只要碰到action,就是最后一级菜单
if (item.children && item.action) {
// 将id和name做一个映射,存入对象中
actionMap[item._id] = item.menuName;
}
// 说明这个是一个菜单,但是下一级不是按钮
if (item.children && !item.action) {
deep(item.children);
}
}
};
// 因为前面item是引用的list,会改变list的值
// 通过两次json格式化,将list更改格式传入
deep(JSON.parse(JSON.stringify(list)));
this.actionMap = actionMap;
},

// 表单重置
handleReset(form) {
this.$refs[form].resetFields();
},
// 创建
handleAdd() {
this.action = "create";
this.showModal = true;
},
// 编辑
handleEdit(row) {
this.action = "edit";
this.showModal = true;
this.$nextTick(() => {
// this.roleForm = row;
this.roleForm = {
_id: row._id,
roleName: row.roleName,
remark: row.remark,
};
});
},
// 删除
async handleDel(_id) {
await this.$api.roleOperate({ _id, action: "delete" });
this.$message.success("删除成功");
this.getRoleList();
},
// 弹框关闭
handleClose() {
this.handleReset("dialogForm");
this.showModal = false;
},
// 提交
handleSubmit() {
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
let { roleForm, action } = this;
let params = { ...roleForm, action };
let res = await this.$api.roleOperate(params);
if (res) {
this.showModal = false;
this.$message.success("创建成功");
this.handleReset("dialogForm");
this.getRoleList();
}
}
});
},
// 分页提交
handleCurrentChange(current) {
this.pager.pageNum = current;
this.getRoleList();
},
handleOpenPermission(row) {
this.curRoleName = row.roleName;
this.curRoleId = row._id;
this.showPermission = true;
let { checkedKeys } = row.permissionList;
setTimeout(() => {
this.$refs.tree.setCheckedKeys(checkedKeys);
});
},
async handlePermissionSubmit() {
// 返回被选中的结点
let nodes = this.$refs.tree.getCheckedNodes();
// 返回被半选中的结点
let halfKeys = this.$refs.tree.getHalfCheckedKeys();
// 按钮数组
let checkedKeys = [];
// 菜单数组
let parentKeys = [];
nodes.map((node) => {
// 如果该节点中没有孩子结点,证明这个节点是一个按钮
if (!node.children) {
checkedKeys.push(node._id);
} else {
// 除了按钮其他的都是菜单,菜单不能作为选中进行赋值,需要设置半选
// 如果直接对菜单进行设置选中的话,那么其子节点都会被选中
parentKeys.push(node._id);
}
});
let params = {
// 查看是选中的哪个角色
_id: this.curRoleId,
permissionList: {
checkedKeys,
halfCheckedKeys: parentKeys.concat(halfKeys),
},
};
await this.$api.updatePermission(params);
this.showPermission = false;
this.$message.success("设置成功");
this.getRoleList();
},
},
};
</script>

<style lang="scss">
.role-manage {
height: 100%;
.query-form {
background-color: #ffffff;
padding: 22px 20px 0;
border-radius: 5px;
}
.base-table {
border-radius: 5px;
background: #ffffff;
margin-top: 20px;
margin-bottom: 20px;
.action {
border-radius: 5px 5px 0px 0px;
background: #ffffff;
padding: 20px;
border-bottom: 1px solid #ece8e8;
}
.pagination {
text-align: right;
padding: 10px;
}
.bottom {
height: 10px;
background: #fff;
height: 25px;
border-radius: 0px 0px 5px 5px;
}
}
}
</style>


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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const router = require('koa-router')()
const util = require('../utils/util')
const Menu = require('../models/menuSchema')

router.prefix('/menu')


// 获取菜单
router.get('/list', async (ctx) => {
const { menuName, menuState } = ctx.request.query;
const params = {}
// 作为查询项,如果有输入条件就进行查询
if(menuName) params.menuName = menuName;
if(menuState) params.menuState = menuState;
let rootList = await Menu.find(params) || [];
// 第一个参数:待修改的预备结构,第二个是根菜单的parentId,第三个是list=[]
// 用来存放父节点
const permissionList = getTreeMenu(rootList, null, []);
ctx.body = util.success(permissionList);
})

// 获取递归树形列表
function getTreeMenu(rootList, id, list){
//将树遍历一遍,将最上层的父菜单给获取到list中
// 初始id为null,获取到根目录
for(let i = 0; i < rootList.length; i++){
// 这一步没啥用,其实就是方便获取到rootlist[i]
let item = rootList[i];
// 这一步之所以这么写,是因为在parentId中是一个数组
// 当parentId只有一个的时候,才是最靠近父节点的
// 然后通过pop()将他出栈,继续寻找最靠近父节点的,递归实现
if(String(item.parentId.slice().pop()) == String(id)){
// 数据库中获取的文件内容在_doc中
list.push(item._doc)
}
}
// 根目录获取到后就需要递归获取子菜单
list.map(item=>{
item.children = [];

// 找到item的孩子结点--看谁的parentId为item._id
getTreeMenu(rootList, item._id, item.children)
// 递归结束,得到树形结构,之后对结构内容进行优化
if(item.children.length ==0){
// 没有孩子结点的话,就直接将该属性删除
delete item.children;
}else if(item.children.length >0 && item.children[0].menuType ==2){
// 快速区分按钮和菜单,用于后期按钮权限控制
item.action = item.children;
}
})
return list;
}



// 菜单编辑功能
router.post('/operate', async (ctx) => {
//用户编辑会传入id,状态,跟内容,将内容解构,由一个json对象转化为多个字段
const { _id, action, ...params } = ctx.request.body;
let res, info;
try {
// 新建功能
if (action == 'add') {
res = await Menu.create(params)
info = '创建成功'
// 编辑功能
} else if (action == 'edit') {
params.updateTime = new Date();
// 这一步是根据mongodb中的_id进行查找,然后将params中的值进行覆盖
res = await Menu.findByIdAndUpdate(_id, params);
info = '编辑成功'
// 删除功能
}else{
// 首先将目标给删除,通过id,接着需要查找他的子菜单一起删除
res = await Menu.findByIdAndRemove(_id);
// 查找其他数据的parentId字段,如果有就目标id,就删除
await Menu.deleteMany({parentId:{$all : [_id]}})
info = '删除成功'
}
ctx.body = util.success({},info)
} catch (error) {
console.error(error);
}
})

module.exports = router;