element-plus的el-tree的双向绑定;el-tree超出省略号;el-tree默认展开;el-tree选择子节点

一、使用

1.效果

el-tree改造了下:

  • 可选可取消
  • 有默认值
  • data1包含父子节点id传递 data2只传子节点id
  • 默认展开
  • 点击节点也可触发选择
  • 节点内容自定义
  • 结果1:获取包含父节点的id
  • 结果2:提取子节点的 ID,不包括父节点
    在这里插入图片描述

2.封装树组件TreeSelect.vue

<template>
  <div>
    <el-scrollbar>
      <div class="ball mb radius overflow">
        <div class="bb" style="background: rgba(124, 162, 121, 0.1); padding: 4px 16px">
          <el-checkbox v-model="allFlag" @change="selectCheckBox($event)">{{ props.title }}</el-checkbox>
        </div>
        <!--
        default-checked-keys:默认展开值(正常来说需要包含父级id的 但是我们后端不要后端id )
        show-checkbox:多选框
        node-key:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
        default-expand-all:是否默认展开所有节点
        expand-on-click-node:是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
        default-checked-keys:默认勾选的节点的 key 的数组
        check-on-click-node:是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
        props:配置选项,具体看下表
        -->

        {{ childKeys }}

        <div style="padding: 4px 20px">
          <el-tree
            :key="keyIndex"
            ref="treeRef"
            class="tree_ref"
            style="max-width: 600px"
            :data="props.data"
            show-checkbox
            node-key="id"
            :default-expand-all="true"
            :expand-on-click-node="false"
            :default-checked-keys="childKeys"
            :check-on-click-node="true"
            :props="defaultProps"
            @check-change="checkChange"
          >
            <template #default="{ node, data }">
              <span class="custom-tree-node">
                <span>{{ node.label }}</span>
                <span style="color: red; margin-left: 10px">id:{{ data.id }}</span>
              </span>
            </template>
          </el-tree>
        </div>
      </div>
    </el-scrollbar>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue';
const emit = defineEmits(['update:childKeysVal']);
const props = defineProps({
  useWhoId: { type: String, default: () => 'son' }, // 使用id是否包含父级 farAndSon 包含父级和自己id   son 只有子级id
  childKeysVal: { type: Array, default: () => [] }, // 初始化选中子节点的 ID
  data: { type: Array, default: () => [] }, // 树数据
  title: { type: String, default: () => '' },
  flag: { type: Boolean, default: () => false }, // 勾选
  allLength: { type: Number, default: () => 0 }, // 全部长度
})


let treeRef = ref(null);
// let childKeys = ref([5, 10]); // 初始化选中子节点的 ID
let childKeys = ref(props.childKeysVal || {})
let allFlag = ref(props.flag) // 仅做回显勾选
const defaultProps = {
  children: 'children',
  label: 'treeName',
};
let keyIndex = ref(1) // 注意该key在需要全id和只需要子集id的时候用法不同

const checkChange = () => {
  // 获取所有选中的节点对象
  const checkedNodes = treeRef.value.getCheckedNodes();

  if (props.useWhoId === 'farAndSon') {
    // 结果1:获取包含父节点的id
    childKeys.value = treeRef.value.getCheckedKeys()
  } else {
    // 结果2:提取子节点的 ID,不包括父节点
    childKeys.value = checkedNodes
      .filter(node => !node.children) // 只保留没有子节点的节点
      .map(node => node.id); // 提取 ID
  }

  emit('update:childKeysVal', childKeys.value) // 同步修改父组件的值

  // console.log('默认值', childKeys.value, checkedNodes); // 只包含子节点的 ID
  keyIndex.value++
}

