Vue管理系统前端系列四组件拆分封装
组件封装
在上一篇记录中,首页中有太多的代码,为了避免代码的臃肿,需要对主要的功能模块拆分,来让代码看起来更简洁,且能进行复用。
拆分后还加了些小功能,加入了修改 title 的代码,修改方式参考vue 动态修改 title。
还增加了当前请求的页面缓存,使用状态管理器处理。监听路由,保存到 state 中,来处理的。 如何监听可参考vue 计算属性和监听属性。
完整效果图如下:

首页布局拆分后结构
拆分后的,布局结构图:

拆分后代码
布局最外层 index 代码,使用头部,侧边栏,主内容栏组成,代码如下:
<!-- 布局的首页 -->
<template>
<div>
<l-header></l-header>
<l-aside></l-aside>
<l-main></l-main>
</div>
</template>
<script>
import LHeader from './components/header'
import LAside from './components/aside'
import LMain from './components/main'
export default {
data() {
return {}
},
//引入组件
components: {
LHeader,
LAside,
LMain,
},
}
</script>
<style lang="scss" scoped></style>
头部 index.vue 代码:
<!-- 头部文件 -->
<template>
<div class="header">
<!-- logo -->
<logo></logo>
<!-- 折叠按钮 -->
<hamburger></hamburger>
<!-- 头部导航栏 -->
<div class="heardNavBar">
<el-menu default-active="1" class="el-menu-demo" background-color="#4b5f6e" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
<el-menu-item index="1" @click="$router.push('/')">首页</el-menu-item>
<el-menu-item index="2" @click="openUrl('#')">使用文档</el-menu-item>
<el-menu-item index="3" @click="openUrl('https://github.com/levy-w-wang/lion-ui')">GitHub</el-menu-item>
</el-menu>
</div>
<!-- 右侧信息 -->
<div style="float:right">
<!-- 全屏 -->
<div style="float:left;line-height: 60px; padding: 0 10px;">
<i class="el-icon-full-screen" @click="toggleFull"></i>
</div>
<!-- 个人信息 -->
<div class="userinfo">
<el-dropdown trigger="hover">
<span class="el-dropdown-link userinfo-inner">
<img src="@assets/img/user.jpg" />
{{ $store.getters.userInfo.username }}<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>
<router-link to="/"><i class="el-icon-s-home"></i>首页</router-link>
</el-dropdown-item>
<el-dropdown-item>
<router-link to="/"><i class="el-icon-s-custom"></i>我的主页</router-link>
</el-dropdown-item>
<el-dropdown-item divided>
<a @click="loginOut()"><i class="el-icon-switch-button"></i>登出</a>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
</template>
<script>
import screenfull from 'screenfull'
import hamburger from './hamburger'
import logo from './logo'
// import { mapState } from 'vuex'
export default {
data() {
return {}
},
computed: {
// ...mapState({
// isCollapse: (state) => state.app.isCollapse,
// }),
},
//引入组件
components: {
hamburger,
logo,
},
// 方法
methods: {
openUrl(url) {
window.open(url)
},
loginOut() {
this.$confirm('确认退出吗?', '提示', {
type: 'warning',
})
.then(() => {
this.$store.commit('logout')
})
.catch(() => {})
},
toggleFull() {
if (!screenfull.isEnabled) {
this.$message({
type: 'warning',
message: 'you browser can not work',
})
return false
}
screenfull.toggle()
},
},
//未挂载DOM,不能访问ref为空数组
//可在这结束loading,还做一些初始化,实现函数自执行,
//可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。
created() {},
//可在这发起后端请求,拿回数据,配合路由钩子做一些事情;可对DOM 进行操作
mounted() {},
}
</script>
<style lang="scss" scoped>
.header {
padding-left: 0px !important;
height: 60px;
line-height: 60px;
width: 100%;
background: #4b5f6e;
color: #fff;
.heardNavBar {
float: left;
background: #4b5f6e;
padding: 0px 0px;
height: 60px;
line-height: 60px;
font-size: 28px;
cursor: pointer;
}
.userinfo {
text-align: right;
padding-right: 24px;
float: right;
padding: 0 10px;
.userinfo-inner {
font-size: 20px;
cursor: pointer;
color: #fff;
img {
width: 40px;
height: 40px;
border-radius: 10px;
margin: 10px 0px 10px 10px;
float: right;
}
}
}
}
</style>
头部中引用的相关组件代码如下
折叠导航栏 hamburger 下的 index.vue 代码:
<template>
<div @click="toggleCollapse">
<svg :class="{ 'is-active': !isCollapse }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
</svg>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'Hamburger',
computed: {
...mapState({
isCollapse: (state) => state.app.isCollapse,
}),
},
methods: {
//折叠导航栏
toggleCollapse: function () {
this.$store.commit('toggleCollapse')
},
},
}
</script>
<style scoped>
.hamburger {
padding-left: 13px;
padding-right: 13px;
text-align: center;
width: 34px;
height: 60px;
line-height: 60px;
float: left;
cursor: pointer;
}
.is-active {
transform: rotate(180deg);
}
</style>
折叠导航栏 logo 下的 index.vue 代码:
<!-- -->
<template>
<div class="logo" :class="isCollapse ? 'logo-collapse-width' : 'logo-width'">
<img v-if="isCollapse" src="@assets/logo6065.png" @click="$router.push('/')" />
<img v-else src="@assets/logo.png" @click="$router.push('/')" />
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {}
},
computed: {
...mapState({
isCollapse: (state) => state.app.isCollapse,
}),
},
}
</script>
<style lang="scss" scoped>
.logo {
float: left;
height: 60px;
padding: 0;
margin: 0;
}
.logo-width {
width: 230px;
}
.logo-collapse-width {
width: 65px;
}
</style>
侧边栏下的 index.vue代码:
<!-- aside -->
<template>
<div class="aside-container" :class="isCollapse ? 'aside-collapse-width' : 'aside-width'">
<!--导航菜单 default-active="1-1"-->
<el-menu class="el-menu-vertical-demo" :class="isCollapse ? 'aside-collapse-width' : 'aside-width'" :collapse-transition="false" :unique-opened="true" :collapse="isCollapse">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">系统管理</span>
</template>
<el-menu-item index="1-1" @click="$router.push('usermanage')">用户管理</el-menu-item>
<el-menu-item index="1-2" @click="$router.push('menumanage')">菜单管理</el-menu-item>
</el-submenu>
<el-menu-item index="2" disabled>
<i class="el-icon-magic-stick"></i>
<span slot="title">导航一</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-reading"></i>
<span slot="title">导航二</span>
</el-menu-item>
</el-menu>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {}
},
//$store.getters.isCollapse
computed: {
...mapState({
isCollapse: (state) => state.app.isCollapse,
}),
mainTabs: {
get() {
return this.$store.state.app.mainTabs
},
set(val) {
this.$store.commit('updateMainTabs', val)
},
},
mainTabsActiveName: {
get() {
return this.$store.state.app.mainTabsActiveName
},
set(val) {
this.$store.commit('updateMainTabsActiveName', val)
},
},
},
watch: {
$route: 'handleRoute',
},
created() {
console.log(this.$route)
this.handleRoute(this.$route)
},
methods: {
// 路由操作处理
handleRoute(route) {
// tab标签页选中, 如果不存在则先添加
var tab = this.mainTabs.filter((item) => item.name === route.name)[0]
if (!tab) {
tab = {
name: route.name,
title: route.meta.title,
icon: route.meta.icon,
}
this.mainTabs = this.mainTabs.concat(tab)
}
this.mainTabsActiveName = tab.name
},
},
}
</script>
<style lang="scss" scoped>
.aside-container {
position: fixed;
top: 0px;
left: 0;
bottom: 0;
z-index: 1020;
.el-menu {
position: absolute;
top: 60px;
bottom: 0px;
text-align: left;
}
}
.aside-width {
width: 230px;
}
.aside-collapse-width {
width: 65px;
}
</style>
内容模块下的 index.vue代码:
<!-- -->
<template>
<div class="main-container clear" :class="isCollapse ? 'position-collapse-left' : 'position-left'">
<!-- 标签页 -->
<el-tabs class="tabs" :class="isCollapse ? 'position-collapse-left' : 'position-left'" v-model="mainTabsActiveName" :closable="true" type="card" @tab-click="selectedTabHandle" @tab-remove="removeTabHandle">
<el-dropdown class="tabs-tools" :show-timeout="0" trigger="hover">
<div style="font-size:20px;width:50px;">
<i class="el-icon-arrow-down"></i>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="tabsCloseCurrentHandle">关闭当前标签</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseOtherHandle">关闭其它标签</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseAllHandle">关闭全部标签</el-dropdown-item>
<el-dropdown-item @click.native="tabsRefreshCurrentHandle">刷新当前标签</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-tab-pane v-for="item in mainTabs" :key="item.name" :label="item.title" :name="item.name">
<span slot="label"> <i :class="item.icon"></i> {{ item.title }} </span>
</el-tab-pane>
</el-tabs>
<!-- 主内容区域 -->
<div class="main-content">
<keep-alive>
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</keep-alive>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {}
},
computed: {
...mapState({
isCollapse: (state) => state.app.isCollapse,
}),
mainTabs: {
get() {
return this.$store.state.app.mainTabs
},
set(val) {
this.$store.commit('updateMainTabs', val)
},
},
mainTabsActiveName: {
get() {
return this.$store.state.app.mainTabsActiveName
},
set(val) {
this.$store.commit('updateMainTabsActiveName', val)
},
},
},
methods: {
// tabs, 选中tab
selectedTabHandle(tab) {
tab = this.mainTabs.filter((item) => item.name === tab.name)
if (tab.length >= 1) {
this.$router.push({ name: tab[0].name })
}
},
// tabs, 删除tab
removeTabHandle(tabName) {
// 当只有首页时,不允许关掉。 若是其它页面可关掉后,push 首页进去
if (this.mainTabs.length == 1 && this.mainTabs[0].name == 'index') {
return
}
this.mainTabs = this.mainTabs.filter((item) => item.name !== tabName)
if (this.mainTabs.length >= 1) {
// 当前选中tab被删除
if (tabName === this.mainTabsActiveName) {
this.$router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => {
this.mainTabsActiveName = this.$route.name
})
}
} else {
this.$router.push('/')
}
},
// tabs, 关闭当前
tabsCloseCurrentHandle() {
this.removeTabHandle(this.mainTabsActiveName)
},
// tabs, 关闭其它
tabsCloseOtherHandle() {
this.mainTabs = this.mainTabs.filter((item) => item.name === this.mainTabsActiveName)
},
// tabs, 关闭全部
tabsCloseAllHandle() {
this.mainTabs = []
this.$router.push('/')
},
// tabs, 刷新当前
tabsRefreshCurrentHandle() {
var tempTabName = this.mainTabsActiveName
this.removeTabHandle(tempTabName)
this.$nextTick(() => {
this.$router.push({ name: tempTabName })
})
},
},
}
</script>
<style lang="scss" scoped>
.main-container {
padding: 0 5px 5px;
position: absolute;
top: 60px;
left: 1px;
right: 1px;
bottom: 0px;
.tabs {
position: fixed;
top: 60px;
right: 50px;
padding-left: 0px;
padding-right: 2px;
z-index: 1020;
height: 40px;
line-height: 40px;
font-size: 14px;
background: rgb(255, 253, 255);
border-color: rgba(200, 206, 206, 0.5);
// border-left-width: 1px;
// border-left-style: solid;
border-bottom-width: 1px;
border-bottom-style: solid;
}
.tabs-tools {
position: fixed;
top: 60px;
right: 0;
z-index: 1020;
height: 40px;
// padding: 0 10px;
font-size: 14px;
line-height: 40px;
cursor: pointer;
border-color: rgba(200, 206, 206, 0.5);
border-left-width: 1px;
border-left-style: solid;
border-bottom-width: 1px;
border-bottom-style: solid;
background: rgba(255, 255, 255, 1);
}
.tabs-tools:hover {
background: rgba(200, 206, 206, 1);
}
.main-content {
position: absolute;
top: 45px;
left: 5px;
right: 5px;
bottom: 5px;
padding: 5px;
// background: rgba(209, 212, 212, 0.5);
}
}
.position-left {
left: 230px;
}
.position-collapse-left {
left: 65px;
}
</style>
状态管理中添加 app 模块
代码如下:
export default {
state: {
// 是否折叠导航栏
isCollapse: false,
// 访问页集合
mainTabs: [],
// 当前访问页名
mainTabsActiveName: '',
},
getters: {
isCollapse: (state) => {
return state.isCollapse
},
},
mutations: {
toggleCollapse(state) {
state.isCollapse = !state.isCollapse
},
updateMainTabs(state, tabs) {
state.mainTabs = tabs
},
updateMainTabsActiveName(state, name) {
state.mainTabsActiveName = name
},
},
actions: {},
}
当然还有一些小的调整点,可参考 git 上的提交版本 首页组件拆分
Vue管理系统前端系列四组件拆分封装的更多相关文章
- Vue管理系统前端系列一vue-cli4.x 初始化项目
目录 项目介绍 技术基础 开发环境 安装工具 快速原型开发 创建项目 配置相关说明 目录结构 项目介绍 lion-ui 是一个基于 RBAC 的管理系统前端项目,采用 vue 和 element-ui ...
- Vue管理系统前端系列二相关工具引入及封装
目录 sass-loader/vuex 等的引入说明 引入 element 引入 axios 1.基本使用 2.封装使用 2.1 开发环境配置请求地址 2.2 配置代理 2.3 添加接口相关文件 sa ...
- Vue管理系统前端系列三登录页和首页及`vuex`管理登录状态
目录 登录页面设计 vuex 对应 用户模块 丰富界面 首页相关代码 登录页面设计 该节记录了登录界面的设计,以及 vuex 的简单实用,然后将首页简单搭建完成. 先看最终效果图 先在 views 文 ...
- Vue管理系统前端系列六动态路由-权限管理实现
目录 为什么要使用动态路由? 主流的两种实现方式 前端控制 后端控制 后端控制路由 实现 添加菜单接口 及 菜单状态管理 根据得到的菜单生成动态路由 根据 vuex 中的暂存的菜单生成侧边菜单栏 退出 ...
- Vue管理系统前端系列五自定义主题
目录 自定义主题 1.安装「主题生成工具」 2.安装白垩主题 3.新建颜色挑选组件 自定义主题 1.安装「主题生成工具」 由于主题工具需要依赖于 node-sass,而node-sass版本兼容性并不 ...
- 【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)
前言 做这个 vueAdmin-template 的主要原因是: vue-element-admin 这个项目的初衷是一个vue的管理后台集成方案,把平时用到的一些组件或者经验分享给大家,同时它也在不 ...
- 辛巴学院-Unity-剑英陪你零基础学c#系列(四)函数和封装
辛巴学院:正大光明的不务正业. 国庆长假结束了,我的心情是这样的: 你总是起不早,起不早独自一个人沉睡到天亮你无怨无悔的梦着那副本我知道你根本就不想上班你总是起不早,起不早放假总是短暂,上班太难请个病 ...
- 循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便. ...
- 前端提升生产力系列三(vant3 vue3 移动端H5下拉刷新,上拉加载组件的封装)
| 在日常的移动端开发中,经常会遇到列表的展示,以及数据量变多的情况下还会有上拉和下拉的操作.进入新公司后发现移动端好多列表,但是在看代码的时候发现,每个列表都是单独的代码,没有任何的封装,都是通过v ...
随机推荐
- Spark 3.0 新特性 之 自适应查询与分区动态裁剪
Spark憋了一年半的大招后,发布了3.0版本,新特性主要与Spark SQL和Python相关.这也恰恰说明了大数据方向的两大核心:BI与AI.下面是本次发布的主要特性,包括性能.API.生态升级. ...
- Android复习准备
1. 四大组件是什么? Activity(活动):用于表现功能 Service(服务):后台运行服务,不提供界面呈现 BroadcastReceiver(广播接收器):用来接收广播 ContentPr ...
- 第十二章 类加载器&反射
12.1.类加载器 12.1.1.类加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载.类的连接.类的初始化这三个步骤来对类进行初始化.如果不出现意外情况,JVM将会连续完成 ...
- Fortify Audit Workbench 笔记 Password Management: Password in Configuration File(明文存储密码)
Password Management: Password in Configuration File(明文存储密码) Abstract 在配置文件中存储明文密码,可能会危及系统安全. Explana ...
- Alink漫谈(十五) :多层感知机 之 迭代优化
Alink漫谈(十五) :多层感知机 之 迭代优化 目录 Alink漫谈(十五) :多层感知机 之 迭代优化 0x00 摘要 0x01 前文回顾 1.1 基本概念 1.2 误差反向传播算法 1.3 总 ...
- Python os.pathconf() 方法
概述 os.pathconf() 方法用于返回一个打开的文件的系统配置信息.高佣联盟 www.cgewang.com Unix 平台下可用. 语法 fpathconf()方法语法格式如下: os.fp ...
- CF R 630 div2 1332 F Independent Set
LINK:Independent Set 题目定义了 独立集和边诱导子图.然而和题目没有多少关系. 给出一棵树 求\(\sum_{E'\neq \varnothing,E'\subset E}w(G( ...
- 如何让img自动适应div容器大小
IMG样式 (横向拉伸,纵向自动匹配大小) width:100%; height:auto; (纵向拉伸,横向自动匹配大小) width:auto; height:100%; DIV样式(元素居中显示 ...
- js数组常用api
数组创建 第一种,使用 Array 构造函数: var arr1 = new Array(); //创建一个空数组 var arr2 = new Array(10); // 创建一个包含10项的数组 ...
- NameNode中的高可用方案
NN中元数据的可靠性是可以保证的,但是其可用性并不高,因为Namenode是单节点的,所以一旦这个节点不能工作,那么整个hdfs都不能工作,但是由于SecondaryNameNode的机制,所以,即便 ...