如何给element添加一个抽屉组件的方法步骤


Posted in Javascript onJuly 14, 2019

近来因为业务需要,对比iview和element库,发现element确实要比实习期间使用的iview强大点,尤其文档更为友好,但是iview的组件功能更多一点,比如分割线和抽屉组件

今天特意手写一个抽屉组件,方便自己使用element库,写好的组件我已经放在我的githup了, 点这里

一、实践

1.分析

一个抽屉组件的z-index必定是在当前页面之上的,在抽屉主体之外的区域还会有一层半透明的遮罩层,知道这些就很容易了

// drawer.vue
<template>
 <div class="mask"></div>
 <div class="drawer">
  <div class="drawer_body"></div>
 </div>
</template>

<style scoped>
.drawer {
 position: absolute;
 height: 100vh;
 top: 0;
 bottom: 0;
 right: 0;
 left: 0;
 z-index: 1000000 !important;
}
.drawer .drawer_body {
 height: 100%;
 position: absolute;
 z-index: 1000001;
 background-color: #fff;
}
.mask {
 height: 100vh;
 width: 100vw;
 position: absolute;
 z-index: 1000000;
 top: 0;
 left: 0;
 background-color: #000;
 opacity: 0.5;
}
</style>

现在已经是我们想要的样子了,接下来是给drawer_body添加样式

如何给element添加一个抽屉组件的方法步骤

作为一个灵活的组件库,我们希望样式是可以随时定制的,所以,接下要添加的样式都 使用props

动态绑定的

参考iview的样式,除了抽屉的宽度,还需要设置抽屉的方向,当我们需要抽屉时让它显示出来,不需要时隐藏它,或者为了更加显眼,甚至给抽屉更换背景色......,这些都是可以实现的,看下面的代码

<script>
export default {
 props: {
 // 是否显示drawer
 drawerVisible: Boolean,
 // drawer方向
 direction: {
  type: String,
  validator(val) {
  return ["right", "left"].indexOf(val) !== -1;
  }
 },
 // drawer宽度
 width: {
  type: Number,
  default: 400
 },
 // drawer背景色
 background: {
  type: String,
  default: "#ffffff"
 },
 // 是否显示遮罩层
 mask: {
  type: Boolean,
  default: true
 }
 }
};
</script>

对于宽度和背景色,你还需要额外的处理下

<div
 class="drawer_body"
 :style="{'right':direction=='right'?'0':'auto',
 'left':direction=='left'?'0':'auto',
 'width':width+'px','background':background}"
>drawer</div>

你只需在使用的地方引入组件,然后提供你想修改的props值

//index.vue
<template>
 <div>
 ...
 <el-button size="mini" @click="visible">显示抽屉</el-button>
 <Drawer
  :drawerVisible="drawerVisible"
  direction="right"
  :mask="true"
  background="aquamarine"
 ></Drawer>
 </div>
</template>
<script>
export default {
 data() {
 return {
  drawerVisible: false
 };
 },
 methods:{
  // 打开抽屉
 visible() {
  this.drawerVisible = true;
 }
 }
}
</script>

如何给element添加一个抽屉组件的方法步骤

2.关闭抽屉

在点击遮罩层的时候,我们希望可以关闭已经打开的抽屉组件,这里如果你直接修改父组件传过来的drawerVisible值,会报错如下

vue.esm.js:629 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or computed property based on the
prop's value. Prop being mutated: "drawerVisible"

这是因为vue是单向数据流的,如果想改变父元素的值,必须使用监听事件的方式,但是2.3.0之后添加了.sync修饰符,所以,正确的做法是使用.sync修饰符

...
<div v-if="drawerVisible" class="mask"></div>
<transition :name="this.direction=='left'?'slide-right':'slide-left'">
  <div v-if="drawerVisible" @click.stop="closeBtn?'':close" class="drawer">
  <div
   class="drawer_body"
   :style="{
   'right':direction=='right'?'0':'auto',
   'left':direction=='left'?'0':'auto',
   'width':width+'px',
   'background': background}"
  >
  </div>
  </div>