// 手动全选
const selectCheckBox = (value,) => {
  // console.log(value, );

  if (props.useWhoId === 'farAndSon') {
    // 结果1:获取包含父节点的id
    if (value) {
      childKeys.value = getAllNodeIds(props.data)
      emit('update:childKeysVal', childKeys.value) // 同步修改父组件的值
      console.log('所有层级id', getAllNodeIds(props.data));
    } else {
      // 取消勾选所有节点
      childKeys.value = []
      emit('update:childKeysVal', childKeys.value) // 同步修改父组件的值
      treeRef.value.setCheckedKeys([]);
    }
    keyIndex.value++
  } else {
    // 结果2:提取子节点的 ID,不包括父节点
    if (value) {
      // 勾选所有节点
      const allKeys = props.data.flatMap(node => getAllNodeKeys(node));
      treeRef.value.setCheckedKeys(allKeys);
    } else {
      childKeys.value = []
      emit('update:childKeysVal', childKeys.value) // 同步修改父组件的值
      // 取消勾选所有节点
      treeRef.value.setCheckedKeys([]);
    }
  }

};
const getAllNodeKeys = (node) => {
  let keys = [node.id];
  if (node.children) {
    node.children.forEach(child => {
      keys = keys.concat(getAllNodeKeys(child));
    });
  }
  // console.log(keys);
  return keys;
}

// 递归函数获取所有节点的 ID
const getAllNodeIds = (nodes) => {
  let ids = [];
  nodes.forEach(node => {
    ids.push(node.id); // 添加当前节点的 ID
    if (node.children) {
      ids = ids.concat(getAllNodeIds(node.children)); // 递归调用以获取子节点的 ID
    }
  });

  return ids;
};
watch(() => childKeys.value, (newOptions) => {
  // 根据监听的值,判断是否全选
  allFlag.value = newOptions.length === props.allLength
}, { deep: true })

</script>
<style lang="scss" scoped>
:deep(.tree_ref) {
  margin-left: 12px;
  .el-tree-node__expand-icon {
    display: none;
  }
}

.ball {
  border: 1px solid #e9e9e9;
}
.mb {
  margin-bottom: 12px;
}
.overflow {
  overflow: hidden;
}
.bb {
  border-bottom: 1px solid #e9e9e9;
}
</style>

3.使用树组件

<template>
  <div>
    <el-scrollbar class="pall">
      <!--
              useWhoId:
              key:刷新
              data:树数据
              flag:全选
              allLength:判断全选的长度
              title:树标题
              childKeysVal:选中的值
           -->
      <TreeSelect
        v-if="data1.length"
        :useWhoId="'farAndSon'"
        :key="keyIndex"
        :data="data1"
        :flag="flag1"
        :allLength="allLength1"
        v-model:childKeysVal="childKeys1"
        :title="'树数据:传递父子勾选'"
      ></TreeSelect>

      <TreeSelect
        v-if="data2.length"
        :useWhoId="'son'"
        :key="keyIndex"
        :data="data2"
        :flag="flag2"
        :allLength="allLength2"
        v-model:childKeysVal="childKeys2"
        :title="'树数据:只传递子勾选'"
      ></TreeSelect>
    </el-scrollbar>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import TreeSelect from './treeSelect.vue'

let keyIndex = ref(1) //刷新树
let childKeys1 = ref([]) // 父子勾选
let childKeys2 = ref([]) // 只传递子勾选
let data1 = ref([]) //树数据
let data2 = ref([]) //树数据
let flag1 = ref(false) // 是否全选
let flag2 = ref(false) // 是否全选
let allLength1 = ref(0) // 树数据长度
let allLength2 = ref(0) // 树数据长度

