一、前端项目搭建
1、项目初始化
安装 vue可视化
npm install --g vue-cli
打开vue的的可视化管理工具页面
vue ui
访问该地址:
访问成功 使用Hbuilder打开项目
2、实现相关依赖的安装
安装 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
3、设置页面路由
删除页面的自动创建好的页面 创建一些自定义页面 设置路由
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( '../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>
4、设置前端发起请求,设置验证码相关内容
<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;
})
}
}
}
</script>
<style scoped>
.elrow{
display: flex;
align-items: center;
text-align: center;
height:100%;
}
</style>
5、创建mock.js
在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
6、在store当中的index.js设置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: {
}
})
7、完善登录的请求
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',()=>{
return Result;
})
在登录页测试
8、定义全局axios拦截器
import axios from "axios"
import router from "./router"
import Element from "element-ui"
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',()=>{
Result.code = 404
Result.msg = "验证码错误"
return Result;
})
的登录页面发起请求测试
二、后台管理界面开发
1、创建index.vue
一般来说,管理系统的页面我们都是头部是一个简单的信息展示系统名称和登录用户信息,然后中间的左边是菜单导航栏,右边是内容,对应到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>
2、设置页面路由和跳转路径
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( '../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
设置跳转路径
设置一下状态码使其跳转成功
3、设置页面布局
引入一些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>
4、设置左侧导航
<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>
5、Vue代码抽取
(1)抽取菜单
<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>
最后效果
(2)index
将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 访问也发现空白
(3)设置子路由
children:[
{
path:'/index',
name:'Index',
component:Index
}
]
再次访问: 但是我们发现上面没有index的内容
设置内容
<router-view>
</router-view>
6、编写导航栏路由
<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( '../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
访问:http://localhost:8080/roles
http://localhost:8080/users
http://localhost:8080/menus
设置页面路由
<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>
点击测试
三、用户登录信息展示
管理界面的右上角的用户信息现在是写死的, 因为我们现在已经登录成功,所以我们可以通过接口去请求获取到当前的用户信息了, 这样我们就可以动态显示用户的信息,这个接口比较简单,然后退出登录的链接也一起完成, 就请求接口同时把浏览器中的缓存删除就退出了哈。
1、实现用户信息页面编写
<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>
2、设置mock.js
Mock.mock('/sys/userInfo','get',()=>{
Result.data = {
id:"1",
username:"test",
avatar:"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
}
return Result;
})
3、设置个人中心的路由
<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;
})
}
},
created() {
this.getCaptcha();
}
}
</script>
<style scoped>
.elrow{
display: flex;
align-items: center;
text-align: center;
height:100%;
}
</style>
4、设置退出登录
<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;
})
四、动态菜单栏的开发
1、修改一下路由规则
{
path:'/sys/users',
name:'SysUser',
component:User
},
{
path:'/sys/roles',
name:'SysRole',
component:Role
},
{
path:'/sys/menus',
name:'SysMenu',
component:Menu
}
2、设置动态菜单的数据
<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>
3、设置mock.js获取导航数据
将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 =>{
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;
}
4、设置动态路由
router.beforeEach((to,from,next)=>{
axios.get("/sys/menu/nav",{
headers:{
Authorization: localStorage.getItem("token")
}
}).then(res =>{
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
访问测试
5、设置动态路由加载一次以后无需二次加载
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 =>{
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()
})
6、实现动态导航
<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>
7、设置侧栏和页面进行动态绑定
- 在menus.js当中设置添加tab 的功能
,
addTab(state,tab) {
let newTabName = '';
state.editableTabs.push({
title: tab.title,
name: tab.name,
});
state.editableTabsValue = tab.name;
},
- 在SideMenu.vue当中添加点击事件
<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)
}
- 完善Tabs.vue
data() {
return {
}
},
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;
}
}
},
- 查看效果
- 我们发现重复点击会重复的添加到上面,现在设置重复点击不会出现重复的信息
menus.js
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>
- 设置点击tab进行内容的切换
<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',
}]
}
四、菜单管理界面开发
1、设置Home的内容
<el-main>
<Tabs></Tabs>
<div style="margin: 0 15px;">
<router-view />
</div>
</el-main>
2、设置Menu.vue
(1)设置Menu.vue
<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>
(2)设置mock.js
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;
})
3、设置Menu.vue新增弹出和修改弹出,以及删除
<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>
五、角色管理
1、设置角色信息的增删改查-权限分配
<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"]
刷新页面
|