Ant Design Vue 实现可变列宽功能,封装一个hook开箱即用

背景:用户需要实现可变列宽功能,但是项目当中UI框架绑定过深,无法通过升级UI框架的方式实现功能,需要在Ant Design Vue2.x上扩展功能。

一,依赖环境

Vue3、TypeScript、interactjs。

二、实现思路

通过interactjs结合table组件components接口使得DOM元素可以拖动,然后获取每次拖动的值进行设置表格宽度即可。

三、具体代码

import { Ref, h, onMounted, onUpdated, ref } from 'vue';
import { Table } from 'ant-design-vue';
import interact from 'interactjs';
import _ from 'lodash-es';
import { userProfile } from '/@/store/modules/user-profile';
import { useRoute } from 'vue-router';

/**
 * 修改表格列宽,并且上传表格
 * @param tableId 表格id 当前业务需求需要保存配置,可根据需求调整
 * @param columns 表格列
 * @param disable 是否能够调整表格宽度
 * @returns 绑定表格的ref变量
 */
export function useChangeColumnWidth(tableId: string, columns: Ref<any[]>, disable?: boolean) {
  let headTr: HTMLElement | null = null;
  let colgroup: HTMLElement | null = null;
  let HeaderColgroup: HTMLElement | null = null;
  const tableRef = ref<InstanceType<typeof Table>>();
  const route = useRoute();
  const id = tableId + route.path.replaceAll('/', '-');
  // ant-design 只定义表头渲染函数
  const ResizeAbleTitle = (props: any, children: any) => {
    const { key, ...restProps } = props;
    if (disable) return h('th', { ...restProps }, children);
    return h(
      'th',
      {
        ...restProps,
        key,
        onVnodeMounted(e: any) {
          interact(e.el).resizable({
            edges: { top: false, left: false, bottom: false, right: true },
            listeners: {
              move: function (event) {
                const { width } = event.rect;
                if (width <= 100) return;
                // 查询当前节点索引
                const i = findIndex(headTr?.children!, event.target);
                if (!colgroup) initElement();
                const handCol = colgroup?.children[i] as HTMLElement;
                const bodyCol = HeaderColgroup?.children[i] as HTMLElement;
                const params = {
                  width,
                  minWidth: 100,
                };
                setElementWidth(event.target, params);
                handCol && setElementWidth(handCol, params);
                bodyCol && setElementWidth(bodyCol, params);
                // 关闭元素的点击事件
                event.target.style.pointerEvents = 'none';
                changeColumnsWidth(
                  columns?.value.find((item) => item.dataIndex === key),
                  width,
                  id,
                  () => {
                    event.target.style.pointerEvents = 'auto';
                  }
                );
              },
            },
          });
        },
      },
      children
    );
  };

  // 初始化元素
  const initElement = () => {
    const table = tableRef.value?.$el as HTMLDivElement;
    // 可根据实际DOM结构调整,这里是需要调整宽度的元素集合
    headTr = table.querySelector('.ant-table-thead > tr');
    colgroup = table.querySelector('.ant-table-body > table > colgroup')!;
    HeaderColgroup = table.querySelector('.ant-table-header > table > colgroup')!;
  };
  onMounted(() => {
    initElement();
  });
  onUpdated(() => {
    initElement();
  });
  return {
    tableRef,
    ResizeAbleTitle,
  };
}

// 设置元素宽度
function setElementWidth(el: HTMLElement, { width, minWidth }: any) {
  Object.assign(el.style, {
    width: `${width}px`,
    minWidth: `${minWidth}px`,
    // height: `${event.rect.height}px`,
    // transform: `translate(${x}px, ${y}px)`,
  });
}

//查找元素所在位置
function findIndex(listNodes: HTMLCollection, el: HTMLElement) {
  for (let i = 0; i < listNodes.length; i++) {
    if (listNodes[i] === el) {
      return i;
    }
  }
  return -1;
}

// 收集用户习惯,防抖处理
const changeColumnsWidth = _.debounce(
  (col: any, width: number, id: string, callbackFn?: () => void) => {
    col.width = width;
//  根据业务需求自行调整,这里可以上传到服务器保存用户习惯
//    userProfile.setUserTableConfig({
//      resourceId: id,
//      tableConfig: JSON.stringify({ [col.dataIndex]: { width } }),
//    });
    callbackFn && callbackFn();
  },
  500
);

四、使用方法

<template>
  <Table ref="tableRef" :components="components"
    :columns="newColumns.length ? newColumns : undefined">
    .....
  </Table>
</template>

<script lang="ts">
import { PropType, computed, ref, watch, defineComponent } from 'vue';
import { Table } from 'ant-design-vue'
import { useChangeColumnWidth } from './hook/useChangeColumnWidth';
export default defineComponent({
  components: { Table },
  props: {
    columns: {
      type: Array as PropType<any[]>,
      default() {
        return [] as PropType<any[]>
      }
    },
    tableId: { // 表格的uuid唯一标识
      type: String as PropType<string>,
      default: ""
    },
    noChangeWidth: { // 禁止修改列宽
      type: Boolean,
      default: false
    },
    scroll: {
      type: Object as PropType<{ x: number, y: number }>,
    }
  },
  setup(props) {
    const filterList = ['operation', 'index']
    const newColumns = ref<any[]>([...formatColumn(props.columns)])
    watch(() => props.columns, (value: any[]) => {
      newColumns.value = formatColumn(value)
    })

    const { tableRef, ResizeAbleTitle } = useChangeColumnWidth(props.tableId, newColumns, props.noChangeWidth);
    const components = {
      header: {
        cell: ResizeAbleTitle,
      },
    };
    return {
      newColumns,
      tableRef,
      components,
    }
  }
})
</script>

<style scoped>
</style>

见谅,时间关系实现过程没有写的很详细,后续有时间再补

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值