2.使用ElementUI搭建前端系统框架二

通常的系统页面采用如下页面布局,在第一章完成项目的基础上,搭建如下前端页面组件实现下图网页区域分配。
在这里插入图片描述

拆分路由表

将路由组件实例与路由表分离

新建/src/router/staticRoutes.js


const staticRoute = [
    {
        path: '/',
        name: 'home',
        component: () => import('../gf/home')
    }
]

export default staticRoute

修改/src/router/index.js文件

import Vue from 'vue'
import Router from 'vue-router'
import staticRoute from './staticRoutes'

Vue.use(Router)

export default new Router({
  mode: 'hash',
  routes: staticRoute
})

新建页面排列组件

新建/src/gf/layout/HeaderPane.vue组件,它是最上层的Logo层

<template>
  <div class="sys-header">
    <slot name="logo"></slot>
    <div class="logo">
        <font color="red">{{orgname}}</font>-{{username}},欢迎回来!
    </div>

    <ul class="userInfo">
      <li class="icon element-icons lis_button" @click="logout">&#xe639;</li>
      <li>
        <b style="font-size:12pt;color:white;">{{username}}</b>
      </li>
    </ul>
  </div>
</template>

<style>

</style>
<script>

export default {
  data(){
    return{
      orgname:'古方红糖信息管理系统',
      username:'admin'
    };
  },
  methods: {
    logout() {
      
    }
  }
}
</script>

在这里插入图片描述
新建/src/gf/layout/LeftPane.vue组件,它位于左侧导航区域

<template>
    <div class="sidediv">
      <div @mouseenter="fn" class="floatdiv">
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo2.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo3.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo4.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo5.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo6.png" alt style="width:25px;" />
        </div>
      </div>
      <div class="fly" v-show="hidde" @mouseleave="leave()">
        <div style="height:25px"></div>
        <ul class="uls">
          <li v-for="(item,i) in arr" @click="fun(i)">{{item.name}}</li>
        </ul>
      </div>

      <div class="menudiv">
        <el-menu
          router
          ref="navbar"
          :default-active="defActive"
          :mode="navMode"
          menu-trigger="click"
          @select="selectMenu"
          @open="openMenu"
          @close="closeMenu"
          unique-opened>
          <div class="nav_css">
            <left-menu  v-for="(item, n) in navList" :item="item" :navIndex="String(n)" :key="n"></left-menu>
          </div>
        </el-menu>
      </div>
    </div>
</template>
<style scoped>
.sidediv{
  
}
.menudiv {
  z-index: -1;
  margin-left: 2.4rem;
  width: 7rem;
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto;
}
.nav_css{
  max-height:33rem;
  overflow: hidden;
  width: 9rem;
  overflow-y: scroll;
  text-align:left;
  line-height:33px;
  border:0;
}
.floatdiv {
  position: absolute;
  width: 2.4rem;
  height: 100%;
  background: #001529;
  float: left;
}
.fly {
  width: 7rem;
  height: 100%;
  float: left;
  background: #001529;
  position: absolute;
  left: 2.4rem;
  z-index: 100;
}
.uls li {
  width: 6rem;
  height: 40px;
  line-height: 40px;
  padding-left: 5px;
  margin-bottom: 14.5px;
  color: white;
  cursor:pointer;
}
</style>
<script>
import { mapState } from "vuex";
import LeftMenu from "./LeftMenu";

export default {
  data() {
    return {
      navBgShow: false,
      hidde: false,
      tagNavList: [],
      hometag: {},
      currenttag: {},
      arr: [
        { name: "待办工作" },
        { name: "关闭页签" },
        { name: "导航切换" }
      ],
      navList: [{name:'系统管理',url:'',child:[{name:'用户管理',visible:true,url:'/gf/home',child:[]}]}]
    };
  },
  props: ["layout"],
  computed: {
    defActive() {
      return this.$route.path;
    },
    navMode() {
      if (this.layout == "left") {
        return "vertical";
      }
      if (this.layout == "top") {
        return "horizontal";
      }
    },
    isDark() {
      return true;
    }
  },
  watch: {
    $route() {

    }
  },
  methods: {
    fn() {
      this.hidde = !this.hidde;
    },
    leave() {
      this.hidde = false;
    },
    fun(i) {
      if (i == 0) {
        this.$router.push("/home");
        this.hidde = false;
      }else if (i == 1) {
         this.closeAllTag();
      }else if (i == 2) {
         this.changeMenu();
      }

      // this.$router.push('/'+this.arr.url[i])
    },
    selectMenu(index, indexPath) {
      let openedMenus = this.$refs.navbar.openedMenus;
      let openMenuList;
      if (indexPath.length > 1) {
        let parentPath = indexPath[indexPath.length - 2];
        openMenuList = openedMenus.slice(openedMenus.indexOf(parentPath) + 1);
      } else {
        openMenuList = openedMenus;
      }
      openMenuList = openMenuList.reverse();
      openMenuList.forEach(ele => {
        this.$refs.navbar.closeMenu(ele);
      });
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    },
    openMenu() {
      if (this.navMode == "horizontal") {
        this.navBgShow = true;
      }
    },

    closeAllTag() {
      this.hometag={};
      this.currenttag={};
      this.tagNavList = this.$store.state.tagNav.openedPageList;

      this.tagNavList.forEach(item =>{
          if (item.path == '/home'){
            this.hometag = item;
          }

          if (item.path == this.$route.path){
            this.currenttag = item;
          }
      })

      this.$store.commit("tagNav/removeTagNav", null);
      this.$store.commit("tagNav/addTagNav", this.hometag);
      this.$store.commit("tagNav/addTagNav", this.currenttag);
    },

    changeMenu() {
      if (this.isBig) {
        this.isBig= false;
      }else{
        this.isBig= true;
      }

      if (this.isSmall) {
        this.isSmall= false;
      }else{
        this.isSmall= true;
      }
    },

    closeMenu() {
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    },
    closeAll() {
      let openMenu = this.$refs.navbar.openedMenus.concat([]);
      openMenu = openMenu.reverse();
      openMenu.forEach(ele => {
        this.$refs.navbar.closeMenu(ele);
      });
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    }
  },
  components: { LeftMenu }
};
</script>

