【Vue】Vue项目实战2:布局Layout

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
}

代码地址

项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值