</transition>
...

methods: {
 close() {
  this.$emit("update:drawerVisible", false);
 }
}

另外,我们还希望在关闭抽屉组件时,我们可以监听到这个事件然后做出反应

methods: {
 close() {
  this.$emit("update:drawerVisible", false);
  this.$emit("close");
 }
}

此时需要在抽屉组件上添加

<Drawer
  :drawerVisible.sync="drawerVisible"
  @close="close"
 >
 </Drawer>
 
methods:{
 close(){
  // 关闭抽屉组件时你要做的事
 }
}

2.动画

动画是UI的灵魂,所以接下来给抽屉组件的显示和隐藏添加动画,我们使用transition的css动画做动画过度效果

//drawer.vue
 <div class="drawer">
 <div class="mask"></div>
 <!-- 不同方向使用不用的动画名称,如果抽屉在左边,则进入方向是朝 → -->
 <transition :name="this.direction=='left'?'slide-right':'slide-left'">
  <div
  class="drawer_body"
  v-if="drawerVisible"
  :style="{'right':direction=='right'?'0':'auto',
  'left':direction=='left'?'0':'auto',
  'width':width+'px',
  'background':background}"
  >drawer</div>
 </transition>
 </div>
</template>
<style scoped>
/*
* ...
*这里省略了写过的样式
*/
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
 will-change: transform;
 transition: transform 300ms;
 position: absolute;
 width: 100vw;
 height: 100vh;
 overflow: hidden;
}
.slide-right-enter,
.slide-right-leave-active {
 transform: translate(-100%, 0);
}
.slide-left-leave-active,
.slide-left-enter {
 transform: translate(100%, 0);
}
</style>

虽然现在已经完全实现了抽屉的功能,但是本着更加精美的原则,我还打算使用slot给它添加辩题和页脚

3.添加标题

标题solt的name值是header

添加标题的目的是为了让抽屉组件看起来更加清楚,此外,除了添加标题,我还想添加个关闭按钮

// 需要添加几个props属性
<script>
export default {
 props: {
 // drawer标题
 title: String,
 // 是否显示关闭按钮
 closeBtn: {
  type: Boolean,
  default: false
 },
 }
};
</script>

你可以选择是否添加标题,是否添加关闭按钮,值的注意的是如果添加了关闭按钮,点击遮罩层就不会自动关闭抽屉组件了

如何给element添加一个抽屉组件的方法步骤

<!--这里要??孪虏季郑?绻?阒谎≡窨?艄乇瞻磁ィ??ustify-content布局是flex-end
如果两者都开启,那justify-content布局是space-between-->
<slot name="header">
 <div
  v-if="title||closeBtn"
  :style="{'justify-content':title?'space-between':'flex-end'}"
  class="title"
 >
  <div v-if="title">{{title}}</div>
  <el-button
  v-if="closeBtn"
  circle
  size="mini"
  icon="el-icon-close"
  @click="close"
  ></el-button>
 </div>
 </slot>

我是这么做到禁用遮罩层点击事件的

<div v-if="drawerVisible" @click.stop="closeBtn?'':close" class="mask"></div>

当然这些你可以使用具名插槽自定义的

<Drawer
 :width="400"
 direction="right"
 :mask="true"
 title="抽屉组件"
 >
 <div v-slot:header>这里是自定义标题</div>
 <div style="height:100px"></div>
</Drawer>

4.添加页脚

页脚solt的name值是footer

为了使得页脚和标题有一定的距离,我给主体内容添加了最小高度

<div style="min-height:82vh;padding: 5px 0">
<slot></slot>
</div>

方法是很类似的,只是我多添加了两个监听事件,确定和取消

//drawer.vue
<slot name="footer">
 <div class="footer">
  <el-button size="mini" type="primary" @click="footerOk">确认</el-button>
  <el-button size="mini" @click="footerCal">取消</el-button>
 </div>
