基于element-ui el-dialog组件封装,可缩放+可移动的弹窗组件

文章介绍了如何创建一个名为SkDialog的Vue组件,该组件继承了el-dialog组件的大部分属性,并添加了全屏切换功能。通过提供的源码,可以实现对话框的拖动、缩放以及全屏显示。此外,还包含了组件的注册和使用方法。
摘要由CSDN通过智能技术生成

源码下载

改组件继承el-dialog组件百分之95属性,可直接对<el-dialog>进行替换。在项目中我的命名为:

SkDialog。废话不多说,直接上代码:

vue代码:搞成组件,路径随意,推荐统一放在组件目录下

<!--
@author: kzy;
 -->
<template>
  <el-dialog ref="skDialog"
             custom-class="dialogHeightFull skDialog"
             :title="title"
             :fullscreen="fullFlag"
             :visible.sync="dialogVisible"
             :append-to-body="appendToBody"
             :lock-scroll="lockScroll"
             :modal-append-to-body="modalAppendToBody"
             :close-on-click-modal="closeOnClickModal"
             :close-on-press-escape="closeOnPressEscape"
             :show-close="false"
             :before-close="beforeClose"
             :center="center"
             :destroy-on-close="destroyOnClose"
             :width="width"
             :top="top"
             v-if="dialogVisible"
             v-sk-dialog-drag
             @closed="closeSkDialog">
    <div slot="title" class="skDialog-header">
      <div class="skDialog-title"><span>{{title}}</span></div>
      <div class="skDialog-icon">
        <i :class="fullFlag? 'el-icon-copy-document' : 'el-icon-full-screen' " @click="IsFullscreen"></i>
        <i class="el-icon-close" @click="closeSkDialog"></i>
      </div>
    </div>
    <div class="el-dialog__body"  v-if="rendered"><slot :style="{height:height}"></slot></div>
    <div class="el-dialog__footer" v-if="$slots.footer">
      <slot name="footer"></slot>
    </div>
    <div class="resize"></div>
  </el-dialog>
</template>

<script>
  export default {
    name: 'SkDialog',
    props: {
      visible: {
        type: Boolean,
        default: false
      },
      titleVisible: {
        type: Boolean,
        default: true
      },
      width: {
        type: String,
        default: '50%'
      },
      height: {
        type: String,
        default: '100%'
      },
      top: {
        type: String,
        default: '30px'
      },
      title: {
        type: String,
        default: ''
      },
      isfullscreen: {
        type: Boolean,
        default: false // 默认全屏
      },

      modal: {
        type: Boolean,
        default: true
      },

      modalAppendToBody: {
        type: Boolean,
        default: true
      },

      appendToBody: {
        type: Boolean,
        default: true
      },

      lockScroll: {
        type: Boolean,
        default: true
      },

      closeOnClickModal: {
        type: Boolean,
        default: false
      },

      closeOnPressEscape: {
        type: Boolean,
        default: false
      },

      showClose: {
        type: Boolean,
        default: true
      },

      customClass: {
        type: String,
        default: ''
      },

      beforeClose: Function,
      center: {
        type: Boolean,
        default: false
      },

      destroyOnClose: Boolean
    },
    data() {
      return {
        full: false, // 全屏
      }
    },
    computed: {
      fullFlag: {
        get: function() {
          return this.full ?? this.isfullscreen;
        },
        set: function(n) {
          return this.full
        }
      },
      dialogVisible:{ //重新设置一个变量,接收父级传递的参数,引用页面需要使用语法糖,才能使用this.$emit('update:visible', false)对父组件值进行更新
        get:function(){
          return this.visible
        },
        set :function(value){
          this.$emit('update:visible', false)
        }
      },
      rendered:{ //重新设置一个变量,接收父级传递的参数
        get:function(){
          return this.visible
        },
      }
    },
    mounted() {
    },
    methods: {
      // 全屏 切换
      IsFullscreen() {
        let dialogDom = document.querySelector(".skDialog");
        if (this.isfullscreen == true) {
          this.full = this.full === null ? false : !this.full
        } else {
          this.full = this.full === null ? true : !this.full
          dialogDom.style.top =`0px`;
          dialogDom.style.left =`auto`;
        }
        // 传过来的全屏钩子函数
        this.$emit('maxFun')
      },
      // 关闭
      closeSkDialog() {
        this.full = null
        this.$emit('update:visible', false)
        // 传过来的关闭钩子函数
        this.$emit('closeFun')
      }
    },
    watch:{
    },
    created() {
      this.full = this.isfullscreen
    },
  }
</script>

<style scoped="scoped">
  >>>.el-dialog__header {
    background-color: #f2f2f2;
    /* height: 48px; */
    padding: 10px 20px 10px;
  }

  .skDialog-header {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
  }

  .skDialog .skDialog-icon i {
    display: inline-block;
    height: 28px;
    width: 28px;
    line-height: 24px;
    text-align: center;
    border-radius: 50%;
    cursor: pointer;
    font-size: 22px;
  }

  .skDialog .skDialog-icon i:hover {
    background-color: #ddd;
  }

  .skDialog .skDialog-icon i::before {
    font-size: 80%;
  }

  >>>.skDialog.dialogHeightFull.el-dialog {
    margin-bottom: 0;
    /*overflow-y: hidden;*/
  }

  .el-dialog__wrapper{
    /*overflow: hidden!important;*/
  }
  .resize{
    position: absolute;
    right: 0;
    bottom: 0;
    content: '';
    width: 10px;
    height: 10px;
    cursor: se-resize;
  }
