详解Vue后台管理系统开发日常总结(组件PageHeader)


Posted in Javascript onNovember 01, 2019

在后台管理系统的日常开发过程中发现对于同一个业务下面的版块不同的开发同事每次都会重复写页面标题的样式,而且不同的页面标题还不太一样。虽然有的页面标题栏承载的元素不一样,但是也有通用的部分,经过多个项目的迭代慢慢地总结与积累完善出了一个通用的页面标题组件<PageHeader/>

下面是一个最常见的标题设计原型:

详解Vue后台管理系统开发日常总结(组件PageHeader)

下面是同事给出的封装方案:

使用方式
<router-back class="router-back" text="详情" />
组件封装代码片段
<template>
 <div>
  <a
   href="javascript:void(0)" rel="external nofollow" 
   :title="title"
   size="15px"
   class="font-icon arrow-left"
   @click="back"
   v-if="!disableRoute"
  ></a>
  <span
   v-show="text.length > 0 && !disableRoute"
   class="vertical-line"
  ></span>
  <span class="text">{{ text }}</span>
 </div>
</template>
<script>
export default {
 name: 'router-back',
 props: {
  text: {
   type: String,
   default: _ => ''
  },
  url: {
   type: [String, Number],
   default: _ => -1
  },
  title: {
   type: String,
   default: _ => '返回'
  },
  disableRoute: {
   type: Boolean,
   default: _ => false
  }
 },
 methods: {
  back () {
   if (typeof this.url === 'number') return this.$router.go(this.url)
   return this.$router.push(this.url)
  }
 }
}
</script>

无对比就没有伤害,这个封装只是争对了单一的情况,并没有任何扩展性和灵性性,而且在组件方法名称和接收的属性上有待考究。所以我果断弃用这个组件,而选择自己的解决方案,虽然也不是很完美,代码质量上相比也没有什么大的改进,但是自我认为还是可以分享一下。

不多废话,先看实际效果图:

详解Vue后台管理系统开发日常总结(组件PageHeader)

注意:截图是在Chrome中缩小后截下的,并不是默认大小。

整个组件是通过Vue组件JSX方式写法来实现的,我的代码质量一般,实现上不一定是最佳的,但是我有点纳闷我一个同事总是说我的多套了一些标签,说:pageHeader还需要优化,减少标签嵌套。下面是实现代码:

import './pageHeader.scss'

const PageHeader = {
 name: 'PageHeader',

 props: {
  // 标题
  title: String,

  // 子标题
  subTitle: String,

  // 返回路径,不适用于带选项卡标题
  path: {
   type: [String, Number],
   default: -1
  },

  // 是否显示回退按钮
  withPath: {
   type: Boolean,
   default: false
  },

  // 子标题显示位置 'right' | 'bottom', 不适用于带选项卡标题
  position: {
   type: String,
   default: 'right'
  },

  // 带选项卡标题开关
  withTab: {
   type: Boolean,
   default: false
  },

  // 选项卡是否引起路由改变
  isRoute: {
   type: Boolean,
   default: false
  },

  // 当前激活选项卡
  activeTab: {
   type: String,
   default: ''
  },

  // 选项卡数据
  options: {
   type: Array,
   default() {
    return [
     {
      title: '',
      field: '',
      path: ''
     }
    ]
   }
  }
 },

 computed: {
  isBottom() {
   return this.position === 'bottom'
  },

  curTab: {
   get: function() {
    return this.activeTab
   },

   set: function(val) {
    if (this.activeTab !== val) {
     if (this.isRoute) {
      this.options.forEach(option => {
       if (option.field === tab) {
        this.$router.push(option.path)
        this.$emit('tabChange', val)
       }
      })
     } else {
      this.$emit('tabChange', val)
     }
    }
   }
  }
 },

 methods: {
  goBack() {
   typeof this.path === 'string'
    ? this.$router.push(this.path)
    : this.$router.go(this.path)
  }
 },

 render(h) {
  const Back = (
   <div class="page-header__back">
    <el-button
     type="text"
     class="page-header__action"
     icon="el-icon-back"
     onClick={this.goBack}
    />
    <span class="page-header__separator mx__m" />
   </div>
  )

  const Header = (
   <div class="page-header-wrap">
    <div class="page-header__main">
     {this.withPath && Back}

     <div class="page-header__title">
      {(this.title || this.$slots.title) && (
       <div
        class={`page-header-title__main ${this.isBottom ? '' : 'fl'}`}
       >
        {this.$slots.title ? this.$slots.title : this.title}
       </div>
      )}

      {(this.subTitle || this.$slots.subTitle) && (
       <div
        class={`page-header-title__sub ${
         this.isBottom ? 'lh__14' : 'fl ml__s'
        }`}
       >
        {this.$slots.subTitle ? this.$slots.subTitle : this.subTitle}
       </div>
      )}
     </div>
    </div>
    {this.$slots.action && (
     <div class={`page-header__aside ${this.isBottom ? 'lh__72' : ''}`}>
      {this.$slots.action}
     </div>
    )}
   </div>
  )

  const TabHeader = (
   <div class="page-header-wrap--tab">
    <div class="page-header-tab__main">
     <el-tabs v-model={this.curTab}>
      {this.options.map(option => (
       <el-tab-pane label={option.title} name={option.field} />
      ))}
     </el-tabs>
    </div>

    {this.$slots.extra && (
     <div class="page-header-tab__extra">{this.$slots.extra}</div>
    )}
   </div>
  )

  return (
   <div class={`page-header ${this.isBottom ? 'pt__20' : 'py__20'}`}>
    {this.withTab ? TabHeader : Header}
   </div>
  )
 }
}