</slot>
//引入的页面
<Drawer
 :width="400"
 direction="right"
 :mask="true"
 title="抽屉组件"
 :footer-ok="footerOk"
 :footer-cal="footerCal"
 >
</Drawer>

还需要在props中添加对应的值

props: {
 footerOk: Function,
 footerCal: Function
 },

关于页脚的布局是这样的

如何给element添加一个抽屉组件的方法步骤

.footer {
 border-top: 0.1px solid #ddd;
 display: flex;
 justify-content: flex-end;
 padding-top: 10px;
}

当然这些你也是可以使用具名插槽自定义的

<Drawer
 :width="400"
 direction="right"
 :mask="true"
 title="抽屉组件"
 >
 <div v-slot:header>这里是自定义标题</div>
 <div style="height:100px"></div>
 <div v-slot:footer>这里是自定义页脚</div>
</Drawer>

5.主体是否可以滚动

前面说过给主体添加了最小高度,但是超过最小高度,可能会被撑开布局,所以我给它添加了滚动功能

// props添加
 // 是否开启滚动
 scroll: {
  type: Boolean,
  default: false
 }

在drawer_body的样式末尾追加overflow-y样式

<div
 class="drawer_body"
 :style="{
  'right':direction=='right'?'0':'auto',
  'left':direction=='left'?'0':'auto',
  'width':width+'px',
  'background': background,
  'overflow-y':scroll?'scroll':'hidden'}"
>
</div>

scroll默认是不开启的,如果你的抽屉要放的内容少,就不用理这个属性,但是当内容撑开抽屉时记得手动开启这个功能复制代码

6.细节的优化

这里说下自己的不足之处,并且如何改进它

a.滚动条bug

当选择抽屉在右边时,动画过程中会出现滚动条,看起来让我的UI组件大打折扣,针对这个问题我打算在组件中监听drawerVisible,当它需要被展示时禁用body的滚动效果,当它不需要被展示时,还原body的展示效果

watch: {
 drawerVisible(n, o) {
  if (n == true) {
  document.documentElement.style.overflowY = "hidden";
  document.documentElement.style.overflowX = "hidden";
  }
 }
 },

b.向下冒泡bug

在点击抽屉以外的区域可以正常关闭抽屉,但是我发现当我点击抽屉非按钮区域时,也会关闭抽屉,这是向下冒泡的bug,这个bug我的解决方案是在drawer_body上添加个无意义的事件,并阻止向上冒泡

<div
 @click.stop="clickBg_" // 注意这里
 class="drawer_body"
 :style="{
  'right':direction=='right'?'0':'auto',
  'left':direction=='left'?'0':'auto',
  'width':width+'px',
  'background': background,
  'overflow-y':scroll?'scroll':'hidden'}"
>
</div>

二、API文档

1.属性

属性 描述 类型 默认
drawerVisible 是否显示drawer Boolean false
direction drawer方向 String left
width drawer宽度 Number 400
background drawer背景色 String #ffffff
mask 是否显示遮罩层 Boolean true
title drawer标题 Boolean true
closeBtn 是否显示关闭按钮 String ---
scroll 是否开启滚动 Boolean false

2.事件

事件 描述 返回值
close 监听关闭事件
footerOk 页脚确认绑定事件,使用默认页脚时有效
footerCal 页脚取消绑定事件,使用默认页脚时有效

3.slot

name 描述
header 页头插槽名称
default 抽屉主体部分,可省略
footer 页脚插槽名称

注意:插槽里的按钮都是使用element内置的组件,如果你的项目里没有引入element库
那最好请使用具名插槽重写页头和页脚部分

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