LeftPane组件采用标准VUE语法写法
在这里插入图片描述
LeftPane组件中引用了组件LeftMenu,需要定义LeftMenu.vue组件
在这里插入图片描述

LeftMenu.vue文件

<template>
    <!-- 如果菜单包含子菜单,进一步调用LeftMenu组件循环展开 -->
    <el-submenu v-if="item.child && item.child.length" :index="navIndex"> 
        <!-- 显示父菜单名称 -->
        <template slot="title"><i v-if="item.icon" class="item.icon"></i>{{ item.name }}</template>
        <!-- 调用LeftMenu组件循环展开 -->
        <left-menu v-for="(subItem,i) in item.child" :key="navIndex+'-'+i" :navIndex="navIndex+'-'+i" :item="subItem" ></left-menu>
    </el-submenu>

    <!-- 如果菜单不包括子菜单,只显示主菜单,其中visible属性决定是否显示 -->
    <el-menu-item v-else-if="item.visible" :index="item.path" :route="{path: item.url}"><i v-if="item.icon" class="item.icon"></i>{{ item.name }}</el-menu-item>
</template>
<script>
export default {
    name: 'LeftMenu',
    props: ['item','navIndex']
}
</script>

定义全局的组件main.vue,作为布局管理全局组件,在路由表中引入

<template>
    <div class="wrapper">
        <template>
            <!-- 
              引用HeaderPane组件 
              其中slot=logo将显示在HeaderPane中的 <slot name="logo"></slot> 根据名称匹配
            -->
            <header-pane v-once>
                <p slot="logo"><img src="~static/images/logo.png"></p>
            </header-pane>
            <!-- 引入左侧组件 -->
            <left-pane></left-pane>
        </template>

    </div>
</template>

<style>
  .el-container{
    top:0px;
  }
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }
  
  .el-aside {
    color: #333;
    overflow-y: auto;
  }
</style>

<script>
import HeaderPane from './HeaderPane'
import LeftPane from './LeftPane'

export default {
    mounted() {

    },
    computed: {
        tagNavList(){
            return [];
        }
    },
    components:{
      HeaderPane,LeftPane
    }
}
</script>

重新定义路由表

const Layout = () => import('../gf/layout/main')

const staticRoute = [
    {
        path: '/gf',
        component: Layout,
        children: [
            {
                path: 'home',
                component: () => import('../gf/home'),
            },
        ]
    }
]

export default staticRoute

当访问http://localhost:8080/#/gf/home时,将Home组件加载到Layout组件上
在这里插入图片描述
将上部的Logo去掉
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

定义右侧办公区域

右边办公区域分为上下两部分,上面定义TabPane组件,下面是路由组件route-view

TabPane.vue

<template>
    <div class="tag-nav">
        <tab-scroll-bar ref="scrollBar">
            <router-link ref="tag" class="tag-nav-item"
                :class="isActive(item) ? 'cur' : ''" v-for="(item, index) in openedList" 
                :to="item.path" :key="index">
                <span class='ivu-tag-dot-inner'></span>
                    {{item.title}}
                <span class='el-icon-close' @click.prevent.stop="closePage(item, index)"></span>
            </router-link>
        </tab-scroll-bar>
    </div>
</template>

<script>
import TabScrollBar from './TabScrollBar'

