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 相关文章推荐
Jquery事件的连接使用示例
Jun 18 Javascript
js中document.write的那点事
Dec 12 Javascript
jQuery on()方法使用技巧详解
Apr 16 Javascript
javascript学习笔记之函数定义
Jun 25 Javascript
比较常见的javascript中定义函数的区别
Nov 09 Javascript
JavaScript继承学习笔记【新手必看】
May 10 Javascript
Ext JS动态加载JavaScript创建窗体的方法
Jun 23 Javascript
JS使用正则表达式过滤多个词语并替换为相同长度星号的方法
Aug 03 Javascript
原生Javascript插件开发实践
Jan 18 Javascript
vue实现按需加载组件及异步组件功能
May 27 Javascript
详解vue-router的Import异步加载模块问题的解决方案
May 13 Javascript
JS实现炫酷轮播图
Nov 15 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
php中ob(Output Buffer 输出缓冲)函数使用方法
2007/07/21 PHP
Thinkphp中Create方法深入探究
2014/06/16 PHP
定位地理位置PHP判断员工打卡签到经纬度是否在打卡之内
2019/05/23 PHP
php 比较获取两个数组相同和不同元素的例子(交集和差集)
2019/10/18 PHP
多浏览器支持的右下角浮动窗口
2010/04/01 Javascript
Javascript 类与静态类的实现
2010/04/01 Javascript
Jquery Ajax的Get方式时需要注意URL地方
2011/04/07 Javascript
Uglifyjs(JS代码优化工具)入门 安装使用
2020/04/13 Javascript
jQery使网页在显示器上居中显示适用于任何分辨率
2014/06/09 Javascript
探寻Javascript执行效率问题
2014/11/12 Javascript
javascript解析xml实现省市县三级联动的方法
2015/07/25 Javascript
js实现浮动在网页右侧的简洁QQ在线客服代码
2015/09/04 Javascript
JS中Eval解析JSON字符串的一个小问题
2016/02/21 Javascript
JS作用域深度解析
2016/12/29 Javascript
详解如何提高 webpack 构建 Vue 项目的速度
2017/07/03 Javascript
解决vue项目打包后提示图片文件路径错误的问题
2018/07/04 Javascript
javascript面向对象三大特征之封装实例详解
2019/07/24 Javascript
vuecli3.x中轻松4步带你使用tinymce的步骤
2020/06/25 Javascript
Python EOL while scanning string literal问题解决方法
2020/09/18 Python
Python的randrange()方法使用教程
2015/05/15 Python
python爬虫获取小区经纬度以及结构化地址
2018/12/30 Python
pycharm 激活码及使用方式的详细教程
2020/05/12 Python
python爬虫用mongodb的理由
2020/07/28 Python
Python如何实现Paramiko的二次封装
2021/01/30 Python
英国网上购买门:Direct Doors
2018/06/07 全球购物
FragranceNet中文网:北美健康美容线上零售商
2020/08/26 全球购物
请解释在new与override的区别
2012/10/29 面试题
留学生求职信
2014/06/03 职场文书
乡镇干部党的群众路线教育实践活动个人对照检查材料
2014/09/24 职场文书
群众路线问题查摆对照检查材料
2014/10/04 职场文书
小学优秀教师先进事迹材料
2014/12/16 职场文书
《风筝》教学反思
2016/02/23 职场文书
MySQL入门命令之函数-单行函数-流程控制函数
2021/04/05 MySQL
图解上海144收音机
2021/04/22 无线电
浅谈Python协程asyncio
2021/06/20 Python
聊聊CSS粘性定位sticky案例解析
2022/06/01 HTML / CSS