Javascript 相关文章推荐
不错的asp中显示新闻的功能
Oct 13 Javascript
利用JS延迟加载百度分享代码,提高网页速度
Jul 01 Javascript
javascript的alert box在java中如何显示多行
May 18 Javascript
动态加载js、css的简单实现代码
May 26 Javascript
jQuery Easyui学习教程之实现datagrid在没有数据时显示相关提示内容
Jul 09 Javascript
利用D3.js实现最简单的柱状图示例代码
Dec 09 Javascript
javascript回调函数的概念理解与用法分析
May 27 Javascript
如何去除富文本中的html标签及vue、react、微信小程序中的过滤器
Nov 21 Javascript
配置eslint规范项目代码风格
Mar 11 Javascript
Node.js中Koa2在控制台输出请求日志的方法示例
May 02 Javascript
理解JavaScript中的Proxy 与 Reflection API
Sep 21 Javascript
深入理解Vue的数据响应式
May 15 Vue.js
详解vuex的简单todolist例子
Jul 14 #Javascript
改进 JavaScript 和 Rust 的互操作性并深入认识 wasm-bindgen 组件
Jul 13 #Javascript
微信小程序解析富文本过程详解
Jul 13 #Javascript
微信小程序wx.navigateTo中events属性实现页面间通信传值,数据同步
Jul 13 #Javascript
微信小程序Echarts覆盖正常组件问题解决
Jul 13 #Javascript
微信小程序图片左右摆动效果详解
Jul 13 #Javascript
vue iview多张图片大图预览、缩放翻转
Jul 13 #Javascript
You might like
支持中文的php加密解密类代码
2011/11/27 PHP
php中使用key,value,current,next和prev函数遍历数组的方法
2015/03/17 PHP
yii2中LinkPager增加总页数和总记录数的实例
2017/08/28 PHP
JS图片预加载 JS实现图片预加载应用
2012/12/03 Javascript
Javascript图像处理思路及实现代码
2012/12/25 Javascript
js 为label标签和div标签赋值的方法
2013/08/08 Javascript
Firefox中通过JavaScript复制数据到剪贴板(Copy to Clipboard 跨浏览器版)
2013/11/22 Javascript
js中setTimeout()与clearTimeout()用法实例浅析
2015/05/12 Javascript
基于JavaScript实现瀑布流效果(循环渐近)
2016/01/27 Javascript
jquery拖动层效果插件用法实例分析(附demo源码)
2016/04/28 Javascript
js获取form表单所有数据的简单方法
2016/08/18 Javascript
fullCalendar中文API官方文档
2017/02/07 Javascript
JS+HTML5实现图片在线预览功能
2017/07/22 Javascript
简单实现js拖拽效果
2017/07/25 Javascript
微信小程序实现文字跑马灯效果
2020/05/26 Javascript
微信小程序实现日历效果
2018/12/28 Javascript
JavaScript私有变量实例详解
2019/01/24 Javascript
JavaScript实现切换多张图片
2021/01/27 Javascript
[30:55]完美世界DOTA2联赛PWL S2 Magma vs LBZS 第二场 11.18
2020/11/18 DOTA
Python中import机制详解
2017/11/14 Python
Python基于whois模块简单识别网站域名及所有者的方法
2018/04/23 Python
解决Python3中的中文字符编码的问题
2018/07/18 Python
python utc datetime转换为时间戳的方法
2019/01/15 Python
python并发编程 Process对象的其他属性方法join方法详解
2019/08/20 Python
Python的条件锁与事件共享详解
2019/09/12 Python
Python函数__new__及__init__作用及区别解析
2020/08/31 Python
html5使用Drag事件编辑器拖拽上传图片的示例代码
2017/08/22 HTML / CSS
如何在存储过程中使用Loop
2016/01/05 面试题
什么是JNDI的上下文?如何初始化JNDI上下文
2012/03/10 面试题
广播电视新闻学专业应届生求职信
2013/10/08 职场文书
博士导师推荐信
2015/03/25 职场文书
新教师2015年度工作总结
2015/07/22 职场文书
教师节随笔
2015/08/15 职场文书
2016年教师寒假学习心得体会
2015/10/09 职场文书
高中班主任培训心得体会
2016/01/07 职场文书
关于MySQL中explain工具的使用
2023/05/08 MySQL