// 获取树
const getTreeData = async () => {
  childKeys1.value = [1, 11, 4, 9, 10] // 父子勾选
  childKeys2.value = [11, 9, 10] // 只传递子勾选
  keyIndex.value++

  // 返回的原始树数据
  let treeData = [
    {
      id: 1,
      treeName: 'Level one 1',
      children: [
        {
          id: 11,
          treeName: 'Level two 1-11',
          children: [], // 没有子集就不要返回children-否则会勾选不上数据
        },
        {
          id: 4,
          treeName: 'Level two 1-1',
          children: [
            {
              id: 9,
              treeName: 'Level three 1-1-1',
            },
            {
              id: 10,
              treeName: 'Level three 1-1-2',
            },
          ],
        },
      ],
    },
    {
      id: 2,
      treeName: 'Level one 2',
      children: [
        {
          id: 5,
          treeName: 'Level two 2-1',
        },
        {
          id: 6,
          treeName: 'Level two 2-2',
        },
      ],
    },
    {
      id: 3,
      treeName: 'Level one 3',
      children: [
        {
          id: 7,
          treeName: 'Level two 3-1',
        },
        {
          id: 8,
          treeName: 'Level two 3-2',
        },
      ],
    },
  ];

  // 删除children是[]的 属性children 否则会影响点击选中
  const removeEmptyChildren = (data) => {
    return data.reduce((acc, item) => {
      // 处理子节点
      if (item.children) {
        item.children = removeEmptyChildren(item.children);

        // 如果子节点为空数组,则删除该属性
        if (Array.isArray(item.children) && item.children.length === 0) {
          delete item.children;
        }
      }

      // 将处理后的节点添加到结果数组
      acc.push(item);
      return acc;
    }, []);
  };

  data1.value = removeEmptyChildren(treeData);
  data2.value = removeEmptyChildren(treeData);
  flag1.value = getAllNodeIds(data1.value).length === childKeys1.value.length
  flag2.value = getAllChildNodeIds(data2.value).length === childKeys2.value.length
  allLength1.value = getAllNodeIds(data1.value).length
  allLength2.value = getAllChildNodeIds(data2.value).length
}

// 获取所有子节点的 ID
const getAllChildNodeIds = (nodes) => {
  let ids = [];
  nodes.forEach(node => {
    if (Array.isArray(node.children)) {
      ids = ids.concat(getAllChildNodeIds(node.children));
    } else {
      ids.push(node.id); // 添加当前节点的 ID
    }
  });
  return ids;
};

// 递归函数获取所有节点的 ID - 父级和子级
const getAllNodeIds = (nodes) => {
  let ids = [];
  nodes.forEach(node => {
    ids.push(node.id); // 添加当前节点的 ID
    if (node.children) {
      ids = ids.concat(getAllNodeIds(node.children)); // 递归调用以获取子节点的 ID
    }
  });

  return ids;
};
onMounted(() => {
  getTreeData()
})

</script>
<style lang="scss" scoped>
:deep(.tree_ref) {
  margin-left: 12px;
  .el-tree-node__expand-icon {
    display: none;
  }
}

.pall {
  padding: 16px 16px;
}
</style>

3.旧版本不建议使用(未封装,直接包含数据和树的单独一个vue文件)

<template>
  <div class="absolute">
    <el-scrollbar class="pall">
      <div class="ball mb radius overflow">
        <div class="bb" style="background: rgba(124, 162, 121, 0.1); padding: 4px 16px;">
          {{allFlag}}
          <el-checkbox v-model="allFlag" @change="selectCheckBox($event,1)">拓扑图树结构</el-checkbox>
        </div>
        <!--
        default-checked-keys:默认展开值(正常来说需要包含父级id的 但是我们后端不要后端id )
        show-checkbox:多选框
        node-key:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
        default-expand-all:是否默认展开所有节点
        expand-on-click-node:是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
        default-checked-keys:默认勾选的节点的 key 的数组
        check-on-click-node:是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
        indent	相邻级节点间的水平缩进,单位为像素
        props:配置选项,具体看下表
        -->
        {{ childKeys }}
        <div style="padding: 4px 20px">
          <el-tree :key="keyIndex" ref="treeRef" class="tree_ref" style="max-width: 600px" :data="data" show-checkbox
            node-key="id" :default-expand-all="true" :expand-on-click-node='false' :default-checked-keys="childKeys"
            :check-on-click-node="true" :props="defaultProps" @check-change="checkChange">
            <template #default="{ node, data }">
              <span class="custom-tree-node">
                <span>{{ node.label }}</span>
                <span style="color:red;margin-left: 10px">id:{{data.id }}</span>
              </span>
            </template>
          </el-tree>
        </div>
      </div>

    </el-scrollbar>
  </div>

</template>

<script setup>
import { ref, watch } from 'vue';

