创建前端项目
1.创建项目有两种方式
vue ui
进入页面
?下一步:
?下一步选手动:
?下一步:
?下一步选2.x版本创建项目
开发工具用WebStorm 2020.3.3
引入Element-ui
yarn add element-ui
在main.js 中添加如下:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
测试element是否添加成功
在HomeView.vue进行测试,测试代码如下:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<el-button>测试elementui</el-button>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'HomeView',
components: {
HelloWorld
}
}
</script>
安装axios、qs、mockjs
axios:一个基于promise的HTTP库,类ajax
qs:查询参数序列化和解析库
mockjs:为我们生成随机数数据的工具类
安装axios
进入项目目录,添加axios依赖
yarn add axios
在main.js 中添加如下:
import axios from 'axios'
Vue.prototype.$http = axios
安装qs
进入项目目录,添加qs依赖
yarn add qs
安装mockjs:为我们生成随机数据的工具库
yarn add mockjs -S
页面路由的开发
删除本身自带的页面
vue模板代码
<template>
</template>
<script>
export default {
name:"Login"
}
</script>
<style scoped>
</style>
在views下创建两个页面Login.vue(登录页面)和Home.vue(首页)
修改路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/Home.vue'
import LoginView from '../views/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/login',
name: 'Login',
component: () => import( '../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
开发登陆页面
在login.vue下进行编写
<template>
<el-row type="flex" class="row-bg" justify="center">
<!-- <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>-->
<!-- <el-col :span="6"><div class="grid-content bg-purple-light"></div></el-col>-->
<el-col :span="6" :lg="7">
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="100px" >
<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 v-model="loginForm.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 srx=""></el-image>
</el-form-item>
<el-form-item>
<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>
</template>
<script>
export default {
name:"Login",
data() {
return {
loginForm: {
username: '',
password: '',
code: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度在5个字符', 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 scoped>
.el-row{
height: 100%;
display: flex;
align-items: center;
text-align: center;
}
.captchaIng{
float: left;
margin-left: 8px;
border-radius: 4px;
}
</style>
效果比较难看进行优化调整优化代码如下
<template>
<div class="loginbody">
<div class="logindata">
<div class="logintext">
<h2>永远商城权限管理</h2>
</div>
<div class="formdata">
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="100px" >
<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 v-model="loginForm.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 srx=""></el-image>
</el-form-item>
<div class="butt">
<el-form-item>
<el-button type="primary" @click="submitForm('loginForm')">登录</el-button>
<el-button @click="resetForm('loginForm')">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script>
export default {
name:"Login",
data() {
return {
loginForm: {
username: '',
password: '',
code: ''
},
checked: false,
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度在5个字符', 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 scoped>
.loginbody {
width: 100%;
height: 100%;
min-width: 1000px;
background-image: url("../assets/login2.jpg");
background-size: 100% 100%;
background-position: center center;
overflow: auto;
background-repeat: no-repeat;
position: fixed;
line-height: 100%;
padding-top: 150px;
}
.logintext {
margin-bottom: 20px;
line-height: 50px;
text-align: center;
font-size: 30px;
font-weight: bolder;
color: white;
text-shadow: 2px 2px 4px #000000;
}
.logindata {
width: 400px;
height: 300px;
transform: translate(-50%);
margin-left: 50%;
}
.butt {
margin-top: 10px;
text-align: center;
}
</style>
发起登录请求,Mock返回测试结果
代码如下:
Login.vue
<template>
<div class="loginbody">
<div class="logindata">
<div class="logintext">
<h2>永远商城权限管理</h2>
</div>
<div class="formdata">
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="80px" >
<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 v-model="loginForm.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 :src="captchaImg" class="captchaImg"></el-image>
</el-form-item>
<div class="butt">
<el-form-item>
<el-button type="primary" @click="submitForm('loginForm')">登录</el-button>
<el-button @click="resetForm('loginForm')">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script>
export default {
name:"Login",
data() {
return {
loginForm: {
username: '',
password: '',
code: '',
token:''
},
checked: false,
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度在5个字符', trigger: 'blur' }
]
},
captchaImg: null
};
},
methods: {
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("/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>
.loginbody {
width: 100%;
height: 100%;
min-width: 1000px;
background-image: url("../assets/login2.jpg");
background-size: 100% 100%;
background-position: center center;
overflow: auto;
background-repeat: no-repeat;
position: fixed;
line-height: 100%;
padding-top: 150px;
}
.logintext {
margin-bottom: 20px;
line-height: 50px;
text-align: center;
font-size: 30px;
font-weight: bolder;
color: white;
text-shadow: 2px 2px 4px #000000;
}
.logindata {
width: 400px;
height: 300px;
transform: translate(-50%);
margin-left: 50%;
}
.butt {
margin-top: 10px;
text-align: center;
}
</style>
store下index.js
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: {
}
})
main.js
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';
import axios from 'axios'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
require("./mock.js")
Vue.use(ElementUI);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
main.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
})
Mock.mock('/login','post',()=>{
//无法在header中传入jwt
return Result
})
axios请求前后拦截处理
axios.js
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
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';
import axios from './axios'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
require("./mock.js")
Vue.use(ElementUI);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
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
})
Mock.mock('/login','post',()=>{
//无法在header中传入jwt
Result.code=400
Result.msg="验证码错误"
return Result
})
后台整体布局、头部样式
代码如下:
views下index.vue
<template>
<el-container class="layout-container">
<el-aside width="200px">Aside</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
<el-dropdown>
<span class="el-dropdown-link">
下拉菜单<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="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
name:"Index"
}
</script>
<style scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.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;
}
</style>
在src下的assets下新建css文件包创建global.css文件
html,body,#app {
height: 100%;
margin: 0;
padding: 0;
}
mian.js
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';
import axios from './axios'
import '@/assets/css/global.css'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
require("./mock.js")
Vue.use(ElementUI);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/Home.vue'
import Index from '../views/Index'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
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
扩展知识
element-ui 中让el-container 高度自适应
在assets 文件夹中,新建一个global.css 文件。
html,body,#app {
height: 100%;
margin: 0;
padding: 0;
}
2、然后在main.js 中引入?global.css
import '@/assets/css/global.css'
3、后续给你所有的组件设置?height:100% ?就可以了。
vue中使用
<el-container class="layout-container">
<el-aside width="300px">
</el-aside>
<el-container>
css
.layout-container {
height: 100%;
}
左侧导航菜单填充
index.vue
<template>
<el-container class="layout-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">
<router-link to="/index">
<el-menu-item index="Index">
<template slot="title">
<i class="el-icon-s-home"></i>
<span>首页</span>
</template>
</el-menu-item>
</router-link>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-s-operation"></i>
<span>系统管理</span>
</template>
<el-submenu index="1-1">
<template slot="title">
<i class="el-icon-s-custom"></i>
<span>用户管理</span>
</template>
<el-menu-item index="1-2">
<template slot="title">
<i class="el-icon-rank"></i>
<span>角色管理</span>
</template>
</el-menu-item>
<el-menu-item index="1-3">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>菜单管理</span>
</template>
</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>系统工具</span>
</template>
<el-menu-item index="2-1">
<template slot="title">
<i class="el-icon-s-order"></i>
<span>数字字典</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
<el-dropdown>
<span class="el-dropdown-link">
下拉菜单<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="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
name:"Index"
}
</script>
<style scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.el-header{
background-color: #17B3A3;
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-menu-vertical-demo{
height: 100%;
}
</style>
拆分抽取Vue组件
在views文件夹下新建inc文件夹
inc文件夹新建SideMenu.vue代码如下
<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="Index">
<template slot="title">
<i class="el-icon-s-home"></i>
<span>首页</span>
</template>
</el-menu-item>
</router-link>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-s-operation"></i>
<span>系统管理</span>
</template>
<el-submenu index="1-1">
<template slot="title">
<i class="el-icon-s-custom"></i>
<span>用户管理</span>
</template>
<el-menu-item index="1-2">
<template slot="title">
<i class="el-icon-rank"></i>
<span>角色管理</span>
</template>
</el-menu-item>
<el-menu-item index="1-3">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>菜单管理</span>
</template>
</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>系统工具</span>
</template>
<el-menu-item index="2-1">
<template slot="title">
<i class="el-icon-s-order"></i>
<span>数字字典</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
name:"SideMenu"
}
</script>
<style scoped>
.el-menu-vertical-demo{
height: 100%;
}
</style>
修改Home.vue
<template>
<el-container class="layout-container">
<el-aside width="200px">
<SideMenu></SideMenu>
</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
<el-dropdown>
<span class="el-dropdown-link">
下拉菜单<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="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideMenu from "@/views/inc/SideMenu";
export default {
name:"Home",
components: {SideMenu}
}
</script>
<style scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.el-header{
background-color: #17B3A3;
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;
}
</style>
修改Index.vue
<template>
<div>main</div>
</template>
<script>
import SideMenu from "@/views/inc/SideMenu";
export default {
name:"Index",
components: {SideMenu}
}
</script>
<style scoped>
</style>
修改router路由下的index.js增加子路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/Home.vue'
import Index from '../views/Index'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
children:[
{
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
编写导航栏的路由
在views文件包下新增sys文件包增加User.vue、Role.vue、Menu.vue
代码如下:
User.vue
<template>
<div>用户管理</div>
</template>
<script>
export default {
name: "User"
}
</script>
<style scoped>
</style>
Role.vue
<template>
<div>权限管理</div>
</template>
<script>
export default {
name: "Role"
}
</script>
<style scoped>
</style>
Menu.vue
<template>
<div>菜单管理</div>
</template>
<script>
export default {
name: "Menu"
}
</script>
<style scoped>
</style>
在路由中增加子菜单
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/Home.vue'
import Index from '../views/Index'
import Menu from '../views/sys/Menu'
import Role from '../views/sys/Role'
import User from '../views/sys/User'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
children:[
{
path: '/index',
name: 'Index',
component: Index
},
{
path: '/user',
name: 'SysUser',
component: User
},
{
path: '/role',
name: 'SyeRole',
component: Role
},
{
path: '/menus',
name: 'SysMenu',
component: Menu
}
]
},
{
path: '/login',
name: 'login',
component: () => import( '../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
在导航
SideMenu.vue增加跳转注解
<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="Index">
<template slot="title">
<i class="el-icon-s-home"></i>
<span>首页</span>
</template>
</el-menu-item>
</router-link>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-s-operation"></i>
<span>系统管理</span>
</template>
</el-submenu>
<router-link to="/user">
<el-menu-item index="1-1">
<template slot="title">
<i class="el-icon-s-custom"></i>
<span>用户管理</span>
</template>
</el-menu-item>
</router-link>
<router-link to="/role">
<el-menu-item index="1-2">
<template slot="title">
<i class="el-icon-rank"></i>
<span>角色管理</span>
</template>
</el-menu-item>
</router-link>
<el-menu-item index="1-3">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>菜单管理</span>
</template>
</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-s-tools"></i>
<span>系统工具</span>
</template>
<el-menu-item index="2-1">
<template slot="title">
<i class="el-icon-s-order"></i>
<span>数字字典</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
name:"SideMenu"
}
</script>
<style scoped>
.el-menu-vertical-demo{
height: 100%;
}
</style>
用户信息展示、修改个人密码
Home.vue
<template>
<el-container class="layout-container">
<el-aside width="200px">
<SideMenu></SideMenu>
</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar :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="{name: 'userCenter'}"> 个人中心</router-link>
</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-link href="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideMenu from "@/views/inc/SideMenu";
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 scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.el-header{
background-color: #17B3A3;
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;
}
</style>
mock.js
<template>
<el-container class="layout-container">
<el-aside width="200px">
<SideMenu></SideMenu>
</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar :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="{name: 'userCenter'}"> 个人中心</router-link>
</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-link href="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideMenu from "@/views/inc/SideMenu";
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 scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.el-header{
background-color: #17B3A3;
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;
}
</style>
index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/Home.vue'
import Index from '../views/Index'
import Menu from '../views/sys/Menu'
import Role from '../views/sys/Role'
import User from '../views/sys/User'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
children:[
{
path: '/index',
name: 'Index',
component: Index
},
{
path: '/userCenter',
name: 'userCenter',
component: () => import( '../views/userCenter.vue')
},
{
path: '/sys/user',
name: 'SysUser',
component: User
},
{
path: '/sys/role',
name: 'SyeRole',
component: Role
},
{
path: '/sys/menus',
name: 'SysMenu',
component: Menu
}
]
},
{
path: '/login',
name: 'login',
component: () => import( '../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
userCenter.vue
<template>
<div style="text-align: center;">
<h2>你好!{{ userInfo.username }} 同学</h2>
<el-form :model="passForm" status-icon :rules="rules" ref="passForm" label-width="100px">
<el-form-item label="旧密码" prop="currentPass">
<el-input type="password" v-model="passForm.currentPass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input type="password" v-model="passForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input type="password" v-model="passForm.checkPass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('passForm')">提交</el-button>
<el-button @click="resetForm('passForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "Login",
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.passForm.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
userInfo: {
},
passForm: {
password: '',
checkPass: '',
currentPass: ''
},
rules: {
password: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 12, message: '长度在 6 到 12 个字符', trigger: 'blur' }
],
checkPass: [
{ required: true, validator: validatePass, trigger: 'blur' }
],
currentPass: [
{ required: true, message: '请输入当前密码', trigger: 'blur' },
]
}
}
},
created() {
this.getUserInfo()
},
methods: {
getUserInfo() {
this.$axios.get("/sys/userInfo").then(res => {
this.userInfo = res.data.data;
})
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const _this = this
this.$axios.post('/sys/user/updatePass', this.passForm).then(res => {
_this.$alert(res.data.msg, '提示', {
confirmButtonText: '确定',
callback: action => {
this.$refs[formName].resetFields();
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
.el-form {
width: 420px;
margin: 50px auto;
}
</style>
前端用户退出操作
Home.vue
<template>
<el-container class="layout-container">
<el-aside width="200px">
<SideMenu></SideMenu>
</el-aside>
<el-container>
<el-header>
<strong>通用管理平台</strong>
<div class="header-avatar">
<el-avatar :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="{name: 'userCenter'}"> 个人中心</router-link>
</el-dropdown-item>
<el-dropdown-item @click.native="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-link href="https://www.yongyuankuaile.com" target="_blank">网站</el-link>
</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideMenu from "@/views/inc/SideMenu";
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
})
},
logout(){
this.$axios.post("/logout").then(res => {
localStorage.clear()
sessionStorage.clear()
this.$store.commit("resetState")
this.$router.push("/login")
})
}
}
}
</script>
<style scoped>
.layout-container {
height: 100%;
}
.header-avatar{
float: right;
width: 210px;
display: flex;
justify-content: space-around;
align-items: center;
}
.el-dropdown-link{
cursor: pointer;
color: #407EFF;
}
.el-icon-arrow-down{
font-size: 12px;
}
.el-header{
background-color: #17B3A3;
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;
}
</style>
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
})
Mock.mock('/login','post',()=>{
//无法在header中传入jwt
Result.code=400
Result.msg="验证码错误"
return Result
})
Mock.mock('/sys/userInfo','get',()=>{
Result.data={
id:"1",
username:"test",
avatar:""
}
return Result
})
Mock.mock('/logout','post',()=>{
return Result
})
index.js
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)
},
resetState:(state) =>{
state.token =''
}
},
actions: {
},
modules: {
}
})
动态导航与动态路由绑定
|