elementui el-dialog 的二次封装

        当我们需要去统一修改项目中对话框的样式的时候,我们可以去修改全局的样式,但是还存在另外一种方式就是去二次封装我们的eldialogl,而为了避免我们在使用自己二次封装的对话框时没有对应的文档,所以我尽量使用官方文档对应的属性,经量做到只需要修改组件名称然后根据官方文档就能使用的效果.

关于 :visible.sync 属性的修改

        看了很多篇的文章都是需要 使用 类似与 :centerDialogVisible.sync="visible" 这种需要重新定义一个属性去做开关,然后关闭使用计算属性去触发 

  computed: {
    centerDialogVisible: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("update:value", val);
      }
    }
  },

父组件通过接受这个自定义事件的回调去关闭,这个和官方文档比起来麻烦了不少 难道我们就不能和官方文档一样使用 双向绑定吗?

        然后优化后的代码就是利用双向绑定的方法去做开关,你只需要在父组件传递开关对应的字段名就ok了.

        

 <m-dialog
      title="提示"
      :visble.sync="dialogVisible"
      width="400px"
      append-to-body
      destroy-on-close
    >
      <Form />
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false"
          >确 定</el-button
        >
      </div>
    </m-dialog>

        上面就是使用案例 将官方的修改成 v-model,和elementPlus的使用一样了,

子组件如何兼容 父组件的:visble.sync

        

如图所示,我们的props下的value就对应着 v-model 传递下来的值,使用的话就是如下图

这样我们就将:visible.sync上对应的值给el-dialog 上面的值对应起来了,所以实现很简单

组件属性和官方如何对应

        实现方式也很简单,那就是和官方的属性值一样不就ok 了,实现如下:

props: {
    title: {
      type: String,
      default: "标题"
    },
    visble: {
      type: Boolean,
      default() {
        return false;
      }
    },
    width: {
      type: String,
      default() {
        return "430px";
      }
    },
    top: {
      type: String,
      default() {
        return "15vh";
      }
    },
    closeOnClickModal: {
      type: Boolean,
      default: true
    },
    center: {
      type: Boolean,
      default: false
    }
  },

那el-dialog 上的效果是啥样的呢 ,如下:
 

<el-dialog
    class="comn_dialog"
    :title="title"
    :visible.sync="dialogVisible"
    :width="width"
    :top="top"
    :close-on-click-modal="closeOnClickModal"
    @close="Cancel"
    append-to-body
    destroy-on-close
    :center="center"
    v-if='dialogVisible'
  >
    <slot>
      <p>弹窗内容自定义</p>
    </slot>
    <div slot="footer">
      <slot name="footer" />
    </div>
  </el-dialog>

完整代码
 

<template>
  <!-- v-dialogDrag -->
  <el-dialog
    class="comn_dialog"
    :title="title"
    :visible.sync="dialogVisible"
    :width="width"
    :top="top"
    :close-on-click-modal="closeOnClickModal"
    :before-close="beforeClose"
    append-to-body
    :center="center"
    v-if="dialogVisible"
    v-dialogDrag="draggable"
  >
    <slot>
      <p>弹窗内容自定义</p>
    </slot>
    <div slot="footer">
      <slot name="footer" />
    </div>
  </el-dialog>
