antdesignvue表格实现伸缩列并限制最大最小宽度和限制操作列不可拖动

antdesignvue表格实现伸缩列并限制最大最小宽度和限制操作

  • 先看效果动图:
    在这里插入图片描述

代码部分:

<template>
  <a-table :rowKey="(record,index)=>index" :columns="columns" :components="components" :data-source="tableData" :scroll="{ x: scrollX }">
    <template v-slot:action>
      <a href="javascript:;">Delete</a>
    </template>
  </a-table>
</template>

<script>
import Vue from "vue";
import VueDraggableResizable from "vue-draggable-resizable";
Vue.component("vue-draggable-resizable", VueDraggableResizable);
export default {
    
    
  name: "App",
  data() {
    
    
    return {
    
    
      columns: [
        {
    
    
          title: "项目名称",
          dataIndex: "name",
          key: "name",
          width: 150,
          fixed: "left",
          ellipsis: true // 伸缩列过小时省略号显示
        },
        {
    
    
          title: "项目类型",
          dataIndex: "type",
          key: "type",
          width: 150,
          ellipsis: true
        },
        {
    
    
          title: "项目编号",
          dataIndex: "num",
          key: "num",
          width: 150,
          ellipsis: true
        },
        {
    
    
          title: "项目状态",
          dataIndex: "status",
          key: "status",
          width: 100,
          ellipsis: true
        },
        {
    
    
          title: "备注1",
          dataIndex: "commet1",
          key: "commet1",
          width: 200,
          ellipsis: true
        },
        {
    
    
          title: "备注2",
          dataIndex: "commet2",
          key: "commet2",
          width: 200,
          ellipsis: true
        },
        {
    
    
          title: "备注3",
          dataIndex: "commet3",
          key: "commet3",
          width: 200,
          ellipsis: true
        },
        {
    
    
          title: "备注4",
          dataIndex: "commet4",
          key: "commet4",
          width: 200,
          ellipsis: true
        },
        {
    
    
          title: "操作",
          dataIndex: "action",
          key: "action",
          scopedSlots: {
    
     customRender: "action" },
          width: 200,
          fixed: "right",
        },
      ],
      tableData: [
        {
    
    
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
        {
    
    
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
        {
    
    
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
      ],
    };
  },
  computed: {
    
    
    // 动态获取scrollX,防止数组固定列的大小变化
    scrollX() {
    
    
      return this.columns.reduce((preVal, curVal) => {
    
    
        return preVal + curVal.width
      }, 0)
    }
  },
  created() {
    
    
    this.initDrag(this.columns);
  },
  methods: {
    
    
    initDrag(columns) {
    
    
      const draggingMap = {
    
    };
      columns.forEach((col) => {
    
    
        draggingMap[col.key] = col.width;
      });
      const draggingState = Vue.observable(draggingMap);
      // resizeableTitle要小写,不然会有报错
      const resizeableTitle = (h, props, children) => {
    
    
        let thDom = null;
        const {
    
     key, ...restProps } = props;
        const col = columns.find((col) => {
    
    
          const k = col.dataIndex || col.key;
          return k === key;
        });
        // col.key === 'action'限制操作列不可伸缩
        if (!col.width || col.key === 'action') {
    
    
          return <th {
    
    ...restProps}>{
    
    children}</th>;
        }
        const onDrag = (x) => {
    
    
          draggingState[key] = 0;
          let width = Math.max(x, 100) // 此处100为伸缩时的最小宽度
          width = Math.min(width, 400) // 此处400为伸缩时的最大宽度
          col.width = width 
        };

        const onDragstop = () => {
    
    
          draggingState[key] = thDom.getBoundingClientRect().width;
        };
        return (
          <th
            {
    
    ...restProps}
            v-ant-ref={
    
    (r) => (thDom = r)}
            width={
    
    draggingState[key]}
            class="resize-table-th"
          >
            {
    
    children}
            <vue-draggable-resizable
              key={
    
    col.key}
              class="table-draggable-handle"
              w={
    
    10}
              x={
    
    col.width || draggingState[key]}
              z={
    
    1}
              axis="x"
              draggable={
    
    true}
              resizable={
    
    false}
              onDragging={
    
    onDrag}
              onDragstop={
    
    onDragstop}
            ></vue-draggable-resizable>
          </th>
        );
      };
      this.components = {
    
    
        header: {
    
    
          cell: resizeableTitle,
        },
      };
    },
  },
};
</script>
<style lang="less" scoped>
// 这里的deep必须,不然样式无法穿透内部
/deep/.resize-table-th {
    
    
  position: relative;
  .table-draggable-handle {
    
    
    height: 100% !important;
    bottom: 0;
    left: auto !important;
    right: 0px;
    cursor: col-resize;
    touch-action: none;
    position: absolute;
    transform: none !important;
    &:hover {
    
    
      background: #1890ff;
    }
    &::after {
    
    
      content: "|";
      position: absolute;
      top: 15px;
      left: 3px;
      color: #ccc;
    }
  }
}
</style>

下面新增了拖动表头的功能

需要安装sortablejs $ npm install sortablejs --save
代码如下:

<template>
  <div class="box">
    <a-table
      :rowKey="(record, index) => index"
      :columns="columns"
      :components="components"
      :data-source="tableData"
      :scroll="{ x: scrollX, y: 100 }"
    >
      <template v-slot:action>
        <a href="javascript:;">Delete</a>
      </template>
    </a-table>
  </div>
</template>

<script>
import Sortable from 'sortablejs' 
import Vue from "vue";
import VueDraggableResizable from "vue-draggable-resizable";
Vue.component("vue-draggable-resizable", VueDraggableResizable);
export default {
  name: "App",
  data() {
    return {
      columns: [
        {
          title: "项目名称",
          dataIndex: "name",
          key: "name",
          width: 150,
          fixed: "left",
          ellipsis: true, // 伸缩列过小时省略号显示
        },
        {
          title: "项目类型",
          dataIndex: "type",
          key: "type",
          width: 150,
          ellipsis: true,
        },
        {
          title: "项目编号",
          dataIndex: "num",
          key: "num",
          width: 150,
          ellipsis: true,
        },
        {
          title: "项目状态",
          dataIndex: "status",
          key: "status",
          width: 100,
          ellipsis: true,
        },
        {
          title: "备注1",
          dataIndex: "commet1",
          key: "commet1",
          width: 200,
          ellipsis: true,
        },
        {
          title: "备注2",
          dataIndex: "commet2",
          key: "commet2",
          width: 200,
          ellipsis: true,
        },
        {
          title: "备注3",
          dataIndex: "commet3",
          key: "commet3",
          width: 200,
          ellipsis: true,
        },
        {
          title: "备注4",
          dataIndex: "commet4",
          key: "commet4",
          width: 200,
          ellipsis: true,
        },
        {
          title: "操作",
          dataIndex: "action",
          key: "action",
          scopedSlots: { customRender: "action" },
          width: 200,
          fixed: "right",
        },
      ],
      tableData: [
        {
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
        {
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
        {
          name: "我是项目1111",
          type: "我是项目类型啊啊",
          num: "666-ffffa",
          status: "稳定中",
          commet1: "我是备注啊啊啊啊",
          commet2: "我是备注啊啊啊啊",
          commet3: "我是备注啊啊啊啊",
          commet4: "我是备注啊啊啊啊",
        },
      ],
    };
  },
  computed: {
    // 动态获取scrollX,防止数组固定列的大小变化
    scrollX() {
      return this.columns.reduce((preVal, curVal) => {
        return preVal + curVal.width;
      }, 0);
    },
  },
  created() {
    this.initDrag(this.columns);
  },
  mounted() {
    this.columnDrop()
  },
  methods: {
    initDrag(columns) {
      const draggingMap = {};
      columns.forEach((col) => {
        draggingMap[col.key] = col.width;
      });
      const draggingState = Vue.observable(draggingMap);
      const resizeableTitle = (h, props, children) => {
        let thDom = null;
        const { key, ...restProps } = props;
        const col = columns.find((col) => {
          const k = col.dataIndex || col.key;
          return k === key;
        });
        // col.key === 'action'限制操作列不可伸缩
        if (!col.width || col.key === "action") {
          return <th {...restProps}>{children}</th>;
        }
        const onDrag = (x) => {
          draggingState[key] = 0;
          let width = Math.max(x, 100); // 此处100为伸缩时的最小宽度
          width = Math.min(width, 400); // 此处400为伸缩时的最大宽度
          col.width = width;
        };

        const onDragstop = () => {
          draggingState[key] = thDom.getBoundingClientRect().width;
        };
        return (
          <th
            {...restProps}
            v-ant-ref={(r) => (thDom = r)}
            width={draggingState[key]}
            class="resize-table-th"
          >
            {children}
            <vue-draggable-resizable
              key={col.key}
              class="table-draggable-handle"
              w={10}
              x={col.width || draggingState[key]}
              z={1}
              axis="x"
              draggable={true}
              resizable={false}
              onDragging={onDrag}
              onDragstop={onDragstop}
            ></vue-draggable-resizable>
          </th>
        );
      };
      this.components = {
        header: {
          cell: resizeableTitle,
        },
      };
    },
    //列拖拽
    columnDrop() {
      var _this = this;
      const wrapperTr = document.querySelector(".ant-table-wrapper tr");
      this.sortable = Sortable.create(wrapperTr, {
        handle: ".ant-table-header-column", // 防止拉伸时触发拖拽
        animation: 180,
        delay: 0,
        //之前调用onEnd方法会出现表格DOM不更新以及表头对不上的情况所以更换为onUpdate方法
        onUpdate: function (evt) {
          //修改items数据顺序
          var newIndex = evt.newIndex;
          var oldIndex = evt.oldIndex;
          const newItem = wrapperTr.children[newIndex];
          const oldItem = wrapperTr.children[oldIndex];

          // 先删除移动的节点
          wrapperTr.removeChild(newItem);
          // 再插入移动的节点到原有节点,还原了移动的操作
          if (newIndex > oldIndex) {
            wrapperTr.insertBefore(newItem, oldItem);
          } else {
            wrapperTr.insertBefore(newItem, oldItem.nextSibling);
          }
          // 更新items数组
          var item = _this.columns.splice(oldIndex, 1);
          _this.columns.splice(newIndex, 0, item[0]);
          // 下一个tick就会走patch更新

          // 如果需要缓存表头,比如缓存到vuex中
          //每次更新调取保存用户接口
          // _this.saveColumns("columnChange", _this.columns);
        },
      });
    },
  },
};
</script>
<style lang="less" scoped>
.box {
  padding: 20px;
}
/deep/.resize-table-th {
  position: relative;
  .table-draggable-handle {
    height: 100% !important;
    bottom: 0;
    left: auto !important;
    right: 0px;
    cursor: col-resize;
    touch-action: none;
    position: absolute;
    transform: none !important;
    &:hover {
      background: #1890ff;
    }
    &::after {
      content: "|";
      position: absolute;
      top: 15px;
      left: 3px;
      color: #ccc;
    }
  }
}
</style>

有疑问欢迎一起讨论啊~

猜你喜欢

转载自blog.csdn.net/weixin_42566993/article/details/129329161