ElementUI下拉树型选择器组件

重新封装ElementUI的select组件,显示树型结构选项,实现子级多选。

选中逻辑:

  • 当选中父节点时,自动选中其所有子节点
  • 当取消选中父节点时,自动取消其所有子节点
  • 子节点可以独立选择
  • 最终的值(emit 出去的值)只包含子节点
     

子组件实现:

创建src\components\TreeSelect\index.vue

<!-- 树型下拉选择器组件 -->
<template>
  <div class="tree-select">
    <el-select
      v-model="displaySelection"
      :placeholder="placeholder"
      :clearable="clearable"
      :disabled="disabled"
      :multiple="multiple"
      :collapse-tags="collapseTags"
      @change="handleChange"
      style="width: 100%"
    >
      <el-option
        v-for="item in treeData"
        :key="item.classId"
        :label="getOptionLabel(item)"
        :value="item.classId"
      >
        <span :style="{ paddingLeft: item.level * 20 + 'px' }">
          <span
            v-if="item.children && item.children.length > 0"
            class="parent-node"
          >
            {{ item.className }}
          </span>
          <span v-else>{{ item.className }}</span>
        </span>
      </el-option>
    </el-select>
  </div>
</template>

<script>
export default {
  name: "TreeSelect",
  props: {
    // 选中的值
    value: {
      type: [String, Number, Array],
      default: "",
    },
    // 树形数据
    data: {
      type: Array,
      default: () => [],
    },
    // 占位符
    placeholder: {
      type: String,
      default: "请选择",
    },
    // 是否可清空
    clearable: {
      type: Boolean,
      default: true,
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 是否多选
    multiple: {
      type: Boolean,
      default: false,
    },
    // 多选时是否将选中值按文字的形式展示
    collapseTags: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      displaySelection: this.value, // 显示的选中值,用于显示在el-select中
      treeData: [], // 树形数据,包含层级信息
      nodeMap: new Map(), // 存储节点之间的关系
    };
  },
  watch: {
    // 监听value变化,更新显示的选中值
    value(val) {
      this.displaySelection = val;
    },
    // 监听data变化,重新初始化树形数据
    data: {
      // 深度监听,确保每次变化都重新初始化
      handler(val) {
        // 清空节点关系映射
        this.initTreeData(val);
      },
      // 立即执行,确保组件初始化时也进行数据初始化
      immediate: true,
    },
  },
  methods: {
    // 初始化树形数据,添加层级信息
    initTreeData(data, level = 0, parentPath = "", parentId = null) {
      // 初始化结果数组
      const result = [];

      // 遍历数据
      data.forEach((item) => {
        // 创建节点对象,并复制item属性,同时添加level和parentId属性
        const node = { ...item, level, parentId };

        // 添加显示路径
        node.path = parentPath
          ? `${parentPath}->${item.className}`
          : item.className;

        // 将节点添加到结果数组中
        result.push(node);

        // 存储节点关系
        this.nodeMap.set(node.classId, {
          parent: parentId,
          children: item.children
            ? item.children.map((child) => child.classId)
            : [], // 子节点ID数组
        });

        // 处理子节点
        if (item.children && item.children.length > 0) {
          // 递归调用initTreeData处理子节点,并将结果添加到结果数组中
          const children = this.initTreeData(
            item.children,
            level + 1,
            node.path,
            node.classId
          );
          result.push(...children);
        }
      });

      // 将结果赋值给treeData属性
      this.treeData = result;

      // 返回结果
      return result;
    },

    // 获取所有子节点ID
    getAllChildrenIds(nodeId) {
      const childrenIds = [];
      // 获取当前节点的子节点ID数组,并递归获取所有子孙的ID
      const node = this.nodeMap.get(nodeId);
      if (node && node.children) {
        // 遍历当前节点的所有子节点
        node.children.forEach((childId) => {
          // 将子节点的ID加入数组
          childrenIds.push(childId);
          // 递归获取当前子节点的所有子孙ID,并将结果添加到childrenIds数组中
          childrenIds.push(...this.getAllChildrenIds(childId));
        });
      }
      return childrenIds;
    },

    // 获取选项显示标签
    getOptionLabel(item) {
      // 如果是多选,并且是多选的父节点,则显示路径
      return item.path || item.className;
    },

    // 处理选择变化
    handleChange(val) {
      if (!this.multiple) {
        // 单选直接触发input和change事件
        this.$emit("input", val);
        this.$emit("change", val);
        return;
      }
      // 转换为Set,以便去重并方便后续操作
      const newSelection = new Set(Array.isArray(val) ? val : [val]);
      // 最终选中的值集合,用于存储最终的选中结果
      const finalSelection = new Set();

      // 处理所有选中的值
      newSelection.forEach((id) => {
        // 获取当前节点的信息,包括是否有子节点等信息
        const node = this.nodeMap.get(id);
        if (node) {
          if (node.children && node.children.length > 0) {
            // 如果是父节点,添加所有子节点
            const childrenIds = this.getAllChildrenIds(id);
            childrenIds.forEach((childId) => finalSelection.add(childId));
          } else {
            // 如果是子节点,直接添加
            finalSelection.add(id);
          }
        }
      });
      // 转换为数组并触发事件
      const result = Array.from(finalSelection);
      this.$emit("input", result);
      this.$emit("change", result);
    },
  },
};
</script>

