基于el-table封装的可拖拽行列、选择列组件的实现

效果

需要环境

vue
elementUI
拖拽插件Sortable.js

需配置属性

示例

<HTable
  :columns="columns"
  :data="list"
  :setColumn="true"
  tableKey="CategoriesList"
  style="width: 100%"
  border
>
  // 这里可以放插槽
  <template slot="create_time" slot-scope="scope">
    {{ scope.column.label + scope.item.prop }}
  </template>
  <template slot="action" slot-scope="scope">
    <el-button type="primary" @click="handleEdit(scope.row)" size="small">
      编辑
    </el-button>
    <el-button @click="handleDelete(scope.row)" type="danger" size="small">
      删除
    </el-button>
  </template>
</HTable>
import HTable from "@/components/HTable";

export default {
  components: { HTable },
  data() {
    return {
      list: [],
      columns: [
        {
          label: "ID", // 描述
          prop: "_id", // 列的唯一值。 必须要有
          checked: true // 是否展示该列
          ... // 一些el-table-column的属性都可以写在这里
        },
        {
          label: "分类名称",
          prop: "name",
          checked: true
        },
        {
          label: "上级分类",
          prop: "parent.name",
          checked: true
        },
        {
          label: "状态",
          prop: "status",
          width: "100",
          checked: true
        },
        {
          label: "创建时间",
          prop: "create_time",
          slotHeaderName: "create_time", // 自定义表头
          checked: true
        },
        {
          label: "操作",
          prop: "action",
          fixed: "right",
          "min-width": "100",
          slotName: "action", // 自定义单元格插槽
          checked: true,
          disabled: true
        }
      ]
    };
  }
};

有用到的话给我点个赞!附组件代码

<template>
  <div class="HTable">
    <div class="settingBox" v-if="setColumn">
      <el-popover
        placement="bottom-end"
        trigger="click"
        popper-class="settingPopper"
      >
        <el-checkbox-group
          v-model="selectCol"
          @change="handleChangeSelectColumn"
        >
          <el-checkbox
            v-for="item in col"
            :key="item.prop"
            :label="item.prop"
            :disabled="item.disabled"
            style="display:block;line-height:2;margin-right:0;"
            >{{ item.label }}</el-checkbox
          >
        </el-checkbox-group>
        <i class="icon el-icon-setting" slot="reference"></i>
      </el-popover>
    </div>
    <el-table
      v-bind="$attrs"
      :data="tableData"
      v-on="$listeners"
      :key="JSON.stringify(checkedCol)"
    >
      <el-table-column
        v-for="(item, index) in checkedCol"
        :key="item.prop"
        v-bind="item"
        :index="index"
        :column-key="item.prop"
      >
        <template v-if="item.slotHeaderName" v-slot:header="scope">
          <slot :name="item.slotHeaderName" v-bind="scope" :item="item"></slot>
        </template>
        <template v-if="item.slotName" v-slot:default="scope">
          <slot :name="item.slotName" v-bind="scope"></slot>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import Sortable from "sortablejs";
