原生 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属性
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
multiple | Boolean | true | 允许选择多个节点 |
checkbox | Boolean | false | 复选框模式,它显示每个节点的复选框 |
checkOnSelect | Boolean | false | 仅适用于复选框模式。当用户单击文本或者复选框时,节点将处于选中状态。 |
autoCheckChildren | Boolean | true | 仅适用于复选框模式。子项将与父项具有相同的选中状态 |
parentSelect | Boolean | false | 通过单击具有子节点的节点,将展开节点。即,我们有两种方法来展开/折叠节点,通过单击箭头和文本 |
keyboardNavigation | Boolean | true | 允许用户使用键盘导航树 |
propertyNames | Object | - | 此选项允许重新定义默认树结构 |
deletion | Boolean | Function | false |
fetchData | Object | - | 设置异步数据 |
dnd | Object | - | dnd的事件包括拖动开始,拖动结束 |
editing | Object | - | 节点的编辑修改,按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>