vue2 element封装表格拖动列顺序、增减列

该博客介绍了如何在Vue.js应用中使用Element UI组件库实现表格列的拖拽调整顺序功能。通过监听mousedown、mousemove和mouseup事件,结合本地存储来保存列顺序,同时提供了表格列的增删配置。此外,还展示了如何在复合表格中整合这些功能,包括导出、分页和自定义操作。
摘要由CSDN通过智能技术生成

在这里插入图片描述

一共三个组件
  1. 拖动表格列的顺序:
<template>
  <div class="drag-table" :class="{ 'table-moving': dragState.dragging }">
    <el-table
      ref="table"
      v-loading="loading"
      element-loading-text="拼命加载中"
      element-loading-spinner="el-icon-loading"
      element-loading-background="rgba(0, 0, 0, 0.8)"
      highlight-current-row
      size="mini"
      :data="data"
      :row-key="tableKey"
      tooltip-effect="dark"
      border
      max-height="623"
      @selection-change="selectData"
      :cell-class-name="cellClassName"
      :header-cell-class-name="headerCellClassName"
    >
      <slot name="table-select"></slot>
      <el-table-column
        show-overflow-tooltip
        v-for="(col, index) in tableHeader"
        :key="index"
        :prop="col.prop"
        :label="col.label"
        :width="col.width"
        :min-width="col.minWidth"
        :type="col.type"
        :header-align="col.headerAlign"
        :column-key="index.toString()"
        :render-header="renderHeader"
      >
        <template slot-scope="scope">
          <slot name="table-body" :data="scope"></slot>
        </template>
      </el-table-column>
      <slot name="table-handle"></slot>
    </el-table>
  </div>
</template>

<script>
/**
 * @author        全易
 * @time          2021-04-16 12:39:56  星期五
 * @description   拖动表格列的顺序
 */

export default {
  name: "TableColumnDrag",
  data() {
    return {
      tableHeader: [],
      dragState: {
        start: -9, // 起始元素的 index
        end: -9, // 移动鼠标时所覆盖的元素 index
        dragging: false, // 是否正在拖动
        direction: undefined, // 拖动方向
      },
    };
  },
  created() {
    this.tableHeader =
      JSON.parse(localStorage.getItem(this.headId)) || this.header;
  },
  watch: {
    header(val, oldVal) {
      this.tableHeader = val;
    },
  },
  props: {
    headId: {
      type: String,
      default: "",
    },
    loading: {
      type: Boolean,
      default: true,
    },
    tableKey: {
      type: [String, Number],
      default: "",
    },
    data: {
      default: function () {
        return [];
      },
      type: Array,
    },
    header: {
      default: function () {
        return [];
      },
      type: Array,
    },
  },
  methods: {
    renderHeader(createElement, { column }) {
      return createElement(
        "div",
        {
          class: ["thead-cell"],
          on: {
            mousedown: ($event) => {
              this.handleMouseDown($event, column);
            },
            mousemove: ($event) => {
              this.handleMouseMove($event, column);
            },
          },
        },
        [
          // 添加 <a> 用于显示表头 label
          createElement("span", column.label),
          // 添加一个空标签用于显示拖动动画
          createElement("span", {
            class: ["virtual"],
          }),
        ]
      );
    },
    // 开始拖动
    handleMouseDown(e, column) {
      this.dragState.dragging = true;
      this.dragState.start = parseInt(column.columnKey);
      // 给拖动时的虚拟容器添加宽高
      let table = document.querySelector(".drag-table");
      let virtual = document.getElementsByClassName("virtual");
      for (let item of virtual) {
        item.style.height = table.clientHeight - 1 + "px";
        item.style.width = item.parentElement.parentElement.clientWidth + "px";
      }
      document.addEventListener("mouseup", this.handleMouseUp);
      console.log("1111111111");
    },
    // 拖动中
    handleMouseMove(e, column) {
      if (this.dragState.dragging) {
        let index = parseInt(column.columnKey); // 记录起始列
        if (index - this.dragState.start !== 0) {
          this.dragState.direction =
            index - this.dragState.start < 0 ? "left" : "right"; // 判断拖动方向
          this.dragState.end = parseInt(column.columnKey);
        } else {
          this.dragState.direction = undefined;
        }
      } else {
        return false;
      }
      console.log("222222222222");
    },
    // 拖动易位
    dragColumn({ start, end, direction }) {
      let tempData = [];
      let left = direction === "left";
      let min = left ? end : start - 1;
      let max = left ? start + 1 : end;
      for (let i = 0; i < this.tableHeader.length; i++) {
        if (i === end) {
          tempData.push(this.tableHeader[start]);
        } else if (i > min && i < max) {
          tempData.push(this.tableHeader[left ? i - 1 : i + 1]);
        } else {
          tempData.push(this.tableHeader[i]);
        }
      }
      this.tableHeader = tempData;
      console.log("33333333333", this.tableHeader);
      localStorage.setItem(this.headId, JSON.stringify(this.tableHeader));
    },
    // 结束拖动
    handleMouseUp() {
      this.dragColumn(this.dragState);
      // 初始化拖动状态
      this.dragState = {
        start: -9,
        end: -9,
        dragging: false,
        direction: undefined,
      };
      document.removeEventListener("mouseup", this.handleMouseUp);
      console.log("4444444444");
    },
    headerCellClassName({ column, columnIndex }) {
      let active =
        columnIndex === this.dragState.end
          ? `darg_active_${this.dragState.direction}`
          : "";
      let start = columnIndex === this.dragState.start ? "darg_start" : "";
      return `${active} ${start}`;
    },
    cellClassName({ column, columnIndex }) {
      return columnIndex === this.dragState.start ? "darg_start" : "";
    },
    // 选择数据
    selectData(val) {
      // console.log(val);
      this.$emit("selectData", val);
    },
    // 重新渲染表格
    resetTable() {
      this.$refs.table.doLayout();
    },
  },
};
</script>

