树
Quasar Tree代表一个高度可配置的组件,用于显示分层数据,如树结构的目录。
安装 编辑 /quasar.conf.js
:framework: { components: ['QTree' ] }
基本用法 这是您可以编写的最简单的树:<template > <q-tree :nodes ="simple" node-key ="label" /> </template > <script > export default { data: () => ({ simple: [ { label: 'Satisfied customers' , children: [ { label: 'Good food' , children: [ { label : 'Quality ingredients' }, { label : 'Good recipe' } ] }, { label: 'Good service (disabled node)' , disabled: true , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , children: [ { label : 'Happy atmosphere' }, { label : 'Good table presentation' }, { label : 'Pleasing decor' } ] } ] } ] }) } </script >
请注意,节点必须具有由每个键的属性定义的唯一键。在上面的例子中,标签是唯一的,所以我们使用label
属性来定义这些键。但是,您可以将任何属性添加到节点(如’id’或任何您想要的),然后使用该属性(如node-key="id"
)。
Vue属性
Vue属性
类型
说明
nodes
数组
Tree的Vue模型
node-key
字符串
用作唯一键的节点属性。
color
字符串
连接线的颜色。
control-color
字符串
复选框的颜色。
text-color
字符串
文本的颜色。
dark
布尔
在黑暗背景下渲染时。
icon
字符串
每个节点的连接器图标。
selected
任何
使用.sync 。所选节点的唯一键值。
tick-strategy
字符串
‘leaf’、’leaf-filtered’、’strict’、’none’之一。
ticked
数组
使用.sync 。打勾节点的唯一键集合。
expanded
数组
使用.sync 。扩展节点的唯一键集合。
default-expand-all
布尔
第一次渲染时展开所有节点。
accordion
布尔
展开一个节点关闭它的兄弟节点。
filter
字符串
过滤节点时使用的字符串。
filter-method
函数
自定义过滤方法。
no-nodes-label
字符串
当没有节点可用时,覆盖默认的i18n消息。
no-results-label
字符串
过滤后没有节点可用时,覆盖默认的i18n消息。
节点模型结构 以下描述了QTree v-model拥有的节点属性。
节点属性
类型
说明
label
字符串
节点的标签
icon
字符串
节点的图标
img
字符串
节点的图像。使用静态文件夹。例如:’statics/mountains.png’
avatar
字符串
节点的头像。使用静态文件夹。例如:’statics/boy-avatar.png’
children
数组
子元素节点数组。
disabled
布尔
节点是否被禁用?
expandable
布尔
节点是否可扩展?
tickable
布尔
当使用打勾策略时,每个节点显示一个复选框。应该禁用节点的复选框?
noTick
布尔
在使用打勾策略时,节点是否显示复选框?
tickStrategy
字符串
仅此节点覆盖全局打勾策略。 ‘leaf’、’leaf-filtered’、’strict’、’none’之一。
lazy
布尔
子元素应该懒加载吗?在这种情况下也不要指定’children’属性。
header
字符串
节点头部范围的插槽名称,没有必需的“header”前缀。例如:’story’是指’header-story’范围的插槽。
body
字符串
节点主体范围的插槽名称,没有必需的“body-”前缀。例如:’story’是指’body-story’范围的插槽。
Selection vs Ticking, Expansion
Selection(通过QTreeselected
属性)是指当前选择的节点(用不同的背景突出显示)。
Ticking(通过QTreeticked
属性)引用与每个节点相关的复选框。
Expansion(通过QTreeexpanded
属性)是指被扩展的节点。
上述所有属性都需要.sync
修饰符才能正常工作。例:<q-tree selected.sync ="selection" ...
勾选策略 有三种勾选策略: ‘leaf’、’leaf-filtered’、’strict’ 外加默认值’none’——禁止勾选。
策略
说明
leaf
勾选节点只是叶子节点。 勾选节点也会影响父节点的打勾状态(父节点变成部分打勾状态或打勾状态)以及其子节点(所有可打勾的子节点都打勾)。
leaf-filtered
与leaf
相同的概念,只是这个策略只适用于过滤的节点(过滤后保持可见的节点)。
strict
勾选节点独立于父节点或子节点状态。
您可以为QTree应用全局勾选策略,并通过在nodes
模型中指定tickStrategy
来本地更改某个节点的勾选策略。
自定义过滤方法 您可以通过指定filter-method
属性来自定义过滤方法。 下面的方法实际上是默认的过滤策略:<template > <q-tree :filter-method ="myFilterMethod" ... > </template > <script > export default { methods: { myFilterMethod (node, filter) { const filt = filter.toLowerCase() return node.label && node.label.toLowerCase().indexOf(filt) > -1 } } } </script >
例子 节点图标/头像/图像,控制扩展和着色 <template > <div > <q-btn color ="secondary" @click ="togglePropsGoodServiceExpand" label ="Toggle 'Good service' expansion" /> <q-tree :nodes ="props" :expanded.sync ="propsExpanded" color ="red" node-key ="label" /> </div > </template > <script > export default { data: () => ({ propsExpanded: ['Satisfied customers' , 'Pleasant surroundings' ], props: [ { label: 'Satisfied customers' , avatar: 'statics/boy-avatar.png' , children: [ { label: 'Good food' , icon: 'restaurant_menu' , children: [ { label : 'Quality ingredients' }, { label : 'Good recipe' } ] }, { label: 'Good service' , icon: 'room_service' , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , icon: 'photo' , children: [ { label: 'Happy atmosphere' , img: 'statics/parallax1.jpg' }, { label: 'Good table presentation' , img: 'statics/parallax2.jpg' }, { label: 'Pleasing decor' , img: 'statics/mountains.jpg' } ] } ] } ] }), methods: { togglePropsGoodServiceExpand () { const index = this .propsExpanded.indexOf('Good service' ) if (index > -1 ) { this .propsExpanded.splice(index, 1 ) } else { this .propsExpanded.push('Good service' ) } } } } </script >
定制节点(头部和主体插槽) <template > <q-tree :nodes ="customize" node-key ="label" default-expand-all > <div slot ="header-root" slot-scope ="prop" class ="row items-center" > <img src ="~assets/quasar-logo.svg" class ="avatar q-mr-sm" > <div > {{ prop.node.label }} <q-chip color ="orange" small > New!</q-chip > </div > </div > <div slot ="header-generic" slot-scope ="prop" class ="row items-center" > <q-icon :name ="prop.node.icon || 'star'" color ="orange" size ="28px" class ="q-mr-sm" /> <div class ="text-weight-bold text-primary" > {{ prop.node.label }}</div > </div > <div slot ="body-story" slot-scope ="prop" > <span class ="text-weight-thin" > The story is:</span > {{ prop.node.story }} </div > <div slot ="body-toggle" slot-scope ="prop" > <p class ="caption" > {{ prop.node.caption }}</p > <q-toggle v-model ="prop.node.enabled" label ="I agree to the terms and conditions" /> </div > </q-tree > </template > <script > export default { data: () => ({ customize: [ { label: 'Satisfied customers' , header: 'root' , children: [ { label: 'Good food' , icon: 'restaurant_menu' , header: 'generic' , children: [ { label: 'Quality ingredients' , header: 'generic' , body: 'story' , story: 'Lorem ipsum dolor sit amet.' }, { label: 'Good recipe' , body: 'story' , story: 'A Congressman works with his equally conniving wife to exact revenge on the people who betrayed him.' } ] }, { label: 'Good service' , header: 'generic' , body: 'toggle' , caption: 'Why are we as consumers so captivated by stories of great customer service? Perhaps it is because...' , enabled: false , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , children: [ { label : 'Happy atmosphere' }, { label : 'Good table presentation' , header : 'generic' }, { label : 'Pleasing decor' } ] } ] } ] }) } </script >
应用默认的头部和主体插槽 <template > <q-tree :nodes ="customize" node-key ="label" default-expand-all > <div slot ="default-header" slot-scope ="prop" class ="row items-center" > <q-icon :name ="prop.node.icon || 'share'" color ="orange" size ="28px" class ="q-mr-sm" /> <div class ="text-weight-bold text-primary" > {{ prop.node.label }}</div > </div > <div slot ="default-body" slot-scope ="prop" > <div v-if ="prop.node.story" > <span class ="text-weight-thin" > This node has a story</span > : {{ prop.node.story }} </div > <span v-else class ="text-weight-thin" > This is some default content.</span > </div > </q-tree > </template > <script > export default { data: () => ({ customize: [ { label: 'Satisfied customers' , header: 'root' , children: [ { label: 'Good food' , icon: 'restaurant_menu' , header: 'generic' , children: [ { label: 'Quality ingredients' , header: 'generic' , body: 'story' , story: 'Lorem ipsum dolor sit amet.' }, { label: 'Good recipe' , body: 'story' , story: 'A Congressman works with his equally conniving wife to exact revenge on the people who betrayed him.' } ] }, { label: 'Good service' , header: 'generic' , body: 'toggle' , caption: 'Why are we as consumers so captivated by stories of great customer service? Perhaps it is because...' , enabled: false , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , children: [ { label : 'Happy atmosphere' }, { label : 'Good table presentation' , header : 'generic' }, { label : 'Pleasing decor' } ] } ] } ] }) } </script >
过滤节点 <template > <div > <q-input v-model ="filter" stack-label ="Filter" clearable class ="q-mb-sm" /> <q-tree :nodes ="simple" :filter ="filter" default-expand-all node-key ="label" /> </div > </template > <script > export default { data: () => ({ filter: '' , simple: [ { label: 'Satisfied customers' , children: [ { label: 'Good food' , children: [ { label : 'Quality ingredients' }, { label : 'Good recipe' } ] }, { label: 'Good service (disabled node)' , disabled: true , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , children: [ { label : 'Happy atmosphere' }, { label : 'Good table presentation' }, { label : 'Pleasing decor' } ] } ] } ] }) } </script >
手风琴模式(兄弟节点在展开时收缩) <q-tree :nodes ="simple" accordion node-key ="label" />
可选节点 <template > <div > <div class ="q-mb-sm" > <q-btn size ="sm" color ="primary" @click ="selectGoodService" label ="Select 'Good service'" /> <q-btn v-if ="selected" size ="sm" color ="red" @click ="unselectNode" label ="Unselect node" /> </div > <q-tree :nodes ="props" default-expand-all :selected.sync ="selected" node-key ="label" /> </div > </template > <script > export default { data: () => ({ selected: null , props: [ { label: 'Satisfied customers' , avatar: 'statics/boy-avatar.png' , children: [ { label: 'Good food' , icon: 'restaurant_menu' , children: [ { label : 'Quality ingredients' }, { label : 'Good recipe' } ] }, { label: 'Good service' , icon: 'room_service' , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , icon: 'photo' , children: [ { label: 'Happy atmosphere' , img: 'statics/parallax1.jpg' }, { label: 'Good table presentation' , img: 'statics/parallax2.jpg' }, { label: 'Pleasing decor' , img: 'statics/mountains.jpg' } ] } ] } ] }), methods: { selectGoodService () { if (this .selected !== 'Good service' ) { this .selected = 'Good service' } }, unselectNode () { this .selected = null }, } } </script >
可选节点和策略 <template > <div > <div class ="q-mb-sm row no-wrap items-center" > <q-select v-model ="tickStrategy" color ="secondary" stack-label ="Tick Strategy" :options ="[ {label: 'None', value: 'none'}, {label: 'Leaf', value: 'leaf'}, {label: 'Leaf Filtered', value: 'leaf-filtered'}, {label: 'Strict', value: 'strict'} ]" class ="q-ma-none q-mr-sm" style ="width: 150px" /> <q-input v-if ="tickStrategy === 'leaf-filtered'" color ="secondary" stack-label ="Filter" v-model ="tickFilter" class ="q-ma-none" clearable /> </div > <q-tree :nodes ="tickable" color ="secondary" default-expand-all :ticked.sync ="ticked" :tick-strategy ="tickStrategy" :filter ="tickFilter" node-key ="label" /> </div > </template > <script > export default { data: () => ({ ticked: [], tickStrategy: 'leaf' , tickFilter: null , tickable: [ { label: 'Satisfied customers' , avatar: 'statics/boy-avatar.png' , children: [ { label: 'Good food' , icon: 'restaurant_menu' , children: [ { label : 'Quality ingredients' }, { label : 'Good recipe' } ] }, { label: 'Good service' , icon: 'room_service' , children: [ { label : 'Prompt attention' }, { label : 'Professional waiter' } ] }, { label: 'Pleasant surroundings' , icon: 'photo' , children: [ { label: 'Happy atmosphere (not tickable)' , tickable: false , img: 'statics/parallax1.jpg' }, { label: 'Good table presentation (disabled node)' , disabled: true , img: 'statics/parallax2.jpg' }, { label: 'Pleasing decor' , img: 'statics/mountains.jpg' } ] }, { label: 'Extra information (has no tick)' , noTick: true , icon: 'photo' }, { label: 'Forced tick strategy (to "strict" in this case)' , tickStrategy: 'strict' , icon: 'school' , children: [ { label: 'Happy atmosphere' , img: 'statics/parallax1.jpg' }, { label: 'Good table presentation' , img: 'statics/parallax2.jpg' }, { label: 'Very pleasing decor' , img: 'statics/mountains.jpg' } ] } ] } ] }) } </script >
延迟加载节点 <template > <q-tree :nodes ="lazy" default-expand-all node-key ="label" @lazy-load ="onLazyLoad" /> </template > <script > export default { data: () => ({ lazy: [ { label: 'Node 1' , children: [ { label : 'Node 1.1' , lazy : true }, { label : 'Node 1.2' , lazy : true } ] }, { label: 'Node 2' , lazy: true }, { label: 'Lazy load empty' , lazy: true }, { label: 'Node is not expandable' , expandable: false , children: [ { label : 'Some node' } ] } ] }), methods: { onLazyLoad ({ node, key, done, fail }) { setTimeout(() => { if (key.indexOf('Lazy load empty' ) > -1 ) { done([]) return } const label = node.label done([ { label : `${label} .1` }, { label : `${label} .2` , lazy : true }, { label: `${label} .3` , children: [ { label : `${label} .3.1` , lazy : true }, { label : `${label} .3.2` , lazy : true } ] } ]) }, 1000) } } } </script >