H5树组件的开发

原生 JS 实现树组件
需求分析:

使用`el-tree`组件,渲染树状数据。

要求默认子孩子展开,点击父节点展开、收起树,箭头随收起展开变化,异步请求数据。

主要思路:递归
  • - 使用ul li 构建树结构
  • - 设置每个树节点的唯一标识

`valueKey`属性,每个树节点的唯一标识,必须设置

  • - 点击父级节点时控制子节点的折叠:

是否在点击节点的时候展开或者收缩节点。 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。

  • - 通过样式去控制展开和收起

使用transform CSS样式,通过子孩子的length数量去判断当前样式是展开还是收起状态。

代码实现
<template>
  <div>
    <ul v-if="data.length" class="ul-list">
      <li
        v-for="(item, i) in data"
        :key="i"
        @click.stop="selectItem(item)"
        v-show="expandFlag"
      >
        <div class="item">
          <!-- 展开的图标 -->

          <van-icon
            v-if="!item.isLeaf"
            name="arrow"
            class="expandIcon"
            @click.stop="expandItem(item, i)"
            :class="[item.children && item.children.length ? '' : 'disabled']"
            :style="{
              transform: expandArr.includes(i)
                ? ' rotate(90deg)'
                : ' rotate(0deg)',
            }"
          />
          <div class="list-item-label">
            <img src="../../../assets/images/cityIcon.png" alt="" algin />
            <!-- 选项名 -->
            <div style="padding-left: 0.04rem" @click="change(item)">
              {{ item.label }}
            </div>
          </div>
          <!-- 选择的图标 -->
        </div>
        <list-menu
          v-if="item.children"
          @input="input"
          :data="item.children"
          :valueKey="valueKey"
          :labelKey="labelKey"
          :disabledKey="disabledKey"
          :value="value"
          :toastText="toastText"
          :expandFlag="expandArr.includes(i)"
          style="margin-left: 0.16rem"
        />
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  // 组件名必写
  name: "ListMenu",
  props: {
    // 选中的值的属性名,必传
    valueKey: String,
    // 在页面要展示的选项属性名,必传
    labelKey: String,
    // 不可选的唯一标识,如item[disabledKey]未true则不可选择,非必传
    disabledKey: String,
    // 选中的值,必传
    value: Object,
    // 控制展开,不需要传
    expandFlag: {
      type: Boolean,
      default: true,
    },
    firstExpend: {
      type: Boolean,
      default: false,
    },
    // 总数据,必传
    data: Array,
    // 不可选提示文字,非必传
    toastText: String,
  },
  data() {
    return {
      // 当前级组件已展开的项
      expandArr: [],
    };
  },
  async mounted() {
    if (this.firstExpend) this.expandArr.push(0);
  },
  methods: {
    // 子组件逐级传递选中项
    input(item) {
      this.$emit("input", item);
    },
    // 选择
    selectItem(item) {
      // industryDeptType为1表示时不可选择该工会
      if (this.disabledKey && item[this.disabledKey]) {
        if (this.toastText) {
          alert(this.toastText);
        }
        return;
      }
      this.$emit("input", item);
    },
    // 展开
    async expandItem(item, i) {
      console.log(this.expandArr);
      if (!item.children || item.children.length === 0) {
        try {
          const { result } = await this.$api.getFavoritesTree({
            code: item.code,
            searchName: "",
          });
          if (result.length !== 0) {
            item.children = result;
          } else item.isLeaf = true;

          console.log(item);
        } catch (error) {}
      }

      if (item.children && item.children.length) {
        let index = this.expandArr.indexOf(i);
        if (index > -1) {
          this.expandArr.splice(index, 1);
        } else {
          this.expandArr.push(i);
        }
      }
    },
    change(item) {
      this.$EventBus.$emit("changShow", { ...item });
    },
  },
};
</script>
<style lang="scss" scoped>
.ul-list {
  width: 100%;
  color: #2a2a2a;
  font-size: 0.16rem;
  overflow: hidden;
  background: #fff;
}
li {
  .item {
    padding: 0.06rem 0.12rem;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    width: 100%;
    height: 0.52rem;
    .list-item-label {
      align-items: center;
      height: 0.52rem;
      line-height: 0.52rem;
      flex: 1;
      display: flex;
      justify-content: flex-start;
      border-bottom: 0.8px solid #e1e1e180;
      img {
        margin: 0 0.06rem;
        height: 0.16rem;
        width: 0.16rem;
      }
    }
    .expandIcon {
      height: 0.16rem;
      width: 0.16rem;
      border-radius: 50%;
      position: relative;
      opacity: 0.7;

      &.disabled {
        border-color: #ddd;
        &:after {
          color: #ddd;
        }
      }
    }
    .selectIcon {
      height: 0.32rem;
      width: 0.32rem;
      border: 1.5px solid;
      border-radius: 50%;
      position: relative;
      &:after {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
      &.checked {
        border-color: #ff6633;
        background: #ff6633;
        &:after {
          font-size: 0.16rem;
          color: #fff;
          content: "✓";
        }
      }
      &.noChecked {
        border-color: #ff6633;
      }
      &.disabled {
        border-color: #ddd;
      }
    }
    h1 {
      margin-right: 0.04rem;
      padding: 0 0.16rem;
      position: relative;
      top: 0.08rem;
      height: 2rem;
      line-height: 0.44rem;
      font-size: 0.44rem;
      flex: 1;
      white-space: nowrap;
      color: #2a2a2a;
      overflow-x: auto;
    }
  }
  > ul {
    border-bottom: 0;
    padding-left: 1rem;
    li {
      .item {
        padding-left: 1rem;
      }
    }
  }
}
</style>

父组件:

<mTree
        :data="lists"
        v-model="selectVal"
        value-key="treeId"
        label-key="name"
        disabled-key="disabled"
        toastText="该选项不可选择"
        :firstExpend="true"
/>
Liquor Tree组件

Liquor Tree 是一款轻量级树形选择器,对移动端友好,可拖放,支持键盘快捷键,每个操作动作都有事件记录,与 Vue 高度整合。Liquor Tree 代码简洁,扩展性强,可根据你的应用场景按需定制。

Vue 树组件,可让您以美观和逻辑的方式呈现层次结构的数据。

支持移动,并具有各种响应事件。 灵活的配置选项,并支持键盘导航。

文档(https://amsik.github.io/liquor-tree/)    

github 地址 :
GitHub - amsik/liquor-tree: Tree component based on Vue.js

组件特点 :
  • 拖放 移动友好

  • 每个动作的事件

  • 灵活的配置

  • 每页任意数量的实例

  • 多选

  • 键盘导航

  • 过滤

  • 分类

  • 与 Vuex 集成

安装 :
$ npm install liquor-tree
$ yarn add liquor-tree
引入:
import Vue from 'vue'
import LiquorTree from 'liquor-tree'
 
// global registration
Vue.use(LiquorTree) 
 
// or
Vue.component(LiquorTree.name, LiquorTree) 
 
// or
import LiquorTree from 'liquor-tree'
 
// local registration
export default {
  name: 'your-awesome-component',
  components: {
    [LiquorTree.name]: LiquorTree 
  }
}

注册:

<script src="https://cdn.jsdelivr.net/npm/liquor-tree/dist/liquor-tree.umd.js"></script>
组件的options属性
名称类型默认值描述
multipleBooleantrue允许选择多个节点
checkboxBooleanfalse复选框模式,它显示每个节点的复选框
checkOnSelectBooleanfalse仅适用于复选框模式。当用户单击文本或者复选框时,节点将处于选中状态。
autoCheckChildrenBooleantrue仅适用于复选框模式。子项将与父项具有相同的选中状态
parentSelectBooleanfalse通过单击具有子节点的节点,将展开节点。即,我们有两种方法来展开/折叠节点,通过单击箭头和文本
keyboardNavigationBooleantrue允许用户使用键盘导航树
propertyNamesObject-此选项允许重新定义默认树结构
deletionBooleanFunctionfalse
fetchDataObject-设置异步数据
dndObject-dnd的事件包括拖动开始,拖动结束
editingObject-节点的编辑修改,按enter键或者单击页面的任意区域时,更改将被应用。
Structure结构
{
  "id": Number,
  "text": String,
  "data": Object,
  "children": Array,
  "state": Object
}
  • id: 节点id

  • text: 节点label

  • data: 每个节点的中间数据。你想要什么都行。这个对象是为每个节点创建的,VueJS使这个属性反应。

  • children: 节点子节点

  • state: 允许用户设置节点的状态。状态包括以下:

代码示例
<template>
  <div class="commonTreeSelect">
    <van-popup v-model="showPicker" position="bottom" round>
      <div class="header-warp">
        <div class="header">
          <span class="btn" @click="cacelPicker">取消</span>
          <span class="btn" @click="confirmPicker">确定</span>
        </div>
        <van-search class="search-input" v-model="searchVal" placeholder="请输入关键字" />
      </div>
      <div class="tree-wrap">
        <tree
          ref="tree"
          v-model="selectedNode"
          :autoCheckChildren="false"
          :filter="searchVal"
          :data="items"
          :options="options"
          @node:checked="onNodeCheck"
          @node:expanded="onNodeExpand"
        >
          <div slot-scope="{ node }" class="node-container">
            <div class="node-text">{{ node.text }}</div>
            <div class="node-controls">
              <a href="#" @click.stop="addChildNode(node)">新增</a>
              <a href="#" @click.stop="editNode(node)">编辑</a>
              <a href="#" @click.stop="removeNode(node)">删除</a>
            </div>
          </div>
        </tree>
      </div>
    </van-popup>
  </div>
</template>

<script>
import Vue from 'vue'
import LiquorTree from 'liquor-tree'
Vue.use(LiquorTree)
export default {
  name: 'commonTree',
  props: {
    showPicker: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      showText: '',
      searchVal: '',
      items: [
        { id: 0, text: 'Item 1' },
        { id: 1, text: 'Item 2' },
        {
          id: 2,
          text: 'Item 3',
          children: [
            { id: 3, text: 'Item 3.1' },
            { id: 4, text: 'Item 3.2' },
          ],
        },
        {
          id: 5,
          text: 'Item 4',
          children: [
            {
              id: 6,
              text: 'Item 4.1',
              children: [
                {
                  id: 7,
                  text: 'Item 4.1.1',
                },
                { id: 8, text: 'Item 4.1.2' },
                { id: 9, text: 'Item 4.1.3' },
              ],
            },
            {
              id: 10,
              text: 'Item 4.2',
              children: [
                { id: 11, text: 'Item 4.2.1' },
                { id: 12, text: 'Item 4.2.2' },
                { id: 13, text: 'Item 4.2.3' },
              ],
            },
          ],
        },
      ],
      options: {
        propertyNames: {
          text: 'text',
          children: 'children',
          state: 'state',
        },
        filter: {
          plainList: true,
        },
        checkbox: true,
        checkOnSelect: true,
        autoCheckChildren: true,
      },
      selectedNode: null,
    }
  },
  methods: {
    // 编辑节点
    editNode(node, e) {
      node.startEditing()
    },
    // 删除节点
    removeNode(node) {
      if (confirm('Are you sure?')) {
        node.remove()
      }
    },
    // 新增节点
    addChildNode(node) {
      if (node.enabled()) {
        node.append('New Node')
      }
    },
    // 点击展开节点时,只保留一个节点是打开的状态,自动关闭其他打开的节点
    onNodeExpand(node) {
      const expandedNodes = this.$refs.tree.findAll({ state: { expanded: true } })
      expandedNodes.forEach(expandedNode => {
        if (expandedNode.id !== node.id && node.depth === expandedNode.depth) {
          expandedNode.collapse()
        }
      })
    },
    // 取消
    cacelPicker() {
      this.showText = ''
      this.showPicker = false
      this.$refs.tree.findAll({ text: /^Java/, state: { checked: false } })
      this.$emit('cacelPicker')
    },
    // 确定
    confirmPicker() {
      const selection = this.$refs.tree.findAll({ text: /^Java/, state: { disabled: false } })
      const textList = selection.tree.checkedNodes.map(v => {
        return v.data.text
      })
      const idList = selection.tree.checkedNodes.map(v => {
        return v.id
      })
      this.showText = textList.length > 0 ? textList.join(',') : ''
      const idStr = idList.length > 0 ? idList.join(',') : ''
      this.showPicker = false
      console.log(1, this.selectedNode)
      console.log(2, this.items)
      this.$emit('confirmPicker', idStr)
    },
    onNodeCheck(node) {
      console.log(12, node)
    },
  },
  mounted() {},
}
</script>

<style lang="less" scoped>
.commonTreeSelect {
  width: 100%;
  height: 100%;
  .header-warp {
    width: 100%;
    .header {
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-size: 14px;
      padding: 0 12px;
    }
  }
  /deep/.tree-wrap {
    padding-bottom: 10px;
    .tree {
      max-height: 245px;
      overflow-y: auto;
      ul {
        li {
          width: 100%;
          .tree-checkbox {
            width: 16px;
            height: 16px;
          }
          .tree-anchor {
            width: 100%;
            font-size: 14px;
          }
          .tree-arrow.has-child:after {
            width: 6px;
            height: 6px;
          }
          .tree-checkbox.checked:after {
            left: 5px;
            top: 1px;
            height: 8.5px;
            width: 4px;
          }
          .node-container {
            display: flex;
            width: 100%;
            .node-text {
              margin-right: 10px;
            }
            .node-controls {
              a {
                color: #1989fa;
                margin-right: 6px;
              }
            }
          }
        }
      }
    }
  }
}
</style>
 vue-treeselect 组件

Vue-Treeselect是一款基于Vue.js的树形选择器,它可以帮助开发者快速构建一个可以多选的树形选择器,支持异步加载数据,支持搜索等功能。

GitCode : https://gitcode.com/riophae/vue-treeselect/overview?utm_source=csdn_github_accelerator&isLogin=1

官网 : https://vue-treeselect.js.org/

组件特点:

treeselecte 是一个具有嵌套选项的多选择组件,支持 Vue.js。

  • - 支持嵌套选项的单个和多个选项 
  • - 模糊匹配 
  • - 异步搜索 
  • - 支持嵌套选项的单个和多个选择 
  • - 支持嵌套选项的单个和多个选择,使用回车键等 
  • - 丰富的选项和高度可定制的 
  • - 支持多种浏览器 
  • - 必须 Vue 2.2+ 
安装
$ npm install --save @riophae/vue-treeselect
$ yarn add --save @riophae/vue-treeselect
引入
import Vue from 'vue'
import vueTreeselect from 'vue-treeselect'
 
// global registration
Vue.use(vueTreeselect) 
 
// or
Vue.component(vueTreeselect.name, vueTreeselect) 
 
// or
import vueTreeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
 
// local registration
export default {
  name: 'vueTreeselect',
  components: {
    [vueTreeselect.name]: vueTreeselect 
  }
}

注册:

<script src="https://cdn.jsdelivr.net/npm/@riophae/vue-treeselect@^0.4.0/dist/vue-treeselect.umd.min.js"></script>
API - Props
  • options:树形结构的数据,必填。
  • multiple:是否可以多选,默认为false。
  • load-options:异步加载子节点数据的函数,可选。
  • auto-load-root-options:是否自动加载根节点数据,默认为false。
props: {
  /**
   * 即使有禁用的选定节点,是否允许重置值
   */
  allowClearingDisabled: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 选择/取消选择祖先节点时,是否应选择/取消选中其禁用的后代
   * 和 allowClearingDisabled 一起使用
   */
  allowSelectingDisabledDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 菜单是否应始终打开
   */
  alwaysOpen: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否将菜单加到body上
   */
  appendToBody: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否启用异步搜索模式
   */
  async: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否自动将组件集中在装载上?
   */
  autoFocus: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 装载时自动加载根选项。当设置为“false”时,打开菜单时将加载根选项。
   */
  autoLoadRootOptions: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 当用户取消选择一个节点时,会自动取消选择其祖先。仅适用于平面模式。
   */
  autoDeselectAncestors: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户取消选择节点时,会自动取消选择其子节点。仅适用于平面模式。
   */
  autoDeselectDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户选择一个节点时,会自动选择其祖先。仅适用于平面模式。
   */
  autoSelectAncestors: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户选择一个节点时,会自动选择其子节点。仅适用于平面模式。
   */
  autoSelectDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 如果没有文本输入,按退格键是否删除最后一项。
   */
  backspaceRemoves: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 在清除所有输入字段之前进行处理的函数。
   * 返回“false”以防止清除值
   * @type {function(): (boolean|Promise<boolean>)}
   */
  beforeClearAll: {
    type: Function,
    default: constant(true),
  },
 
  /**
   * 在叶节点之前显示分支节点?
   */
  branchNodesFirst: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否应该缓存每个搜索请求的结果?
   */
  cacheOptions: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 是否显示重置值的“×”按钮?
   */
  clearable: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 清楚文本,multiple为true时
   */
  clearAllText: {
    type: String,
    default: 'Clear all',
  },
 
  /**
   * 选择后是否清除搜索输入。
   * 仅当“multiple”为“true”时使用。
   * 对于单选模式,无论道具值如何,它总是**在选择一个选项后清除输入。
   */
  clearOnSelect: {
    type: Boolean,
    default: false,
  },
 
  /**
   * “×”按钮的标题。
   */
  clearValueText: {
    type: String,
    default: 'Clear value',
  },
 
  /**
   * 选择选项后是否关闭菜单?
   * 仅当“multiple”为“true”时使用。
   */
  closeOnSelect: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 加载时应自动展开多少级别的分支节点。
   * 设置Infinity以使所有分支节点在默认情况下展开。
   */
  defaultExpandLevel: {
    type: Number,
    default: 0,
  },
 
  /**
   * 在用户开始搜索之前要显示的默认选项集。用于异步搜索模式。
   * 当设置为“true”时,将自动加载作为空字符串的搜索查询结果。
   * @type {boolean|node[]}
   */
  defaultOptions: {
    default: false,
  },
 
  /**
   * 如果没有文本输入,按delete键是否删除最后一项。
   */
  deleteRemoves: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 用于连接隐藏字段值的多个值的分隔符。
   */
  delimiter: {
    type: String,
    default: ',',
  },
 
  /**
   * 仅显示与搜索值直接匹配的节点,不包括其祖先。
   *
   * @type {Object}
   */
  flattenSearchResults: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否阻止选择分支节点?
   */
  disableBranchNodes: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 禁用控制?
   */
  disabled: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否禁用模糊匹配功能?
   */
  disableFuzzyMatching: {
    type: Boolean,
    default: false,
  },
 
  /**
   *是否启用平面模式。非平面模式(默认)是指: 
   *   - 每当检查分支节点时,它的所有子节点也将被检查
   *   - 每当一个分支节点检查了所有子节点时,该分支节点本身也会被检查
   * 设置“true”以禁用此机制
   */
  flat: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 将以所有事件作为最后一个参数进行传递。
   * 有助于识别事件的起源。
  */
  instanceId: {
    // Add two trailing "$" to distinguish from explictly specified ids.
    default: () => `${instanceId++}$$`,
    type: [String, Number],
  },
 
  /**
   * Joins multiple values into a single form field with the `delimiter` (legacy mode).
   * 使用“分隔符”将多个值合并到一个表单字段中(传统模式)。
  */
  joinValues: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 限制所选选项的显示。
   * 其余部分将隐藏在limitText字符串中。
   */
  limit: {
    type: Number,
    default: Infinity,
  },
 
  /**
   * 函数,在选定元素超过定义的限制时处理显示的消息
   * @type {function(number): string}
   */
  limitText: {
    type: Function,
    default: function limitTextDefault(count) { // eslint-disable-line func-name-matching
      return `and ${count} more`
    },
  },
 
  /**
   * 加载选项时显示的文本。
   */
  loadingText: {
    type: String,
    default: 'Loading...',
  },
 
  /**
   * 用于动态加载选项
   * @type {function({action: string, callback: (function((Error|string)=): void), parentNode: node=, instanceId}): void}
   */
  loadOptions: {
    type: Function,
  },
 
  /**
   * 要筛选的节点属性。
   */
  matchKeys: {
    type: Array,
    default: constant(['label']),
  },
 
  /**
   *  设置菜单的`maxHeight`样式值
   */
  maxHeight: {
    type: Number,
    default: 300,
  },
 
  /**
   * 设置`true`允许选择多个选项(即多选模式)。
   */
  multiple: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 为html表单生成一个隐藏的<input />标签,其中包含这个字段名。
   */
  name: {
    type: String,
  },
 
  /**
   * 当分支节点没有子节点时显示的文本。
   */
  noChildrenText: {
    type: String,
    default: 'No sub-options.',
  },
 
  /**
   * 没有可用选项时显示的文本。
   */
  noOptionsText: {
    type: String,
    default: 'No options available.',
  },
 
  /**
   * 没有匹配的搜索结果时显示的文本。
   */
  noResultsText: {
    type: String,
    default: 'No results found...',
  },
 
  /**
   * 用于规范化源数据。
   * @type {function(node, instanceId): node}
   */
  normalizer: {
    type: Function,
    default: identity,
  },
 
  /**
   * 默认情况下(' auto '),菜单将在控件下方打开。如果空间不够,vue-treeselect将自动翻转菜单。可以使用其他四个选项之一来强制菜单始终按指定的方向打开。
可接受的值:
   *   - `"auto"`
   *   - `"below"`
   *   - `"bottom"`
   *   - `"above"`
   *   - `"top"`
   */
  openDirection: {
    type: String,
    default: 'auto',
    validator(value) {
      const acceptableValues = ['auto', 'top', 'bottom', 'above', 'below']
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * 是否在单击控件时自动打开菜单。
   */
  openOnClick: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 控件获得焦点时是否自动打开菜单。
   */
  openOnFocus: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 可用选项的数组。
   * @type {node[]}
   */
  options: {
    type: Array,
  },
 
  /**
   * 字段占位符,没有值时显示。
   */
  placeholder: {
    type: String,
    default: 'Select...',
  },
 
  /**
   * 在需要的时候应用HTML5的required属性。
   */
  required: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 文本显示询问用户是否重试加载子选项。
   */
  retryText: {
    type: String,
    default: 'Retry?',
  },
 
  /**
   * 重试按钮的标题。
   */
  retryTitle: {
    type: String,
    default: 'Click to retry',
  },
 
  /**
   * 启用搜索功能?
   */
  searchable: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 搜索祖先节点。
   */
  searchNested: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 提示异步搜索的文本提示。
   */
  searchPromptText: {
    type: String,
    default: 'Type to search...',
  },
 
  /**
   * 是否在每个分支节点的标签旁边显示子节点计数。
   */
  showCount: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 与`showCount`一起使用,指定应该显示哪种类型的计数。
   * Acceptable values:
   *   - "ALL_CHILDREN"
   *   - "ALL_DESCENDANTS"
   *   - "LEAF_CHILDREN"
   *   - "LEAF_DESCENDANTS"
   */
  showCountOf: {
    type: String,
    default: ALL_CHILDREN,
    validator(value) {
      const acceptableValues = [ALL_CHILDREN, ALL_DESCENDANTS, LEAF_CHILDREN, LEAF_DESCENDANTS]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * 搜索时是否显示子项计数。
   * 当未指定时,回退到`showCount`的值。
   * @type {boolean}
   */
  showCountOnSearch: null,
 
  /**
   * 选择的选项应该以何种顺序显示在trigger中并在`value`数组中排序。
   * Used for multi-select mode only.
   * Acceptable values:
   *   - "ORDER_SELECTED"
   *   - "LEVEL"
   *   - "INDEX"
   */
  sortValueBy: {
    type: String,
    default: ORDER_SELECTED,
    validator(value) {
      const acceptableValues = [ORDER_SELECTED, LEVEL, INDEX]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * 控件的选项卡索引。
   */
  tabIndex: {
    type: Number,
    default: 0,
  },
 
  /**
   * 控件的值。
   * 单选模式应该是`id`或`node`对象,多选模式应该是一个`id`或`node`对象数组。
   * 它的格式取决于`valueFormat`属性。
   * 在大多数情况下,只需使用`v-model`即可。
   * @type {?Array}
   */
  value: null,
 
  /**
   * 在多选模式下,哪种类型的节点应该包含在`value`数组中。
   * 可接受值:
   *   - "ALL" - 任何被检查的节点都将被包含在`value`数组中
   *   - "BRANCH_PRIORITY" (default) - 如果检查了分支节点,它的所有后代节点将被排除在`value`数组之外
   *   - "LEAF_PRIORITY" - 如果检查了分支节点,则该节点本身及其分支后代将被排除在`value`数组之外,但其叶子后代将被包含在内
   *   - "ALL_WITH_INDETERMINATE" - 任何被检查的节点都会被包含在`value`数组中,加上不确定的节点
   */
  valueConsistsOf: {
    type: String,
    default: BRANCH_PRIORITY,
    validator(value) {
      const acceptableValues = [ALL, BRANCH_PRIORITY, LEAF_PRIORITY, ALL_WITH_INDETERMINATE]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * `value`属性的格式。
   * 注意,当设置为`"object"`时,在`value`属性中的每个`node`对象中只需要`id`和`label`属性。
   * Acceptable values:
   *   - "id"
   *   - "object"
   */
  valueFormat: {
    type: String,
    default: 'id',
  },
 
  /**
   * 菜单的Z-index。
   */
  zIndex: {
    type: [Number, String],
    default: 999,
  }
}
代码实例
<!-- Vue SFC -->
<template>
	<div id="app">
		<Treeselect
			v-model="selectedNodes"
			:maxHeight="400"
			:multiple="true"
			:always-open="true"
			:options="options"
			:normalizer="normalize"
			valueFormat="object"
			placeholder="请输入搜索关键字"
			noResultsText="未查询到您的搜索"
		/>
		<div class="tree-footer">
			<van-button round type="info" @click="addInstitution"
				>添加</van-button
			>
			<van-button round @click="closeAddInstitution">取消</van-button>
		</div>
	</div>
</template>
 
<script>
// import the component
import Treeselect from '@riophae/vue-treeselect';
import '@riophae/vue-treeselect/dist/vue-treeselect.css';
 
export default {
	// register the component
	components: { Treeselect },
	data() {
		return {
			// define the default selectedNodes
			selectedNodes: null,
			// define options
			options: [
				{
					id: '1',
					label: 'Item 1',
					children: [
						{
							id: '1-1',
							label: 'Item 1-1',
							children: [
								{ id: '1-1-1', label: 'Item 1-1-1' },
								{ id: '1-1-2', label: 'Item 1-1-2' },
								{ id: '1-1-3', label: 'Item 1-1-3' },
							],
						},
						{
							id: '1-2',
							label: 'Item 1-2',
							children: [
								{ id: '1-2-1', label: 'Item 1-2-1' },
								{ id: '1-2-2', label: 'Item 1-2-2' },
								{ id: '1-2-3', label: 'Item 1-2-3' },
							],
						},
					],
				},
				{
					id: '2',
					label: 'Item 2',
					children: [
						{
							id: '2-1',
							label: 'Item 2-1',
							children: [
								{
									id: '2-1-1',
									label: 'Item 2-1-1',
								},
								{
									id: '2-1-2',
									label: 'Item 2-1-2',
								},
								{
									id: '2-1-3',
									label: 'Item 2-1-3',
								},
							],
						},
						{
							id: '2-2',
							label: 'Item 2-2',
							children: [
								{
									id: '2-2-1',
									label: 'Item 2-2-1',
								},
								{
									id: '2-2-1',
									label: 'Item 2-2-2',
								},
								{
									id: '2-2-1',
									label: 'Item 2-2-3',
								},
							],
						},
					],
				},
				{
					id: '3',
					label: 'Item 3',
					children: [
						{
							id: '3-1',
							label: 'Item 3-1',
							children: [
								{
									id: '3-1-1',
									label: 'Item 3-1-1',
								},
								{
									id: '3-1-2',
									label: 'Item 3-1-2',
								},
								{
									id: '3-1-3',
									label: 'Item 3-1-3',
								},
							],
						},
						{
							id: '3-2',
							label: 'Item 3-2',
							children: [
								{
									id: '3-2-1',
									label: 'Item 3-2-1',
								},
								{
									id: '3-2-2',
									label: 'Item 3-2-2',
								},
								{
									id: '3-2-3',
									label: 'Item 3-2-3',
								},
							],
						},
					],
				},
			],
		};
	},
	methods: {
		// 自定义密钥名称
		normalize(options) {
			return {
				id: options.id,
				label: options.shortName,
				children: options.branchs,
			};
		},
		addInstitution() {
			if (!this.selectedNodes.length) {
				Notify('请先选择');
				return;
			}
			this.$emit('addInstitution', this.selectedNodes);
		},
		closeAddInstitution() {
			this.$emit('closeAddInstitution');
		},
	},
};
</script>

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡夫卡的小熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值