export default PageHeader

上面的代码在实现上之前没见有考虑到通过this.$router.go(-1)回到上一个页面,而是直接采用this.$router.push(path),这种需要传path的方式,后来看了最前面同事写的方案后借鉴过来,改进了一下。这个代码实现很简单没有什么需要讲的,下面是组件使用的实际例子,当然如果能写个单元测试文件来测试组件更好,但是我Jest只停留在入门水平,平时也就写些最简单的assert,然后过代码覆盖率。

由于代码在处理选项卡时,并没有对额外的插槽extra作处理,所以在使用时需要在对应的标签上模拟一下<el-tabs/>下面的线。这里直接使用了Css-in-js的一种实现styled-components的Vue版vue-styled-components,来实现在JSX中实现类似.vue中样式的scoped功能。但是并不建议用,因为Vue版的没有更新,使用的人也不多,不像React社区那么活跃。

import styled from 'vue-styled-components'
import PageHeader from '~/components/pageHeader'

const PageHeaderAction = styled.div`
 border-bottom: 2px solid #e4e7ed;
 padding-bottom: 6px;
`

const UiPageHeader = {
 name: 'UiPageHeader',
 components: {
  PageHeader
 },

 data() {
  return {
   tabActive: '01',
   tabOptions: [
    {
     title: '我的任务',
     field: '01'
    },
    {
     title: '我的流程',
     field: '02'
    },
    {
     title: '店铺任务',
     field: '03'
    },
    {
     title: '店铺流程',
     field: '04'
    }
   ]
  }
 },

 methods: {
  onTabChange(tab) {
   console.log(tab)
  }
 },

 render(h) {
  return (
   <div>
    <el-row>
     <PageHeader title="标题"/>
    </el-row>
    <el-row>
     <PageHeader title="标题 + 默认回退" withPath={true}/>
     <PageHeader title="标题 + 指定回退路径" withPath={true} path="/4/dashboard"/>
    </el-row>
    <el-row>
     <PageHeader title="标题 + 右边描述" subTitle="我是页面标题描述文字,默认显示在标题右边"/>
     <PageHeader title="标题 + 下边描述" subTitle="我是页面标题描述文字,指定显示在标题下边" position="bottom"/>
     <PageHeader
      title="标题 + 回退 + 右边描述"
      withPath={true}
      subTitle="我是页面标题描述文字,默认显示在标题右边"
     />
     <PageHeader
      title="标题 + 回退 + 下边描述"
      withPath={true}
      subTitle="我是页面标题描述文字,指定显示在标题下边"
      position="bottom"
     />
    </el-row>
    <el-row>
     <PageHeader>
      <template slot="title">
       标题插槽示例
       <i class="el-icon-milk-tea"/>
       <strike style="color: #ff8e00">Yah!</strike>
      </template>
     </PageHeader>
     <PageHeader title="标题描述插槽示例">
      <template slot="subTitle">
       我是页面标题描述文字
       <i class="el-icon-milk-tea"/>
       <strike style="color: #ff8e00">Yah!</strike>
      </template>
     </PageHeader>
     <PageHeader title="标题栏右则附加操作按钮示例">
      <template slot="action">
       <el-button type="primary">保存</el-button>
      </template>
     </PageHeader>
     <PageHeader title="标题栏右则附加操作按钮示例2" subTitle="我是页面标题描述文字">
      <template slot="action">
       <el-button class="btn-link" type="text">页面跳转锚点</el-button>
      </template>
     </PageHeader>
     <PageHeader
      withPath={true}
      title="标题栏右则附加操作按钮示例3"
      subTitle="我是页面标题描述文字"
      position="bottom">
      <template slot="action">
       <el-button type="primary">保存</el-button>
      </template>
     </PageHeader>
    </el-row>
    <el-row>
     <h3>Tab选项卡标题示例</h3>
     <div>选项卡功能比较单一,只支持Element-ui默认的水平显示</div>
     <PageHeader
      withTab={true}
      activeTab={this.tabActive}
      options={this.tabOptions}
      onTabChange={this.onTabChange}
     />
    </el-row>
    <el-row>
     <h3>选项卡 + 标题右边附加操作按钮</h3>
     <PageHeader
      withTab={true}
      activeTab={this.tabActive}
      options={this.tabOptions}
      onTabChange={this.onTabChange}
     >
      <template slot="extra">
       <PageHeaderAction>
        <el-button
         type="primary"
         size="small"
         icon="el-icon-plus"
         onClick={this.onCreate}
        >
         新建
        </el-button>
       </PageHeaderAction>
      </template>
     </PageHeader>
    </el-row>
   </div>
  )
 }
}

