赞
踩
| Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【一、前端】 | 跳转 |
|---|---|
| Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【二、后端】 | 跳转 |
| Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【三、系统权限】 | 跳转 |
安装 vue可视化
npm install --g vue-cli
打开vue的的可视化管理工具页面
vue ui
访问该地址:









访问成功

使用Hbuilder打开项目


安装 element-ui
yarn add element-ui
安装成功

引入element-ui

import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI); new Vue({ router, store, render: h => h(App) }).$mount('#app')
安装axios
axios:一个基于promise的HTTP库,类ajax
yarn add axios

引入全局axios

import axios from 'axios'
Vue.prototype.$axios = axios
安装qs
qs:查询参数序列化和解析库
yarn add qs
安装mockjs
mockjs:为我们生成随机数据的工具库
yarn add mockjs
删除页面的自动创建好的页面


创建一些自定义页面



设置路由

import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/login', name: 'Login', component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
设置登录页面
找到Element UI的页面


<template> <div> <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="活动名称" prop="name"> <el-input v-model="ruleForm.name"></el-input> </el-form-item> <el-form-item label="活动区域" prop="region"> <el-select v-model="ruleForm.region" placeholder="请选择活动区域"> <el-option label="区域一" value="shanghai"></el-option> <el-option label="区域二" value="beijing"></el-option> </el-select> </el-form-item> <el-form-item label="活动时间" required> <el-col :span="11"> <el-form-item prop="date1"> <el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.date1" style="width: 100%;"> </el-date-picker> </el-form-item> </el-col> <el-col class="line" :span="2">-</el-col> <el-col :span="11"> <el-form-item prop="date2"> <el-time-picker placeholder="选择时间" v-model="ruleForm.date2" style="width: 100%;"> </el-time-picker> </el-form-item> </el-col> </el-form-item> <el-form-item label="即时配送" prop="delivery"> <el-switch v-model="ruleForm.delivery"></el-switch> </el-form-item> <el-form-item label="活动性质" prop="type"> <el-checkbox-group v-model="ruleForm.type"> <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox> <el-checkbox label="地推活动" name="type"></el-checkbox> <el-checkbox label="线下主题活动" name="type"></el-checkbox> <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox> </el-checkbox-group> </el-form-item> <el-form-item label="特殊资源" prop="resource"> <el-radio-group v-model="ruleForm.resource"> <el-radio label="线上品牌商赞助"></el-radio> <el-radio label="线下场地免费"></el-radio> </el-radio-group> </el-form-item> <el-form-item label="活动形式" prop="desc"> <el-input type="textarea" v-model="ruleForm.desc"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button> <el-button @click="resetForm('ruleForm')">重置</el-button> </el-form-item> </el-form> </div> </template> <script> export default { data() { return { ruleForm: { name: '', region: '', date1: '', date2: '', delivery: false, type: [], resource: '', desc: '' }, rules: { name: [{ required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' } ], region: [{ required: true, message: '请选择活动区域', trigger: 'change' }], date1: [{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }], date2: [{ type: 'date', required: true, message: '请选择时间', trigger: 'change' }], type: [{ type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }], resource: [{ required: true, message: '请选择活动资源', trigger: 'change' }], desc: [{ required: true, message: '请填写活动形式', trigger: 'blur' }] } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } </script> <style> </style>
运行测试
http://localhost:8080/login

修改页面设置为登录页面

