[更新]: 树形组件基础演示
This commit is contained in:
parent
71e07cb0ac
commit
0617787e6a
@ -3,157 +3,227 @@
|
|||||||
<template>
|
<template>
|
||||||
<lay-tree
|
<lay-tree
|
||||||
:data="data"
|
:data="data"
|
||||||
|
:onlyIconControl="iconCtrl"
|
||||||
|
:showLine="showLine"
|
||||||
|
@node-click="handleClick"
|
||||||
>
|
>
|
||||||
</lay-tree>
|
</lay-tree>
|
||||||
</template>i
|
<br/>
|
||||||
|
只能通过节点左侧图标来展开收缩:
|
||||||
|
<br/>
|
||||||
|
<lay-switch v-model="iconCtrl"></lay-switch>
|
||||||
|
<br/>
|
||||||
|
只能通过节点左侧图标来展开收缩:
|
||||||
|
<br/>
|
||||||
|
<lay-switch v-model="showLine"></lay-switch>
|
||||||
|
<br/>
|
||||||
|
当前点击的节点:
|
||||||
|
<br/>
|
||||||
|
<pre>
|
||||||
|
{{ clickNode }}
|
||||||
|
</pre>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const data = ref([{
|
const data = ref([
|
||||||
title: '一级1'
|
{
|
||||||
,id: 1
|
title: '一级1',
|
||||||
,field: 'name1'
|
id: 1,
|
||||||
,checked: true
|
field: 'name1',
|
||||||
,spread: true
|
checked: true,
|
||||||
,children: [{
|
spread: true,
|
||||||
title: '二级1-1 可允许跳转'
|
children: [
|
||||||
,id: 3
|
{
|
||||||
,field: 'name11'
|
title: '二级1-1 可允许跳转',
|
||||||
,href: 'https://www.layui.com/'
|
id: 3,
|
||||||
,children: [{
|
field: 'name11',
|
||||||
title: '三级1-1-3'
|
href: 'https://www.layui.com/',
|
||||||
,id: 23
|
children: [
|
||||||
,field: ''
|
{
|
||||||
,children: [{
|
title: '三级1-1-3',
|
||||||
title: '四级1-1-3-1'
|
id: 23,
|
||||||
,id: 24
|
field: '',
|
||||||
,field: ''
|
children: [
|
||||||
,children: [{
|
{
|
||||||
title: '五级1-1-3-1-1'
|
title: '四级1-1-3-1',
|
||||||
,id: 30
|
id: 24,
|
||||||
,field: ''
|
field: '',
|
||||||
},{
|
children: [
|
||||||
title: '五级1-1-3-1-2'
|
{
|
||||||
,id: 31
|
title: '五级1-1-3-1-1',
|
||||||
,field: ''
|
id: 30,
|
||||||
}]
|
field: '',
|
||||||
}]
|
},
|
||||||
},{
|
{
|
||||||
title: '三级1-1-1'
|
title: '五级1-1-3-1-2',
|
||||||
,id: 7
|
id: 31,
|
||||||
,field: ''
|
field: '',
|
||||||
,children: [{
|
},
|
||||||
title: '四级1-1-1-1 可允许跳转'
|
],
|
||||||
,id: 15
|
},
|
||||||
,field: ''
|
],
|
||||||
,href: 'https://www.layui.com/doc/'
|
},
|
||||||
}]
|
{
|
||||||
},{
|
title: '三级1-1-1',
|
||||||
title: '三级1-1-2'
|
id: 7,
|
||||||
,id: 8
|
field: '',
|
||||||
,field: ''
|
children: [
|
||||||
,children: [{
|
{
|
||||||
title: '四级1-1-2-1'
|
title: '四级1-1-1-1 可允许跳转',
|
||||||
,id: 32
|
id: 15,
|
||||||
,field: ''
|
field: '',
|
||||||
}]
|
href: 'https://www.layui.com/doc/',
|
||||||
}]
|
},
|
||||||
},{
|
],
|
||||||
title: '二级1-2'
|
},
|
||||||
,id: 4
|
{
|
||||||
,spread: true
|
title: '三级1-1-2',
|
||||||
,children: [{
|
id: 8,
|
||||||
title: '三级1-2-1'
|
field: '',
|
||||||
,id: 9
|
children: [
|
||||||
,field: ''
|
{
|
||||||
,disabled: true
|
title: '四级1-1-2-1',
|
||||||
},{
|
id: 32,
|
||||||
title: '三级1-2-2'
|
field: '',
|
||||||
,id: 10
|
},
|
||||||
,field: ''
|
],
|
||||||
}]
|
},
|
||||||
},{
|
],
|
||||||
title: '二级1-3'
|
},
|
||||||
,id: 20
|
{
|
||||||
,field: ''
|
title: '二级1-2',
|
||||||
,children: [{
|
id: 4,
|
||||||
title: '三级1-3-1'
|
spread: true,
|
||||||
,id: 21
|
children: [
|
||||||
,field: ''
|
{
|
||||||
},{
|
title: '三级1-2-1',
|
||||||
title: '三级1-3-2'
|
id: 9,
|
||||||
,id: 22
|
field: '',
|
||||||
,field: ''
|
disabled: true,
|
||||||
}]
|
},
|
||||||
}]
|
{
|
||||||
},{
|
title: '三级1-2-2',
|
||||||
title: '一级2'
|
id: 10,
|
||||||
,id: 2
|
field: '',
|
||||||
,field: ''
|
},
|
||||||
,spread: true
|
],
|
||||||
,children: [{
|
},
|
||||||
title: '二级2-1'
|
{
|
||||||
,id: 5
|
title: '二级1-3',
|
||||||
,field: ''
|
id: 20,
|
||||||
,spread: true
|
field: '',
|
||||||
,children: [{
|
children: [
|
||||||
title: '三级2-1-1'
|
{
|
||||||
,id: 11
|
title: '三级1-3-1',
|
||||||
,field: ''
|
id: 21,
|
||||||
},{
|
field: '',
|
||||||
title: '三级2-1-2'
|
},
|
||||||
,id: 12
|
{
|
||||||
,field: ''
|
title: '三级1-3-2',
|
||||||
}]
|
id: 22,
|
||||||
},{
|
field: '',
|
||||||
title: '二级2-2'
|
},
|
||||||
,id: 6
|
],
|
||||||
,field: ''
|
},
|
||||||
,children: [{
|
],
|
||||||
title: '三级2-2-1'
|
},
|
||||||
,id: 13
|
{
|
||||||
,field: ''
|
title: '一级2',
|
||||||
},{
|
id: 2,
|
||||||
title: '三级2-2-2'
|
field: '',
|
||||||
,id: 14
|
spread: true,
|
||||||
,field: ''
|
children: [
|
||||||
,disabled: true
|
{
|
||||||
}]
|
title: '二级2-1',
|
||||||
}]
|
id: 5,
|
||||||
},{
|
field: '',
|
||||||
title: '一级3'
|
spread: true,
|
||||||
,id: 16
|
children: [
|
||||||
,field: ''
|
{
|
||||||
,children: [{
|
title: '三级2-1-1',
|
||||||
title: '二级3-1'
|
id: 11,
|
||||||
,id: 17
|
field: '',
|
||||||
,field: ''
|
},
|
||||||
,fixed: true
|
{
|
||||||
,children: [{
|
title: '三级2-1-2',
|
||||||
title: '三级3-1-1'
|
id: 12,
|
||||||
,id: 18
|
field: '',
|
||||||
,field: ''
|
},
|
||||||
},{
|
],
|
||||||
title: '三级3-1-2'
|
},
|
||||||
,id: 19
|
{
|
||||||
,field: ''
|
title: '二级2-2',
|
||||||
}]
|
id: 6,
|
||||||
},{
|
field: '',
|
||||||
title: '二级3-2'
|
children: [
|
||||||
,id: 27
|
{
|
||||||
,field: ''
|
title: '三级2-2-1',
|
||||||
,children: [{
|
id: 13,
|
||||||
title: '三级3-2-1'
|
field: '',
|
||||||
,id: 28
|
},
|
||||||
,field: ''
|
{
|
||||||
},{
|
title: '三级2-2-2',
|
||||||
title: '三级3-2-2'
|
id: 14,
|
||||||
,id: 29
|
field: '',
|
||||||
,field: ''
|
disabled: true,
|
||||||
}]
|
},
|
||||||
}]
|
],
|
||||||
}])
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '一级3',
|
||||||
|
id: 16,
|
||||||
|
field: '',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '二级3-1',
|
||||||
|
id: 17,
|
||||||
|
field: '',
|
||||||
|
fixed: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '三级3-1-1',
|
||||||
|
id: 18,
|
||||||
|
field: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '三级3-1-2',
|
||||||
|
id: 19,
|
||||||
|
field: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '二级3-2',
|
||||||
|
id: 27,
|
||||||
|
field: '',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '三级3-2-1',
|
||||||
|
id: 28,
|
||||||
|
field: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '三级3-2-2',
|
||||||
|
id: 29,
|
||||||
|
field: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const iconCtrl = ref(false)
|
||||||
|
const showLine = ref(true)
|
||||||
|
const clickNode = ref(null)
|
||||||
|
|
||||||
|
function handleClick(node) {
|
||||||
|
clickNode.value = node
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
@ -9,6 +9,7 @@ import { TreeNode } from '/@src/module/tree/tree.type'
|
|||||||
|
|
||||||
interface TreeEntityProps{
|
interface TreeEntityProps{
|
||||||
node: TreeNode
|
node: TreeNode
|
||||||
|
onlyIconControl: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EmitEvent {
|
interface EmitEvent {
|
||||||
@ -31,7 +32,7 @@ const renderLineShort = (node: TreeNode): boolean => {
|
|||||||
(node._parentNode._nextSibling === null ||
|
(node._parentNode._nextSibling === null ||
|
||||||
//上一层父级有延伸线
|
//上一层父级有延伸线
|
||||||
(node._parentNode._nextSibling && !node._parentNode._nextSibling.children))
|
(node._parentNode._nextSibling && !node._parentNode._nextSibling.children))
|
||||||
)
|
) as boolean
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 展开收起 icon样式
|
* 展开收起 icon样式
|
||||||
@ -41,10 +42,22 @@ const nodeIconType = (node: TreeNode): string => {
|
|||||||
return !node.spread ? 'layui-icon-addition' : 'layui-icon-subtraction'
|
return !node.spread ? 'layui-icon-addition' : 'layui-icon-subtraction'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发射至外层
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
function handleNodeClick (node: TreeNode) {
|
function handleNodeClick (node: TreeNode) {
|
||||||
emit('node-click', node)
|
emit('node-click', node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归事件
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
function innerClick (node: TreeNode) {
|
||||||
|
emit('node-click', node)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<template v-if="node.children && node.children.length > 0">
|
<template v-if="node.children && node.children.length > 0">
|
||||||
@ -56,9 +69,9 @@ function handleNodeClick (node: TreeNode) {
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="layui-tree-entry">
|
<div class="layui-tree-entry">
|
||||||
<div class="layui-tree-main" @click="handleNodeClick(node)">
|
<div class="layui-tree-main" @click.prevent.stop="!onlyIconControl && handleNodeClick(node)">
|
||||||
<span class="layui-tree-iconClick layui-tree-icon">
|
<span class="layui-tree-iconClick layui-tree-icon">
|
||||||
<LayIcon :type="nodeIconType(node)"></LayIcon>
|
<LayIcon :type="nodeIconType(node)" @click.prevent.stop="handleNodeClick(node)"></LayIcon>
|
||||||
</span>
|
</span>
|
||||||
<span class="layui-tree-txt">{{ node.title }}</span>
|
<span class="layui-tree-txt">{{ node.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -72,7 +85,8 @@ function handleNodeClick (node: TreeNode) {
|
|||||||
v-for="(item, index) in node.children"
|
v-for="(item, index) in node.children"
|
||||||
:key="index"
|
:key="index"
|
||||||
:node="item"
|
:node="item"
|
||||||
@node-click="handleNodeClick"
|
@node-click="innerClick"
|
||||||
|
:onlyIconControl="onlyIconControl"
|
||||||
></LayTreeEntity>
|
></LayTreeEntity>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,9 +99,9 @@ function handleNodeClick (node: TreeNode) {
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="layui-tree-entry">
|
<div class="layui-tree-entry">
|
||||||
<div class="layui-tree-main">
|
<div class="layui-tree-main" @click.prevent.stop="!onlyIconControl && handleNodeClick(node)">
|
||||||
<span class="layui-tree-iconClick">
|
<span class="layui-tree-iconClick">
|
||||||
<LayIcon type="layui-icon-file"></LayIcon>
|
<LayIcon type="layui-icon-file" @click.prevent.stop="handleNodeClick(node)"></LayIcon>
|
||||||
</span>
|
</span>
|
||||||
<span class="layui-tree-txt">{{ node.title }}</span>
|
<span class="layui-tree-txt">{{ node.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,12 +3,13 @@ import { VNode, VNodeChild } from 'vue'
|
|||||||
import TreeEntity from './TreeEntity.vue'
|
import TreeEntity from './TreeEntity.vue'
|
||||||
import { useTreeData } from '/@src/module/tree/useTreeData'
|
import { useTreeData } from '/@src/module/tree/useTreeData'
|
||||||
import { TreeNode } from '/@src/module/tree/tree.type'
|
import { TreeNode } from '/@src/module/tree/tree.type'
|
||||||
|
import { getEmitNode } from '/@src/module/tree/treeHelper'
|
||||||
|
|
||||||
type EditAction = 'add' | 'update' | 'del'
|
type EditAction = 'add' | 'update' | 'del'
|
||||||
|
|
||||||
type EditType = boolean | EditAction[]
|
type EditType = boolean | EditAction[]
|
||||||
|
|
||||||
export declare interface TreeData {
|
interface TreeData {
|
||||||
/**
|
/**
|
||||||
* 节点唯一索引值,用于对指定节点进行各类操作
|
* 节点唯一索引值,用于对指定节点进行各类操作
|
||||||
*/
|
*/
|
||||||
@ -107,40 +108,42 @@ export declare interface TreeProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface EmitData {
|
interface EmitData {
|
||||||
/**
|
/**
|
||||||
* 当前点击的节点数据
|
* 当前点击的节点数据
|
||||||
*/
|
*/
|
||||||
data: TreeData
|
data: TreeData
|
||||||
/**
|
/**
|
||||||
* 节点的展开状态
|
* 节点的展开状态
|
||||||
|
* remove
|
||||||
*/
|
*/
|
||||||
state: 'open' | 'close' | 'normal'
|
state?: 'open' | 'close' | 'normal'
|
||||||
/**
|
/**
|
||||||
* 当前节点元素
|
* 当前节点元素
|
||||||
|
* remove
|
||||||
*/
|
*/
|
||||||
elem: Element | VNode | VNodeChild
|
elem?: Element | VNode | VNodeChild
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeEmits {
|
interface TreeEmits {
|
||||||
/**
|
/**
|
||||||
* 节点被点击后触发
|
* 节点被点击后触发
|
||||||
* @param e 事件
|
* @param e 事件
|
||||||
* @param treeNode
|
* @param treeNode
|
||||||
*/
|
*/
|
||||||
(e: 'on-click', treeNode: EmitData): void
|
(e: 'node-click', treeNode: EmitData): void
|
||||||
/**
|
/**
|
||||||
* 点击复选框时触发
|
* 点击复选框时触发
|
||||||
* @param e 事件
|
* @param e 事件
|
||||||
* @param treeNode
|
* @param treeNode
|
||||||
*/
|
*/
|
||||||
(e: 'on-check', treeNode: EmitData): void
|
(e: 'node-check', treeNode: EmitData): void
|
||||||
/**
|
// /**
|
||||||
* 操作节点的回调
|
// * 操作节点的回调
|
||||||
* @param e 事件
|
// * @param e 事件
|
||||||
* @param treeNode
|
// * @param treeNode
|
||||||
*/
|
// */
|
||||||
(e: 'on-operate', treeNode: EmitData): void
|
// (e: 'node-operate', treeNode: EmitData): void
|
||||||
(e: 'update:spreadKeys', spreadKeys: string[]): void
|
(e: 'update:spreadKeys', spreadKeys: string[]): void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +166,8 @@ const {
|
|||||||
|
|
||||||
function handleNodeClick(node: TreeNode) {
|
function handleNodeClick(node: TreeNode) {
|
||||||
updateInnerTreeData(innerTreeData.value, node)
|
updateInnerTreeData(innerTreeData.value, node)
|
||||||
|
const emitNode = getEmitNode(props.data!, node)
|
||||||
|
emit('node-click', { data: emitNode! })
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -177,6 +182,7 @@ export default {
|
|||||||
v-for="(node) in innerTreeData"
|
v-for="(node) in innerTreeData"
|
||||||
:key="node.id"
|
:key="node.id"
|
||||||
:node="node"
|
:node="node"
|
||||||
|
:onlyIconControl="onlyIconControl"
|
||||||
@node-click="handleNodeClick"
|
@node-click="handleNodeClick"
|
||||||
></TreeEntity>
|
></TreeEntity>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,32 +116,37 @@ export interface EmitData {
|
|||||||
/**
|
/**
|
||||||
* 节点的展开状态
|
* 节点的展开状态
|
||||||
*/
|
*/
|
||||||
state: 'open' | 'close' | 'normal'
|
state?: 'open' | 'close' | 'normal'
|
||||||
/**
|
/**
|
||||||
* 当前节点元素
|
* 当前节点元素
|
||||||
*/
|
*/
|
||||||
elem: Element | VNode | VNodeChild
|
elem?: Element | VNode | VNodeChild
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeEmits {
|
export declare interface TreeEmits {
|
||||||
/**
|
/**
|
||||||
* 节点被点击后触发
|
* 节点被点击后触发
|
||||||
* @param e 事件
|
* @param e 事件
|
||||||
* @param treeNode
|
* @param treeNode
|
||||||
*/
|
*/
|
||||||
(e: 'on-click', treeNode: EmitData): void
|
(e: 'node-click', treeNode: EmitData): void
|
||||||
/**
|
/**
|
||||||
* 点击复选框时触发
|
* 点击复选框时触发
|
||||||
* @param e 事件
|
* @param e 事件
|
||||||
* @param treeNode
|
* @param treeNode
|
||||||
*/
|
*/
|
||||||
(e: 'on-check', treeNode: EmitData): void
|
(e: 'node-check', treeNode: EmitData): void
|
||||||
/**
|
/**
|
||||||
* 操作节点的回调
|
* 操作节点的回调
|
||||||
* @param e 事件
|
* @param e 事件
|
||||||
* @param treeNode
|
* @param treeNode
|
||||||
*/
|
*/
|
||||||
(e: 'on-operate', treeNode: EmitData): void
|
// (e: 'on-operate', treeNode: EmitData): void
|
||||||
|
/**
|
||||||
|
* update:spreadKeys
|
||||||
|
* @param e
|
||||||
|
* @param spreadKeys
|
||||||
|
*/
|
||||||
(e: 'update:spreadKeys', spreadKeys: (string | number)[]): void
|
(e: 'update:spreadKeys', spreadKeys: (string | number)[]): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { TreeData, TreeNode } from '/@src/module/tree/tree.type'
|
import { TreeData, TreeNode } from '/@src/module/tree/tree.type'
|
||||||
|
import { Nullable } from '/@src/module/type'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加父级parentId
|
* 添加父级parentId
|
||||||
@ -83,3 +84,26 @@ export const getTreeSpreadKeys = (data: TreeData[]): (string | number)[] => {
|
|||||||
}
|
}
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取点击的原数据的节点
|
||||||
|
* @param data
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
export const getEmitNode = (data: TreeData[], node: TreeNode): Nullable<TreeData> => {
|
||||||
|
let item: Nullable<TreeData> = null
|
||||||
|
const len = data.length
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
if (data[i].id === node.id) {
|
||||||
|
item = data[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (data[i].children && data[i].children.length > 0) {
|
||||||
|
item = getEmitNode(data[i].children, node)
|
||||||
|
if (item) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ export const useTreeData: UseTreeData = (props, emit) => {
|
|||||||
}
|
}
|
||||||
return getTreeSpreadKeys(props.data)
|
return getTreeSpreadKeys(props.data)
|
||||||
},
|
},
|
||||||
set: (value) => {
|
set: (value: (string | number)[]) => {
|
||||||
emit('update:spreadKeys', value)
|
emit('update:spreadKeys', value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user