【sgTree】自定义组件:加载el-tree树节点整棵树数据,实现增删改操作。

简介: 【sgTree】自定义组件:加载el-tree树节点整棵树数据,实现增删改操作。


特性

  1. 可以自定义主键、配置选项
  2. 支持预定义节点图标:folder文件夹|normal普通样式
  3. 多个提示文本可以自定义
  4. 支持动态接口增删改节点
  5. 可以自定义根节点id
  6. 可以设置最多允许添加的层级深度
  7. 支持拖拽排序,排序过程还可以针对拖拽的节点深度进行自定义限制
  8. 支持隐藏一级节点(根节点)复选框☑
  9. 支持屏蔽一级节点(根节点)勾选☑
  10. 支持跨节点层级拖拽排序

sgTree源码

<template>
  <div :class="$options.name" :styleType="styleType">
    <div class="tree-header" v-if="!readonly_">
      <div class="sg-left">
        <template v-if="uploadData">
          <el-tooltip
            popper-class="sg-el-tooltip"
            :enterable="false"
            effect="dark"
            :content="`支持拖拽到树上传文件`"
            placement="top-start"
            :transition="`none`"
          >
            <el-button
              type="text"
              icon="el-icon-upload"
              size="mini"
              @click="(d) => $refs.sgUpload.triggerUploadFile()"
            >
              批量导入
            </el-button>
          </el-tooltip>
          <el-button type="text" icon="el-icon-download" size="mini" @click="downloadTpl">
            下载模板
          </el-button>
        </template>
      </div>
      <div class="sg-right">
        <el-button type="text" size="mini" @click.stop="addRoot"
          >{{ (data.text || {}).addRootButtonText || `添加根节点`
          }}<i class="el-icon-circle-plus-outline"></i
        ></el-button>
      </div>
    </div>
    <div class="tree-container">
      <el-tree
        :class="
          hideRootNodeCheckbox === '' || hideRootNodeCheckbox
            ? 'hideRootNodeCheckbox'
            : ''
        "
        ref="tree"
        :data="treeData"
        :node-key="mainKey"
        :props="
          data.props || {
            label: 'label', //指定节点标签为节点对象的某个属性值
            children: 'children', //指定子树为节点对象的某个属性值
            disabled: 'leaf', //指定节点选择框是否禁用为节点对象的某个属性值
            isLeaf: 'leaf', //指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效
          }
        "
        :icon-class="`${data.iconType}-tree-node`"
        :indent="data.indent || 10"
        @current-change="current_change"
        @node-click="nodeClick"
        highlight-current
        @node-drag-start="nodeDragStart"
        @node-drag-enter="nodeDragEnter"
        @node-drag-leave="nodeDragLeave"
        @node-drag-over="nodeDragOver"
        @node-drag-end="nodeDragEnd"
        @node-drop="nodeDrop"
        :draggable="draggable === '' || draggable"
        :allow-drop="allowDrop"
        :allow-drag="allowDrag"
        :show-checkbox="showCheckbox"
        :check-strictly="checkStrictly"
        @check-change="handleCheckChange"
        @check="handleCheck"
        :default-expand-all="defaultExpandAll === '' || defaultExpandAll"
        :default-checked-keys="defaultCheckedKeys"
        :check-on-click-node="checkOnClickNode === '' || checkOnClickNode"
      >
        <el-popover
          popper-class="tree-el-popover"
          placement="right"
          trigger="hover"
          title=""
          content=""
          :transition="`none`"
          :disabled="readonly_"
          slot-scope="{ node, data }"
        >
          <span class="right">
            <el-button
              title="添加"
              type="text"
              size=""
              icon="el-icon-circle-plus-outline"
              @click.stop="addNode(node, data)"
              v-if="showAddButton(node)"
              >添加</el-button
            >
            <el-button
              title="删除"
              type="text"
              size=""
              icon="el-icon-remove-outline"
              @click.stop="remove(node, data)"
              >删除</el-button
            >
          </span>
          <div slot="reference" class="node-label">
            <img
              class="icon"
              v-if="data.customIconURL"
              :src="data.customIconURL"
              :style="data.customIconStyle"
            />
            <div class="left" :title="node.label">
              {{ node.label }}
            </div>
          </div>
        </el-popover>
      </el-tree>
 
      <!-- 上传组件 -->
      <sgUpload
        :disabledWhenShowSels="['.v-modal']"
        :drag="uploadData ? allowDragUpload : false"
        ref="sgUpload"
        :data="uploadData"
        @uploadSuccess="uploadSuccess"
        @uploadError="uploadError"
        @importError="importError"
        @showLoading="showLoading"
        @hideLoading="hideLoading"
        hideUploadTray
      />
    </div>
  </div>