</style>

拖动及缩放操作js代码:

/**
 @author: kzy;
 */
import Vue from 'vue'
Vue.directive("SkDialogDrag", {
  bind(el, binding, vnode, oldVnode) {
    const windowW = document.body.clientWidth;
    const windowH = document.body.clientHeight
    //设置弹框可拉伸最小宽高
    let minWidth = 400;
    let minHeight = 300;
    const dialogHeaderEl = el.querySelector(".el-dialog__header");
    //弹窗
    const dragDom = el.querySelector(".el-dialog");
    //头部加上可拖动cursor
    dialogHeaderEl.style.cursor = "move";

    // 获取style属性
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);

    let moveDown = e => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;

      // 获取到的值带px 正则匹配替换
      let styL, styT, styMT;
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (sty.left.includes("%")) {
        styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, "") / 100);
        styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, "") / 100);
      } else {
        styL = +sty.left.replace(/\px/g, "");
        styT = +sty.top.replace(/\px/g, "");
        styMT = +sty.marginTop.replace(/\px/g, "")
      }

      document.onmousemove = function (e) {
        const titleH = dialogHeaderEl.offsetHeight;

        // 通过事件委托,计算移动的距离
        let l = e.clientX - disX;
        let t = e.clientY - disY;

        // 移动边界处理
        if (t < 0 && (t + styMT + styT) <= 0 || t < 0 && (styMT + styT) <= 0) {
          t = -(styMT + styT);
        } else if (t > 0 && t > (document.body.clientHeight - styMT - styT - titleH)) {
          t = document.body.clientHeight - styMT - styT - titleH;
        }

        // 移动当前元素
        dragDom.style.left = `${l + styL}px`;
        dragDom.style.top = `${t + styT}px`;

      };

      document.onmouseup = function (e) {
        document.onmousemove = null;
        document.onmouseup = null;
      };

    };

    dialogHeaderEl.onmousedown = moveDown;

    dragDom.onmousemove = function (e) {
      const dialogBODY = el.querySelector(".el-dialog__body");
      const resizeDom= el.querySelector(".resize");
      const titleH = dialogHeaderEl.offsetHeight;
      dragDom.onmousedown = e => {
        const clientX = e.clientX;
        const clientY = e.clientY;
        let elW = dragDom.clientWidth;
        let elH = dragDom.clientHeight;
        let EloffsetLeft = dragDom.offsetLeft;
        let EloffsetTop = dragDom.offsetTop;
        let ELscrollTop = el.scrollTop;
        let resizeW = resizeDom.clientWidth, resizeH = resizeDom.clientHeight;

        //判断点击的位置是不是为头部
        if (
          clientX > EloffsetLeft &&
          clientX < EloffsetLeft + elW &&
          clientY > EloffsetTop &&
          clientY < EloffsetTop + 100
        ) {
          //如果是头部
          e.preventDefault(); // 移动时禁用默认事件
        } else {
          document.onmousemove = function (e) {
            //鼠标拖拽
            if (
              clientX > EloffsetLeft + elW - resizeW  &&
              clientX < EloffsetLeft + elW
            ) {
              //往左拖拽
              if (clientX > e.clientX) {
                if (dragDom.clientWidth < minWidth) {
                } else {
                  dragDom.style.width = elW - (clientX - e.clientX) * 2 + "px";
                }
              }
              //往右拖拽
              if (clientX <= e.clientX) {
                let targetTW = elW + (e.clientX - clientX) * 2
                targetTW > windowW ?
                  dragDom.style.width = windowW + "px" :
                  dragDom.style.width = targetTW + "px"
              }
            }
            //底部鼠标拖拽位置
            if (
              ELscrollTop + clientY > EloffsetTop + elH - resizeH &&
              ELscrollTop + clientY < EloffsetTop + elH
            ) {
              //往上拖拽
              if (clientY > e.clientY) {
                if (dragDom.clientHeight < minHeight) {
                } else {
                  dragDom.style.height = elH - (clientY - e.clientY) * 1 + "px";
                  dialogBODY.style.height = elH - (clientY - e.clientY) * 1 - titleH + "px";
                }
              }
              //往下拖拽
              if (clientY <= e.clientY) {
                e.clientY >= windowH ?
                  dragDom.style.height = windowH - dragDom.offsetTop + "px" :
                  dragDom.style.height = elH + (e.clientY - clientY) * 1 + "px";
                dialogBODY.style.height = elH - (clientY - e.clientY) * 1 - titleH + "px";
              }
            }
          };
          //结束
          document.onmouseup = function (e) {
            document.onmousemove = null;
            document.onmouseup = null;
          };
        }
      };
    };
  }
});
export default Vue.directive('SkDialogDrag')

将skdialog.js注册为指令:

目录:

import SkDialogDrag from './dialog/skdialog'
const install = function(Vue) {
  Vue.directive('SkDialogDrag', SkDialogDrag)
}

指令使用:(v-指令名称(SkDialogDrag))

效果图:

注:吃水不忘挖井人,在 原作者wjqocean 的基础之上改进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静梵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值