2.使用ElementUI搭建前端系统框架二
程序员文章站
2022-05-10 21:02:41
...
通常的系统页面采用如下页面布局,在第一章完成项目的基础上,搭建如下前端页面组件实现下图网页区域分配。
拆分路由表
将路由组件实例与路由表分离
新建/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"></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
上一篇: LINUX好用到爆的常用命令(上)