1、先从页面分析,系统从登陆需要login.vue页面,再到系统内部的主页面main.vue,其次就是主页面内的子页面。
login.vue页面
点击查看代码
<template>
<div class="login">
<div class="main blur">
<ul>
<li>
<img src="@/assets/img/logo1.png" alt="">
</li>
<li>
<input type="text" v-model="username" class="username" placeholder="User name" autocomplete="new-password"/>
</li>
<li>
<input type="password" v-model="password" class="password" placeholder="Password" autocomplete="new-password"/>
</li>
<li>
<p>forgot password ?</p>
</li>
<li class="btn">
<a-button type="primary" block @click="login">
Sign in
</a-button>
</li>
</ul>
</div>
</div>
</template>
<script>
import { Login } from '@/api/index'
import { message } from 'ant-design-vue'
export default {
name: 'Login',
components: {},
data () {
return {
username: '',
password: ''
}
},
watch: {
},
created () {
},
mounted () {
window.addEventListener('keydown', (e) => {
if (e.keyCode === 13) {
this.login()
}
})
},
methods: {
login () {
if (this.username && this.password) {
Login({
username: this.username,
password: this.password
}).then(res => {
message.success(res.msg)
if (res.data.token) {
this.$storejs.set('token', res.data.token)
}
this.$router.push('/main/home')
}).catch(err => {
console.log(err)
})
}
}
}
}
</script>
<style lang='scss' scoped>
.login {
height: 100%;
background: url("../../assets/img/login_bg.png") no-repeat center center;
background-size: 100% 100%;
.main {
position: fixed;
right: 20%;
top: 15%;
width: 4rem;
height: 6rem;
box-shadow: 0px 0px .24rem -.06rem black;
}
.blur {
position: absolute;
overflow: hidden;
z-index: 1;
}
.blur::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
margin: -.3rem;
background-image: url("../../assets/img/login_bg.png");
background-position: center;
background-size: 100% 100%;
background-attachment: fixed;
filter: blur(10px);
}
.main {
li {
background: rgba(255, 255, 255, 0.3);
&:nth-of-type(1){
text-align: center;
padding-top: .4rem;
background: rgba(255, 255, 255, 0);
img{
height: 1.8rem;
width: 60%;
}
}
p{
height: .5rem;
line-height: .5rem;
text-align: center;
color: black;
opacity: .5;
}
.username,
.password {
width: 100%;
border: none;
height: .6rem;
font-size: .2rem;
line-height: .5rem;
padding-left: .1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.4);
background: rgba(255, 255, 255, 0.1);
border-radius: 0;
outline: none;
}
}
.btn{
height: 1rem;
text-align: center;
button{
border-radius: 0;
width: 90%;
height: 60%;
}
}
}
}
</style>
main.vue主页面
点击查看代码
<template>
<div class="home">
<a-layout id="components-layout-demo-side" style="min-height: 100vh">
<a-layout-sider v-model="collapsed" :trigger="null" collapsible>
<img src="@/assets/img/logo.png" alt="" :class="['logo', collapsed?'':'logoImg']">
<!-- 左侧菜单列表 -->
<menu-tree :treeData="treeData" defaultSelectItemKey="home"></menu-tree>
</a-layout-sider>
<a-layout>
<a-layout-header style="background: #fff; padding: 0">
<a-icon
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="() => (collapsed = !collapsed)"
/>
</a-layout-header>
<a-layout-content style="margin: 0 16px">
<a-breadcrumb style="margin: 16px 0">
<a-breadcrumb-item>User</a-breadcrumb-item>
<a-breadcrumb-item>Bill</a-breadcrumb-item>
</a-breadcrumb>
<div class="content">
<!-- 右侧主要内容展示部分 -->
<router-view></router-view>
</div>
</a-layout-content>
<a-layout-footer style="text-align: center">
满地都是六便士,他却抬头看见了月亮,他不理睬六便士,却伸手追寻触碰月光
</a-layout-footer>
</a-layout>
</a-layout>
</div>
</template>
<script>
// import router from '@/router/index'
// import { getMenuList } from '@/api/index'
// import { createMenuTree } from '@/utils/index'
export default {
name: 'Layout',
data () {
return {
treeData: [],
collapsed: false
}
},
components: {
},
created () {
this.getMenuList()
},
methods: {
getMenuList () {
// console.log(this.$router.options.routes[0])
this.treeData = this.$router.options.routes[1].children
// this.$router.getRoutes()
// getMenuList().then((res) => {
// // parent = createMenuTree(res, parent)
// console.log(createMenuTree(res))
// this.treeData = createMenuTree(res, parent)
// })
}
}
}
</script>
<style lang="scss" scoped>
.home{
.content{
padding: 24px;
background: #fff;
min-height: 360px;
}
}
#components-layout-demo-side .logo {
height: 70px;
transform: translateX(-5px);
}
#components-layout-demo-side .logoImg{
margin-left: 56px;
}
#components-layout-demo-side .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
#components-layout-demo-side .trigger:hover {
color: #1890ff;
}
::v-deep{
.ant-layout-sider{
background: black;
}
.ant-menu-dark, .ant-menu-dark .ant-menu-sub{
background: black;
}
.ant-layout-sider-children{
overflow: hidden;
}
.ant-menu-dark .ant-menu-inline.ant-menu-sub{
background: black;
}
}
</style>
点击查看代码
<template>
<div class="menu">
<a-menu theme="dark" :default-selected-keys="[defaultSelectItemKey]" mode="inline">
<template v-for="treeItem in treeData">
<a-sub-menu :key="treeItem.path" v-if="treeItem.children && treeItem.children.length">
<span slot="title">
<a-icon :type="treeItem.meta.icon" />
<span>{{treeItem.meta.title}}</span>
</span>
<menu-tree :treeData="treeItem.children" class="menu_children"></menu-tree>
</a-sub-menu>
<a-menu-item :key="treeItem.path" v-else>
<router-link :to="{ path: `/main/${treeItem.path}` }">
<a-icon :type="treeItem.meta.icon" />
<span>{{treeItem.meta.title}}</span>
</router-link>
</a-menu-item>
</template>
</a-menu>
</div>
</template>
<script>
export default {
name: 'menu-tree',
data () {
return {
}
},
props: {
// 树状结构菜单数据源
treeData: {
type: Array,
default: () => []
},
// 默认展开的项key值
defaultSelectItemKey: {
type: String,
default: () => ['']
}
// 默认展开的列表key值
// defaultOpenItemKey: {
// type: String,
// default: () => ['']
// }
},
created () {
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.menu{
.menu_children{
margin-left: .2rem;
}
}
</style>
2、从路由分析,/login和/main路由属于平级,main.vue页面内的菜单列表的路由属于/main下的子路由,展示的菜单列表也是这一部分路由
/router/index.js
点击查看代码
import routerObj from './routesImport'
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
redirect: '/login'
},
// 菜单列表页面展示
{
path: '/main',
name: 'main',
// 特别注意layout公共组件不能使用前端工程化导入
component: () => import('../views/LayOut/index.vue'),
children: [
{
path: 'home',
name: 'home',
component: routerObj.Home,
meta: {
title: '首页',
icon: 'home',
auth: true
}
},
{
path: 'users',
name: 'users',
component: routerObj.Users,
meta: {
title: '用户管理',
icon: 'user',
auth: true
}
},
{
path: 'setting',
name: 'setting',
component: routerObj.Setting,
meta: {
title: '系统设置',
icon: 'setting',
auth: true
}
}
]
},
// 登录页面
{
path: '/login',
name: 'login',
component: routerObj.Login
},
// 在动态添加路由的过程中,如果有 404 页面,一定要放在最后添加,否则在登陆的时候添加完页面会重定向到 404 页面。
// 类似于这样,这种规则一定要最后添加。
{
path: '*',
hidden: true,
component: () => routerObj.NotFound
}
]
})
export default router
/router/routesImport.js路由导入处理
点击查看代码
// 工程自动化文件一次导入
const context = require.context('@/views/', true, /index.vue$/)
const routerObj = {}
context.keys().forEach((item) => {
// context(item)返回完整的相对路径,name是获取到当前遍历的组件的name属性值
const name = context(item).default.name
// 获取的是整个组件
routerObj[name] = context(item).default
})
export default routerObj
3、vue渲染方式分析,App.vue下的router-view是用来装对应路由的容器,是最大的主容器,负责展示登录页面和系统内部主页面,main.vue页面分左右两侧,点击左侧的菜单对应展示右边内容。菜单列表所属于/main路由下,所以点击菜单列表时,切换的是/main下的子路由,右边是/main下子路由切换时展示的对应内容,和App.vue同理,所以右侧需要一个容器router-view用来展示/main下子路由的内容
![image]()