<style lang="scss" scoped>
.tree-select {
  .parent-node {
    color: #909399;
    font-weight: bold;
  }

  :deep(.el-select-dropdown__item) {
    padding: 0 20px;
  }
}
</style> 

 父组件使用
 

<template>
    <el-row> 
        <el-button type="primary"
             @click="handleShare">打开弹窗</el-button>
    </el-row>
   
    <el-dialog
      :visible.sync="publishDialogVisible"
      width="600px"
    >
      <el-form
        :model="homeworkForm"
        ref="homeworkForm"
        label-width="80px"
      >
      
        <el-form-item label="发布班级" prop="classId">
          <tree-select
            v-model="homeworkForm.classId"
            :data="classOptions"
            placeholder="请选择班级"
            :multiple="true"
            @change="handleClassChange"
          />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="publishDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitForm('homeworkForm')"
          >确 定</el-button
        >
      </div>
    </el-dialog>
</template>
import TreeSelect from "@/components/TreeSelect";
export default {
  components: {
    TreeSelect,
  },
  data() {
    return {
        //对话框是否显示
        publishDialogVisible: false,
        //提交表单
        homeworkForm: {
            classId: "", //发布班级,数组格式整数,例如[1,2]
        },
        //班级下拉列表数据
        classOptions: [
            {
              id: 1,
              label: "一年级",
              children: [
                {
                  id: 2,
                  label: "一班",
                },
                {
                  id: 3,
                  label: "二班",
                },
              ],
            },
        ],
     }
   },
   methods: {
        // 班级选择变化的处理方法
        handleClassChange(val) {
          console.log("选中的班级:", val);
        },
        handleShare(row) {
          this.publishDialogVisible= true; //居中弹框打开
        },
    }
}

 效果图

Element Plus的Tree Table组件是一个功能强大的工具,用于展示层次结构的数据,并支持多选功能。在Element Plus中,你可以使用`el-tree`组件来实现树形表格,其中包含选项卡式或多列的多选模式。以下是基本步骤: 1. **引入组件**:首先,在你的Vue项目中引入`ElementPlus`的Tree Table组件,通常通过`import { ElTree } from 'element-plus'`。 2. **配置数据**:定义一个树形数据结构,每个节点可能包含`label`(标签)、`children`(子节点数组)和一个布尔属性`checkable`(可选,表示是否可以被选择)。 3. **设置选中状态**:在节点对象上添加`selected`属性来控制当前节点是否被选中。你可以在`<el-tree>`组件的`props`中设置默认的选中状态或提供一个方法来处理选中/取消选中事件。 4. **多选功能**:在`<el-tree>`组件中启用多选,通常通过设置`default-checked-keys`属性来初始化选中的节点,或者使用`@selection-change`事件监听用户的选择变化。 ```html <template> <el-tree :data="treeData" :props="treeProps" :default-checked-keys="selectedKeys" @selection-change="handleSelectionChange" :check-strictly="true" <!-- 如果不需要递归选择,可以关闭 --> ></el-tree> </template> <script> export default { data() { return { treeData: ..., selectedKeys: [], // 初始化的选中节点ID数组 treeProps: { children: 'children', label: 'name', // 树形节点的显示文本 disabled: 'disabled', // 如果有的节点不可选择 }, }; }, methods: { handleSelectionChange(newKeys) { this.selectedKeys = newKeys; // 这里可以对新选中的节点做进一步操作 }, }, }; </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值