let treeRef = ref(null);
let childKeys = ref([5, 10]); // 初始化选中子节点的 ID
const defaultProps = {
  children: 'children',
  label: 'label',
};
let keyIndex = ref(1) // 注意该key在需要全id和只需要子集id的时候用法不同
const data = [
  {
    id: 1,
    label: 'Level one 1',
    children: [
      {
        id: 4,
        label: 'Level two 1-1',
        children: [
          {
            id: 9,
            label: 'Level three 1-1-1',
          },
          {
            id: 10,
            label: 'Level three 1-1-2',
          },
        ],
      },
    ],
  },
  {
    id: 2,
    label: 'Level one 2',
    children: [
      {
        id: 5,
        label: 'Level two 2-1',
      },
      {
        id: 6,
        label: 'Level two 2-2',
      },
    ],
  },
  {
    id: 3,
    label: 'Level one 3',
    children: [
      {
        id: 7,
        label: 'Level two 3-1',
      },
      {
        id: 8,
        label: 'Level two 3-2',
      },
    ],
  },
];

const checkChange = () => {
  // 获取所有选中的节点对象
  const checkedNodes = treeRef.value.getCheckedNodes();

  // // 结果1:获取包含父节点的id
  // childKeys.value = treeRef.value.getCheckedKeys()

  // 结果2:提取子节点的 ID,不包括父节点
  childKeys.value = checkedNodes
    .filter(node => !node.children) // 只保留没有子节点的节点
    .map(node => node.id); // 提取 ID



  console.log('默认值', childKeys.value, checkedNodes); // 只包含子节点的 ID
  keyIndex.value++
}


let allFlag = ref(false)
// 手动全选
const selectCheckBox = (value, num) => {
  // console.log(value, num);

  // // 结果1:获取包含父节点的id
  // if (value) {
  //   childKeys.value = getAllChildNodeIds(data)
  //   console.log('所有层级id', getAllChildNodeIds(data));
  // } else {
  //   // 取消勾选所有节点
  //   childKeys.value = []
  //   treeRef.value.setCheckedKeys([]);
  // }
  // keyIndex.value++

  // 结果2:提取子节点的 ID,不包括父节点
  if (value) {
    // 勾选所有节点
    const allKeys = data.flatMap(node => getAllNodeKeys(node));
    treeRef.value.setCheckedKeys(allKeys);
  } else {
    childKeys.value = []
    // 取消勾选所有节点
    treeRef.value.setCheckedKeys([]);
  }
};

// 递归函数获取所有子节点的 ID
const getAllNodeKeys = (node) => {
  let keys = [node.id];
  if (node.children) {
    node.children.forEach(child => {
      keys = keys.concat(getAllNodeKeys(child));
    });
  }
  console.log(keys);
  return keys;
}

// 递归函数获取所有节点的 ID(注意结果1和2是否需要父节点的id)
const getAllChildNodeIds = (nodes) => {
  let ids = [];
  nodes.forEach(node => {
    if (Array.isArray(node.children)) {
      ids = ids.concat(getAllChildNodeIds(node.children));
      // ids.push(node.id); // 添加当前节点的 ID  结果1:获取包含父节点的id
    } else {
      ids.push(node.id); // 添加当前节点的 ID  结果2:提取子节点的 ID,不包括父节点
    }
  });
  return ids;
};

watch(() => childKeys.value, (newVal) => {
  allFlag.value = newVal.length === getAllChildNodeIds(data).length
}, { immediate: true, deep: true })
</script>
<style lang="scss" scoped>
:deep(.tree_ref) {
  margin-left: 12px;
  .el-tree-node__expand-icon {
    display: none;
  }
}
</style>

二、el-tree超出省略号

1.效果

在这里插入图片描述

2.代码

    <el-tree style="width: 100%;" :data="treeData" :props="defaultProps" :default-expand-all="true" 
    :expand-on-click-node="false" @node-click="activeFun" :indent="4" :highlight-current="true">
      
      <template #default="{ node,  }">
        <span class="custom-tree-node">
          <span><my-tooltips :content="node.label ?? ''" /></span>
        </span>
      </template>
      
    </el-tree>

.custom-tree-node {
  width: calc(100%);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

my-tooltips组件的封装和使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值