5. 布局Layout
主要为总体布局,无需过多赘述。效果如下:
headerNav.vue
// headerNav.vue
<template>
<header class="head-nav rflex " id='header_container'>
<div class="left-nav rflex">
<img class="logo" :src="logo" alt="logo" >
<span class='title'>{{$t('commons.name')}}</span>
</div>
<div class="right-nav" ref="rightNav">
<div class="userinfo-right rflex">
<div class="notify-row">
<ul class="top-menu">
<li class="li-badge">
<el-tooltip class="item" effect="dark" content="访问github" placement="top">
<a :href='github' target="_blank">
<icon-svg icon-class="iconGithub" />
</a>
</el-tooltip>
</li>
<li class="li-badge">
<a href='#' target="_blank" v-popover:qcode>
<icon-svg icon-class="iconwechat" />
<el-popover
ref="qcode"
popper-class="qcodepopper"
placement="bottom"
trigger="hover">
<div class="wechat-area cflex">
<p class="titles">加我微信</p>
<img :src="wechat.wechatImg" style="width:100px" alt="加我微信" />
</div>
</el-popover>
</a>
</li>
<li class="li-badge">
<a href='#' target="_blank" v-popover:qqcode>
<icon-svg icon-class="iconqq" />
<el-popover
ref="qqcode"
popper-class="qcodepopper"
placement="bottom"
trigger="hover">
<div class="wechat-area cflex">
<p class="titles">加入qq群</p>
<img :src="qq.qqImg" style="width:100px" alt="加入qq群" />
</div>
</el-popover>
</a>
</li>
</ul>
</div>
<div class="userinfo">
<el-menu
class="el-menu-demo"
mode="horizontal"
>
<el-submenu index="1" popper-class="langItem">
<template slot="title">
<img :src="langLogo" class='langAvatar' alt="">
</template>
<el-menu-item index="1-1" @click="changeLocale('zh')">
<span class="intro">中文</span>
</el-menu-item>
<el-menu-item index="1-2" @click="changeLocale('en')">
<span class="intro">EngList</span>
</el-menu-item>
</el-submenu>
<el-submenu index="2" popper-class="infoItem">
<template slot="title">
<div class='welcome'>
<span class="name">{{$t('commons.hi')}},</span>
<span class='name avatarname'> {{ $t(`commons.${name}`)}}</span>
</div>
<img :src="avatar" class='avatar' alt="头像">
<!-- <img src="../assets/img/avatar.png" alt="头像" class="avatar"> -->
</template>
<el-menu-item index="2-1" @click="setDialogInfo('info')">{{ $t('commons.infoShow') }}</el-menu-item>
<el-menu-item index="2-2" @click="setDialogInfo('pass')">{{ $t('commons.infoModify') }}</el-menu-item>
<el-menu-item index="2-3" @click="setDialogInfo('logout')">{{ $t('commons.quit') }}</el-menu-item>
</el-submenu>
</el-menu>
</div>
</div>
</div>
</header>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import { setToken,getToken } from '@/utils/auth'
import wechatImg from "@/assets/img/wechat.jpg";
import qqImg from "@/assets/img/qq.png";
import logoImg from "@/assets/img/logo.png";
import chinaImg from "@/assets/img/china.svg";
import americaImg from "@/assets/img/america.svg";
import { github } from "@/utils/env";
export default {
name: 'head-nav',
data(){
return{
logo:logoImg,
langLogo: getToken('langLogo') || americaImg,
chinaImg:chinaImg,
americaImg:americaImg,
wechat:{
wechatImg:wechatImg,
isWechat:false
},
qq:{
qqImg:qqImg,
isQq:false,
},
github:github,
menu:{
userBgcolor:'#f0f2f5'
},
}
},
components:{
},
computed:{
...mapGetters(['name','avatar'])
},
created(){
},
mounted(){
},
methods:{
Wechat(){
this.wechat.isWechat = true;
},
hideWechat(){
this.wechat.isWechat = false;
},
showQq(){
this.qq.isQq = true;
},
hideQq(){
this.qq.isQq = false;
},
logout(){
this.$store.dispatch('LogOut').then(() => {
location.reload();
})
},
/**
* 弹出框-修改密码或者系统设置
* @param {string} cmditem 弹框类型
*/
setDialogInfo(cmditem) {
switch (cmditem) {
case 'info':
this.$router.push('/infoManage/infoShow/infoShow1');
break;
case 'pass':
this.$router.push('/infoManage/infoModify/infoModify1');
break;
case 'logout':
this.logout();
break;
}
},
// 切换语言
changeLocale(type){
setToken('lang',type);
this.$i18n.locale = type;
if(type === 'en'){
this.langLogo = this.americaImg;
}else{
this.langLogo = this.chinaImg;
}
setToken('langLogo',this.langLogo);
}
}
}
</script>
<style scoped lang='less'>
.head-nav {
position: fixed;
top: 0;
left: 0;
z-index: 15;
transition: width .2s;
justify-content: space-between;
height: 60px;
width:100%;
box-sizing: border-box;
background: #fff;
box-shadow:0px 2px 0px 0px rgba(230,224,224,0.5);
.logout {
vertical-align: middle;
cursor: pointer;
}
.left-nav{
flex: 7;
width:100%;
height: 60px;
align-items: center;
justify-content: flex-start;
text-transform: uppercase;
padding-left: 1em;
box-sizing: border-box;
.logo {
height: 36px;
width: 36px;
vertical-align: middle;
display: inline-block;
}
.closeLogo{
width:30px;
height:30px;
}
.title{
font-size: 22px;
i{
color:#FF6C60;
}
}
}
.right-nav{
display: flex;
flex: 3;
width:calc(100% + 180px);
// width: 100%;
margin-right: 15px;
justify-content: space-between;
.userinfo-right{
width:380px;
padding: 0px 10px;
justify-content: space-between;
display: flex;
.notify-row{
line-height:60px;
flex:1;
ul{
display: flex;
justify-content: space-around;
}
}
}
}
}
.middle{
align-items: center;
border:1px solid;
}
.userinfo {
line-height: 60px;
text-align:right;
}
.avatar{
width: 32px;
height: 32px;
border-radius: 50%;
vertical-align: middle;
display: inline-block;
}
.langAvatar{
width: 24px;
height: 24px;
border-radius: 50%;
vertical-align: middle;
display: inline-block;
}
.welcome{
display: inline-block;
vertical-align: middle;
padding: 0 5px;
.name{
line-height: 20px;
text-align: center;
font-size: 12px;
}
.avatarname{
color:#a9d86e;
font-weight:bolder;
font-size: 13px;
}
}
.username {
cursor: pointer;
margin-right: 5px;
}
ul.top-menu > li {
position: relative;
}
</style>
leftMenu.vue
侧变导航栏根据路由权限动态生成的,多层嵌套也不再话下。
// leftMenu.vue
<template>
<div class="menu_left cflex" :style="{width:sidebar.width+'px'}">
<div class="menu_page_list">
<el-menu
class="el_menu"
mode="vertical"
theme="dark"
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
:background-color="menuObj.bgColor"
:text-color="menuObj.textColor"
:active-text-color="menuObj.activeTextColor"
:style="{width:sidebar.width+'px'}"
>
<template v-for="(item,index) in permission_routers">
<!--表示 有一级菜单-->
<router-link v-if="!item.hidden && item.noDropdown" :to="item.path+'/'+item.children[0].path" :key="index">
<el-menu-item class="dropItem "
:index="item.path+'/'+item.children[0].path">
<icon-svg v-if="item.meta.icon" :icon-class="item.meta.icon" style="width:1.8em; height:1.8em; margin-left:0; margin-right:5px;" />
<span v-if="item.meta.title" slot="title">{{ $t(`commons.${item.name}`)}}</span>
</el-menu-item>
</router-link>
<!--表示 有二级或者多级菜单 -->
<el-submenu v-if="item.children && item.children.length >= 1 && !item.hidden && !item.noDropdown"
:index="item.path"
:key="index">
<template slot="title">
<icon-svg v-if="item.meta.icon" :icon-class="item.meta.icon" style="width:1.8em; height:1.8em;margin-left:0; margin-right:5px;" />
<span v-if="item.meta.title" slot="title">{{ $t(`commons.${item.name}`)}}</span>
</template>
<!--直接定位到子路由上,子路由也可以实现导航功能-->
<router-link v-for="(citem,cindex) in item.children" :to="getIindex(citem,item,cindex)" :key="cindex">
<el-menu-item
v-if="citem.meta.routerType != 'topmenu' && citem.meta.title"
:index="getIindex(citem,item,cindex)">
<span slot="title"> {{ $t(`commons.${citem.name}`)}}</span>
</el-menu-item>
</router-link>
</el-submenu>
</template>
</el-menu>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import logoImg from "@/assets/img/logo.png";
export default {
name: "left-Menu",
data() {
return {
menuObj:{
bgColor:'#fff',
textColor:'#666',
activeTextColor:'#ff6428',
},
logo:logoImg
};
},
computed:{
// 配置在store/modules/menu.js
...mapGetters([
'permission_routers',
'isCollapse',
'sidebar',
'menuIndex'
]),
},
created(){
},
mounted(){
},
methods: {
getIindex(citem,item,cindex){
return (citem.meta.titleList)?item.path+'/'+citem.path+'/'+citem.meta.titleList[0].path:item.path+'/'+citem.path;
}
}
};
</script>
<style lang="less" scoped>
@left-bgColor:#fff; // 左侧菜单背景颜色;
@icon-link:#FF6C60;
.menu_left{
border-top:4px solid rgb(70, 196, 125);
position: absolute;
top:60px;
left:0;
// bottom:0;
.menu_page_list {
width:100%;
overflow-y: auto;
overflow-x: hidden;
flex:1;
margin-top:5px;
z-index: 10;
box-shadow: 0 0 10px 0 rgba(230, 224, 224, 0.5);
}
}
& /deep/ .el-menu .dropItem {
padding: 0 px !important;
}
& /deep/.el-submenu {
padding: 0 !important;
}
& /deep/ .el-submenu__title {
padding: 0 20px !important;
}
</style>
footerNav.vue
// footerNav.vue
<template>
<div class='footer'>
<div class="intro cflex">
<p style="font-size:28px;margin:10px;">Contact with me</p>
<p class="rflex">
<span>E-mail</span>
<span>WeChat</span>
<span>github</span>
</p>
<p class="beian">Copyright ©202X Diary **ICP备********号</p>
</div>
</div>
</template>
<script>
export default {
name: "footerNav",
}
</script>
<style lang="less">
.footer{
width: 100%;
padding: 10px 0;
font-size:12px;
text-align: center;
background: #fff;
p{
line-height: 30px;
}
.intro{
width: 340px;
margin: 0 auto;
justify-content: space-between;
align-items: center;
span{
margin: 5px;
}
}
}
</style>
content.vue
// content.vue
<template>
<div class="">
<transition name="fade">
<router-view></router-view>
</transition>
</div>
</template>
<script>
export default {
name: 'content',
}
</script>
<style scoped lang='less'>
.fade-enter-active,
.fade-leave-active {
transition: opacity .3s
}
.fade-enter,
.fade-leave-active {
opacity: 0
}
</style>
home.vue
// home.vue
<template>
<div class="home rflex">
<head-nav class="head_nav" ></head-nav>
<div class="middle">
<left-menu class="menu_left"></left-menu>
<div class="menu_right el-scrollbar" ref="menu_right" :style="{left:sidebar.width+'px'}">
<div class="menu_content" ref="menu_content">
<bread></bread>
<router-view></router-view><!--页面渲染入口-->
</div>
<backTop :ele="$refs.menu_right"></backTop>
<footerNav class="footer_nav"></footerNav>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import HeadNav from './headNav.vue';
import LeftMenu from './leftMenu.vue';
import Bread from './bread.vue';
import FooterNav from './footerNav.vue';
import backTop from '../components//backTop';
export default {
name: 'home',
data(){
return {
}
},
computed:{
...mapGetters(['sidebar']),
},
components:{
HeadNav,
LeftMenu,
Bread,
FooterNav,
backTop
},
}
</script>
<style scoped lang='less'>
.home{
position: relative;
.middle{
.menu_right{
overflow:auto;
position: absolute;
right:0;
top:0;
// bottom:0;
background:#F6F7FC;
.menu_content{
position: relative;
// margin-top:60px;
width:100%;
background:#f0f2f5;
}
}
}
.footer_nav{
// bottom:0;
// position: relative;
// margin-bottom: 0;
}
}
& /deep/ .el-scrollbar::-webkit-scrollbar-thumb {
box-shadow:0px 1px 3px rgba(0,0,0,0.3) inset;
background-color:#FF6C60;
border-radius: 4px;
}
</style>
index.js
// index.js
import bread from './bread.vue'
import headNav from './headNav.vue'
import leftMenu from './leftMenu.vue'
import Layout from './home.vue'
import Content from './content.vue'
import footerNav from './footerNav.vue'
export {
Layout,
Content,
bread,
headNav,
leftMenu,
footerNav
}