<style lang="less" scoped>
.drag-table {
  /deep/.el-table {
    .darg_start {
      background-color: #f3f3f3;
    }
    .darg_active_left {
      .virtual {
        border-left: 2px dotted #666;
        z-index: 99;
      }
    }
    .darg_active_right {
      .virtual {
        border-right: 2px dotted #666;
        z-index: 99;
      }
    }
    .virtual {
      position: fixed;
      display: block;
      width: 0;
      height: 0;
      margin-left: -10px;
      background: none;
      border: none;
    }
    .thead-cell {
      cursor: grab;
    }
  }
}
.table-moving {
  /deep/.el-table {
    .thead-cell {
      cursor: e-resize !important;
    }
    .el-table__fixed {
      cursor: not-allowed;
    }
  }
}
</style>
  1. 表格增减列:
<template>
  <div class="table-column-config">
    <el-checkbox
      :indeterminate="isIndeterminate"
      v-model="isCheckAll"
      @change="selectAll"
      >全选</el-checkbox
    >
    <hr />
    <el-checkbox-group v-model="defaulte" :min="2" @change="selecting">
      <el-checkbox v-for="column in whole" :label="column" :key="column">
        {{ column }}
      </el-checkbox>
    </el-checkbox-group>
  </div>
</template>

<script>
/**
 * @author        全易
 * @time          2021-04-15 09:50:12  星期四
 * @description   配置表格列的增减
 */

export default {
  name: "TableColumnIncDec",
  data() {
    return {
      isIndeterminate: true,
      isCheckAll: false,
      original: [],
      defaulte: [],
      whole: [],
    };
  },
  props: {
    dataId: {
      type: String,
      default: "",
    },
    now: {
      type: Array,
      default: [],
    },
    all: {
      type: Array,
      default: [],
    },
  },
  watch: {
    all: {
      handler(now) {
        // console.log(now);
        this.original = now;
        this.whole = now.map((item) => {
          return item.label;
        });
      },
      immediate: true,
    },
    now: {
      handler(now) {
        // console.log(now);
        this.defaulte = now.map((item) => {
          return item.label;
        });
      },
      immediate: true,
    },
  },
  methods: {
    // 全选
    selectAll(val) {
      console.log(val);
      val ? (this.defaulte = this.whole) : (this.defaulte.length = 2);
      this.isIndeterminate = false;

      this.configTable();
    },
    // 单选
    selecting(value) {
      // console.log(value);
      this.defaulte = value;
      this.isCheckAll = value.length === this.whole.length;
      this.isIndeterminate =
        value.length > 0 && value.length < this.whole.length;

      this.configTable();
    },
    // 配置表格
    configTable() {
      console.log(this.original, this.defaulte);
      let result = this.defaulte.map((item) => {
        let now;
        for (let i = 0, length = this.original.length; i < length; i++) {
          if (item === this.original[i].label) {
            now = {
              label: this.original[i].label,
              prop: this.original[i].prop,
            };
          }
        }
        // console.log(now);
        return now;
      });
      console.log(result);
      console.log(this.dataId);
      localStorage.setItem(this.dataId, JSON.stringify(result));
      this.$emit("ok", result);
    },
  },
};
</script>

<style lang="less" scoped>
.el-checkbox-group {
  display: grid;
  .el-checkbox {
    margin-right: 0;
  }
}
</style>
  1. 合并复合表格
    记得引入前两个表格
