Vue管理系统前端之组件拆分封装详解


Posted in Javascript onAugust 23, 2020

组件封装

在上一篇记录中,首页中有太多的代码,为了避免代码的臃肿,需要对主要的功能模块拆分,来让代码看起来更简洁,且能进行复用。

拆分后还加了些小功能,加入了修改 title 的代码,修改方式参考vue 动态修改 title。

还增加了当前请求的页面缓存,使用状态管理器处理。监听路由,保存到 state 中,来处理的。 如何监听可参考vue 计算属性和监听属性。

完整效果图如下:

Vue管理系统前端之组件拆分封装详解

首页布局拆分后结构

拆分后的,布局结构图:

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组件拆分封装内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
Apr 10 Javascript
Jquery+WebService 校验账号是否已被注册的代码
Jul 12 Javascript
25个优雅的jQuery Tooltip插件推荐
May 25 Javascript
JS获取客户端IP地址、MAC和主机名的7个方法汇总
Jul 21 Javascript
jQuery中的read和JavaScript中的onload函数的区别
Aug 27 Javascript
判断是否存在子节点的实现代码
May 18 Javascript
浅谈js中的延迟执行和定时执行
May 31 Javascript
全面了解构造函数继承关键apply call
Jul 26 Javascript
简单谈谈关于Angular Cli打包的事
Sep 05 Javascript
从0到1构建vueSSR项目之路由的构建
Mar 07 Javascript
Javascript读取上传文件内容/类型/字节数
Apr 30 Javascript
原生JavaScript实现弹幕组件的示例代码
Oct 12 Javascript
Vue中keep-alive组件的深入理解
Aug 23 #Javascript
google广告之另类js调用实现代码
Aug 22 #Javascript
JS typeof fn === 'function' &amp;&amp; fn()详解
Aug 22 #Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
Aug 22 #Javascript
JavaScript中的函数式编程详解
Aug 22 #Javascript
微信小程序中data-key属性之数据传输(经验总结)
Aug 22 #Javascript
Vue 电商后台管理项目阶段性总结(推荐)
Aug 22 #Javascript
You might like
PHP4引用文件语句的对比
2006/10/09 PHP
php数字游戏 计算24算法
2012/06/10 PHP
PHP入门教程之操作符与控制结构流程详解
2016/09/09 PHP
在IE模态窗口中自由查看HTML源码的方法
2007/03/08 Javascript
node.js 一个简单的页面输出实现代码
2012/03/07 Javascript
input链接页面、打开新网页等等的具体实现
2013/12/30 Javascript
用console.table()调试javascript
2014/09/04 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
jQuery如何封装输入框插件
2016/08/19 Javascript
浅析vue component 组件使用
2017/03/06 Javascript
详解在Angularjs中ui-sref和$state.go如何传递参数
2017/04/24 Javascript
深入理解Commonjs规范及Node模块实现
2017/05/17 Javascript
ReactNative列表ListView的用法
2017/08/02 Javascript
bootstrap table sum总数量统计实现方法
2017/10/29 Javascript
解决vue-quill-editor上传内容由于图片是base64的导致字符太长的问题
2018/08/20 Javascript
分享5个顶级的JavaScript Ajax组件库
2018/09/16 Javascript
详解JavaScript 新语法之Class 的私有属性与私有方法
2019/04/23 Javascript
vue+element实现表单校验功能
2019/05/20 Javascript
JS数组降维的实现Array.prototype.concat.apply([], arr)
2020/04/28 Javascript
解决Antd Table表头加Icon和气泡提示的坑
2020/11/17 Javascript
[01:44]《为梦想出发》—联想杯DOTA2完美世界全国高校联赛
2015/09/30 DOTA
Python网络爬虫与信息提取(实例讲解)
2017/08/29 Python
Python 多进程原理及实现
2020/12/21 Python
亚洲领先的旅游体验市场:Voyagin
2019/11/23 全球购物
化学相关工作求职信
2013/10/02 职场文书
寒假家长评语大全
2014/04/16 职场文书
2014年三万活动总结
2014/04/26 职场文书
阳光体育活动总结
2014/04/30 职场文书
美化环境标语
2014/06/20 职场文书
医院标语大全
2014/06/23 职场文书
2015年机关作风和效能建设工作总结
2015/07/23 职场文书
七年级上册生物的课件
2019/08/07 职场文书
如何使用Python实现一个简易的ORM模型
2021/05/12 Python
Spring Boot 排除某个类加载注入IOC的操作
2021/08/02 Java/Android
Vue OpenLayer测距功能的实现
2022/04/20 Vue.js