为了防止接口异常,在上一章的基础上创建一个mork文件夹写了个menu.json放入public文件里:
Element官网:https://element.eleme.cn/#/zh-CN/guide/design
{
"data": [
{
"id": 101,
"authName": "用户管理",
"path": "users",
"children": [
{
"id": 1,
"authName": "用户列表",
"path": "users",
"children": []
},
{
"id": 2,
"authName": "添加用户",
"path": "addusers",
"children": []
}
]
},
{
"id": 102,
"authName": "分类管理",
"path": "categories",
"children": [
{
"id": 1,
"authName": "分类列表",
"path": "categories",
"children": []
},{
"id": 2,
"authName": "添加分类",
"path": "addcategories",
"children": []
}
]
},
{
"id": 103,
"authName": "商品管理",
"path": "goods",
"children": [
{
"id": 1,
"authName": "商品列表",
"path": "goods",
"children": []
}
]
}
],
"meta": {
"msg": "获取菜单列表成功",
"status": 200
}
}
App.vue里放入路由容器:
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<style lang="scss">
</style>
view下LoginView.vue:
<template>
<!-- el-form 组件
:model="ruleForm" ruleForm是data中定义的存储的是用户名和密码值的对象 通过model传给el-form组件 -->
<!-- :rules="rules" rules是data中定义的对象目的是校验用户名和密码的规则 -->
<!-- ref="ruleForm" 咱们可以通过ref来获取el-form组件内部的方法 比如:validate校验方法 resetFields重置方法 -->
<!-- status-icon 是在表单校验错误的时候 输入框中出现的提示小图标-->
<!-- label-width="200px" 是用来控制用户名和密码文本的宽度 -->
<div class="myform">
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px">
<!-- label 控制输入框的文本 prop="username" 是对应表单域model中的username字段
规则的名字需要和表单域model中的字段一模一样 -->
<el-form-item label="用户名" prop="username">
<!-- v-model 里面对应的是data中的数据 -->
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<!-- autocomplete="off" 作用是把输入框的自动提示功能关闭 -->
<el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<!-- 提交的时候把ref里面的名字传过去目的是为了使用refs方法 来调用el-form里面的 validate校验方法-->
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<!-- 提交的时候把ref里面的名字传过去目的是为了使用refs方法 来调用el-form里面的 resetFields重置方法-->
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name:"LoginView",
data() {
var checkUser = (rule, value, callback) => {
console.log('用户名:',value)
if(value.trim()==''){
callback(new Error('请输入用户名'));
}else if(!/^[0-9a-zA-Z_\u4e00-\u9fa5@.]{5,12}$/.test(value)){
callback(new Error('用户名为5-10位中英文数字或者下划线'));
}
else{
callback();
}
};
var validatePass = (rule, value, callback) => {
console.log('密码:',value)
if (value.trim()=='') {
callback(new Error('请输入密码'));
} else{
callback();
}
};
return {
ruleForm: {
password: '',
username: ''
},
rules: {
password: [
{ validator: validatePass, trigger: 'blur' }
],
username: [
{ validator: checkUser, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
/* el-form组件的validate方法 在回调函数中
如果 valid 为 true 则表示表单校验通过
为false则表示不通过 */
if (valid) {
axios.post('https://api.***.ed***********',{
email:this.ruleForm.username,
password:this.ruleForm.password
})
.then(res=>{
let {access_token} = res.data
this.$message.success('登陆成功')
/* 当登录成功 把用户名和token存入本地缓存中方便后续使用 */
localStorage.token = access_token
/* 登录成功后过一秒跳转首页 */
setTimeout(()=>{
this.$router.push({name:'index'})
},1000)
})
/* 登录失败(包括用户名或者密码不对会走catch) */
.catch(()=>{
this.$message.error('登陆失败')
})
} else {
this.$message.error('您输入的有误')
}
});
},
resetForm(formName) {
/* 通过vue中的$refs方法来调用组件el-form中的 resetFields重置方法 */
this.$refs[formName].resetFields();
}
}
}
</script>
<style lang="scss">
.myform{
width:600px;
margin:50px auto;
}
</style>
view下IndexView.vue:
//这里做了点改动,相比上一章这里采取动态路由来获取侧边栏以及二级菜单:
相关注意点已用注释表明:
<template>
<!-- 100vh全屏展示 -->
<el-container style="height: 100vh; border: 1px solid #eee">
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<!-- <el-menu :default-openeds="['1', '3']"> 表示默认展开第几个菜单 -->
<!-- 1对应了el-submenu index="1" -->
<!-- :default-openeds="openList" 不可以直接['1'] 需要一个变量openList代替
因为值会变,如果写死 ['1'] 那么就永远不会变
否则点二级菜单 一级菜单会自动合上-->
<el-menu
:default-openeds="openList"
:router="true"
:default-active="pagepath"
:unique-opened="true"
>
<!-- :unique-opened="true" 表示始终只打开一个栏点开一个栏另外的会收起来-->
<!-- default-active="/index/users 表示一进入页面就默认激活/index/users 导航菜单栏 -->
<!-- 不能写死值,要用监听器解决 -->
<!-- 把router属性改成true才能实现点击跳转 -->
<!-- index接收的是字符串类型,(i+1)是数字类型,所以使用toString方法转成字符串,传给index -->
<!-- 因为i是从0开始的 所以需要+1 -->
<el-submenu
:index="(i + 1).toString()"
v-for="(v, i) in navList"
:key="i"
>
<template slot="title"
><i class="el-icon-menu"></i>{{ v.authName }}</template
>
<!-- <template slot="title">分组一</template> -->
<!-- el-menu-item index="1-1" 表示第一个导航里面的第一个子项 -->
<!-- 子选项需要改成例如: 1-1格式 以字符串的形式传给index属性 -->
<!-- 因为子选项也是一个数组所以需要再次循环 -->
<el-menu-item
:index="'/index/' + item.path"
v-for="(item, index) in v.children"
:key="index"
>
{{ item.authName }}
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>查看</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
openList: ["1"],
navList: [],
pagepath: "/index/users",
};
},
watch: {
/* 当路由发生变化的时候,就把最新的地址给到pagepath变量
作用是为了保持路由菜单栏的高亮显示,以及解决点击不跳转的bug */
$route: {
handler: function (newV) {
console.log(newV);
this.pagepath = newV.path;
},
immediate: true,
},
},
created: function () {
this.getNaviList();
},
methods: {
getNaviList: function () {
axios
.get("/mork/menu.json", {
headers: {
Authorization: localStorage.token,
},
})
.then((res) => {
console.log(res);
let { data, meta } = res.data;
/* 数据获取成功 */
if (meta.status == 200) {
this.navList = data;
/* 动态添加路由 */
/* 因为第一个路由是默认的所以我们从第二个路由开始动态添加 */
console.log(this.navList);
let arr = this.navList.slice(1,3)
/* 循环路由数组 动态添加路由 */
console.log(arr);
arr.forEach(v => {
/* 我们尽量使用v.children[0].path 原因是我们的路径名用的是子路由的 */
/* 如果我们直接写死 v.children[0].path 会导致只有一个子路由的路径被动态添加了
如果有多个就无法生效, 所以我们要二次循环v.children,从而实现多个二级子路由
能够被动态的添加*/
v.children.forEach(r=>{
this.$router.addRoute("index",
{
path:r.path,
name:r.path,
component:()=>import(`@/views/${r.path.substring(0,1).toUpperCase()+r.
path.substring(1)}View.vue`),
},
);
})
this.$router.addRoute("index",
{
path:v.children[0].path,
name:v.children[0].path,
component:()=>import(`@/views/${v.children[0].path.substring(0,1).toUpperCase()+v.children[0].
path.substring(1)}View.vue`),
},
);
});
console.log(this.$router);
} else {
/* 防止数据获取失败给出相应的后台提示 */
this.$message.error(meta.msg);
}
})
.catch((err) => {
console.log(err);
});
},
},
};
</script>
<style scoped>
.el-header {
background-color: #b3c0d1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
router下路由配置:采取动态路由,之前的注释掉了:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'login',
component:()=>import('@/views/LoginView.vue')
},
{
path:'/index',
name:'index',
/* 已进入页面就默认进入二级路由users页面 */
redirect:'/index/users',
component:()=>import('@/views/IndexView.vue'),
children:[{
path:'users',
name:'users',
component:()=>import('../views/UsersView.vue'),
},
{
path:'addusers',
name:'addusers',
component:()=>import('../views/AddusersView.vue'),
}
/* {
path:'roles',
name:'roles',
component:()=>import('../views/RolesView.vue'),
},
{
path:'rights',
name:'rights',
component:()=>import('../views/RightsView.vue'),
},
{
path:'goods',
name:'goods',
component:()=>import('../views/GoodsView.vue'),
},
{
path:'params',
name:'params',
component:()=>import('../views/ParamsView.vue'),
},
{
path:'categories',
name:'categories',
component:()=>import('../views/CategoriesView.vue'),
},
{
path:'categories',
name:'categories',
component:()=>import('../views/CategoriesView.vue'),
},
{
path:'orders',
name:'ordes',
component:()=>import('../views/OrdersView.vue'),
},
{
path:'reports',
name:'reports',
component:()=>import('../views/ReportsView.vue'),
}, */
]
}
]
const router = new VueRouter({
routes
})
export default router
views下跳转的AddcategoriesView.vue:
<template>
<div><h1>添加分类</h1></div>
</template>
<script>
export default {
}
</script>
<style>
</style>