export default {
  name: "HTable",
  props: {
    tableKey: String,
    columns: {
      type: Array,
      default() {
        return [];
      }
    },
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    setColumn: {
      type: Boolean,
      default: false
    }
  },
  watch: {
    columns: {
      handler(newVal) {
        let localVal = this.getStorageCol();
        let hotVal = [];
        if (localVal) {
          hotVal = this.dataDiff(newVal, localVal);
        } else {
          hotVal = [...newVal];
        }
        this.col = hotVal.map(
          (item, index) =>
            (item = { ...item, index, checked: item.checked || false })
        );
        this.checkedCol = this.checkedColFun(this.col);
        this.selectCol = this.checkedCol.map(item => (item = item.prop));
      },
      immediate: true
    },
    data: {
      handler(newVal) {
        this.tableData = [...newVal];
      },
      immediate: true
    },
    col: {
      handler(newVal) {
        this.setStorageCol(newVal);
      },
      deep: true,
      immediate: true
    }
  },
  data() {
    return {
      tableData: [],
      col: [],
      checkedCol: [],
      selectCol: []
    };
  },

  mounted() {
    document.body.ondrop = function(event) {
      event.preventDefault();
      event.stopPropagation();
    };
    this.$nextTick(() => {
      this.rowDrop();
      this.columnDrop();
    });
  },
  methods: {
    drap() {
      this.$nextTick(() => {
        this.rowDrop();
        this.columnDrop();
      });
    },

    handleChangeSelectColumn() {
      this.col.forEach(item => {
        if (this.selectCol.includes(item.prop)) {
          item.checked = true;
        } else {
          item.checked = false;
        }
      });
      this.checkedCol = this.checkedColFun(this.col);
      this.drap();
    },

    rowDrop() {
      const tbody = document.querySelector(".el-table__body-wrapper tbody");
      Sortable.create(tbody, {
        onEnd: ({ newIndex, oldIndex }) => {
          [this.tableData[newIndex], this.tableData[oldIndex]] = [
            this.tableData[oldIndex],
            this.tableData[newIndex]
          ];
          this.drap();
          this.$emit("dropRow", {
            drapRow: this.tableData[oldIndex],
            targetRow: this.tableData[newIndex],
            drapRowIndex: oldIndex,
            targetRowIndex: newIndex,
            data: this.tableData
          });
        }
      });
    },
    columnDrop() {
      const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
      Sortable.create(wrapperTr, {
        animation: 180,
        delay: 0,
        onEnd: ({ newIndex, oldIndex }) => {
          const oldItem = this.checkedCol[oldIndex];
          const newItem = this.checkedCol[newIndex];
          [this.col[newItem.index].index, this.col[oldItem.index].index] = [
            oldItem.index,
            newItem.index
          ];
          this.col.sort((a, b) => {
            return a.index - b.index;
          });
          this.checkedCol = this.checkedColFun(this.col);
          this.tableData = this.tableData.slice(0, this.tableData.length);
          this.drap();
          this.$emit("dropCol", {
            colItem: oldItem,
            newIndex: newIndex,
            oldIndex: oldIndex,
            column: this.checkedCol
          });
        }
      });
    },
    checkedColFun(arr) {
      return arr.filter(item => item.checked);
    },
    setStorageCol(data) {
      if (this.tableKey && data && data.length > 0) {
        localStorage.setItem("HTable-" + this.tableKey, JSON.stringify(data));
      }
    },
    getStorageCol() {
      let datajson = localStorage.getItem("HTable-" + this.tableKey);
      return datajson ? JSON.parse(datajson) : "";
    },
    dataDiff(newVal, localVal) {
      let nl = newVal.length;
      let ll = localVal.length;
      if (nl != ll) {
        return newVal;
      } else {
        let np = newVal.map(item => item.prop).sort();
        let lp = localVal.map(item => item.prop).sort();
        if (np.join() != lp.join()) {
          return newVal;
        } else {
          let nnl = [];
          for (let i = 0; i < localVal.length; i++) {
            const item_l = localVal[i];
            for (let j = 0; j < newVal.length; j++) {
              const item_n = newVal[j];
              if (item_l.prop === item_n.prop) {
                nnl.push({
                  ...item_n,
                  index: item_l.index
                });
              }
            }
          }
          return nnl;
        }
      }
    }
  }
};
</script>

<style lang="less" scoped>
.HTable {
  position: relative;
  .settingBox {
    width: 36px;
    height: 36px;
    border-radius: 2px;
    border: 1px solid #ebeef5;
    border-bottom: 0;
    margin-left: auto;
    position: relative;
    .icon {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
      width: 36px;
      height: 36px;
      text-align: center;
      font-size: 20px;
      line-height: 36px;
      color: #909399;
      cursor: pointer;
    }
  }
}
</style>
<style lang="less">
.settingPopper {
  min-width: 100px !important;
}
</style>

到此这篇关于基于el-table封装的可拖拽行列、选择列组件的实现的文章就介绍到这了,更多相关el-table 可拖拽行列内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们! 

(0)