</template>
 
<script>
import sgUpload from "@/vue/components/admin/sgUpload";
export default {
  name: "sgTree",
  components: {
    sgUpload,
  },
  data() {
    return {
      // 动态树:增删改_________________________________________________________
      rootNode: null, //根节点
      rootResolve: null, //根节点
      focusNodeId: null, //聚焦高亮新添加ID
      mainKey: "id", //默认主键
      defaultRootId: "root", //默认根节点ID就是root
      maxAddLevel: null, // 最多允许添加的层级
      allowDragUpload: true, //在拖拽节点过程中控制上传组件能否拖拽上传
      // _________________________________________________________
      readonly_: null,
    };
  },
  props: [
    "styleType", //样式风格
    "treeData",
    "data",
    "readonly",
    "draggable", //是否开启拖拽节点功能
    "uploadData",
    /* 例子 uploadData: {
            accept: '.xls,.xlsx',
            actionUrl: `${this.$d.API_ROOT_URL}/core/resource/upload`,
        }, */
    "allowNodeDrag",
    "allowNodeDrop", //是否允许拖拽的方法判断
    "showCheckbox", //节点是否可被选择
    "checkStrictly", //在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
    "hideRootNodeCheckbox", //隐藏一级节点复选框☑
    "disabledRootNode", //屏蔽一级节点勾选☑
    "defaultExpandAll", //展开所有节点
    "defaultCheckedKeys", //默认勾选的节点的 key 的数组
    "checkOnClickNode", //是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
  ],
 
  watch: {
    readonly: {
      handler(newValue, oldValue) {
        this.readonly_ = newValue === "" || newValue;
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
    data: {
      /* 
      data.iconType= 节点图标:
      folder  文件夹
      normal  普通样式
      plus    加减符号样式
      win11    win11系统箭头样式
      */
      handler(d) {
        d.nodeKey && (this.mainKey = d.nodeKey); //主键
        d.rootId && (this.defaultRootId = d.rootId); //根节点ID
        d.maxAddLevel && (this.maxAddLevel = d.maxAddLevel); // 最多允许添加的层级
      },
      deep: true,
      immediate: true,
    },
  },
  methods: {
    showLoading(file) {
      this.$emit(`showLoading`, file);
    },
    hideLoading(file) {
      this.$emit(`hideLoading`, file);
    },
    // 取消选中
    unCheckAll(d) {
      this.$refs.tree.setCheckedKeys([]);
      this.handleCheckChange([], []);
    },
    handleCheckChange(data, checked, indeterminate) {
      this.$emit(`checkChange`, {
        checkedNodes: this.$refs.tree.getCheckedNodes(),
        checkedLeafOnlyNodes: this.$refs.tree.getCheckedNodes(true, false), //(leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数,1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false【注意:懒加载树形不管用!必须要明确叶子节点展开后面没有子节点了才能识别!】
        data,
        checked,
        indeterminate,
      });
    },
    handleCheck(data, status) {
      /* 
      共两个参数,依次为:
      1、传递给 data 属性的数组中该节点所对应的对象
      2、树目前的选中状态对象,包含 checkedNodes、checkedKeys、halfCheckedNodes、halfCheckedKeys 四个属性 
      */
      this.$emit(`check`, { data, status });
    },
    // 拖拽----------------------------------------
    nodeDragStart(node, ev) {
      this.allowDragUpload = false;
      this.$emit(`nodeDragStart`, node, ev);
    },
    nodeDragEnter(draggingNode, dropNode, ev) {
      this.$emit(`nodeDragEnter`, draggingNode, dropNode, ev);
    },
    nodeDragLeave(draggingNode, dropNode, ev) {
      this.$emit(`nodeDragLeave`, draggingNode, dropNode, ev);
    },
    nodeDragOver(draggingNode, dropNode, ev) {
      this.$emit(`nodeDragOver`, draggingNode, dropNode, ev);
    },
    nodeDragEnd(draggingNode, dropNode, dropType, ev) {
      // dropType有'before'、'after'、'inner'和'none'4种情况
      this.allowDragUpload = true;
      if (dropType === `none`) return; //自己拖拽到自己身上
      this.$emit(`nodeDragEnd`, draggingNode, dropNode, dropType, ev);
    },
    nodeDrop(draggingNode, dropNode, dropType, ev) {
      // dropType有'before'、'after'、'inner'和'none'4种情况
      if (dropType === `none`) return; //自己拖拽到自己身上
      this.$emit(`nodeDrop`, draggingNode, dropNode, dropType, ev);
    },
    allowDrop(draggingNode, dropNode, dropType) {
      // 拖拽时判定目标节点能否被放置。dropType 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后(注意:很奇葩上面node开头的绑定方法dropType有before、after、inner和none4种情况)
      return this.allowNodeDrop
        ? this.allowNodeDrop(draggingNode, dropNode, dropType)
        : true;
    },
    allowDrag(draggingNode) {
      return this.allowNodeDrag ? this.allowNodeDrag(draggingNode) : true;
    },
    // ----------------------------------------
    showAddButton(node) {
      if (this.maxAddLevel) {
        return node.level < this.maxAddLevel; // 最多允许添加的层级
      } else return true;
    },
    downloadTpl(d) {
      this.$emit(`downloadTpl`);
    },
    uploadSuccess(d, f) {
      this.$emit(`uploadSuccess`, d, f);
    },
    uploadError(d, f) {
      this.$emit(`uploadError`, d, f);
    },
    importError(d, f) {
      this.$emit(`importError`, d, f);
    },
    // 聚焦到某一个节点
    focusNode(id, { triggerCurrentChange = true } = {}) {
      if (!id) return;
      this.$nextTick(() => {
        this.expandAllNodes(true);
        // 展开父节点&当前节点----------------------------------------
        /* let currentNode = this.$refs.tree.getNode(id);
        let parentNode = (currentNode || {}).parent;
        parentNode && !parentNode.expanded && parentNode.expand(); //展开父节点(否者不展开会感觉怪怪的)
        currentNode && !currentNode.expanded && currentNode.expand(); //展开当前节点 */
        // ----------------------------------------
        this.$refs.tree.setCurrentKey(id); //高亮显示某个节点
        triggerCurrentChange &&
          this.$emit(`currentChange`, this.$refs.tree.getCurrentNode());
        this.$nextTick(() => {
          setTimeout(() => {
            let dom = this.$refs.tree.$el.querySelector(`.el-tree-node.is-current`);
            dom &&
              dom.scrollIntoView({
                behavior: "smooth",
                block: "nearest",
                inline: "nearest",
              }); //缓慢滚动
          }, 500);
        });
      });
    },
    // 展开or折叠所有节点
    expandAllNodes(expanded = true) {
      this.$refs.tree.store._getAllNodes().forEach((node) => {
        node.loaded && (node.expanded = expanded); //已经加载了的节点才展开
      });
    },
    // 添加根节点
    addRoot() {
      this.addNode(this.$refs.tree.root, { [this.mainKey]: this.defaultRootId });
    },
    //通过id勾选节点
    setCheckedKeys(ids) {
      this.$refs.tree.setCheckedKeys(ids);
    },
    // 通过id展开指定节点(通常是用于外部调用)
    expandNodeById(id) {
      let node = this.$refs.tree.getNode(id);
      node.expand();
    },
    // 添加节点
    addNode(node, data) {
      let resolve = (d) => {
        if (data.ID === this.defaultRootId) {
          this.treeData.unshift(d);
        } else {
          data.children || this.$set(data, "children", []);
          data.children.push(d);
        }
        node.expand();
      };
      let reject = (d) => {
        // this.rootLoading = false;
        node.loading = false;
        this.$message.error(d.msg); //添加节点失败
      };
      this.$emit(`addNode`, { node, data, resolve, reject });
    },
    // 删除节点
    remove(node, data) {
      this.$confirm(
        (this.data.text || {}).removeConfirmTip ||
          `此操作将永久删除该节点及其下面的节点,是否继续?`,
        (this.data.text || {}).removeConfirmTitle || `提示`,
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: `确定`,
          cancelButtonText: `取消`,
          type: "warning",
        }
      )
        .then(() => {
          this.removeNodeData(node, data);
        })
        .catch(() => {});
    },
    // 删除节点数据(通过接口向后台删除数据)
    removeNodeData(node, data) {
      node.loading = true; //出现加载动画
      let resolve = (d) => {
        node.loading = false;
        this.$message.success(`删除成功`);
 
        // 从父节点异步删除子节点
        const parent = node.parent;
        const children = parent.data.children || parent.data;
        const index = children.findIndex((d) => d[this.mainKey] === data[this.mainKey]);
        children.splice(index, 1);
 
        // 从显示界面删除节点(有bug,只是删除了树节点的Virtual DOM,实际数据还在)
        /* let childNodes = node.parent.childNodes; childNodes.splice( childNodes.findIndex((d) => d.data[this.mainKey] === data[this.mainKey]), 1 ); */
      };
      let reject = (d) => {
        // this.rootLoading = false;
        node.loading = false;
        this.$message.error(d.msg); //删除失败
      };
      this.$emit(`removeNode`, { node, data, resolve, reject });
    },
    // 当前选中节点变化时触发的事件
    current_change(d) {
      this.$emit(`currentChange`, d);
    },
    //点击节点
    nodeClick(d) {
      this.focusNodeId = null;
      this.$emit(`nodeClick`, d);
    },
  },
};
</script>
 
<style lang="scss" scoped>
@import "~@/css/sg";
 
.sgTree {
  $treeHeaderHeight: 30px;
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: nowrap;
  flex-direction: column;
  white-space: nowrap;
  flex-shrink: 0;
  flex-grow: 1;
  position: relative;
 
  .tree-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: $treeHeaderHeight;
 
    & > .sg-left {
    }
 
    & > .sg-right {
    }
  }
 
  .tree-container {
    position: relative;
    overflow: auto;
    box-sizing: border-box;
    height: calc(100% - #{$treeHeaderHeight});
    flex-shrink: 0;
    flex-grow: 1;
    user-select: none;
    @include scrollbarHover();
    /* >>> .tree-container .el-tree .el-tree-node__content {
      cursor: pointer;
    } */
    >>> .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
      background-color: #409eff22; // 高亮当前选中节点背景
    }
 
    >>> .el-tree {
      * {
        transition: none;
      }
 
      .el-tree-node__children {
        min-width: max-content; //这样才会出现水平滚动条
      }
 
      // 高亮当前即将拖拽放入的节点
      .el-tree-node.is-drop-inner > .el-tree-node__content {
        color: white;
        background-color: #409eff;
      }
 
      .normal-tree-node,
      .plus-tree-node,
      .folder-tree-node,
      .win11-tree-node {
        & + label:not(.el-checkbox) {
          /*单行省略号*/
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
 
        flex-shrink: 0;
        display: block;
        padding: 0 !important;
        margin: 0;
        width: 20px;
        height: 20px;
        margin-right: 5px;
        background: transparent url("~@/../static/img/fileType/folder/folder.svg")
          no-repeat center / contain;
 
        margin-left: 20px;
 
        & ~ span:not(.el-icon-loading) {
          width: 100%;
 
          .node-label {
            height: 40px;
            display: flex;
            align-items: center;
            box-sizing: border-box;
            padding-right: 20px;
            margin-right: -20px; //让右侧更多空间成为el-popover
            img.icon {
              width: 20px;
              height: 20px;
              object-position: center;
              object-fit: contain;
              margin-left: -25px;
              margin-right: 5px;
            }
          }
        }
 
        &.expanded {
          flex-shrink: 0;
          transform: rotate(0deg);
          background-image: url("~@/../static/img/fileType/folder/folder-open.svg");
        }
        &.is-leaf {
          background-image: none;
        }
      }
 
      .normal-tree-node {
        margin-left: 10px;
        background-image: url("~@/../static/img/fileType/folder/arrow-right.svg");
 
        &.expanded {
          transform: rotate(90deg); //旋转角度
          background-image: url("~@/../static/img/fileType/folder/arrow-right.svg");
        }
 
        &.is-leaf {
          background-image: none;
        }
      }
      .plus-tree-node {
        margin-left: 10px;
        background-image: url("~@/../static/img/fileType/folder/plus.svg");
 
        &.expanded {
          background-image: url("~@/../static/img/fileType/folder/minus.svg");
        }
        &.is-leaf {
          background-image: none;
        }
      }
      .win11-tree-node {
        margin-left: 10px;
        background-image: url("~@/../static/img/fileType/folder/win11-right.svg");
 
        &.expanded {
          transform: rotate(90deg); //旋转角度
          background-image: url("~@/../static/img/fileType/folder/win11-right.svg");
        }
        &.is-leaf {
          background-image: none;
        }
      }
 
      // 隐藏一级节点的复选框
      &.hideRootNodeCheckbox > div > .el-tree-node__content .el-checkbox {
        display: none;
      }
    }
  }
 
  &[styleType="win11"] {
    .tree-container {
      border-radius: 0;
      background: white;
      border-right: 1px solid #f7f7f7;
      .el-tree {
        padding-left: 0;
      }
 
      >>> .el-tree-node__content {
        border-radius: 4px;
        &:hover {
          background-color: #d9d9d944; // 移入中节点背景
        }
      }
 
      >>> .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
        background-color: #d9d9d9; // 高亮当前选中节点背景
      }
 
      // 高亮当前即将拖拽放入的节点
      >>> .el-tree .el-tree-node.is-drop-inner > .el-tree-node__content {
        color: white;
        background-color: #409eff;
      }
    }
  }
}
 