<template>
  <div class="custom-table">
    <div class="table-handle">
      <el-button-group class="functions">
        <slot name="function"></slot>
        <el-dropdown trigger="click" @command="exporting">
          <el-button size="small">
            导出<i class="el-icon-arrow-down el-icon--right"></i>
          </el-button>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item command="1">导出本页</el-dropdown-item>
            <el-dropdown-item command="2">导出全部</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </el-button-group>
      <div class="table-config">
        <slot name="config"></slot>
        <el-popover trigger="manual" v-model="config.show" width="160">
          <TableColumnIncDec
            :dataId="dataId"
            :now="config.nowColumn"
            :all="config.allColumn"
            @ok="okConfigTable"
          />
          <el-button
            slot="reference"
            size="small"
            icon="el-icon-setting"
            title="表格配置"
            @click="config.show = !config.show"
          ></el-button>
        </el-popover>
      </div>
    </div>
    <TableColumnDrag
      ref="table"
      :loading="loading"
      :headId="headId"
      :data="data"
      :tableKey="tableKey"
      :header="config.nowColumn"
      @selectData="selectData"
    >
      <template v-slot:table-select>
        <slot name="select"></slot>
      </template>
      <template v-slot:table-body="{data}">
        <slot name="body" :data="data"></slot>
      </template>
      <template v-slot:table-handle>
        <slot name="handle"></slot>
      </template>
    </TableColumnDrag>
    <el-pagination
      @size-change="dataSizeChange"
      @current-change="handleCurrentChange"
      :page-sizes="[15, 20, 30, 50]"
      :page-size="pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
    >
    </el-pagination>
  </div>
</template>

<script>
/**
 * @author        全易
 * @time          2021-04-15 14:23:16  星期四
 * @description   复合表格
 */

export default {
  name: "CustomTable",
  data() {
    return {};
  },
  props: {
    headId: {
      type: String,
      default: "",
    },
    dataId: {
      type: String,
      default: "",
    },
    loading: {
      type: Boolean,
      default: true,
    },
    data: {
      type: Array,
      default: [],
    },
    tableKey: {
      type: [String, Number],
      default: "",
    },
    total: {
      type: Number,
      default: 0,
    },
    pageSize: {
      type: Number,
      default: 0,
    },
    config: {
      type: Object,
      default: {},
    },
  },
  methods: {
    dataSizeChange(val) {
      console.log(`每页 ${val}`);
      this.$emit("dataSize", val);
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.$emit("handleCurrent", val);
    },
    // 选择数据
    selectData(val) {
      // console.log(val);
      this.$emit("selectData", val);
    },
    // 确定配置表格列
    okConfigTable(data) {
      // console.log(data);
      this.config.nowColumn = data;
      this.$refs.table.resetTable();
    },
    // 导出
    exporting(command) {
      this.$emit("exporting", command);
    },
  },
};
</script>

<style lang="less" scoped>
</style>


调用:

举个例子

 <CustomTable
   tableKey="userId"
   :loading="loading"
   :data="tableData"
   :dataId="tableDataId"
   :headId="tableHeadId"
   :total="total"
   :pageSize="queryForm.pageSize"
   :config="tableConfig"
   @exporting="exporting"
   @handleCurrent="handleCurrentChange"
   @dataSize="dataSizeChange"
 >
   <template v-slot:body="{ data }">
     <el-popover
       v-if="data.column.property === 'avatar'"
       trigger="hover"
       placement="left"
     >
       <img
         slot="reference"
         :src="data.row.avatar"
         alt="用户头像"
         style="width: 28px; height: 28px"
       />
       <img
         :src="data.row.avatar"
         alt="用户头像"
         style="width: 180px; height: 180px"
       />
     </el-popover>
     <el-tag
       v-else-if="data.column.property === 'runStatus'"
       :type="data.row.runStatus === '正常' ? 'success' : 'danger'"
     >
       {{ data.row.runStatus }}
     </el-tag>
     <span v-else-if="data.column.property === 'userId'">{{
       data.row.userId
     }}</span>
     <span v-else-if="data.column.property === 'loginName'">
       {{ data.row.loginName }}
     </span>
     <span v-else-if="data.column.property === 'userName'">
       {{ data.row.userName }}</span
     >
     <span v-else-if="data.column.property === 'phonenumber'">
       {{ data.row.phonenumber }}
     </span>
     <span v-else-if="data.column.property === 'balance'">
       {{ data.row.balance }}
     </span>
     <span v-else-if="data.column.property === 'integral'">
       {{ data.row.integral }}
     </span>
   </template>
   <template v-slot:handle>
     <el-table-column label="操作" width="200" fixed="right">
       <template slot-scope="scope">
         <el-button @click="detail(scope.row)" type="primary" size="mini"
           >查看</el-button
         >
         <el-button
           :type="scope.row.runStatus === '正常' ? 'info' : 'danger'"
           size="mini"
           @click="changeStatus(scope.row)"
         >
           {{ scope.row.runStatus === "正常" ? "冻结" : "解冻" }}
         </el-button>
         <el-dropdown
           trigger="click"
           @command="userHistory($event, scope.row)"
         >
           <el-button size="small">
             记录<i class="el-icon-arrow-down el-icon--right"></i>
           </el-button>
           <el-dropdown-menu slot="dropdown">
             <el-dropdown-item command="1">订单记录</el-dropdown-item>
             <el-dropdown-item command="2">退款记录</el-dropdown-item>
             <el-dropdown-item command="3">提现记录</el-dropdown-item>
             <el-dropdown-item command="4">充值记录</el-dropdown-item>
           </el-dropdown-menu>
         </el-dropdown>
       </template>
     </el-table-column>
   </template>
 </CustomTable>

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

美酒没故事°

谢谢看官

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

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

打赏作者

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

抵扣说明:

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

余额充值