相关推荐

  • vue修改Element的el-table样式的4种方法

    修改Element中的el-table样式,可以使用以下几种方法: 1. row-style 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style. 2. cell-style 单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有单元格设置一样的 Style. 3. header-row-style 表头行的 style 的回调方法,也可以使用一个固定的 Object 为所有表头行设置一样的 Style. 4. header-c

  • vue完美实现el-table列宽自适应

    背景 Element UI 是 PC 端比较流行的 Vue.js UI 框架,它的组件库基本能满足大部分常见的业务需求.但有时候会有一些定制性比较高的需求,组件本身可能没办法满足.最近在项目里就碰到了. 很多页面都需要用到表格组件el-table.如果没有给el-table-column指定宽度,默认情况下会平均分配给剩余的列.在列数比较多的情况,如果el-table宽度限定在容器内,单元格里的内容就会换行.强制不换行,内容要么在单元格内滚动,要么就会溢出或被截断. 产品想要的效果是:内容保持单

  • 详解el-table表头文字换行的三种方式

    目录 问题描述 效果图 三种方式的代码 总结 问题描述 表格中的表头一般都是不换行的,不过有时候在某些业务场景中,需要让表头的文字换行展示一下,我们先看一下效果图 效果图 三种方式的代码 看注释就行啦. 演示的话,直接复制粘贴运行就行啦 <template> <div class="vueWrap"> <el-table style="width: 900px" :data="tableBody" border :h

  • vue+element获取el-table某行的下标,根据下标操作数组对象方式

    1.在vue中对数组中的某个对象进行操作时(替换.删除),都需要用到该对象在数组中的下标. 前端代码: scope.$index :获取当前行的下标 scope.row:获取当前行的对象 效果图: 思路:通过点击事件将该对象在数组中的下标传递到方法中,然后对数组进行操作 即可根据下标删除数组中对应的对象. 补充知识:vue-element-upload 文件上传打开选择文件弹框前进行提示或操作 在项目中使用文件上传功能时,需求是不能直接弹出弹框,要先二次确认或进行提示.引申开来,即可在打开选择文

  • vue中el-table实现自动吸顶效果(支持fixed)

    目录 前言 实现思路 效果: 使用: 主要源码: 前言 看了很多案例,从简单的角度,position:sticky,似乎是比较理想的选择,可是当el-table设置了fixed后,这里的fixed会失效.最后还是采用了js监听滚动的思路实现. 实现思路 表格距离顶部的距离 设置表格距离顶部多少就吸顶-offsetTop1 获取滚动条滚动的距离 当滚动条滚动 offsetTop1 后,表格就自动吸顶 效果: 使用: 在el-table标签中配置:v-sticky="{ top: 0, parent

  • vue el-table实现自定义表头

    本文实例为大家分享了vue el-table实现自定义表头的具体代码,供大家参考,具体内容如下 el-table可以通过设置 Scoped slot 来实现自定义表头. 文档说明如下: 代码实现: <template> <el-dialog width="50%" :visible.sync="isShow" :before-close="beforeClose" title="自定义设备类型属性">

  • vue 使用 sortable 实现 el-table 拖拽排序功能

    本文给大家介绍vue 使用 sortable 实现 el-table 拖拽排序功能,具体内容如下所示: npm 下载: npm install sortablejs --save 引入: import Sortable from "sortablejs"; 代码: <template> <div class="table"> <el-table ref="dragTable" :data="tableDat

  • vue+el-table实现合并单元格

    本文实例为大家分享了el-table实现合并单元格的具体代码,供大家参考,具体内容如下 el-table合并单元格(vue+element) - 先在el-table放入:span-method="arraySpanMethod" <el-table :header-cell-style="{background:'#eef1f6',color:'#606266'}" :data="merchantList" border :span-me

  • vue el-table实现行内编辑功能

    最近做一个vue前后端分离的项目,前端框架用element ui,在 使用 el-table 的过程中,需要实现行内编辑,效果大概是这样: 分为下面几个步骤: (1) 自定义 el-table 的表头(即添加 "新增" 按钮): <el-table :data="propTableData.col.filter(data => handleAdd || data.name.toLowerCase().includes(handleAdd.toLowerCase()

  • element el-table表格的二次封装实现(附表格高度自适应)

    前言 在公司实习使用vue+element-ui框架进行前端开发,使用表格el-table较为多,有些业务逻辑比较相似,有些地方使用的重复性高,如果多个页面使用相同的功能,就要多次重复写逻辑上差不多的代码,所以打算对表格这个组件进行封装,将相同的代码和逻辑封装在一起,把不同的业务逻辑抽离出来.话不多说,下面就来实现一下吧. 一.原生el-tbale代码--简单の封装 这里直接引用官方的基础使用模板,直接抄过来(✪ω✪),下面代码中主要是抽离html部分,可以看出每个el-table-column

随机推荐