export default {
    data(){
        return {
            defaultPage: '/gf/home'
        }
    },
    computed: {
        openedList(){
            return [{title:'用户管理',path:'/gf/home'}];
        }
    },
    mounted(){
        //this.openPage()
    },
    watch: {
        $route(){
            this.openPage()
            this.scrollToCurPage()
        }
    },
    methods: {
        openPage(){
            if(this.$router.getMatchedComponents()[1])
            {
                this.$store.commit("auth/addPage", {
                    name: this.$router.getMatchedComponents()[1].name,
                    path: this.$route.path,
                    title: this.$route.meta.name
                })
            }
        },
        isActive(item){
            return item.path === this.$route.path
        },
        closePage(item, index){
            this.$store.commit("auth/removePage", item)
            if(this.$route.path == item.path){
                if(index){
                    this.$router.push(this.openedList[index-1].path)
                } else {
                    this.$router.push(this.defaultPage)
                    if(this.$route.path == "/gf/home"){
                        this.openPage()
                    }
                }
            } 
        },
        scrollToCurPage(){
            this.$nextTick(() =>{
                for (let item of this.$refs.tag) {
                    if (item.to === this.$route.path) {
                        this.$refs.scrollBar.scrollToCurPage(item.$el)
                        break
                    }
                }
            })
        }
    },
    components: {TabScrollBar}
}
</script>

默认打开的Tab页签使用静态计算属性定义,这个需要动态参数化

    computed: {
        openedList(){
            return [{title:'用户管理',path:'/gf/home'}];
        }
    },

其中引用了TabScrollBar组件,控制Tab页签排列。

import TabScrollBar from './TabScrollBar'
components: {TabScrollBar}

TabScrollBar.vue

<template>
    <div class="scroll-wrap" ref="scrollWrap" @wheel.prevent="scroll">
        <div class="scroll-cont" ref="scrollCont" :style="{left: left + 'px'}">
            <slot></slot>
        </div>
    </div>
</template>

<style lang="scss" scoped>
.scroll-wrap{
    position: relative;
    top:0px;
    width: 100%;
    height: 100%;
    white-space: nowrap;
}
.scroll-cont{
    position: absolute;
    transition: all .3s ease;
}
</style>

<script>
export default {
    data(){
        return {
            left: 0,
            wheelSpeed: 30,
        }
    },
    mounted(){

    },
    methods: {
        scroll(e){
            const scrollWrapWidth = this.$refs.scrollWrap.offsetWidth
            const scrollContWidth = this.$refs.scrollCont.offsetWidth
            if(scrollContWidth > scrollWrapWidth){
                const scrollSpace = e.deltaY > 0 ? -1 * this.wheelSpeed : this.wheelSpeed
                if(e.deltaY > 0){ 
                    if(Math.abs(this.left + scrollSpace) <= (scrollContWidth - scrollWrapWidth)){
                        this.left += scrollSpace
                    }
                } else {
                    if(this.left + scrollSpace < 0){
                        this.left += scrollSpace
                    } else {
                        this.left = 0
                    }
                }
            } else {
                return
            }
        },
        scrollToCurPage(tar){
            const scrollWrapWidth = this.$refs.scrollWrap.offsetWidth
            const tarWidth = tar.offsetWidth
            const tarLeft = tar.offsetLeft
            
            if(tarLeft < -1 * this.left){
                this.left = -tarLeft
            } else if(tarLeft + tarWidth > scrollWrapWidth){
                this.left = -(tarLeft + tarWidth - scrollWrapWidth)
            }
        }
    }
}
</script>


修改全局组件main.vue,在其中加入TabPane组件。
在这里插入图片描述

<template>
    <div class="wrapper">
        <template>
            <!-- 
              引用HeaderPane组件 
              其中slot=logo将显示在HeaderPane中的 <slot name="logo"></slot> 根据名称匹配
            -->
            <header-pane v-once>
                <p slot="logo"><img src="~static/images/logo.png"></p>
            </header-pane>
            <!-- 引入左侧组件 -->
            <left-pane></left-pane>
        </template>
        <div class="sys-content">
            <tab-pane></tab-pane>
            <keep-alive :include="tagNavList">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>

<style>
  .el-container{
    top:0px;
  }
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }
  
  .el-aside {
    color: #333;
    overflow-y: auto;
  }
</style>

<script>
import HeaderPane from './HeaderPane'
import LeftPane from './LeftPane'
import TabPane from './TabPane'

export default {
    mounted() {

    },
    computed: {
        tagNavList(){
            return [];
        }
    },
    components:{
      HeaderPane,LeftPane,TabPane
    }
}
</script>
import HeaderPane from './HeaderPane'
import LeftPane from './LeftPane'
import TabPane from './TabPane'

    components:{
      HeaderPane,LeftPane,TabPane
    }

访问页面效果为
在这里插入图片描述
下一步需要参数化和事件响应控制

下载前端代码gfvue_v1.2.zip
链接:https://pan.baidu.com/s/1IOpuw-mx1BcnTd-gfftLsw
提取码:d5ba

发布了189 篇原创文章 · 获赞 34 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qixiang_chen/article/details/104423771
今日推荐