IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java 之SpringBoot+Vue实现后台管理系统的开发【一、前端】 -> 正文阅读

[Java知识库]Java 之SpringBoot+Vue实现后台管理系统的开发【一、前端】

一、前端项目搭建

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(/* 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>

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;
				})
			}
			//Result - code、msg、data
		}
	}
</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',()=>{
	//无法在header当中存入jwt
	return Result;
})

在登录页测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8、定义全局axios拦截器

在这里插入图片描述
在这里插入图片描述

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;
})

的登录页面发起请求测试
在这里插入图片描述
在这里插入图片描述

二、后台管理界面开发

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(/* webpackChunkName: "about" */ '../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(/* webpackChunkName: "about" */ '../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',()=>{
	//无法在header当中存入jwt
	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;
				})
			}
			//Result - code、msg、data
		},
		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 =>{
		//拿到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;
	}

4、设置动态路由

在这里插入图片描述

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

访问测试
在这里插入图片描述

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 =>{
			//拿到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()
})

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 {
				//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;
				}
			}
			
		},
  • 查看效果
    在这里插入图片描述
    在这里插入图片描述
  • 我们发现重复点击会重复的添加到上面,现在设置重复点击不会出现重复的信息
    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"]

刷新页面
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-27 17:14:02  更:2022-05-27 17:15:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 12:20:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码