vue实现可拖拽dialog封装

一、实现modal弹窗组件

<template>
  <div
    v-if="visible"
    class="customer-dialog"
    id="customer-dialog"
    :style="dialogStyles"
    v-dialogDrag:[dialogDrag]
  >
    <div class="dialog-container">
      <div
        class="dialog-header"
        id="dialog-header"
        :style="{
          '--dialog-center': `${center ? 'center' : 'left'}`
        }"
      >
        <slot name="header" v-if="footerHide">
          <span class="dialog__title">{{ title }}</span>
        </slot>
        <button class="dialog__header-close" @click="closeDialog">
          <i class="el-dialog__close el-icon el-icon-close"></i>
        </button>
      </div>
      <div class="dialog-body">
        <slot> </slot>
      </div>
      <div
        class="dialog-footer"
        :style="{
          '--dialog-center': `${center ? 'center' : 'right'}`
        }"
        v-if="footerHide"
      >
        <slot name="footer">
          <el-button size="small" @click="closeDialog">取消</el-button>
        </slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'CustomDialog',
  props: {
    title: {
      type: String,
      default: 'Default Title'
    },
    /**
     * @description 是否开启拖拽功能
     * @default false
     */
    dialogDrag: {
      type: Boolean,
      default: false
    },
    width: {
      type: [Number, String],
      default: 500
    },
    maxHeight: {
      type: [Number, String],
      default: 800
    },
    /**
     * 未传值时候,默认
     * top: 30%
     * 传入值时候,以传入值为准
     */
    top: {
      type: Number,
      default: 0
    },
    /**
     * 未传值时候,默认
     * left: 50%
     * 传入值时候,以传入值为准
     */
    left: {
      type: Number,
      default: 0
    },
    zIndex: {
      type: Number,
      default: 9999
    },
    /**
     * 是否显示
     * @default false
     */
    value: {
      type: Boolean,
      default: false
    },
    /**
     * 是否居中
     * @default true
     */
    center: {
      type: Boolean,
      default: false
    },
    /**
     * 是否隐藏footer
     * @default false
     */
    footerHide: {
      type: Boolean,
      default: false
    },

    /**
     * 是否隐藏header
     * @default false
     */
    headerHide: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    dialogStyles () {
      return {
        '--dialog-width':
          typeof this.width === 'number' ? `${this.width}px` : this.width,
        '--dialog-max-height':
          typeof this.maxHeight === 'number'
            ? `${this.maxHeight}px`
            : this.maxHeight,
        '--dialog-top': this.top
          ? typeof this.top === 'number'
            ? `${this.top}px`
            : this.top
          : '30%',
        '--dialog-left': this.left
          ? typeof this.left === 'number'
            ? `${this.left}px`
            : this.left
          : '50%',
        '--dialog-z-index': this.zIndex
      };
    },
    visible: {
      get () {
        return this.value;
      },
      set (val) {
        this.$emit('input', val);
      }
    }
  },
  methods: {
    closeDialog () {
      this.visible = false;
    }
  }
};
</script>

<style scoped lang="scss">
.customer-dialog {
  width: var(--dialog-width);
  max-width: calc(100vw - 32px);
  max-height: var(--dialog-max-height);
  color: rgba(0, 0, 0, 0.88);
  line-height: 1.5714285714285714;
  list-style: none;
  margin: 0 auto;
  position: fixed;
  top: var(--dialog-top);
  left: var(--dialog-left);
  transform: translate(-50%, -50%);
  overflow: auto;
  margin: 0;
  box-sizing: border-box;
  background-color: #ffffff;
  background-clip: padding-box;
  border: 0;
  border-radius: 8px;
  box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
    0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
  pointer-events: auto;
  z-index: var(--dialog-z-index);
  // overflow-y: auto;
}