.tree-el-popover {
  .el-button {
    padding-top: 0;
    padding-bottom: 0;
  }
}
</style>

应用

<template>
  <div :class="$options.name">
    <sgTree
      v-loading="loading"
      :key="$route.query.BMID + sgTree_fresh"
      :treeData="treeData"
      :data="treeConfigData"
      @currentChange="currentChange"
      @addNode="addNode"
      @removeNode="removeNode"
      :uploadData="{
        name: `file`,
        accept: '.xls,.xlsx',
        actionUrl: `${$d.API_ROOT_URL}/core/column/importColumn`, //批量导入树结构接口
        actionData: {
          BMID: $global.getBMID(),
          PID: `root`,
          sgLog: `强哥请求来源:${$options.name}导入栏目xls`,
        },
      }"
      @uploadSuccess="uploadSuccess"
      @uploadError="uploadError"
      @importError="importError"
      @downloadTpl="downloadTpl"
      draggable
      :allowNodeDrop="allowNodeDrop"
      @nodeDragEnd="nodeDragEnd"
    />
  </div>
</template>
 
<script>
import sgTree from "@/vue/components/admin/sgTree";
export default {
  name: "sgBody",
  components: {
    sgTree,
  },
  data() {
    return {
      sgTree_fresh: false,
      autoId: 0, //自增编号
      treeConfigData: {
        nodeKey: `ID`, //主键
        props: { label: "MC", isLeaf: "leaf" }, //配置选项
        iconType: "plus", //节点图标:folder文件夹|normal普通样式|plus加减符号样式
        text: {
          addRootButtonText: "添加根目录", //添加根节点按钮文本
          removeConfirmTitle: "警告!!!", //删除节点提示标题
          removeConfirmTip: "此操作将永久删除该节点及其下面的子节点,是否继续?", //删除节点提示内容
        },
      },
      treeData: [],
      loading: false,
    };
  },
  created() {
    this.initTreeData();
  },
  methods: {
    //初始化数据
    initTreeData({ d } = {}) {
      this.$global.获取整棵树的数据({
        cb: (d) => {
          this.treeData = d;
        },
      });
    },
    // 拖拽节点相关方法----------------------------------------
    allowNodeDrop(draggingNode, dropNode, dropType) {
      // 只允许拖拽同级别前后排序
      let isPrevOrNext = dropType === "prev" || dropType === "next";
      // 同在第一级根节点下
      let isSameRootLevel =
        draggingNode.level === dropNode.level && draggingNode.level === 1;
      // 同在一个节点(非根节点)下
      let isSameChildLevel =
        draggingNode.parent &&
        dropNode.parent &&
        draggingNode.parent.data &&
        dropNode.parent.data &&
        draggingNode.parent.data.ID === dropNode.parent.data.ID;
      return isPrevOrNext && (isSameRootLevel || isSameChildLevel);
    },
    nodeDragEnd(draggingNode, dropNode, dropType, ev) {
      // 只允许拖拽同级别前后排序
      let isBeforeOrAfter = dropType === "before" || dropType === "after";
      if (isBeforeOrAfter) {
        /* console.log("被拖拽的节点", draggingNode.data.MC, draggingNode.data.PXZ); console.log("停靠的节点", dropNode.data.MC, dropNode.data.PXZ); */
        let theSameLevelDatas = (dropNode.parent.childNodes || []).map((v) => v.data); // 获取同一级节点数据
        theSameLevelDatas.forEach((v, i) => (v.PXZ = i)); //重新排序
        // console.log("拖拽排序", theSameLevelDatas); //这里需要调用后台接口
        let IDS = theSameLevelDatas.map((v) => v.ID); //排序后的ID顺序数组
        let data = {
          IDS,
          sgLog: `强哥请求来源:${this.$options.name}更改同一层级树节点排序值`,
        };
        this.$d.修改节点排序({
          data,
          r: {
            s: (d) => {
              // console.log("【成功】", d);
            },
          },
        });
      }
    },
    // ----------------------------------------
    // 获取当前聚焦节点的数据
    currentChange(d) {
      console.log(``, d);
    },
    // 添加节点
    addNode({ data, resolve }) {
      this.$d.新增节点({
        data: {
          MC: `新增节点名称(${++this.autoId})`,
        },
        doing: {
          l: { show: () => (this.loading = true), close: () => (this.loading = false) },
          s: (d) => resolve(d),
          f: (d) => reject(d), //删除失败
        },
      });
    },
    // 删除节点
    removeNode({ node, data, resolve, reject }) {
      this.$d.删除节点({
        data: { ID: data.ID },
        doing: {
          s: (d) => resolve(d),
          f: (d) => reject(d), //删除失败
        },
      });
    },
    updateList(d) {},
    uploadSuccess(d, f) {
      this.sgTree_fresh = !this.sgTree_fresh;
    },
    uploadError(d, f) {
      this.$message.error(d.msg);
    },
    // 导入失败
    importError(d, f) {},
    // 下载导入模板
    downloadTpl(d) {},
  },
};
</script>