<template> <div> <el-row class="elrow" type="flex" justify="center"> <el-col :span="8"> <el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="100px" class="demo-loginForm"> <el-form-item label="账号" prop="username" style="width: 380px;"> <el-input v-model="loginForm.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password" style="width: 380px;"> <el-input placeholder="请输入密码" v-model="loginForm.password" show-password></el-input> </el-form-item> <el-form-item label="验证码" prop="code" style="width: 380px;"> <el-input v-model="loginForm.code" style="width: 172px;float: left;"></el-input> <el-image style="width: 80px; height: 40px;float: left;padding-left: 25px;" :src="url" :fit="fit"></el-image> </el-form-item> <el-form-item style="width: 380px;"> <el-button type="primary" @click="submitForm('loginForm')">登录</el-button> <el-button @click="resetForm('loginForm')">重置</el-button> </el-form-item> </el-form> </el-col> </el-row> </div> </template> <script> export default { data() { return { loginForm: { username: '', password: '', code:'', }, rules: { username: [ { required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请设置密码', trigger: 'blur' }], code: [{ required: true, message: '请输入验证码', trigger: 'change' }] } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } </script> <style scoped> .elrow{ display: flex; align-items: center; text-align: center; height:100%; } </style>

<template> <div> <el-row class="elrow" type="flex" justify="center"> <el-col :span="8"> <el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="100px" class="demo-loginForm"> <el-form-item label="账号" prop="username" style="width: 380px;"> <el-input v-model="loginForm.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password" style="width: 380px;"> <el-input placeholder="请输入密码" v-model="loginForm.password" show-password></el-input> </el-form-item> <el-form-item label="验证码" prop="code" style="width: 380px;"> <el-input v-model="loginForm.code" style="width: 172px;float: left;"></el-input> <el-image style="width: 80px; height: 40px;float: left;padding-left: 25px;" :src="captchaImg" :fit="fit"></el-image> </el-form-item> <el-form-item style="width: 380px;"> <el-button type="primary" @click="submitForm('loginForm')">登录</el-button> <el-button @click="resetForm('loginForm')">重置</el-button> </el-form-item> </el-form> </el-col> </el-row> </div> </template> <script> export default { data() { return { loginForm: { username: '', password: '', code:'', token:'' }, rules: { username: [ { required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请设置密码', trigger: 'blur' }], code: [{ required: true, message: '请输入验证码', trigger: 'change' }] }, captchaImg:null }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$axios.post("url",this.loginForm).then(res =>{ }) } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); }, getCaptcha(){ this.$axios.post("/captcha").then(res =>{ this.loginForm.token = res.data.data.token; this.captchaImg = res.data.data.captchaImg; }) } //Result - code、msg、data } } </script> <style scoped> .elrow{ display: flex; align-items: center; text-align: center; height:100%; } </style>

在main.js当中引入mock.js

require("./mock.js")
完善mock.js

const Mock = require('mockjs') const Random = Mock.Random let Result = { code: 200, msg: '操作成功', data: null } Mock.mock('/captcha','get',()=>{ Result.data = { token: Random.string(32), captchaImg:Random.dataImage('120x40','p7n5w') } return Result; })
修改Login.vue,设置发起请求

getCaptcha(){
this.$axios.get("/captcha").then(res =>{
console.log("/captcha")
console.log(res)
this.loginForm.token = res.data.data.token;
this.captchaImg = res.data.data.captchaImg;
})
}
查看页面
http://localhost:8080/login

SET_TOKEN
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { token:'' }, getters: { }, mutations: { SET_TOKEN:(state, token ) =>{ state.token = token localStorage.setItem("token",token) } }, actions: { }, modules: { } })

submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$axios.post("/login",this.loginForm).then(res =>{
const jwt = res.headers['authorization']
this.$store.commit('SET_TOKEN',jwt);
this.$router.push("/home")
})
} else {
console.log('error submit!!');
return false;
}
});
},
设置mock.js

Mock.mock('/login','post',()=>{
//无法在header当中存入jwt
return Result;
})
在登录页测试





import axios from "axios" import router from "./router" import Element from "element-ui" //axios.defaults.baseURL = "http://localhost:8081" const request = axios.create({ timeout: 5000, headers: { 'Content-Type': "application/json;charset=utf-8" } }) request.interceptors.request.use(config => { config.headers['Authorization'] = localStorage.getItem("token") return config; }); request.interceptors.response.use(response => { let res = response.data if (res.code === 200) { return response } else { Element.Message.error(!res.msg ? '系统异常' : res.msg) return Promise.reject(response.data.msg) } }, error => { if (error.response.data) { error.message = error.response.data.msg } if (error.response.status === 401) { router.push("/login") } Element.Message.error(error.message, { duration: 3000 }) return Promise.reject(error) } ) export default request
在main.js当中引入axios.js

在mock.js当中这请求返回数据

Mock.mock('/login','post',()=>{
//无法在header当中存入jwt
Result.code = 404
Result.msg = "验证码错误"
return Result;
})
的登录页面发起请求测试


一般来说,管理系统的页面我们都是头部是一个简单的信息展示系统名称和登录用户信息,然后中间的左边是菜单导航栏,右边是内容,对应到elementui的组件中,我们可以找到这个
Container布局容器用于布局,方便快速搭建页面的基本结构。
而我们采用这个布局:

直接使用Element UI提供的布局
创建index.vue


<template> <div> <el-container> <el-aside width="200px">Aside</el-aside> <el-container> <el-header>Header</el-header> <el-main>Main</el-main> </el-container> </el-container> </div> </template> <script> </script> <style> </style>

import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Index from '../views/index.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: 'index', name: 'Index', component: Index }, { path: '/login', name: 'Login', component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
设置跳转路径

设置一下状态码使其跳转成功



引入一些ELement UI提供的样式

<style> .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } .el-aside { background-color: #D3DCE6; color: #333; text-align: center; line-height: 200px; } .el-main { background-color: #E9EEF3; color: #333; text-align: center; line-height: 160px; } body > .el-container { margin-bottom: 40px; } .el-container:nth-child(5) .el-aside, .el-container:nth-child(6) .el-aside { line-height: 260px; } .el-container:nth-child(7) .el-aside { line-height: 320px; } .el-container{ padding: 0; margin: 0; height: 100%; } </style>

设置一些元件

<template> <el-container> <el-aside width="200px">Aside</el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar> <el-dropdown> <span class="el-dropdown-link"> Admin<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main>Main</el-main> </el-container> </el-container> </template> <script> </script> <style> .header-avatar{ float: right; width: 210px; display: flex; justify-content: space-around; align-items: center; } body { margin: 0; padding: 0; height: 100%; } .template{ padding: 0; margin: 0; height: 100%; } .el-dropdown-link{ cursor: pointer; color: #409EFF; } .el-container{ padding: 0; margin: 0; height: 100%; } .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } .el-aside { background-color: #D3DCE6; color: #333; text-align: center; line-height: 200px; } .el-main { background-color: #E9EEF3; color: #333; text-align: center; line-height: 160px; } .el-container:nth-child(5) .el-aside, .el-container:nth-child(6) .el-aside { line-height: 260px; } .el-container:nth-child(7) .el-aside { line-height: 320px; } </style>


<template> <el-container> <el-aside width="200px"> <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <el-menu-item index="0"> <template slot="title"> <i class="el-icon-s-home"></i> <span slot="title">主页</span> </template> </el-menu-item> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>系统管理</span> </template> <el-menu-item-group> <el-menu-item index="1-1">用户管理</el-menu-item> <el-menu-item index="1-2">角色管理</el-menu-item> <el-menu-item index="1-2">菜单管理</el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span>系统工具</span> </template> <el-menu-item-group> <el-menu-item index="1-1">数字字典</el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"> </el-avatar> <el-dropdown> <span class="el-dropdown-link"> Admin<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main>Main</el-main> </el-container> </el-container> </template> <script> </script> <style> .header-avatar { float: right; width: 210px; display: flex; justify-content: space-around; align-items: center; } body { margin: 0; padding: 0; height: 100%; } .el-aside{ background-color: #545c64; } .template { padding: 0; margin: 0; height: 100%; } .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-container { padding: 0; margin: 0; height: 100%; } .el-header { background-color: #0bd18f; color: #333; text-align: center; line-height: 60px; } .el-main { color: #333; text-align: center; line-height: 160px; } </style>






<template> <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <el-menu-item index="0"> <template slot="title"> <i class="el-icon-s-home"></i> <span slot="title">主页</span> </template> </el-menu-item> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>系统管理</span> </template> <el-menu-item-group> <el-menu-item index="1-1">用户管理</el-menu-item> <el-menu-item index="1-2">角色管理</el-menu-item> <el-menu-item index="1-2">菜单管理</el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span>系统工具</span> </template> <el-menu-item-group> <el-menu-item index="1-1">数字字典</el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </template> <script> </script> <style> .el-menu-vertical-demo{ height: 100%; } </style>
在index.vue当中引用SideMenu


<template> <el-container> <el-aside width="200px"> <SideMenu></SideMenu> </el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"> </el-avatar> <el-dropdown> <span class="el-dropdown-link"> Admin<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main>Main</el-main> </el-container> </el-container> </template> <script> import SideMenu from "./inc/SideMenu.vue" export default{ name: "Index", components:{ SideMenu } } </script> <style> .header-avatar { float: right; width: 210px; display: flex; justify-content: space-around; align-items: center; } body { margin: 0; padding: 0; height: 100%; } .el-aside{ background-color: #545c64; } .template { padding: 0; margin: 0; height: 100%; } .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-container { padding: 0; margin: 0; height: 100%; } .el-header { background-color: #0bd18f; color: #333; text-align: center; line-height: 60px; } </style>
最后效果

将index.vue的内容全部抽取到Home

<template> <el-container> <el-aside width="200px"> <SideMenu></SideMenu> </el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"> </el-avatar> <el-dropdown> <span class="el-dropdown-link"> Admin<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main>Main</el-main> </el-container> </el-container> </template> <script> import SideMenu from "./inc/SideMenu.vue" export default{ name: "Home", components:{ SideMenu } } </script> <style> </style>
index.vue当中

访问页面:http://localhost:8080/index
访问也发现空白


children:[
{
path:'/index',
name:'Index',
component:Index
}
]
再次访问:

但是我们发现上面没有index的内容
设置内容

<router-view>
</router-view>





<template>
<div>用户管理</div>
</template>
<script>
</script>
<style>
</style>

<template>
<div>角色管理</div>
</template>
<script>
</script>
<style>
</style>

<template>
<div>菜单管理</div>
</template>
<script>
</script>
<style>
</style>
设置子路由


import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Index from '../views/index.vue' import Menu from '../views/sys/Menu.vue' import Role from '../views/sys/Role.vue' import User from '../views/sys/User.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home, children:[ { path:'/index', name:'Index', component:Index }, { path:'/users', name:'SysUser', component:User }, { path:'/roles', name:'SysRole', component:Role }, { path:'/menus', name:'SysMenu', component:Menu } ] }, { path: '/index', name: 'Index', component: Index }, { path: '/login', name: 'Login', component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
访问:http://localhost:8080/roles

设置页面路由

<template> <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <router-link to="/index"> <el-menu-item index="0"> <template slot="title"> <i class="el-icon-s-home"></i> <span slot="title">主页</span> </template> </el-menu-item> </router-link> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>系统管理</span> </template> <el-menu-item-group> <router-link to="/users"> <el-menu-item index="1-1"> <template> <i class="el-icon-s-custom" ></i> <span>用户管理</span> </template> </el-menu-item> </router-link> <router-link to="/roles"> <el-menu-item index="1-2"> <template> <i class="el-icon-rank" ></i> <span>角色管理</span> </template> </el-menu-item> </router-link> <router-link to="/menus"> <el-menu-item index="1-3"> <template> <i class="el-icon-menu" ></i> <span>菜单管理</span> </template> </el-menu-item> </router-link> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span>系统工具</span> </template> <el-menu-item-group> <el-menu-item index="2-1"> <template> <i class="el-icon-menu" ></i> <span>数字字典</span> </template> </el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </template> <script> export default{ name: "SideMenu", } </script> <style> .header-avatar { float: right; width: 210px; display: flex; justify-content: space-around; align-items: center; } body { margin: 0; padding: 0; height: 100%; } .el-aside { background-color: #545c64; } .template { padding: 0; margin: 0; height: 100%; } .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-container { padding: 0; margin: 0; height: 100%; } .el-header { background-color: #0bd18f; color: #333; text-align: center; line-height: 60px; } </style>
点击测试

管理界面的右上角的用户信息现在是写死的,
因为我们现在已经登录成功,所以我们可以通过接口去请求获取到当前的用户信息了,
这样我们就可以动态显示用户的信息,这个接口比较简单,然后退出登录的链接也一起完成,
就请求接口同时把浏览器中的缓存删除就退出了哈。


<template> <el-container> <el-aside width="200px"> <SideMenu></SideMenu> </el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" :src="userInfo.avatar"> </el-avatar> <el-dropdown> <span class="el-dropdown-link"> {{userInfo.username}}<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main> <router-view> </router-view> </el-main> </el-container> </el-container> </template> <script> import SideMenu from "./inc/SideMenu.vue" export default{ name: "Home", components:{ SideMenu }, data(){ return { userInfo:{ id:"", username:"", avatar:"" } } }, created() { this.getUserInfo(); }, methods:{ getUserInfo(){ this.$axios.get("/sys/userInfo").then(res =>{ this.userInfo= res.data.data; }) } } } </script> <style> </style>

Mock.mock('/sys/userInfo','get',()=>{
//无法在header当中存入jwt
Result.data = {
id:"1",
username:"test",
avatar:"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
}
return Result;
})


<router-link to="/userCenter"> 个人中心 </router-link>

{
path:'/userCenter',
name:'UserCenter',
component: () => import('../views/UserCenter.vue')
},
创建UserCenter.vue


<template> <div> <el-row class="elrow" type="flex" justify="center"> <el-col :span="8"> <el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="100px" class="demo-loginForm"> <div>修改用户信息</div> <el-form-item label="账号" prop="username" style="width: 380px;"> <el-input v-model="loginForm.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password" style="width: 380px;"> <el-input placeholder="请输入密码" v-model="loginForm.password" show-password></el-input> </el-form-item> <el-form-item label="验证码" prop="code" style="width: 380px;"> <el-input v-model="loginForm.code" style="width: 172px;float: left;"></el-input> <el-image style="width: 80px; height: 40px;float: left;padding-left: 25px;" :src="captchaImg" ></el-image> </el-form-item> <el-form-item style="width: 380px;"> <el-button type="primary" @click="submitForm('loginForm')">修改</el-button> <el-button @click="resetForm('loginForm')">重置</el-button> </el-form-item> </el-form> </el-col> </el-row> </div> </template> <script> export default { data() { return { loginForm: { username: '', password: '', code:'', token:'' }, rules: { username: [ { required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请设置密码', trigger: 'blur' }], code: [{ required: true, message: '请输入验证码', trigger: 'change' }] }, captchaImg:null }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$axios.post("/updateUser",this.loginForm).then(res =>{ const jwt = res.headers['authorization'] this.$store.commit('SET_TOKEN',jwt); this.$router.push("/index") }) } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); }, getCaptcha(){ this.$axios.get("/captcha").then(res =>{ console.log("/captcha") console.log(res) this.loginForm.token = res.data.data.token; this.captchaImg = res.data.data.captchaImg; }) } //Result - code、msg、data }, created() { this.getCaptcha(); } } </script> <style scoped> .elrow{ display: flex; align-items: center; text-align: center; height:100%; } </style>



<el-dropdown-item @click.native="logout" >退出</el-dropdown-item>

logout(){
this.$axios.post("/logout").then(res =>{
localStorage.clear();
sessionStorage.clear();
this.$store.commit("resetState");
this.$router.push("/login")
})
}

resetState:(state) =>{
state.token = ''
}
设置模拟登出

Mock.mock('/logout','post',()=>{
return Result;
})



{
path:'/sys/users',
name:'SysUser',
component:User
},
{
path:'/sys/roles',
name:'SysRole',
component:Role
},
{
path:'/sys/menus',
name:'SysMenu',
component:Menu
}

<template> <el-menu default-active="2" class="el-menu-vertical-demo" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <router-link to="/index"> <el-menu-item index="0"> <template slot="title"> <i class="el-icon-s-home"></i> <span slot="title">主页</span> </template> </el-menu-item> </router-link> <el-submenu :index="menu.name" v-for="menu in menuList"> <template slot="title"> <i :class="menu.icon"></i> <span>{{menu.title}}</span> </template> <el-menu-item-group> <router-link :to="item.path" v-for="item in menu.children"> <el-menu-item :index="item.name"> <template> <i :class="item.icon"></i> <span slot="title">{{item.title}}</span> </template> </el-menu-item> </router-link> </el-menu-item-group> </el-submenu> </el-menu> </template> <script> export default { name: "SideMenu", data() { return { menuList: [{ title: '系统管理', name: 'SysMange', icon: 'el-icon-location', path: '', children: [{ title: '用户管理', name: 'SysUser', icon: 'el-icon-s-custom', path: '/sys/users', children: [] }] }, { title: '系统工具', name: 'SysTools', icon: 'el-icon-tools', path: '', children: [{ title: '数字字典', name: 'SysDict', icon: 'el-icon-s-order', path: '/sys/dicts', children: [] }] } ] } }, methods: { } } </script> <style> .header-avatar { float: right; width: 210px; display: flex; justify-content: space-around; align-items: center; } body { margin: 0; padding: 0; height: 100%; } .el-aside { background-color: #545c64; } .template { padding: 0; margin: 0; height: 100%; } .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-container { padding: 0; margin: 0; height: 100%; } .el-header { background-color: #0bd18f; color: #333; text-align: center; line-height: 60px; } </style>

将SideMenu的数据清空

设置mockjs

Mock.mock('/sys/menu/nav', 'get', () => { let nav = [{ title: '系统管理', name: 'SysMange', icon: 'el-icon-location', component:'', path: '', children: [{ title: '用户管理', name: 'SysUser', icon: 'el-icon-s-custom', path: '/sys/users', component:'sys/User', children: [] }] }, { title: '系统工具', name: 'SysTools', icon: 'el-icon-tools', path: '', component:'', children: [{ title: '数字字典', name: 'SysDict', icon: 'el-icon-s-order', path: '/sys/dicts', component:'', children: [] }] } ]; let authoritys = []; Result.data = { nav: nav, authoritys: authoritys } return Result; })
创建新模块



import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default { state: { }, getters: { }, mutations: { }, actions: { }, modules: { } }
在store当中的index当中引用上面创建好的menus.js

完善menus.js

menuList:[],
authoritys:[]
设置路由规则

router.beforeEach((to,from,next)=>{ axios.get("/sys/menu/nav",{ headers:{ Authorization: localStorage.getItem("token") } }).then(res =>{ //拿到menuList store.state.commit("setMenuList",this.data.data.nva); //拿到用户权限 store.state.commit("setPermList",this.data.data.authoritys); }) next() })
设置菜单方法

import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default { state: { menuList:[], permList:[] }, getters: { }, mutations: { setMenuList(state,menus){ state.menuList = menus; }, setPermList(state,perms){ state.permList = perms; } }, actions: { }, modules: { } }

get(){
return this.$store.state.menus.menuList;
}

router.beforeEach((to,from,next)=>{ axios.get("/sys/menu/nav",{ headers:{ Authorization: localStorage.getItem("token") } }).then(res =>{ //拿到menuList store.commit("setMenuList",res.data.data.nav); //拿到用户权限 store.commit("setPermList",res.data.data.authoritys); console.log(store.state.menus.menuList) //设置动态绑定路由 let newRoutes = router.options.routes; res.data.data.nav.forEach(menu => { if(menu.children){ menu.children.forEach(e =>{ //生成路由 let route = menuToRoute(e) //把路由添加到路由器管理中 if(route){ newRoutes[0].children.push(route) } }) } }) console.log("newRoutes") console.log(newRoutes) router.addRoutes(newRoutes) }) next() }) //导航转成路由 const menuToRoute = (menu) => { if(!menu.component){ return null; } let route = { name: menu.name, path: menu.path, meta: { icon: menu.icon, title: menu.title } } route.component = () => import('@/views/'+ menu.component +'.vue') return route; } export default router
访问测试

menus.js

import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default { state: { menuList:[], permList:[], hasRoutes: false }, getters: { }, mutations: { setMenuList(state,menus){ state.menuList = menus; }, setPermList(state,perms){ state.permList = perms; }, changeRouteStatus(state,hasRoutes){ state.hasRoutes = hasRoutes; } }, actions: { }, modules: { } }

router.beforeEach((to,from,next)=>{ let hasRoute = store.state.menus.hasRoutes; if(!hasRoute){ axios.get("/sys/menu/nav",{ headers:{ Authorization: localStorage.getItem("token") } }).then(res =>{ //拿到menuList store.commit("setMenuList",res.data.data.nav); //拿到用户权限 store.commit("setPermList",res.data.data.authoritys); console.log(store.state.menus.menuList) //设置动态绑定路由 let newRoutes = router.options.routes; res.data.data.nav.forEach(menu => { if(menu.children){ menu.children.forEach(e =>{ //生成路由 let route = menuToRoute(e) //把路由添加到路由器管理中 if(route){ newRoutes[0].children.push(route) } }) } }) router.addRoutes(newRoutes) hasRoute = true store.commit("changeRouteStatus",hasRoute); }) } next() })


<template> <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab"> <el-tab-pane v-for="(item, index) in editableTabs" :key="item.name" :label="item.title" :name="item.name"> {{item.content}} </el-tab-pane> </el-tabs> </template> <script> export default { data() { return { editableTabsValue: '2', editableTabs: [{ title: 'Tab 1', name: '1', content: 'Tab 1 content' }, { title: 'Tab 2', name: '2', content: 'Tab 2 content' }], tabIndex: 2 } }, methods: { handleTabsEdit(targetName, action) { if (action === 'add') { let newTabName = ++this.tabIndex + ''; this.editableTabs.push({ title: 'New Tab', name: newTabName, content: 'New Tab content' }); this.editableTabsValue = newTabName; } if (action === 'remove') { let tabs = this.editableTabs; let activeName = this.editableTabsValue; if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName) { let nextTab = tabs[index + 1] || tabs[index - 1]; if (nextTab) { activeName = nextTab.name; } } }); } this.editableTabsValue = activeName; this.editableTabs = tabs.filter(tab => tab.name !== targetName); } } } } </script> <style> .el-main{ padding: 0px; text-align: center; } </style>
查看效果
在Home当中引入该内容

<template> <el-container> <el-aside width="200px"> <SideMenu></SideMenu> </el-aside> <el-container> <el-header> <strong>xxx后台管理系统</strong> <div class="header-avatar"> <el-avatar size="medium" :src="userInfo.avatar"> </el-avatar> <el-dropdown> <span class="el-dropdown-link"> {{userInfo.username}}<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item> <router-link to="/userCenter"> 个人中心 </router-link> </el-dropdown-item> <el-dropdown-item @click.native="logout" >退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <el-link href="" target="_blank">网站</el-link> <el-link href="" target="_blank">B站</el-link> </div> </el-header> <el-main> <Tabs> </Tabs> <router-view/> </el-main> </el-container> </el-container> </template> <script> import SideMenu from "./inc/SideMenu.vue" import Tabs from "./inc/Tabs.vue" export default{ name: "Home", components:{ SideMenu,Tabs }, data(){ return { userInfo:{ id:"", username:"", avatar:"" } } }, created() { this.getUserInfo(); }, methods:{ getUserInfo(){ this.$axios.get("/sys/userInfo").then(res =>{ this.userInfo= res.data.data; }) }, logout(){ this.$axios.post("/logout").then(res =>{ localStorage.clear(); sessionStorage.clear(); this.$store.commit("resetState"); this.$router.push("/login") }) } } } </script> <style> a{ text-decoration: none; } </style>


,
addTab(state,tab) {
let newTabName = '';
state.editableTabs.push({
title: tab.title,
name: tab.name,
});
state.editableTabsValue = tab.name;
},

<el-menu-item :index="item.name" @click="selectMenu(item)">
<template>
<i :class="item.icon"></i>
<span slot="title">{{item.title}}</span>
</template>
</el-menu-item>

selectMenu(item){
this.$store.commit("addTab",item)
}

data() { return { //editableTabsValue: this.$store.state.menus.editableTabsValue, //editableTabs: this.$store.state.menus.editableTabs, } }, computed:{ editableTabs: { get(){ return this.$store.state.menus.editableTabs; }, set(val){ this.$store.state.menus.editableTabs = val; } }, editableTabsValue:{ get(){ return this.$store.state.menus.editableTabsValue; }, set(val){ this.$store.state.menus.editableTabsValue = val; } } },



addTab(state,tab) {
let index = state.editableTabs.findIndex(e => e.name === tab.name )
if(index === -1){
state.editableTabs.push({
title: tab.title,
name: tab.name,
});
}
state.editableTabsValue = tab.name;
},

SideMenu.vue

<el-menu-item index="0" @click="selectMenu({name:'Index',title:'首页'})">
<template slot="title">
<i class="el-icon-s-home"></i>
<span slot="title">主页</span>
</template>
</el-menu-item>


<el-menu :default-active="this.$store.state.menus.editableTabsValue" class="el-menu-vertical-demo" background-color="#545c64" text-color="#fff"
active-text-color="#ffd04b">
<router-link to="/index">
<el-menu-item index="Index" @click="selectMenu({name:'Index',title:'首页'})">
<template slot="title">
<i class="el-icon-s-home"></i>
<span slot="title">主页</span>
</template>
</el-menu-item>
</router-link>
实现高亮的同步

设置点击tap内容切换

@tab-click="clickTab"

clickTab(target){
this.$router.push({name: target.name})
}

设置删除对菜单的时候设置对应内容切换,设置首页不能删除

removeTab(targetName) { let tabs = this.editableTabs; let activeName = this.editableTabsValue; if(targetName === 'Index'){ return } if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName) { let nextTab = tabs[index + 1] || tabs[index - 1]; if (nextTab) { activeName = nextTab.name; } } }); } this.editableTabsValue = activeName; this.editableTabs = tabs.filter(tab => tab.name !== targetName); this.$router.push({name: activeName}) },
设置通过ip路径访问的时候,设置对应tabs

<template> <div id="app"> <router-view/> </div> </template> <script> export default{ name:"App", watch:{ $route(to,from){ if(to.path != '/login'){ let obj = { name: to.name, title: to.meta.title } this.$store.commit("addTab",obj) } } } } </script> <style> body html { margin: 0; padding: 0; height: 100%; } #app { margin: 0; padding: 0; height: 800px; } .router-view{ height: 100%; } nav a { font-weight: bold; color: #2c3e50; } nav a.router-link-exact-active { color: #42b983; } </style>
http://localhost:8080/sys/roles

设置退出登录后清除tab


resetState: (state) => {
state.menuList = [],
state.permList = [],
state.hasRoutes = false,
state.editableTabsValue = 'Index',
state.editableTabs = [{
title: '首页',
name: 'Index',
}]
}




<el-main>
<Tabs></Tabs>
<div style="margin: 0 15px;">
<router-view />
</div>
</el-main>

<template> <div> <el-form :inline="true"> <el-form-item> <el-button type="primary" @click="dialogVisible = true">新增</el-button> </el-form-item> </el-form> <el-table :data="tableData" style="width: 100%;margin-bottom: 20px;" row-key="id" stripe border default-expand-all :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> <el-table-column prop="name" label="名称" sortable width="180"> </el-table-column> <el-table-column prop="perm" label="权限编码" sortable width="180"> </el-table-column> <el-table-column prop="icon" label="图标"> </el-table-column> <el-table-column prop="type" label="类型"> <template slot-scope="scope"> <el-tag size="small" v-if="scope.row.type === 0"> 目录 </el-tag> <el-tag size="small" v-else-if="scope.row.type === 1" type="success"> 菜单 </el-tag> <el-tag size="small" v-else-if="scope.row.type === 2" type="info"> 按钮 </el-tag> </template> </el-table-column> <el-table-column prop="path" label="菜单URL"> </el-table-column> <el-table-column prop="component" label="菜单组件"> </el-table-column> <el-table-column prop="orderNum" label="排序号"> </el-table-column> <el-table-column prop="statu" label="状态"> <template slot-scope="scope"> <el-button size="small" type="success" v-if="scope.row.statu === 1">正常</el-button> <el-button size="small" type="danger" v-else-if="scope.row.statu === 0">禁用</el-button> </template> </el-table-column> <el-table-column prop="type" label="操作"> <template slot-scope="scope"> <el-button type="text">编辑</el-button> <el-divider direction="vertical"></el-divider> <template> <el-popconfirm title="这是一段内容确定删除吗?" > <el-button type="text" slot="reference">删除</el-button> </el-popconfirm> </template> </template> </el-table-column> </el-table> <!-- 新增对话框--> <el-dialog title="提示" :visible.sync="dialogVisible" width="50%" :before-close="handleClose"> <el-form ref="editForm" :rules="editFormRules" :model="editForm" label-width="100px"> <el-form-item label="上级菜单" prop="parentId"> <el-select v-model="editForm.parentId" placeholder="请选择上级菜单"> <template v-for="item in tableData"> <el-option :label="item.name" :value="item.id"> </el-option> <template v-for="child in item.children"> <el-option :label="child.name" :value="child.id"> <span>{{ "-- "+child.name}}</span> </el-option> </template> </template> </el-select> </el-form-item> <el-form-item label="菜单名称" prop="name"> <el-input v-model="editForm.name"></el-input> </el-form-item> <el-form-item label="权限编码" prop="perms"> <el-input v-model="editForm.perms"></el-input> </el-form-item> <el-form-item label="图标" > <el-input v-model="editForm.name"></el-input> </el-form-item> <el-form-item label="菜单URL" > <el-input v-model="editForm.name"></el-input> </el-form-item> <el-form-item label="菜单组件" > <el-input v-model="editForm.name"></el-input> </el-form-item> <el-form-item label="类型" prop="type" lable-width="100px" > <el-radio-group v-model="editForm.type"> <el-radio :lable="0">目录</el-radio> <el-radio :lable="1">菜单</el-radio> <el-radio :lable="2">按钮</el-radio> </el-radio-group> </el-form-item> <el-form-item label="状态" prop="statu" lable-width="100px" > <el-radio-group v-model="editForm.statu"> <el-radio :lable="0">禁用</el-radio> <el-radio :lable="1">正常</el-radio> </el-radio-group> </el-form-item> <el-form-item label="排序号" prop="orderNum" label-width="100px"> <el-input-number v-model="editForm.orderNum" :min="1" label="排序号">1</el-input-number> </el-form-item> <el-form-item > <el-button type="primary" @click="onSubmit">立即创建</el-button> <el-button>取消</el-button> </el-form-item> </el-form> </el-dialog> </div> </template> <script> export default { data() { return { dialogVisible: false, tableData: [], 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: { load(tree, treeNode, resolve) { setTimeout(() => { resolve([{ id: 31, date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }, { id: 32, date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }]) }, 1000) }, handleClose(done) { this.$confirm('确认关闭?') .then(_ => { done(); }) .catch(_ => {}); }, onSubmit() { console.log('submit!'); }, getMenuTree(){ this.$axios.get("/sys/menu/list").then(res => { this.tableData = res.data.data }) } }, created(){ this.getMenuTree(); }, } </script> <style> </style>

Mock.mock('/sys/menu/list', 'get', () => { let menus = [{ id: 1, date: '2016-05-02', name: '王小虎1', address: '上海市普陀区金沙江路 1518 弄', statu: 1, type: 0, }, { id: 2, date: '2016-05-04', name: '王小虎2', address: '上海市普陀区金沙江路 1517 弄', statu: 0., type: 1, }, { id: 3, date: '2016-05-01', name: '王小虎3', address: '上海市普陀区金沙江路 1519 弄', statu: 1, type: 0, children: [{ id: 31, date: '2016-05-01', name: '王小虎4', address: '上海市普陀区金沙江路 1519 弄', statu: 1, type: 2, }, { id: 32, date: '2016-05-01', name: '王小虎5', address: '上海市普陀区金沙江路 1519 弄', statu: 0, type: 0, }] }, { id: 4, date: '2016-05-03', name: '王小虎6', address: '上海市普陀区金沙江路 1516 弄', statu: 1, type: 2, }] Result.data = menus return Result; })


<template> <div> <el-form :inline="true"> <el-form-item> <el-button type="primary" @click="dialogVisible = true">新增</el-button> </el-form-item> </el-form> <el-table :data="tableData" style="width: 100%;margin-bottom: 20px;" row-key="id" border stripe default-expand-all :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> <el-table-column prop="name" label="名称" sortable width="180"> </el-table-column> <el-table-column prop="perms" label="权限编码" sortable width="180"> </el-table-column> <el-table-column prop="icon" label="图标"> </el-table-column> <el-table-column prop="type" label="类型"> <template slot-scope="scope"> <el-tag size="small" v-if="scope.row.type === 0">目录</el-tag> <el-tag size="small" v-else-if="scope.row.type === 1" type="success">菜单</el-tag> <el-tag size="small" v-else-if="scope.row.type === 2" type="info">按钮</el-tag> </template> </el-table-column> <el-table-column prop="path" label="菜单URL"> </el-table-column> <el-table-column prop="component" label="菜单组件"> </el-table-column> <el-table-column prop="orderNum" label="排序号"> </el-table-column> <el-table-column prop="statu" label="状态"> <template slot-scope="scope"> <el-tag size="small" v-if="scope.row.statu === 1" type="success">正常</el-tag> <el-tag size="small" v-else-if="scope.row.statu === 0" type="danger">禁用</el-tag> </template> </el-table-column> <el-table-column prop="icon" label="操作"> <template slot-scope="scope"> <el-button type="text" @click="editHandle(scope.row.id)">编辑</el-button> <el-divider direction="vertical"></el-divider> <template> <el-popconfirm title="这是一段内容确定删除吗?" @confirm="delHandle(scope.row.id)"> <el-button type="text" slot="reference">删除</el-button> </el-popconfirm> </template> </template> </el-table-column> </el-table> <!--新增对话框--> <el-dialog title="提示" :visible.sync="dialogVisible" width="600px" :before-close="handleClose"> <el-form :model="editForm" :rules="editFormRules" ref="editForm" label-width="100px" class="demo-editForm"> <el-form-item label="上级菜单" prop="parentId"> <el-select v-model="editForm.parentId" placeholder="请选择上级菜单"> <template v-for="item in tableData"> <el-option :label="item.name" :value="item.id"></el-option> <template v-for="child in item.children"> <el-option :label="child.name" :value="child.id"> <span>{{ "- " + child.name }}</span> </el-option> </template> </template> </el-select> </el-form-item> <el-form-item label="菜单名称" prop="name" label-width="100px"> <el-input v-model="editForm.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="权限编码" prop="perms" label-width="100px"> <el-input v-model="editForm.perms" autocomplete="off"></el-input> </el-form-item> <el-form-item label="图标" prop="icon" label-width="100px"> <el-input v-model="editForm.icon" autocomplete="off"></el-input> </el-form-item> <el-form-item label="菜单URL" prop="path" label-width="100px"> <el-input v-model="editForm.path" autocomplete="off"></el-input> </el-form-item> <el-form-item label="菜单组件" prop="component" label-width="100px"> <el-input v-model="editForm.component" autocomplete="off"></el-input> </el-form-item> <el-form-item label="类型" prop="type" label-width="100px"> <el-radio-group v-model="editForm.type"> <el-radio :label=0>目录</el-radio> <el-radio :label=1>菜单</el-radio> <el-radio :label=2>按钮</el-radio> </el-radio-group> </el-form-item> <el-form-item label="状态" prop="statu" label-width="100px"> <el-radio-group v-model="editForm.statu"> <el-radio :label=0>禁用</el-radio> <el-radio :label=1>正常</el-radio> </el-radio-group> </el-form-item> <el-form-item label="排序号" prop="orderNum" label-width="100px"> <el-input-number v-model="editForm.orderNum" :min="1" label="排序号">1</el-input-number> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('editForm')">立即创建</el-button> <el-button @click="resetForm('editForm')">重置</el-button> </el-form-item> </el-form> </el-dialog> </div> </template> <script> export default { data() { return { dialogVisible: false, tableData: [], 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: { load(tree, treeNode, resolve) { setTimeout(() => { resolve([{ id: 31, date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }, { id: 32, date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }]) }, 1000) }, submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$axios.post("/sys/menu/"+(this.editForm.id?'update':'save'),this.loginForm) .then(res =>{ this.$message({ showClose:true, message: '恭喜你,操作成功', type: 'success', onClose:()=>{ this.getMenuTree() } }); this.dialogVisible = false }) } else { console.log('error submit!!'); return false; } }); }, resetForm() { console.log('submit!'); }, getMenuTree(){ this.$axios.get("/sys/menu/list").then(res => { this.tableData = res.data.data }) }, editHandle(id){ this.$axios.get('/sys/menu/info/'+id).then(res =>{ this.editForm = res.data.data this.dialogVisible = true }) }, resetForm(formName) { this.$refs[formName].resetFields(); this.dialogVisible = false this.editForm = {} }, handleClose() { this.resetForm('editForm') }, delHandle(id) { this.$axios.post("/sys/menu/delete/" + id).then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { this.getMenuTree() } }); }) } }, created(){ this.getMenuTree(); }, } </script> <style> </style>




<template> <div> <el-form :inline="true"> <el-form-item> <el-input v-model="searchForm.name" placeholder="名称" clearable> </el-input> </el-form-item> <el-form-item> <el-button @click="getRoleList">搜索</el-button> </el-form-item> <el-form-item> <el-button type="primary" @click="dialogVisible = true">新增</el-button> </el-form-item> <el-form-item> <el-popconfirm title="这是确定批量删除吗?" @confirm="delHandle(null)"> <el-button type="danger" slot="reference" :disabled="delBtlStatu">批量删除</el-button> </el-popconfirm> </el-form-item> </el-form> <el-table ref="multipleTable" :data="tableData" tooltip-effect="dark" style="width: 100%" border stripe @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="name" label="名称" width="120"> </el-table-column> <el-table-column prop="code" label="唯一编码" show-overflow-tooltip> </el-table-column> <el-table-column prop="remark" label="描述" show-overflow-tooltip> </el-table-column> <el-table-column prop="statu" label="状态"> <template slot-scope="scope"> <el-tag size="small" v-if="scope.row.statu === 1" type="success">正常</el-tag> <el-tag size="small" v-else-if="scope.row.statu === 0" type="danger">禁用</el-tag> </template> </el-table-column> <el-table-column prop="icon" label="操作"> <template slot-scope="scope"> <el-button type="text" @click="permHandle(scope.row.id)">分配权限</el-button> <el-divider direction="vertical"></el-divider> <el-button type="text" @click="editHandle(scope.row.id)">编辑</el-button> <el-divider direction="vertical"></el-divider> <template> <el-popconfirm title="这是一段内容确定删除吗?" @confirm="delHandle(scope.row.id)"> <el-button type="text" slot="reference">删除</el-button> </el-popconfirm> </template> </template> </el-table-column> </el-table> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" :current-page="current" :page-size="size" :total="total"> </el-pagination> <!--新增对话框--> <el-dialog title="提示" :visible.sync="dialogVisible" width="600px" :before-close="handleClose"> <el-form :model="editForm" :rules="editFormRules" ref="editForm" label-width="100px" class="demo-editForm"> <el-form-item label="角色名称" prop="name" label-width="100px"> <el-input v-model="editForm.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="唯一编码" prop="code" label-width="100px"> <el-input v-model="editForm.code" autocomplete="off"></el-input> </el-form-item> <el-form-item label="描述" prop="remark" label-width="100px"> <el-input v-model="editForm.remark" autocomplete="off"></el-input> </el-form-item> <el-form-item label="状态" prop="statu" label-width="100px"> <el-radio-group v-model="editForm.statu"> <el-radio :label=0>禁用</el-radio> <el-radio :label=1>正常</el-radio> </el-radio-group> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('editForm')">立即创建</el-button> <el-button @click="resetForm('editForm')">重置</el-button> </el-form-item> </el-form> </el-dialog> <el-dialog title="分配权限" :visible.sync="permDialogVisible" width="600px"> <el-form :model="permForm"> <el-tree :data="permTreeData" show-checkbox ref="permTree" :default-expand-all=true node-key="id" :check-strictly=true :props="defaultProps"> </el-tree> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="permDialogVisible = false">取 消</el-button> <el-button type="primary" @click="submitPermFormHandle('permForm')">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Role", data() { return { searchForm: {}, delBtlStatu: true, total: 0, size: 10, current: 1, dialogVisible: false, editForm: { }, tableData: [], editFormRules: { name: [ {required: true, message: '请输入角色名称', trigger: 'blur'} ], code: [ {required: true, message: '请输入唯一编码', trigger: 'blur'} ], statu: [ {required: true, message: '请选择状态', trigger: 'blur'} ] }, multipleSelection: [], permDialogVisible: false, permForm: {}, defaultProps: { children: 'children', label: 'name' }, permTreeData: [] } }, created() { this.getRoleList() this.$axios.get('/sys/menu/list').then(res => { this.permTreeData = res.data.data }) }, methods: { toggleSelection(rows) { if (rows) { rows.forEach(row => { this.$refs.multipleTable.toggleRowSelection(row); }); } else { this.$refs.multipleTable.clearSelection(); } }, handleSelectionChange(val) { console.log("勾选") console.log(val) this.multipleSelection = val; this.delBtlStatu = val.length == 0 }, handleSizeChange(val) { console.log(`每页 ${val} 条`); this.size = val this.getRoleList() }, handleCurrentChange(val) { console.log(`当前页: ${val}`); this.current = val this.getRoleList() }, resetForm(formName) { this.$refs[formName].resetFields(); this.dialogVisible = false this.editForm = {} }, handleClose() { this.resetForm('editForm') }, 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.size = res.data.data.size this.current = res.data.data.current this.total = res.data.data.total }) }, submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$axios.post('/sys/role/' + (this.editForm.id?'update' : 'save'), this.editForm) .then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { this.getRoleList() } }); this.dialogVisible = false this.resetForm(formName) }) } else { console.log('error submit!!'); return false; } }); }, editHandle(id) { this.$axios.get('/sys/role/info/' + id).then(res => { this.editForm = res.data.data this.dialogVisible = true }) }, delHandle(id) { var ids = [] if (id != null) { ids.push(id) } else { this.multipleSelection.forEach(row => { ids.push(row.id) }) } console.log(ids) this.$axios.post("/sys/role/delete", ids).then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { this.getRoleList() } }); }) }, permHandle(id) { this.permDialogVisible = true this.$axios.get("/sys/role/info/" + id).then(res => { this.$refs.permTree.setCheckedKeys(res.data.data.menuIds) this.permForm = res.data.data }) }, submitPermFormHandle(formName) { var menuIds = this.$refs.permTree.getCheckedKeys() console.log(menuIds) this.$axios.post('/sys/role/perm/' + this.permForm.id, menuIds).then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { this.getRoleList() } }); this.permDialogVisible = false this.resetForm(formName) }) } } } </script> <style scoped> .el-pagination { float: right; margin-top: 22px; } </style>




用户的增删改查以及对应的权限

<template> <div> <el-form :inline="true"> <el-form-item> <el-input v-model="searchForm.username" placeholder="用户名" clearable > </el-input> </el-form-item> <el-form-item> <el-button @click="getUserList">搜索</el-button> </el-form-item> <el-form-item> <el-button type="primary" @click="dialogVisible = true">新增</el-button> </el-form-item> <el-form-item> <el-popconfirm title="这是确定批量删除吗?" @confirm="delHandle(null)"> <el-button type="danger" slot="reference" :disabled="delBtlStatu" >批量删除</el-button> </el-popconfirm> </el-form-item> </el-form> <el-table ref="multipleTable" :data="tableData" tooltip-effect="dark" style="width: 100%" border stripe @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column label="头像" width="50"> <template slot-scope="scope"> <el-avatar size="small" :src="scope.row.avatar"></el-avatar> </template> </el-table-column> <el-table-column prop="username" label="用户名" width="120"> </el-table-column> <el-table-column prop="code" label="角色名称"> <template slot-scope="scope"> <el-tag size="small" type="info" v-for="item in scope.row.sysRoles">{{item.name}}</el-tag> </template> </el-table-column> <el-table-column prop="email" label="邮箱"> </el-table-column> <el-table-column prop="phone" label="手机号"> </el-table-column> <el-table-column prop="statu" label="状态"> <template slot-scope="scope"> <el-tag size="small" v-if="scope.row.statu === 1" type="success">正常</el-tag> <el-tag size="small" v-else-if="scope.row.statu === 0" type="danger">禁用</el-tag> </template> </el-table-column> <el-table-column prop="created" width="200" label="创建时间" > </el-table-column> <el-table-column prop="icon" width="260px" label="操作"> <template slot-scope="scope"> <el-button type="text" @click="roleHandle(scope.row.id)">分配角色</el-button> <el-divider direction="vertical"></el-divider> <el-button type="text" @click="repassHandle(scope.row.id, scope.row.username)">重置密码</el-button> <el-divider direction="vertical"></el-divider> <el-button type="text" @click="editHandle(scope.row.id)">编辑</el-button> <el-divider direction="vertical"></el-divider> <template> <el-popconfirm title="这是一段内容确定删除吗?" @confirm="delHandle(scope.row.id)"> <el-button type="text" slot="reference">删除</el-button> </el-popconfirm> </template> </template> </el-table-column> </el-table> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" :current-page="current" :page-size="size" :total="total"> </el-pagination> <!--新增对话框--> <el-dialog title="提示" :visible.sync="dialogVisible" width="600px" :before-close="handleClose"> <el-form :model="editForm" :rules="editFormRules" ref="editForm"> <el-form-item label="用户名" prop="username" label-width="100px"> <el-input v-model="editForm.username" autocomplete="off"></el-input> <el-alert title="初始密码为888888" :closable="false" type="info" style="line-height: 12px;" ></el-alert> </el-form-item> <el-form-item label="邮箱" prop="email" label-width="100px"> <el-input v-model="editForm.email" autocomplete="off"></el-input> </el-form-item> <el-form-item label="手机号" prop="phone" label-width="100px"> <el-input v-model="editForm.phone" autocomplete="off"></el-input> </el-form-item> <el-form-item label="状态" prop="statu" label-width="100px"> <el-radio-group v-model="editForm.statu"> <el-radio :label="0">禁用</el-radio> <el-radio :label="1">正常</el-radio> </el-radio-group> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="resetForm('editForm')">取 消</el-button> <el-button type="primary" @click="submitForm('editForm')">确 定</el-button> </div> </el-dialog> <!-- 分配权限对话框 --> <el-dialog title="分配角色" :visible.sync="roleDialogFormVisible" width="600px"> <el-form :model="roleForm"> <el-tree :data="roleTreeData" show-checkbox ref="roleTree" :check-strictly=checkStrictly node-key="id" :default-expand-all=true :props="defaultProps"> </el-tree> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="roleDialogFormVisible=false">取 消</el-button> <el-button type="primary" @click="submitRoleHandle('roleForm')">确 定</el-button> </div> </el-dialog> </div> </template> <script> export default { name: "Role", data() { return { searchForm: {}, delBtlStatu: true, total: 0, size: 10, current: 1, dialogVisible: false, editForm: { }, tableData: [], editFormRules: { username: [ {required: true, message: '请输入用户名称', trigger: 'blur'} ], email: [ {required: true, message: '请输入邮箱', trigger: 'blur'} ], statu: [ {required: true, message: '请选择状态', trigger: 'blur'} ] }, multipleSelection: [], roleDialogFormVisible: false, defaultProps: { children: 'children', label: 'name' }, roleForm: {}, roleTreeData: [], treeCheckedKeys: [], checkStrictly: true } }, created() { this.getUserList() this.$axios.get("/sys/role/list").then(res => { this.roleTreeData = res.data.data.records }) }, methods: { toggleSelection(rows) { if (rows) { rows.forEach(row => { this.$refs.multipleTable.toggleRowSelection(row); }); } else { this.$refs.multipleTable.clearSelection(); } }, handleSelectionChange(val) { console.log("勾选") console.log(val) this.multipleSelection = val; this.delBtlStatu = val.length == 0 }, handleSizeChange(val) { console.log(`每页 ${val} 条`); this.size = val this.getUserList() }, handleCurrentChange(val) { console.log(`当前页: ${val}`); this.current = val this.getUserList() }, resetForm(formName) { this.$refs[formName].resetFields(); this.dialogVisible = false this.editForm = {} }, handleClose() { this.resetForm('editForm') }, getUserList() { this.$axios.get("/sys/user/list", { params: { username: this.searchForm.username, current: this.current, size: this.size } }).then(res => { this.tableData = res.data.data.records this.size = res.data.data.size this.current = res.data.data.current this.total = res.data.data.total }) }, submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$axios.post('/sys/user/' + (this.editForm.id?'update' : 'save'), this.editForm) .then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { this.getUserList() } }); this.dialogVisible = false }) } else { console.log('error submit!!'); return false; } }); }, editHandle(id) { this.$axios.get('/sys/user/info/' + id).then(res => { this.editForm = res.data.data this.dialogVisible = true }) }, delHandle(id) { var ids = [] if (id) { ids.push(id) } else { 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() } }); }) }, roleHandle (id) { this.roleDialogFormVisible = true this.$axios.get('/sys/user/info/' + id).then(res => { this.roleForm = res.data.data let roleIds = [] res.data.data.sysRoles.forEach(row => { roleIds.push(row.id) }) this.$refs.roleTree.setCheckedKeys(roleIds) }) }, submitRoleHandle(formName) { var roleIds = this.$refs.roleTree.getCheckedKeys() console.log(roleIds) this.$axios.post('/sys/user/role/' + this.roleForm.id, roleIds).then(res => { this.$message({ showClose: true, message: '恭喜你,操作成功', type: 'success', onClose:() => { 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: () => { } }); }) }) } } } </script> <style scoped> .el-pagination { float: right; margin-top: 22px; } </style>
上面的菜单、角色、用户有增删改查操作,但是不是每个用户都有权限的,
没权限的用户我们应该隐藏按钮,因此我们需要通过条件来判断按钮是否应该显示,
那么应该怎么定义一个方法可以让全局都能使用呢?
我们再src下面新建一个js文件用于定义一个全局使用的方法:



import Vue from "vue"
Vue.mixin({
methods: {
hasAuth(perm) {
var authority = this.$store.state.menus.permList
return authority.indexOf(perm) > -1
}
}
})
之前我们在加载菜单的时候说过,我们同时要加载权限数据,现在就需要用到权限数据了,这里数组,因此我们通过按钮的权限是否在所有权限列表内就行了。
mixin的作用是多个组件可以共享数据和方法,在使用mixin的组件中引入后,mixin中的方法和属性也就并入到该组件中,可以直接使用,在已有的组件数据和方法进行了扩充。
引入mixin分全局引用和局部引用。
然后我们需要在main.js中引入这个文件

import global from './globalFun'

v-if="hasAuth('sys:user:save')"
刷新页面
没有也新增按钮

测试有对应去权限

let authoritys = ['sys:user:list', "sys:user:save", "sys:user:delete"]
刷新页面

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。