</template>
<script>
export default {
  props: {
    title: {
      type: String,
      default: "标题"
    },
    visble: {
      type: Boolean,
      default() {
        return false;
      }
    },
    width: {
      type: String,
      default() {
        return "430px";
      }
    },
    top: {
      type: String,
      default() {
        return "15vh";
      }
    },
    closeOnClickModal: {
      type: Boolean,
      default: false
    },
    center: {
      type: Boolean,
      default: false
    },
    draggable: {
      type: Boolean,
      default: true
    }
  },
  methods: {
    Cancel() {
      this.$emit("close");
    },
    beforeClose(done) {
      done();
    }
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visble;
      },
      set(val) {
        // this.$emit("updateVisible", val);
        this.$emit("update:visble", val);
      }
    }
  },
  created() {},
  directives: {
    dialogDrag: {
      bind(el, binding) {
        console.log(131231, el, binding);
        // 自定义属性,判断是否可拖拽
        if (!binding.value) return;

        const dialogHeaderEl = el.querySelector(".el-dialog__header");
        const dragDom = el.querySelector(".el-dialog");
        dialogHeaderEl.style.cursor = "move";
        // 禁止拖拽时选中标题中文本内容
        dialogHeaderEl.style.userSelect = "none";
        // 获取原有属性
        const sty = (function() {
          if (document.body.currentStyle) {
            // 在 IE 下兼容写法
            return (dom, attr) => dom.currentStyle[attr];
          }
          return (dom, attr) => getComputedStyle(dom, null)[attr];
        })();

        dialogHeaderEl.onmousedown = e => {
          // 鼠标按下,计算当前元素距离可视区的距离
          const disX = e.clientX - dialogHeaderEl.offsetLeft;
          const disY = e.clientY - dialogHeaderEl.offsetTop;
          const screenWidth = document.body.clientWidth; // body当前宽度
          const screenHeight = document.documentElement.clientHeight; // 可见区域高度

          const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
          const dragDomHeight = dragDom.offsetHeight; // 对话框高度

          const minDragDomLeft = dragDom.offsetLeft;
          const maxDragDomLeft =
            screenWidth - dragDom.offsetLeft - dragDomWidth;

          const minDragDomTop = dragDom.offsetTop;
          const maxDragDomTop =
            screenHeight - dragDom.offsetTop - dragDomHeight;

          // 获取到的值带px 正则匹配替换
          let styL = sty(dragDom, "left");
          if (styL === "auto") styL = "0px";
          let styT = sty(dragDom, "top");

          if (styL.includes("%")) {
            styL = +document.body.clientWidth * (+styL.replace(/%/g, "") / 100);
            styT =
              +document.body.clientHeight * (+styT.replace(/%/g, "") / 100);
          } else {
            styL = +styL.replace(/px/g, "");
            styT = +styT.replace(/px/g, "");
          }

          document.onmousemove = function(e) {
            // 通过事件委托,计算移动的距离
            let left = e.clientX - disX;
            let top = e.clientY - disY;

            // 边界处理
            if (-left > minDragDomLeft) {
              left = -minDragDomLeft;
            } else if (left > maxDragDomLeft) {
              left = maxDragDomLeft;
            }

            if (-top > minDragDomTop) {
              top = -minDragDomTop;
            } else if (top > maxDragDomTop) {
              top = maxDragDomTop;
            }

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

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

          return false; // 防止文本选中
        };
      }
    }
  }
};
</script>
<style lang="scss">
.comn_dialog {
  .el-dialog__body {
    padding: 20px 20px 0px;
  }
}
</style>

组件全局定义

        在main.js 文件中引入我们的二次疯转组件 如下

import MDialog from "./components/Dialog";
Vue.component("MDialog", MDialog);

组件使用

        使用就是 将 前面的 el 变成 m 就ok啦, 快关绑定换成 v-model

 <m-dialog
      title="提示"
      :visible.sync="dialogVisible"
      width="400px"
      append-to-body
      destroy-on-close
    ></m-dialog>

和官方属性一样就行了, 没有的自己加!

适配对话框拖动

本部分参考了文章 链接icon-default.png?t=N7T8https://blog.csdn.net/jiciqiang/article/details/139731640 

        文章中的使用时全局指令,由于我只在dialog.vue内部进行使用,所以我就定义成布局的啦,详细的流程可以去看上方大佬的详细文档,我就提供下落实后的完整方法:

方案1:(存在很多细节部分处理不是很好)

        

 directives: {
    dialogDrag: {
      bind: el => {
        // 获取弹框标题区域DOM节点
        const headerDOM = el.querySelector(".el-dialog__header");
        // 修改鼠标图标样式
        headerDOM.style.cursor = "move";
        // 禁止拖拽时选中标题中文本内容
        headerDOM.style.userSelect = "none";
        // 获取弹框区域的DOM节点
        const dialogDOM = el.querySelector(".el-dialog");

        let isDown = false; //是否按下
        // 鼠标按下时坐标位置
        let clientX = 0;
        let clientY = 0;
        // 按下时弹框位置
        let dialogLeft = 0;
        let dialogTop = 0;
        // 定义函数判断当前是否在可见范围内
        function boundingRange() {
          const bounding = dialogDOM.getBoundingClientRect();
          return {
            top: bounding.top >= 0, // 表示顶部在可见范围
            left: bounding.left >= 0, // 表示左侧在可见范围
            right: bounding.left < window.innerWidth - bounding.width, // 表示右侧在指定范围
            bottom: bounding.top < window.innerHeight - bounding.height // 表示底部在指定范围
          };
        }
        // 更新数据
        function update(e) {
          // 获取当前鼠标按钮位置坐标
          clientX = e.clientX;
          clientY = e.clientY;
          // 获取弹框位置(默认情况弹框样式left和top可能不存在,当为NaN时初始化为0)
          dialogLeft = isNaN(parseFloat(dialogDOM.style.left))
            ? 0
            : parseFloat(dialogDOM.style.left);
          dialogTop = isNaN(parseFloat(dialogDOM.style.top))
            ? 0
            : parseFloat(dialogDOM.style.top);
        }
        // 监听鼠标按下事件
        headerDOM.onmousedown = e => {
          isDown = true;

          update(e);
          // 获取当前鼠标按钮位置坐标
          clientX = e.clientX;
          clientY = e.clientY;
          // 获取弹框位置(默认情况弹框样式left和top可能不存在,当为NaN时初始化为0)
          dialogLeft = isNaN(parseFloat(dialogDOM.style.left))
            ? 0
            : parseFloat(dialogDOM.style.left);
          dialogTop = isNaN(parseFloat(dialogDOM.style.top))
            ? 0
            : parseFloat(dialogDOM.style.top);
        };
        // 监听鼠标移动事件
        dialogDOM.onmousemove = e => {
          // 不按下的时候,执行移动操作
          if (isDown) {
            // 获取DOM边界范围
            const range = boundingRange();
            // 获取当前移动到的位置坐标,与按下位置坐标进行计算,获取移动距离
            const distX = e.clientX - clientX;
            const distY = e.clientY - clientY;
            // 判断左侧或右侧是否可移动
            if ((range.left && distX < 0) || (range.right && distX >= 0))
              dialogDOM.style.left = dialogLeft + distX + "px";
            // 判断顶部或者底部是否可移动
            if ((range.top && distY < 0) || (range.bottom && distY >= 0))
              dialogDOM.style.top = dialogTop + distY + "px";

            // 更新起始位数据
            update(e);
          }
        };
        // 监听鼠标松开事件
        headerDOM.onmouseup = e => (isDown = false);
        window.onmouseup = () => (isDown = false);
      }
    }
  }

        直接放在created methods通级下,然后再el-dialog下进行使用 

