ant-design-vue 拖拽树形表格 vue3.0 ts

技术: ant-design-vue ts vue3.0

需求:

1.任意级进行表格拖拽
2.鼠标滑过进行编辑

效果图

在这里插入图片描述

原理:

ant的tree组件基础下进行改写

代码如下:

<template>
  <div class="basic-drop-tree-table">
    <div class="flex table-head">
      <div
        v-for="columnsItem in columns"
        :key="columnsItem.key"
        :style="countColWidth"
        class="table-box"
      >
        {{ columnsItem.title }}
      </div>
      <p class="table-head-action">操作</p>
    </div>
    <a-tree
      class="draggable-tree"
      draggable
      block-node
      :tree-data="gData"
      :fieldNames="fieldNames"
      :defaultExpandAll="true"
      @drop="onDrop"
    >
      <template #title="item">
        <div class="flex">
          <slot name="icon" :iconData="item"></slot>
          <div
            v-for="bb in columns"
            :key="bb.key"
            :style="countColWidth"
            class="table-box table-row-action"
          >
            <div v-if="item.change === true" class="flex input-width">
              <a-input
                :value="item[bb.key]"
                @change="inputChange($event, item, bb)"
                @press-enter="save(item)"
                @blur="save(item)"
              />
            </div>
            <div v-else>{{ item[bb.key] }}</div>
          </div>
          <div class="table-row-action">
            <a-button v-if="item.change === false" type="link" @click="actionFn(item)">
              编辑
            </a-button>
            <a-button v-else type="link" @click="save(item)">保存</a-button>
            <slot name="action" :item="item"></slot>
          </div>
        </div>
      </template>
    </a-tree>
  </div>
</template>
<script lang="ts" setup>
  import type { TreeProps } from 'ant-design-vue/es/tree';
  import useTreeData from '@/hooks/component/useTreeData';
  import { computed } from 'vue';
  const props = defineProps<{
    originDate: any[]; //原始表格数据
    treeData: TreeProps['treeData']; //树形表格数据
    fieldNames: TreeProps['fieldNames']; //替换字段
    columns: any[]; // 表头
  }>();
  const { onDrop, gData } = useTreeData(props.treeData, props.fieldNames);

  const countColWidth = computed(() => {
    return `width : calc( (100% - 150px) / ${props.columns.length} )`;
  });

  const emits = defineEmits<{
    (e: 'editableSave', treeData: TreeProps['treeData']): void;
  }>();
  //添加控制可编写状态
  props.originDate.forEach((item) => {
    item.change = false;
  });

  /**可编辑 */
  const inputChange = ($event, item, bb) => {
    item.data[bb.key] = $event.target.value;
    emits('editableSave', props.treeData);
  };
  const save = (item) => {
    item.data.change = false;
  };

  const actionFn = (item) => {
    item.data.change = true;
  };
</script>
<style lang="scss" scoped>
  .basic-drop-tree-table {
    border: 1px solid #f0f0f0;

    :deep(.ant-tree .ant-tree-treenode) {
      padding: 2px 0px;
      line-height: 40px;
      border-bottom: 1px solid #f0f0f0;

      &:last-child {
        border-bottom: none;
      }
    }

    :deep(.ant-tree-switcher) {
      margin: auto 0px;
    }

    .table-head {
      background: #fafafa;
      color: #000000d9;
      font-weight: 500;
      line-height: 40px;
      border-bottom: 1px solid #f0f0f0;
      padding-left: 24px;

      .table-head-action {
        margin: auto 0px;
      }
    }

    .table-row-action {
      display: flex;
      align-items: center;
    }

    .table-box {
      height: 40px;
      line-height: 40px;
      margin: auto 0px;
    }

    .input-width {
      width: 80%;
    }

    .tree-title {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
  }
</style>

拖拽的hooks:

import type { AntTreeNodeDropEvent, TreeProps } from 'ant-design-vue/es/tree';
import { ref } from 'vue';

const useTreeData = (treeData: TreeProps['treeData'], fieldNames: TreeProps['fieldNames']) => {
  type TreeDataItem = any;
  const gData = ref<TreeProps['treeData']>(treeData);
  const onDrop = (info: AntTreeNodeDropEvent) => {
    const dropKey = info.node.key;
    const dragKey = info.dragNode.key; //拖拽的id值
    const dropPos = (info.node.pos as string).split('-');
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);

    const loop = (data: TreeProps['treeData'], key: string | number, callback: any) => {
      data?.forEach((item, index) => {
        if (item[fieldNames.key] === key) {
          return callback(item, index, data);
        }
        if (item.children) {
          return loop(item.children, key, callback);
        }
      });
    };
    const data = [...gData.value];
    // Find dragObject
    let dragObj: TreeDataItem;
    // 拖拽的那个值,拖拽值的索引,拖拽值所在的数组
    loop(data, dragKey, (item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
      arr?.splice(index, 1);
      dragObj = item;
    });

    if (!info.dropToGap) {
      // Drop on the content
      loop(data, dropKey, (item: TreeDataItem) => {
        item.children = item.children || [];
        /// where to insert 示例添加到头部,可以是随意位置
        item.children.unshift(dragObj);
      });
    } else if (
      (info.node.children || []).length > 0 && // Has children
      info.node.expanded && // Is expanded
      dropPosition === 1 // On the bottom gap
    ) {
      loop(data, dropKey, (item: TreeDataItem) => {
        item.children = item.children || [];
        // where to insert 示例添加到头部,可以是随意位置
        item.children.unshift(dragObj);
      });
    } else {
      let ar: TreeProps['treeData'] = [];
      let i = 0;
      loop(data, dropKey, (_item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
        ar = arr;
        i = index;
      });
      if (dropPosition === -1) {
        ar.splice(i, 0, dragObj);
      } else {
        ar.splice(i + 1, 0, dragObj);
      }
    }
    gData.value = data;
    console.log('data', data);
  };

  return { onDrop, gData };
};
export default useTreeData;

git :

git上面有案例
文件:src\views\table\ant\index.vue


版权声明:本文为susanliy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。