组件封装
为了避免组件代码的臃肿,这里对主要的功能部件进行封装,保证代码的模块化和简洁度。
组件结构
组件封装重构后,试图组件结构如下图所示
代码一览
Home组件被简化,包含导航、头部和主内容三个组件。
Home.vue
<template> <div class="container"> <!-- 导航菜单栏 --> <MenuBar></MenuBar> <!-- 头部区域 --> <HeadBar></HeadBar> <!-- 主内容区域 --> <Main></Main> </div> </template> <script> import HeadBar from "./HeadBar/HeadBar" import MenuBar from "./MenuBar/MenuBar" import Main from "./Main/Main" export default { components:{ HeadBar, MenuBar, Main } }; </script> <style scoped lang="scss"> .container { position:absolute; top: 0px; left: 0px; right: 0px; background: #4b5f6e; } </style>
HeadBar.vue
<template> <div class="container"> <!-- 导航菜单隐藏显示切换 --> <span class="collapse-switcher" @click.prevent="collapse"> <i class="el-icon-menu"></i> </span> <!-- 导航菜单 --> <span class="nav-bar"> <el-menu :default-active="activeIndex" class="el-menu-demo" text-color="#fff" active-text-color="#ffd04b" mode="horizontal" @select="selectNavBar()"> <el-menu-item index="1" @click="$router.push('/')">{{$t("common.home")}}</el-menu-item> <el-menu-item index="2">{{$t("common.doc")}}</el-menu-item> <el-menu-item index="3">{{$t("common.msgCenter")}}</el-menu-item> </el-menu> </span> <span class="tool-bar"> <!-- 主题切换 --> <ThemePicker class="theme-picker"></ThemePicker> <!-- 语言切换 --> <LangSelector class="lang-selector"></LangSelector> <!-- 用户信息 --> <el-dropdown class="user-info-dropdown" trigger="hover"> <span class="el-dropdown-link"><img :src="this.userAvatar" /> {{username}}</span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>{{$t("common.myMsg")}}</el-dropdown-item> <el-dropdown-item>{{$t("common.config")}}</el-dropdown-item> <el-dropdown-item divided @click.native="logout">{{$t("common.logout")}}</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </span> </div> </template> <script> import mock from "@/mock/index.js"; import ThemePicker from "@/components/ThemePicker" import LangSelector from "@/components/LangSelector" export default { components:{ ThemePicker, LangSelector }, data() { return { isCollapse: false, username: "Louis", userAvatar: "", activeIndex: '1' }; }, methods: { selectNavBar(key, keyPath) { console.log(key, keyPath) }, // 语言切换 handleCommand(command) { let array = command.split(':') let lang = array[0] === '' ? 'zh_cn' : array[0] let label = array[1] document.getElementById("language").innerHTML = label this.$i18n.locale = lang }, //折叠导航栏 collapse: function() { this.isCollapse = !this.isCollapse; }, //退出登录 logout: function() { var _this = this; this.$confirm("确认退出吗?", "提示", { type: "warning" }) .then(() => { sessionStorage.removeItem("user"); this.$router.push ("/login"); }) .catch(() => {}); } }, mounted() { this.sysName = "I like Kitty"; var user = sessionStorage.getItem("user"); if (user) { this.userName = user; this.userAvatar = require("@/assets/user.png"); } } }; </script> <style scoped lang="scss"> .container { position: absolute; left: 200px; right: 0px; height: 60px; line-height: 60px; .collapse-switcher { 40px; float: left; cursor: pointer; border-color: rgba(111, 123, 131, 0.8); border-left- 1px; border-left-style: solid; border-right- 1px; border-right-style: solid; color: white; background: #504e6180; } .nav-bar { margin-left: auto; float: left; .el-menu { background: #504e6180; } } .tool-bar { float: right; .theme-picker { padding-right: 10px; } .lang-selector { padding-right: 10px; font-size: 15px; color: #fff; cursor: pointer; } .user-info-dropdown { font-size: 20px; padding-right: 20px; color: #fff; cursor: pointer; img { 40px; height: 40px; border-radius: 10px; margin: 10px 0px 10px 10px; float: right; } } } } </style>
MenuBar.vue
<template> <div class="menu-bar-container"> <!-- logo --> <div class="logo" :class="isCollapse?'menu-bar-collapse-width':'menu-bar-width'"> <img :src="this.logo" /> <div>{{isCollapse?'':sysName}}</div> </div> <!-- 导航菜单 --> <el-menu default-active="1-1" :class="isCollapse?'menu-bar-collapse-width':'menu-bar-width'" @open="handleopen" @close="handleclose" @select="handleselect" :collapse="isCollapse"> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span slot="title">{{$t("sys.sysMng")}}</span> </template> <el-menu-item index="1-1" @click="$router.push('user')">{{$t("sys.userMng")}}</el-menu-item> <el-menu-item index="1-2" @click="$router.push('dept')">{{$t("sys.deptMng")}}</el-menu-item> <el-menu-item index="1-3" @click="$router.push('role')">{{$t("sys.roleMng")}}</el-menu-item> <el-menu-item index="1-4" @click="$router.push('menu')">{{$t("sys.menuMng")}}</el-menu-item> <el-menu-item index="1-5" @click="$router.push('log')">{{$t("sys.logMng")}}</el-menu-item> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span slot="title">{{$t("sys.sysMonitor")}}</span> </template> </el-submenu> <el-menu-item index="3" disabled> <i class="el-icon-document"></i> <span slot="title">{{$t("sys.nav3")}}</span> </el-menu-item> <el-menu-item index="4"> <i class="el-icon-setting"></i> <span slot="title">{{$t("sys.nv4")}}</span> </el-menu-item> </el-menu> </div> </template> <script> export default { data() { return { isCollapse: false, sysName: "", logo: "", }; }, methods: { handleopen() { console.log('handleopen'); }, handleclose() { console.log('handleclose'); }, handleselect(a, b) { console.log('handleselect'); } }, mounted() { this.sysName = "I like Kitty"; this.logo = require("@/assets/logo.png"); } }; </script> <style scoped lang="scss"> .menu-bar-container { .el-menu { position:absolute; top: 60px; bottom: 0px; text-align: left; } .logo { position:absolute; top: 0px; height: 60px; line-height: 60px; background: #4b5f6e; img { 40px; height: 40px; border-radius: 0px; margin: 10px 10px 10px 10px; float: left; } div { font-size: 22px; color: white; text-align: left; } } .menu-bar-width { 200px; } .menu-bar-collapse-width { 65px; } } </style>
Main.vue
<template> <div class="container"> <el-breadcrumb separator="/" class="breadcrumb"> <el-breadcrumb-item v-for="item in $route.matched" :key="item.path"> <a href="www.baidu.com">{{ item.name }}</a> </el-breadcrumb-item> </el-breadcrumb> <transition name="fade" mode="out-in"> <router-view></router-view> </transition> </div> </template> <script> export default { data() { return { }; }, methods: { }, mounted() { } }; </script> <style scoped lang="scss"> .container { position: absolute; top: 60px; bottom: 0px; left: 200px; right: 0px; .breadcrumb { padding: 10px; border-color: rgba(38, 86, 114, 0.2); border-bottom- 1px; border-bottom-style: solid; background: rgba(138, 158, 170, 0.2); } } </style>
国际化语言切换也被封装成为了组件 LangSelector
LangSelector/index.js
<template> <el-dropdown class="lang-selector" @command="handleCommand"> <span class="el-dropdown-link"> <span id="language">中文</span><i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item command="zh_cn:中文">中文</el-dropdown-item> <el-dropdown-item command="en_us:English">English</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </template> <script> export default { methods: { // 语言切换 handleCommand(command) { let array = command.split(':') let lang = array[0] === '' ? 'zh_cn' : array[0] let label = array[1] document.getElementById("language").innerHTML = label this.$i18n.locale = lang } } } </script>
组件封装重构之后,同步修改路由配置
import Vue from 'vue' import Router from 'vue-router' import Login from '@/views/Login' import NotFound from '@/views/404' import Home from '@/views/Home' import Intro from '@/views/Intro' import User from '@/views/SysMng/User' import Dept from '@/views/SysMng/Dept' import Role from '@/views/SysMng/Role' import Menu from '@/views/SysMng/Menu' import Log from '@/views/SysMng/Log' Vue.use(Router) const router = new Router({ routes: [ { path: '/', name: '首页', component: Home, children: [ { path: '', component: Intro, name: '系统介绍' }, { path: '/user', component: User, name: '用户管理' }, { path: '/dept', component: Dept, name: '机构管理' }, { path: '/role', component: Role, name: '角色管理' }, { path: '/menu', component: Menu, name: '菜单管理' }, { path: '/log', component: Log, name: '日志管理' } ] }, { path: '/login', name: '登录', component: Login } ,{ path: '/404', name: 'notFound', component: NotFound } ] }) router.beforeEach((to, from, next) => { // 登录界面登录成功之后,会把用户信息保存在会话 // 存在时间为会话生命周期,页面关闭即失效。 let user = sessionStorage.getItem('user'); if (to.path == '/login') { // 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页 if(user) { next({ path: '/' }) } else { next() } } else { // 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面 if (!user) { next({ path: '/login' }) } else { next() } } }) export default router
测试效果
封装重构之后,启动界面,效果跟之前差别不大。