在Vue2项目中,Element UI是一个非常流行的UI组件库,提供了丰富的组件来帮助开发者快速构建用户界面。本文将介绍如何在Vue2项目中使用Element UI的对话框组件(el-dialog)实现拖拽功能。

目录

1. 项目结构

2. 实现拖拽功能的对话框组件

3. 封装拖拽的全局自定义指令

3.1创建draggable.js

3.2.创建index.js3.

3.3.全局引用

3.4.使用方法

4. 总结


1. 项目结构

首先,确保你的项目中已经安装了Element UI。如果没有安装,可以通过以下命令进行安装:

npm install element-ui --save

然后在你的Vue项目中引入Element UI:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

2. 实现拖拽功能的对话框组件

接下来,我们将创建一个包含表格和对话框的组件,并在对话框中实现拖拽功能。

直接复制即可食用

<template>
  <div>
    <!-- 表格展示 -->
    <el-table :data="tableData" style="width: 100%" border>
      <!-- 表格列定义 -->
      <el-table-column
        prop="name"
        label="名称"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        prop="standard"
        label="标准值"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        prop="expected"
        label="期望值"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        prop="experience"
        label="经验值"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        prop="warning"
        label="预警值"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        prop="emergency"
        label="紧急值"
        width="180"
        header-align="center"
        align="center"
      ></el-table-column>
      <el-table-column
        label="操作"
        width="120"
        header-align="center"
        align="center"
      >
        <!-- 操作按钮 -->
        <template slot-scope="scope">
          <el-button @click="handleEdit(scope.row)" type="primary" size="small"
            >编辑</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <!-- 可拖拽的弹窗 -->
    <el-dialog
      width="50%"
      :visible.sync="dialogVisible"
      title="编辑"
      v-draggable
    >
      <!-- 表单 -->
      <el-form
        :model="editForm"
        :rules="rules"
        ref="editFormRef"
        label-width="100px"
      >
        <!-- 表单项 -->
        <el-form-item label="名称:">
          <el-input v-model="editForm.name" disabled></el-input>
        </el-form-item>
        <el-form-item label="标准值:" prop="standard">
          <el-input
            v-model="editForm.standard"
            placeholder="请输入标准值"
          ></el-input>
        </el-form-item>
        <el-form-item label="期望值:" prop="expected">
          <el-input
            v-model="editForm.expected"
            placeholder="请输入期望值"
          ></el-input>
        </el-form-item>
        <el-form-item label="经验值:" prop="experience">
          <el-input
            v-model="editForm.experience"
            placeholder="请输入经验值"
          ></el-input>
        </el-form-item>
        <el-form-item label="预警值:" prop="warning">
          <el-input
            v-model="editForm.warning"
            placeholder="请输入预警值"
          ></el-input>
        </el-form-item>
        <el-form-item label="紧急值:" prop="emergency">
          <el-input
            v-model="editForm.emergency"
            placeholder="请输入紧急值"
          ></el-input>
        </el-form-item>
      </el-form>
      <!-- 弹窗底部按钮 -->
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSave">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    // 输入验证函数
    const validateInput = (rule, value, callback) => {
      const regex = /^([<>]=?|=)?\d+$/; // 允许的格式:符号和数字
      if (value && !regex.test(value)) {
        callback(new Error("输入格式不正确,只能包含符号和数字"));
      } else {
        callback();
      }
    };

    return {
      tableData: [], // 表格数据
      dialogVisible: false, // 控制弹窗显示
      editForm: {
        name: "",
        standard: "",
        expected: "",
        experience: "",
        warning: "",
        emergency: "",
      }, // 表单数据
      rules: {
        standard: [{ validator: validateInput, trigger: "blur" }],
        expected: [{ validator: validateInput, trigger: "blur" }],
        experience: [{ validator: validateInput, trigger: "blur" }],
        warning: [{ validator: validateInput, trigger: "blur" }],
        emergency: [{ validator: validateInput, trigger: "blur" }],
      }, // 表单验证规则
    };
  },
  mounted() {
    // 模拟后端返回的数据
    const backendData = {
      name: "项目2",
      standard: {
        original: ">100",
        symbol: ">",
        number: "100",
      },
      expected: {
        original: "<100",
        symbol: "<",
        number: "100",
      },
      experience: {
        original: "<100",
        symbol: "<",
        number: "100",
      },
      warning: {
        original: "<100",
        symbol: "<",
        number: "100",
      },
      emergency: {
        original: "<100",
        symbol: "<",
        number: "100",
      },
    };

    // 格式化数据以便在表格中显示
    this.tableData = [
      {
        name: backendData.name,
        standard: backendData.standard.original,
        expected: backendData.expected.original,
        experience: backendData.experience.original,
        warning: backendData.warning.original,
        emergency: backendData.emergency.original,
      },
    ];
  },
  methods: {
    // 编辑按钮点击事件
    handleEdit(row) {
      this.editForm = { ...row };
      this.dialogVisible = true;
    },
    // 保存按钮点击事件
    handleSave() {
      this.$refs.editFormRef.validate((valid) => {
        if (valid) {
          // 分离符号和数字,并添加原始内容字段
          const result = {};
          for (let key in this.editForm) {
            const match = this.editForm[key].match(/^([<>]=?|=)?(\d+)$/);
            if (match) {
              result[key] = {
                original: this.editForm[key],
                symbol: match[1] || "",
                number: match[2],
              };
            }
          }

          // 这里可以调用API传递数据给后端
          console.log(result, "result");

          this.dialogVisible = false;
        } else {
          console.log("表单验证失败");
          return false;
        }
      });
    },
  },
  directives: {
    // 自定义指令:实现弹窗拖拽功能
    draggable: {
      // bind钩子函数在指令第一次绑定到元素时调用
      bind(el) {
        // 获取弹窗头部元素
        const dialogHeaderEl = el.querySelector(".el-dialog__header");
        // 获取整个弹窗元素
        const dragDom = el.querySelector(".el-dialog");

        // 设置鼠标样式为移动
        dialogHeaderEl.style.cursor = "move";

        // 监听鼠标按下事件
        dialogHeaderEl.onmousedown = (e) => {
          // 计算鼠标相对弹窗头部的偏移量
          const disX = e.clientX - dialogHeaderEl.offsetLeft;
          const disY = e.clientY - dialogHeaderEl.offsetTop;

          // 获取可视区域的宽高
          const screenWidth = document.body.clientWidth;
          const screenHeight = document.body.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;

          // 用于存储弹窗的位置信息
          let styL, styT;

          // 处理弹窗位置的百分比值转换为像素值
          if (window.getComputedStyle(dragDom).left.includes("%")) {
            // 如果位置使用百分比,转换为像素值
            styL =
              +document.body.clientWidth *
                (+window.getComputedStyle(dragDom).left.replace(/%/g, "") / 100);
            styT =
              +document.body.clientHeight *
                (+window.getComputedStyle(dragDom).top.replace(/%/g, "") / 100);
          } else {
            // 如果位置使用像素值,直接获取数值
            styL = +window.getComputedStyle(dragDom).left.replace(/px/g, "");
            styT = +window.getComputedStyle(dragDom).top.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;`;
          };

          // 监听鼠标松开事件
          document.onmouseup = function () {
            // 清除鼠标移动和松开事件的监听
            document.onmousemove = null;
            document.onmouseup = null;
          };
        };
      },
    },
  },
};
</script>

<style>
/* 自定义样式 */
.el-table th {
  background-color: #f5f7fa;
  color: #333;
  font-weight: bold;
}

.dialog-footer {
  text-align: right;
}
</style>

3. 封装拖拽的全局自定义指令

在上面的代码中,我们通过自定义指令v-draggable实现了对话框的拖拽功能。下面我们将进行全局封装方便使用:

新建directives文件夹里面创建draggable.js和index.js

directives文件夹主要存放全局的自定义指令

draggable.js是我们封装的拖拽功能

index.js为/统一注册指令,方便管理

3.1创建draggable.js
//src\directives\draggable.js
// 拖拽指令
export const draggable = {
  bind(el) {
    // 获取弹窗头部元素
    const dialogHeaderEl = el.querySelector(".el-dialog__header");
    // 获取整个弹窗元素
    const dragDom = el.querySelector(".el-dialog");

    // 设置鼠标样式为移动
    dialogHeaderEl.style.cursor = "move";

    // 监听鼠标按下事件
    dialogHeaderEl.onmousedown = (e) => {
      // 计算鼠标相对弹窗头部的偏移量
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;

      // 获取可视区域的宽高
      const screenWidth = document.body.clientWidth;
      const screenHeight = document.body.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;

      // 用于存储弹窗的位置信息
      let styL, styT;

      // 处理弹窗位置的百分比值转换为像素值
      if (window.getComputedStyle(dragDom).left.includes("%")) {
        styL =
          +document.body.clientWidth *
          (+window.getComputedStyle(dragDom).left.replace(/%/g, "") / 100);
        styT =
          +document.body.clientHeight *
          (+window.getComputedStyle(dragDom).top.replace(/%/g, "") / 100);
      } else {
        styL = +window.getComputedStyle(dragDom).left.replace(/px/g, "");
        styT = +window.getComputedStyle(dragDom).top.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;`;
      };

      // 监听鼠标松开事件
      document.onmouseup = function () {
        // 清除鼠标移动和松开事件的监听
        document.onmousemove = null;
        document.onmouseup = null;
      };
    };
  },
}; 
3.2.创建index.js.
import { draggable } from './draggable'

// 统一注册指令
export default {
  install(Vue) {
    Vue.directive('draggable', draggable)
    // 这里可以注册更多的全局指令
  }
} 
3.3.全局引用

3.4.使用方法

在需要使用的地方加上v-draggable即可

4. 总结

通过以上步骤,我们成功在Vue2项目中使用Element UI的对话框组件实现了拖拽功能。这个功能可以提升用户体验,特别是在需要频繁操作对话框的场景中。希望本文对你有所帮助!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