vue2-自定义指令:el-dialog全屏

4,848 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

el-dialog中展示的内容过多时,无法完全展示出来,需要更改el-dialog或者添加滚动条才能展示出来。最近有个需求就是,为了尽可能多的展示内容,在用户的操作下,直接让el-dialog充满整个窗口,就是el-dialog全屏,当然,也得能够还原。

思路

那怎么才能够让el-dialog变大还原呢?无非就是通过更改样式来实现,在用户的操作下,根据不同的值将el-dialog的样式进行更改。

代码

  1. 首先,创建一个普通的el-dialog弹窗,然后在上面添加即将实现的全屏指令v-fullScreen
<el-dialog
  :visible.sync="visible"
  width="500px"
  title="弹窗表单"
  v-fullScreen
>
  <div style="height: 500px;">
    表单内容
  </div>
</el-dialog>

效果: 微信截图_20220610102126.png 因为还没开始编写指令,所以只是一个普通的弹窗。

  1. 然后,创建书写指令的js文件,然后,将基础的指令代码写上:
const fullScreenDirective = Vue => {
  Vue.directive('fullScreen', {
    bind: function (el, binding, vnode) {}
  })
}

export default fullScreenDirective

因为我打算将所有的变大还原操作都在指令内部完成,不接收任何参数,因此只使用bind这个钩子就行了。
bind是在组件挂载指令时就会触发的,且只触发一次,可以在这里面将最大化和还原的逻辑都写上。

  1. 接着,获取el-dialog中的header元素,把改变窗口大小的图标按钮加到header尾部,需要注意的是,header尾部还有一个关闭按钮,不要与之重叠,详细看下面的代码和注释:
const fullScreenDirective = Vue => {
  Vue.directive('fullScreen', {
    bind: function (el, binding, vnode) {
      // el是挂载指令的dom元素,在这个指令里面就是`.el-dialog__wrapper`
      const header = el.querySelector('.el-dialog__header')
      if (!header) return
      // 将header的paddingRight变大,然后将全屏按钮`float:right`,这样可以保证全屏按钮在header右侧,且不会与关闭按钮重叠
      header.style.paddingRight = '43px'

       // 创建全屏按钮,并设定样式
      const fullScreenBtn = document.createElement('button')
      fullScreenBtn.type = 'button'
      fullScreenBtn.style = 'float:right;padding:0;background: 0 0;border:0;outline:0;cursor:pointer;font-size:16px;'

      const fullScreenIcon = document.createElement('i')
      fullScreenIcon.className = 'el-icon el-icon-full-screen'
      fullScreenBtn.append(fullScreenIcon)

      // 将全屏按钮添加到header中
      header.append(fullScreenBtn)
    }
  })
}

export default fullScreenDirective

效果: 微信截图_20220610103402.png 可以看到,按钮已经添加上去了。因为个人不喜欢写太多的全局class,所以指令里的样式都是写在style上。

  1. 给按钮添加点击事件,这个比较简单,通过addEventListener即可,代码如下:
fullScreenBtn.addEventListener('click', () => {})

关键是点击之后根据全屏标志将el-dialog的大小进行变大或者还原,那么,可以先定义一个全屏标志还表示来表示当前el-dialog是变大还是还原:

let isFullScreen = false

接下来,确定怎么设定样式让el-dialog全屏或者还原的。首先,确定要更改的dom元素是.el-dialog,也就是用户能看到的白色区域。然后,样式要怎么去改?
个人的方案是:

最大化时,通过position:absolute,然后将topbottomleftright都设置为零,达到全屏的目的,但是因为.el-dialog本身设置了width,宽度无法全屏,要将width:100%即可;位置上,.el-dialog设置了margin-top,要将其置为零;
然后就是还原,因为高度的变大是position:absolutetopbottom都为零的原因,所以将position设置为realtive即可还原高度,宽度的话,需要先使用一个变量记录原来的宽度,还原时将width设置回去即可;位置上,同样也是使用一个变量记录原本margin-top,还原时设置回去即可。 此时代码如下:

const fullScreenDirective = Vue => {
  Vue.directive('fullScreen', {
    bind: function (el, binding, vnode) {
      // 是否全屏的标志
      let isFullScreen = false

      // 获取要改变带下的dom元素
      const dialog = el.querySelector('.el-dialog')
      const header = el.querySelector('.el-dialog__header')

      if (!header || !dialog || !vnode) return
      header.style.paddingRight = '43px'
      
      // 从el-dialog组件实例中获取原本的width和margin-top
      const { width, top } = vnode.componentInstance
      
      const fullScreenBtn = document.createElement('button')
      fullScreenBtn.type = 'button'
      fullScreenBtn.style = 'float:right;padding:0;background: 0 0;border:0;outline:0;cursor:pointer;font-size:16px;'

      const fullScreenIcon = document.createElement('i')
      fullScreenIcon.className = 'el-icon el-icon-full-screen'
      fullScreenBtn.append(fullScreenIcon)

      // 全屏按钮点击事件
      fullScreenBtn.addEventListener('click', () => {
        // 更改全屏标志
        isFullScreen = !isFullScreen
        // 根据标志对弹窗的大小进行处理
        handleFullScreen(dialog, isFullScreen, top, width)
        return false
      })

      header.append(fullScreenBtn)
    }
  })
}

在上面的代码中,定义了handleFullScreen函数来处理弹窗大小的改变,接收四个参数,分别是要改变大小的dom元素dialog,全屏标志isFullScreen、原本的margin-top、原本的宽度width,实现代码如下,主要是对样式进行操作:

function handleFullScreen (dialog, isFullScreen, marginTop, width) {
  if (!dialog) return false
  if (isFullScreen) {
    dialog.style.marginTop = '0'
    dialog.style.position = 'absolute'
    dialog.style.top = '0px'
    dialog.style.bottom = '0px'
    dialog.style.left = '0px'
    dialog.style.width = '100%'
  } else {
    dialog.style.marginTop = marginTop
    dialog.style.width = width
    dialog.style.position = 'relative'
  }
}

至此,就是实现了点击全屏按钮的功能: 动画1.gif

  1. header被双击时,也要能够改变弹窗的大小,方便用户的操作。基本代码与全屏按钮一样,不再赘述:
// header添加阴影,方便用户区分弹窗的`header`和`body`
header.style.boxShadow = '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
// 监听双击事件,与全屏按钮一样的逻辑操作:
header.addEventListener('dblclick', () => {
  isFullScreen = !isFullScreen
  handleFullScreen(dialog, isFullScreen, top, width)
  return false
})

效果: 动画1.gif

完整代码

function handleFullScreen (dialog, isFullScreen, marginTop, width) {
  if (!dialog) return false
  if (isFullScreen) {
    dialog.style.marginTop = '0'
    dialog.style.position = 'absolute'
    dialog.style.top = '0px'
    dialog.style.bottom = '0px'
    dialog.style.left = '0px'
    dialog.style.width = '100%'
  } else {
    dialog.style.marginTop = marginTop
    dialog.style.width = width
    dialog.style.position = 'relative'
  }
}

const fullScreenDirective = Vue => {
  Vue.directive('fullScreen', {
    bind: function (el, binding, vnode) {
      let isFullScreen = false

      const dialog = el.querySelector('.el-dialog')
      const header = el.querySelector('.el-dialog__header')
      if (!header || !dialog || !vnode) return
      header.style.paddingRight = '43px'
      header.style.boxShadow = '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
      const { width, top } = vnode.componentInstance

      const fullScreenBtn = document.createElement('button')
      fullScreenBtn.type = 'button'
      fullScreenBtn.style = 'float:right;padding:0;background: 0 0;border:0;outline:0;cursor:pointer;font-size:16px;'

      const fullScreenIcon = document.createElement('i')
      fullScreenIcon.className = 'el-icon el-icon-full-screen'
      fullScreenBtn.append(fullScreenIcon)

      fullScreenBtn.addEventListener('click', () => {
        isFullScreen = !isFullScreen
        handleFullScreen(dialog, isFullScreen, top, width)
        return false
      })

      header.append(fullScreenBtn)

      header.addEventListener('dblclick', () => {
        isFullScreen = !isFullScreen
        handleFullScreen(dialog, isFullScreen, top, width)
        return false
      })
    }
  })
}

export default fullScreenDirective