export default UiPageHeader
注意:在上面的代码中render()方法中传了个h参考是因为我们的脚手架是公司架构师自己Webpack搞的,如果是用@vue/cli生成的项目是不需要这个参数的。

最后:写这个的目的是为了在工作中有所积累,写了几年业务系统,发现并没有留下什么,以文章的方式记录是一种不错的方式,希望能养成好习惯,坚持写作,在写的时候思考提升自我。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jtable列中自定义button示例代码
Nov 21 Javascript
javascript制作坦克大战全纪录(1)
Nov 27 Javascript
jQuery基于ID调用指定iframe页面内的方法
Jul 06 Javascript
手机Web APP如何实现分享多平台功能
Aug 19 Javascript
Vue.js组件tabs实现选项卡切换效果
Dec 01 Javascript
jquery实时获取时间的简单实例
Jan 26 Javascript
基于JQuery的Ajax方法使用详解
Aug 16 jQuery
浅谈vuepress 踩坑记
Apr 18 Javascript
vue-cli 打包后提交到线上出现 &quot;Uncaught SyntaxError:Unexpected token&quot; 报错
Nov 06 Javascript
jquery实现简单拖拽效果
Jul 20 jQuery
vue.js+element 默认提示中英文操作
Nov 11 Javascript
微信小程序中wxs文件的一些妙用分享
Feb 18 Javascript
vue设置导航栏、侧边栏为公共页面的例子
Nov 01 #Javascript
Vue中jsx不完全应用指南小结
Nov 01 #Javascript
vue导航栏部分的动态渲染实例
Nov 01 #Javascript
微信小程序自定义tabbar custom-tab-bar 6s出不来解决方案(cover-view不兼容)
Nov 01 #Javascript
Vue-cli项目部署到Nginx服务器的方法
Nov 01 #Javascript
浅谈vue-router路由切换 组件重用挖下的坑
Nov 01 #Javascript
浅析js实现网页截图的两种方式
Nov 01 #Javascript
You might like
php 正则匹配函数体
2009/08/25 PHP
新手学习PHP的一些基础知识分享
2011/07/27 PHP
解析php中反射的应用
2013/06/18 PHP
PHP实现的蚂蚁爬杆路径算法代码
2015/12/03 PHP
ThinkPHP 5.x远程命令执行漏洞复现
2019/09/23 PHP
php把文件设置为插件的技巧方法
2020/02/03 PHP
HR vs ForZe BO3 第二场 2.13
2021/03/10 DOTA
JScript中的undefined和&quot;undefined&quot;的区别
2007/03/08 Javascript
JQuery.closest(),parent(),parents()寻找父结点
2012/02/17 Javascript
js实现上传图片预览的方法
2015/02/09 Javascript
有关Promises异步问题详解
2015/11/13 Javascript
jQuery.datatables.js插件用法及api实例详解
2016/10/28 Javascript
vue2.0 中#$emit,$on的使用详解
2017/06/07 Javascript
使用bootstrap实现下拉框搜索功能的实例讲解
2018/08/10 Javascript
小程序server请求微信服务器超时的解决方法
2019/05/21 Javascript
解决layer.prompt无效的问题
2019/09/24 Javascript
Vue 图片压缩并上传至服务器功能
2020/01/15 Javascript
three.js 利用uv和ThreeBSP制作一个快递柜功能
2020/08/18 Javascript
[02:02]特效爆炸!DOTA2珍宝之瓶待你开启
2018/08/21 DOTA
用Python写的图片蜘蛛人代码
2012/08/27 Python
wxPython框架类和面板类的使用实例
2014/09/28 Python
python中logging包的使用总结
2018/02/28 Python
python读取excel指定列数据并写入到新的excel方法
2018/07/10 Python
python列表使用实现名字管理系统
2019/01/30 Python
jupyternotebook 撤销删除的操作方式
2020/04/17 Python
HTML5和以前HTML4的区别整理
2013/10/20 HTML / CSS
澳大利亚领先的睡衣品牌:Peter Alexander
2016/08/16 全球购物
Hibernate持久层技术
2013/12/16 面试题
汽车维修专业个人求职信范文
2014/01/01 职场文书
成绩单公证书
2014/04/10 职场文书
酒店节能降耗方案
2014/05/08 职场文书
先进班组事迹材料
2014/12/25 职场文书
中学生综合素质自我评价
2015/03/06 职场文书
创业计划书之餐饮馄饨店
2019/07/18 职场文书
某学校的2019年度工作报告范本
2019/10/11 职场文书
2019年市场部个人述职报告(三篇)
2019/10/23 职场文书