.dialog-container {
  width: 100%;
  background: white;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  .dialog-header {
    width: 100%;
    background: rgba(0, 0, 0, 0.5);
    background-color: rgba(230, 233, 240, 0.15);
    border-bottom: 1px #e6e9f0 solid;
    padding: 10px 0;
    .dialog__title {
      font-weight: bold;
      display: block;
      font-size: 1rem;
      font-weight: bold;
      height: 32px;
      line-height: 32px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      margin-left: 15px;
    }
    .dialog__header-close {
      position: absolute;
      top: 10px;
      right: 10px;
      padding: 5px;
      background: transparent;
      border: none;
      outline: none;
      cursor: pointer;
      font-size: 16px;
      &:hover {
        font-size: 16px;
        font-weight: bold;
        color: rgba(0, 0, 0, 0.88);
        background-color: rgba(0, 0, 0, 0.06);
        transition: all 0.3s;
      }
    }
  }
  .dialog-body {
    box-sizing: border-box;
    font-size: 14px;
    line-height: 1.5714285714285714;
    word-wrap: break-word;
    flex: 1;
    overflow: auto;
    padding: 0 20px;
    position: relative;
  }
}

.dialog-header,
.dialog-body,
.dialog-footer {
  text-align: var(--dialog-center);
  margin-bottom: 10px;
}
.dialog-footer {
  display: flex;
  justify-content: var(--dialog-center);
  align-items: center;
  padding: 0 10px;
}
.footer-title {
  margin-right: 10px;
}
</style>

二、实现拖动自定义指令

/*
 * @Description:拖拽指令
 * @Author: rjl
 * @Date: 2024-07-10 18:03:37
 * @LastEditTime: 2024-07-16 20:34:54
 * @LastEditors: Ran junlin
 */
/**
 * 可拖动弹窗
 * @description 使用 v-dialogDrag 调用指令;
 */

import { nextTick } from 'vue';
export default {
    bind (el, binding) {
        nextTick(() => {
            const { arg } = binding;
          console.log(arg,'arg');
            if (!arg) return;
            const dialogHeaderEl = document.querySelector('.dialog-header');
            const dragDom = document.querySelector('.customer-dialog');
            if (!dialogHeaderEl || !dragDom) {
                return console.log('dom不存在');
            }
            dialogHeaderEl.style.cursor = 'move';

            // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
            const sty =
                dragDom.currentStyle || window.getComputedStyle(dragDom, null);

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

                // 获取到的值带px 正则匹配替换
                let styL, styT;

                // 注意在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, '');
                }

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

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

                    //将此时的位置传出去
                    //binding.value({x:e.pageX,y:e.pageY})
                };

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

三、使用

<Modal
  dialogDrag
  v-model="dialogVisible"
  width="550px"
  :left="790"
  :top="240"
  title="区域查车"
>
  <div class="body">
    <RTable
      ref="table"
      v-loading="pending"
      border
      :tableData="tableData"
      row-key="id"
      size="small"
      stripe
      width="100%"
      height="100%"
      :column="columns"
    />
  </div>
</Modal>

相关推荐

  1. vue实现dialog封装

    2024-07-19 22:34:02       19 阅读
  2. Vue3封装的弹窗

    2024-07-19 22:34:02       44 阅读
  3. 使el-dialog实现弹窗

    2024-07-19 22:34:02       37 阅读
  4. js实现元素方法

    2024-07-19 22:34:02       59 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 22:34:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 22:34:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 22:34:02       45 阅读
  4. Python语言-面向对象

    2024-07-19 22:34:02       55 阅读

热门阅读

  1. 网络安全-网络安全及其防护措施4

    2024-07-19 22:34:02       18 阅读
  2. php cms 如何适配php8.3

    2024-07-19 22:34:02       13 阅读
  3. 聚类优化:Scikit-Learn中的数据标签分配艺术

    2024-07-19 22:34:02       16 阅读
  4. 【乐吾乐2D可视化组态编辑器】切换画面

    2024-07-19 22:34:02       17 阅读
  5. 第4章 Express路由的深入理解(一)

    2024-07-19 22:34:02       16 阅读
  6. CSS实现从上往下过渡效果

    2024-07-19 22:34:02       16 阅读
  7. 每类数据保留前n条(sql)

    2024-07-19 22:34:02       12 阅读