关联懒加载树节点组件

image.png


相关文章
【el-tree】树形结构拖拽,拖动修改分组
【el-tree】树形结构拖拽,拖动修改分组
1343 1
|
JavaScript 数据安全/隐私保护
vue3+element-plus权限控制实现(el-tree父子级不关联情况处理)
后台管理系统常见的权限控制需求,这里讲button实现交互细节处理, 取消选中子级menu/button,父级不关联取消; 选中/取消父级catalog/menu,子级全部选中/取消; 选中/取消部分子级menu/button,父级关联半选中状态(indeterminate=true);
814 2
|
JSON JavaScript 数据格式
Elementui Tree 树形控件删除子节点
Elementui Tree 树形控件删除子节点
435 1
|
前端开发 虚拟化
简单记录使用 ElementPlus 的虚拟化树形控件(el-tree-v2)心得
这篇文章分享了作者使用ElementPlus的虚拟化树形控件`el-tree-v2`的心得,展示了其基本用法和如何通过自定义模板来增强树节点的交互性。
4523 1
简单记录使用 ElementPlus 的虚拟化树形控件(el-tree-v2)心得
element中tree组件的选中获取和返显
本文介绍了如何在Element UI的tree组件中获取选中的节点值以及如何进行节点的默认选中(返显)。主要通过使用`getCheckedKeys()`和`getHalfCheckedKeys()`方法来获取完全和半选中的节点,然后使用`setCheckedKeys()`方法来设置默认选中的节点。
1738 2
element中tree组件的选中获取和返显
|
存储 前端开发 Java
Element el-upload 文件上传/图片上传/拖拽上传/附带参数/附带请求头部详解
文目录 1. 前言 2. 基本用法 2.1 前端部分 2.2 后端部分 2.3 获取后端返回信息 3. 外观功能介绍 3.1 拖拽上传 3.2 显示图片 3.3 设置文件列表样式 3.4 显示提示信息 4. 事件功能介绍 4.1 限制上传文件数量 4.2 限制上传文件类型和大小 4.3 移除文件处理 4.4 手动上传 5. 附带参数 6. 附带请求头部 7. 小结
8045 0
|
JSON 前端开发 数据格式
使用 el-tree 实现计算每个非叶子节点的后代节点的个数并显示
本文介绍了如何使用ElementPlus的`el-tree`组件实现计算并显示每个非叶子节点后代节点的个数,以及后代节点中ID为一万倍数的个数。
491 1
使用 el-tree 实现计算每个非叶子节点的后代节点的个数并显示
|
JavaScript
如何对ElementUI、ElementPlus中的Tree树组件进行美化,如增加辅助线、替换展开收起图标、点击节点后文字高亮等效果?本文给你答案!
本文介绍了如何对ElementUI和ElementPlus的Tree树组件进行美化,包括增加辅助线、替换展开收起图标、点击节点后文字高亮等效果,并提供了详细的代码示例和实现效果。
4190 0
如何对ElementUI、ElementPlus中的Tree树组件进行美化,如增加辅助线、替换展开收起图标、点击节点后文字高亮等效果?本文给你答案!
|
API
Vue3组件通信全解析:利用props、emit、provide/inject跨层级传递数据,expose与ref实现父子组件方法调用
Vue3组件通信全解析:利用props、emit、provide/inject跨层级传递数据,expose与ref实现父子组件方法调用
3906 0
Element-ui 中树形控件(Tree)实现增删改功能
Element-ui 中树形控件(Tree)实现增删改功能
1738 0
Element-ui 中树形控件(Tree)实现增删改功能