方案2:(觉得挺好用)

        使用方法是  v-drags="true" 第二个参数用于判断是否可拖动

  drags: {
      bind(el, binding) {
        console.log(131231, el, binding);
        // 自定义属性,判断是否可拖拽
        if (!binding.value) return;

        const dialogHeaderEl = el.querySelector(".el-dialog__header");
        const dragDom = el.querySelector(".el-dialog");
        dialogHeaderEl.style.cursor = "move";
        // 禁止拖拽时选中标题中文本内容
        dialogHeaderEl.style.userSelect = "none";
        // 获取原有属性
        const sty = (function() {
          if (document.body.currentStyle) {
            // 在 IE 下兼容写法
            return (dom, attr) => dom.currentStyle[attr];
          }
          return (dom, attr) => getComputedStyle(dom, null)[attr];
        })();

        dialogHeaderEl.onmousedown = e => {
          // 鼠标按下,计算当前元素距离可视区的距离
          const disX = e.clientX - dialogHeaderEl.offsetLeft;
          const disY = e.clientY - dialogHeaderEl.offsetTop;
          const screenWidth = document.body.clientWidth; // body当前宽度
          const screenHeight = document.documentElement.clientHeight; // 可见区域高度

          const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
          const dragDomHeight = dragDom.offsetHeight; // 对话框高度

          const minDragDomLeft = dragDom.offsetLeft;
          const maxDragDomLeft =
            screenWidth - dragDom.offsetLeft - dragDomWidth;

          const minDragDomTop = dragDom.offsetTop;
          const maxDragDomTop =
            screenHeight - dragDom.offsetTop - dragDomHeight;

          // 获取到的值带px 正则匹配替换
          let styL = sty(dragDom, "left");
          if (styL === "auto") styL = "0px";
          let styT = sty(dragDom, "top");

          if (styL.includes("%")) {
            styL = +document.body.clientWidth * (+styL.replace(/%/g, "") / 100);
            styT =
              +document.body.clientHeight * (+styT.replace(/%/g, "") / 100);
          } else {
            styL = +styL.replace(/px/g, "");
            styT = +styT.replace(/px/g, "");
          }

          document.onmousemove = function(e) {
            // 通过事件委托,计算移动的距离
            let left = e.clientX - disX;
            let top = e.clientY - disY;

            // 边界处理
            if (-left > minDragDomLeft) {
              left = -minDragDomLeft;
            } else if (left > maxDragDomLeft) {
              left = maxDragDomLeft;
            }

            if (-top > minDragDomTop) {
              top = -minDragDomTop;
            } else if (top > maxDragDomTop) {
              top = maxDragDomTop;
            }

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

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

          return false; // 防止文本选中
        };
      }
    }

vue3 版本的:

const vDrags = {
  mounted(el, binding) {
    // 自定义属性,判断是否可拖拽
    if (!binding.value) return;
    const dialogHeaderEl = el.querySelector(".dialog_header");
    const dragDom = el.querySelector(".dialog_content");
    dialogHeaderEl.style.cssText += ";cursor:move;";
    // dragDom.style.cssText += ';bottom:0px;'

    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = (function () {
      if (document.body.currentStyle) {
        // 在ie下兼容写法
        return (dom, attr) => dom.currentStyle[attr];
      }
      return (dom, attr) => getComputedStyle(dom, null)[attr];
    })();

    dialogHeaderEl.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;
      const screenWidth = document.body.clientWidth; // body当前宽度
      const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)

      const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
      const dragDomheight = dragDom.offsetHeight; // 对话框高度

      const minDragDomLeft = dragDom.offsetLeft;
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;

      const minDragDomTop = dragDom.offsetTop;
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;

      // 获取到的值带px 正则匹配替换
      let styL = sty(dragDom, "left");
      // 为兼容ie
      if (styL === "auto") styL = "0px";
      let styT = sty(dragDom, "top");

      // console.log(styL)
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (styL.includes("%")) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, "") / 100);
        styT = +document.body.clientHeight * (+styT.replace(/%/g, "") / 100);
      } else {
        styL = +styL.replace(/px/g, "");
        styT = +styT.replace(/px/g, "");
      }

      document.onmousemove = function (e) {
        // 通过事件委托,计算移动的距离
        let left = e.clientX - disX;
        let top = e.clientY - disY;
        // 边界处理
        if (-left > minDragDomLeft) {
          left = -minDragDomLeft;
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft;
        }

        if (-top > minDragDomTop) {
          top = -minDragDomTop;
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop;
        }

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

        ChangeTop(top + styT);
      };

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

参考文章

element的Dialog的二次封装_el-dialog二次封装-CSDN博客

Element-UI实现el-dialog弹框拖拽功能_el-dialog 拖拽-CSDN博客

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值