...
...
computed: {
# 选择tab标签时候顺便也要激活当前对应的导航
activeMenu() {
return this.$store.state.menus.editableTabsValue
}
},
methods: {
selectMenu(item) {
console.log(item)
let obj = {
name: item.name,
title: item.title
}
this.$store.commit("addTabs", obj)
}
}
```
因为tabs标签列表我们是存储在store中的,因此我们需要commit提交事件,因此我们在menu.js中添加addTabs方法:
* src/store/modules/menus.js
```plain
mutations: {
addTabs(state, tab) {
console.log(tab)
// 判断是否在栈内
let index = state.editableTabs.findIndex(item => item.name === tab.name)
if (index === -1) {
// 添加到tabs中
state.editableTabs.push(tab)
}
// 当前激活的tab
state.editableTabsValue = tab.name
},
setActiveTab(state, tabName) {
state.editableTabsValue = tabName
},
}
```
添加tab标签的时候注意需要激活指定当前标签,也就是设置editableTabsValue。然后我们也添加了setActiveTab方法,方便其他地方指定激活某个标签。
具体效果如下:

上面的演示看似没什么问题了,但其实细节还是很多的,比如当我们刷新浏览器、或者直接通过输入链接打开页面时候就不会自动帮我们根据链接回显激活Tab。

从上面图中我们可以看出刷新浏览器之后链接/sys/users不变,内容不变,但是Tab却不见了,所以我们需要修补一下,当用户是直接通过输入链接形式打开页面的时候我们也能根据链接自动添加激活指定的tab。那么在哪里添加这个回显的方法呢?router中?其实可以,只不过我们需要做判断,因为每次点击导航都会触发router。有没有更简便的方法?有的!因为刷新或者打开页面都是一次性的行为,所以我们可以在更高层的App.vue中做这个回显动作,具体如下:
* src\App.vue
```plain
export default {
name: "App",
watch: {
// 解决刷新浏览器没有tab的问题
$route(to, from) {
if (to.path != '/login') {
let obj = {
name: to.name,
title: to.meta.title
}
this.$store.commit("addTabs", obj)
}
}
}
}
```
上面代码可以看到,除了login页面,其他页面都会触发addTabs方法,这样我们就可以添加tab和激活tab了。

完美搞定!
## 11. 个人中心
个人中心用来展示用户的基本信息和修改密码,相对简单:

* src/views/UserCenter.vue
```plain
你好!{{ userInfo.username }} 同学
提交
重置
export default {
name: "Login",
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.passForm.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
userInfo: {
},
passForm: {
password: '111111',
checkPass: '111111',
currentPass: '111111'
},
rules: {
password: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 12, message: '长度在 6 到 12 个字符', trigger: 'blur' }
],
checkPass: [
{ required: true, validator: validatePass, trigger: 'blur' }
],
currentPass: [
{ required: true, message: '请输入当前密码', trigger: 'blur' },
]
}
}
},
created() {
this.getUserInfo()
},
methods: {
getUserInfo() {
this.$axios.get("/sys/userInfo").then(res => {
this.userInfo = res.data.data;
})
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const _this = this
this.$axios.post('/sys/user/updataPass', this.passForm).then(res => {
_this.$alert(res.data.msg, '提示', {
confirmButtonText: '确定',
callback: action => {
this.$refs[formName].resetFields();
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
```
## 12. 菜单界面

菜单管理我们用到了Table表格组件的树形结构数据,我们只需要根据例子自己组装数据,就可以自动显示出来了,在新增数据的时候有个地方需要讲一下:

这里本应该是个树形数据的结构,但是现有的elementui不是很满足,于是我就拿了个简单的下拉框,然后子菜单就加上一个【- 】作为前缀,这样看起来就像一个树形结构了,也比较清晰。具体代码如下:
* src/views/sys/Menu.vue
```plain
{{ '- ' + child.name }}
```
其他都是基本的增删改查,填数据啥的,就比较繁琐和简单了,贴代码了哈:
```plain
新增
目录
菜单
按钮
禁用
正常
编辑
删除
{{ '- ' + child.name }}
目录
菜单
按钮
禁用
正常
1
export default {
name: "Menu",
data() {
return {
searchForm: {
name: ''
},
tableData: [],
multipleSelection: [],
dialogFormVisible: false,
editForm: {
},
editFormRules: {
parentId: [
{required: true, message: '请选择上级菜单', trigger: 'blur'}
],
name: [
{required: true, message: '请输入名称', trigger: 'blur'}
],
perms: [
{required: true, message: '请输入权限编码', trigger: 'blur'}
],
type: [
{required: true, message: '请选择状态', trigger: 'blur'}
],
orderNum: [
{required: true, message: '请填入排序号', trigger: 'blur'}
],
statu: [
{required: true, message: '请选择状态', trigger: 'blur'}
]
}
}
},
methods: {
getMenuTree() {
this.$axios.get("/sys/menu/list", {
params: {
name: this.searchForm.name
}
}).then(res => {
console.log(res)
this.tableData = res.data.data
})
},
submitEditForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$axios.post('/sys/menu/' + (this.editForm.id? "update" : "save") ,this.editForm)
.then(res => {
console.log(res.data)
this.resetForm(formName)
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getMenuTree()
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
editHandle(id) {
console.log(id)
this.$axios.get("/sys/menu/info/" + id).then(res => {
this.editForm = res.data.data
this.dialogFormVisible = true
})
},
delHandle(id) {
this.$axios.post("/sys/menu/delete/" + id).then(res => {
console.log(res)
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getMenuTree()
}
});
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.editForm = {}
this.dialogFormVisible = false
}
},
created() {
this.getMenuTree()
}
}
```
## 13. 角色界面

角色需要和菜单权限做关联,菜单是个树形结构的,

因为我们父节点是列表,所以注意不要选中父节点就自动选子节点,注意分开哈哈。
贴代码啦:
* src/views/sys/Role.vue
```plain
搜索
新增
批量删除
禁用
正常
分配权限
编辑
删除
禁用
正常
export default {
name: "Role",
data() {
return {
searchForm: {
name: ''
},
tableData: [],
multipleSelection: [],
dialogFormVisible: false,
permDialogFormVisible: false,
delBtnStu: true,
current: 1,
total: 0,
size: 10,
editForm: {
},
editFormRules: {
name: [
{required: true, message: '请输入名称', trigger: 'blur'}
],
code: [
{required: true, message: '请输入唯一编码', trigger: 'blur'}
],
statu: [
{required: true, message: '请选择状态', trigger: 'blur'}
]
},
permForm: {
},
defaultProps: {
children: 'children',
label: 'name'
},
permTreeData: [],
treeCheckedKeys: [],
checkStrictly: true
}
},
methods: {
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handleSelectionChange(val) {
this.multipleSelection = val;
this.delBtnStu = val.length == 0
},
getRoleList() {
this.$axios.get('/sys/role/list', {
params: {
name: this.searchForm.name,
current: this.current,
size: this.size
}
}).then(res => {
this.tableData = res.data.data.records
this.current = res.data.data.current
this.size = res.data.data.size
this.total = res.data.data.total
console.log(res)
})
this.$axios.get("/sys/menu/list").then(res => {
this.permTreeData = res.data.data
})
},
handleSizeChange(val) {
this.size = val
this.getRoleList()
},
handleCurrentChange(val) {
this.current = val
this.getRoleList()
},
submitEditForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$axios.post('/sys/role/' + (this.editForm.id? "update" : "save") ,this.editForm)
.then(res => {
console.log(res.data)
this.resetForm(formName)
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getRoleList()
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
editHandle(id) {
console.log(id)
this.$axios.get("/sys/role/info/" + id).then(res => {
this.editForm = res.data.data
this.dialogFormVisible = true
})
},
delHandle(id) {
console.log(id)
var ids = []
console.log(id ? 31:32)
id ? ids.push(id) : this.multipleSelection.forEach(row => {
ids.push(row.id)
})
console.log(ids)
this.$axios.post("/sys/role/delete", ids).then(res => {
console.log(res)
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getRoleList()
}
});
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.editForm = {}
this.dialogFormVisible = false
this.permDialogFormVisible = false
},
permHandle(id) {
this.permDialogFormVisible = true
this.$axios.get("/sys/role/info/" + id).then(res => {
this.$refs.permTree.setCheckedKeys(res.data.data.menuIds);
this.permForm = res.data.data
console.log("this.treeCheckedKeys")
console.log(this.treeCheckedKeys)
})
},
submitPermForm(formName) {
var menuIds = []
menuIds = this.$refs.permTree.getCheckedKeys()
// menuIds = menuIds.concat(this.$refs.permTree.getHalfCheckedKeys()) // 半选中状态的父节点
console.log(menuIds)
console.log(this.permForm.id)
this.$axios.post("/sys/role/perm/" + this.permForm.id, menuIds).then(res => {
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.resetForm(formName)
}
});
this.permDialogFormVisible = false
})
},
},
created() {
this.getRoleList()
}
}
```
## 14. 用户界面

线上演示:[https://www.markerhub.com/vueadmin/](https://www.markerhub.com/vueadmin/)
用户管理有个操作叫分配角色,和角色添加权限差不多的操作
贴代码啦:
```plain
搜索
新增
批量删除
{{item.name}}
禁用
正常
分配角色
重置密码
编辑
删除
禁用
正常
export default {
name: "User",
data() {
return {
searchForm: {
username: ''
},
editForm: {
},
editFormRules: {
username: [
{required: true, message: '请输入用户名称', trigger: 'blur'}
],
email: [
{required: true, message: '请输入邮箱', trigger: 'blur'}
],
statu: [
{required: true, message: '请选择状态', trigger: 'blur'}
]
},
current: 1,
total: 0,
size: 10,
dialogFormVisible: false,
tableData: [],
multipleSelection: [],
delBtnStu: true,
roleDialogFormVisible: false,
roleForm: {
},
defaultProps: {
children: 'children',
label: 'name'
},
roleTreeData: [],
treeCheckedKeys: [],
}
},
methods: {
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handleSelectionChange(rows) {
this.multipleSelection = rows;
this.delBtnStu = rows.length == 0
},
getUserList() {
this.$axios.get('/sys/user/list', {
params: {
name: this.searchForm.name,
current: this.current,
size: this.size
}
}).then(res => {
this.tableData = res.data.data.records
this.current = res.data.data.current
this.size = res.data.data.size
this.total = res.data.data.total
})
},
handleSizeChange(val) {
this.size = val
this.getUserList()
},
handleCurrentChange(val) {
this.current = val
this.getUserList()
},
submitEditForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$axios.post('/sys/user/' + (this.editForm.id? "update" : "save") ,this.editForm)
.then(res => {
console.log(res.data)
this.resetForm(formName)
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getUserList()
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
editHandle(id) {
console.log(id)
this.$axios.get("/sys/user/info/" + id).then(res => {
this.editForm = res.data.data
this.dialogFormVisible = true
})
},
delHandle(id) {
var ids = []
id ? ids.push(id) : this.multipleSelection.forEach(row => {
ids.push(row.id)
})
console.log(ids)
this.$axios.post("/sys/user/delete", ids).then(res => {
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.getUserList()
}
});
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.editForm = {}
this.dialogFormVisible = false
this.roleDialogFormVisible = false
},
roleHandle(id) {
this.$axios.get("/sys/user/info/" + id).then(res => {
const sysuser = res.data.data
var roleIds = []
sysuser.roles.forEach(row => {
roleIds.push(row.id)
})
console.log("roleIds")
console.log(roleIds)
this.roleForm = res.data.data
console.log("this.treeCheckedKeys")
console.log(this.treeCheckedKeys)
this.$axios.get("/sys/role/list").then(res => {
this.roleTreeData = res.data.data.records
this.$refs.roleTree.setCheckedKeys(roleIds);
})
})
this.roleDialogFormVisible = true
},
submitRoleForm(formName) {
var roleIds = []
roleIds = this.$refs.roleTree.getCheckedKeys()
console.log(roleIds)
console.log(this.roleForm.id)
this.$axios.post("/sys/user/role/" + this.roleForm.id, roleIds).then(res => {
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
this.resetForm(formName)
this.getUserList()
}
});
this.roleDialogFormVisible = false
})
},
repassHandle(id, username) {
this.$confirm('将重置用户【' + username + '】的密码, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios.post("/sys/user/repass", id).then(res => {
this.$message({
showClose: true,
message: '恭喜你,操作成功',
type: 'success',
onClose: () => {
}
});
})
})
}
},
created() {
this.getUserList()
}
}
```
## 15. 按钮权限的控制
上面的菜单、角色、用户有增删改查操作,但是不是每个用户都有权限的,没权限的用户我们应该隐藏按钮,因此我们需要通过条件来判断按钮是否应该显示,那么应该怎么定义一个方法可以让全局都能使用呢?
我们再src下面新建一个js文件用于定义一个全局使用的方法:
* src/globalFun.js
```plain
import Vue from 'vue'
Vue.mixin({
methods: {
hasAuth(perm) {
var authority = this.$store.state.menus.permList
console.log(authority)
return authority.indexOf(perm) > -1
}
}
})
```
之前我们在加载菜单的时候说过,我们同时要加载权限数据,现在就需要用到权限数据了,这里数组,因此我们通过按钮的权限是否在所有权限列表内就行了。
mixin的作用是多个组件可以共享数据和方法,在使用mixin的组件中引入后,mixin中的方法和属性也就并入到该组件中,可以直接使用,在已有的组件数据和方法进行了扩充。引入mixin分全局引用和局部引用。
然后我们需要在main.js中引入这个文件
* src\main.js
```plain
import gobal from "./globalFun"
```
这样全局就可以使用啦,比如我们在新增按钮这里做判断:
* src/views/sys/Menu.vue
```plain
新增
```
通过v-if来判断返回是否为true从而判断是否显示。
## 16. 结束语
视频讲解:https://www.bilibili.com/video/BV1af4y1s7Wh/
线上演示:https://www.markerhub.com/vueadmin/
首发公众号:MarkerHub
撰写人:吕一明
转载请保留此声明,感谢!