refactor(tree): 移注第一版tree的实现
This commit is contained in:
parent
b4477f049b
commit
32252dba94
26
docs/docs/zh-CN/components/colorPicker.md
Normal file
26
docs/docs/zh-CN/components/colorPicker.md
Normal file
@ -0,0 +1,26 @@
|
||||
::: field 基础使用
|
||||
:::
|
||||
|
||||
::: demo
|
||||
|
||||
<template>
|
||||
<lay-color-picker></lay-color-picker>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
::: field icon-picker 属性
|
||||
:::
|
||||
|
||||
| | | |
|
||||
| ---------- | -------- | --- |
|
||||
| v-model | 默认值 | -- |
|
||||
| page | 开启分页 | -- |
|
||||
| showSearch | 启用搜索 | -- |
|
@ -328,6 +328,12 @@ export default {
|
||||
subTitle: 'select',
|
||||
path: '/zh-CN/components/select',
|
||||
},
|
||||
{
|
||||
id: 40,
|
||||
title: '颜色选择器',
|
||||
subTitle: 'colorPicker',
|
||||
path: '/zh-CN/components/colorPicker',
|
||||
},
|
||||
]
|
||||
|
||||
const selected = ref(1)
|
||||
|
@ -219,6 +219,11 @@ const zhCN = [
|
||||
component: () => import('../../docs/zh-CN/components/select.md'),
|
||||
meta: { title: '下拉选择' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/colorPicker',
|
||||
component: () => import('../../docs/zh-CN/components/colorPicker.md'),
|
||||
meta: { title: '颜色选择器' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
@ -57,6 +57,7 @@ import LayCheckboxGroup from './module/checkboxGroup/index'
|
||||
import LaySlider from './module/slider/index'
|
||||
import LayCarousel from './module/carousel/index'
|
||||
import LayCarouselItem from './module/carouselItem/index'
|
||||
import LayColorPicker from './module/colorPicker/index'
|
||||
|
||||
const components: Record<string, IDefineComponent> = {
|
||||
LayRadio,
|
||||
@ -113,7 +114,8 @@ const components: Record<string, IDefineComponent> = {
|
||||
LayCheckboxGroup,
|
||||
LaySlider,
|
||||
LayCarousel,
|
||||
LayCarouselItem
|
||||
LayCarouselItem,
|
||||
LayColorPicker,
|
||||
}
|
||||
|
||||
const install = (app: App, options?: InstallOptions): void => {
|
||||
@ -182,6 +184,7 @@ export {
|
||||
LaySlider,
|
||||
LayCarousel,
|
||||
LayCarouselItem,
|
||||
LayColorPicker,
|
||||
install,
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
import type { App } from 'vue'
|
||||
import Component from './index.vue'
|
||||
import { DefineComponent } from 'vue'
|
||||
|
||||
Component.install = (app: App) => {
|
||||
app.component(Component.name || 'LayColorPicker', Component)
|
||||
}
|
||||
|
||||
export default Component as DefineComponent
|
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayColorPicker'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pre>
|
||||
// todo =_=
|
||||
</pre>
|
||||
</template>
|
@ -1,182 +0,0 @@
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayTreeEntity',
|
||||
}
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import LayIcon from '../icon'
|
||||
import LayCheckbox from '../checkbox'
|
||||
import { TreeNode } from '/@src/module/tree/tree.type'
|
||||
|
||||
type EventType = 'icon' | 'node'
|
||||
|
||||
interface TreeEntityProps {
|
||||
node: TreeNode
|
||||
showCheckbox?: boolean
|
||||
updateCheckedByNode: (node: TreeNode) => void
|
||||
}
|
||||
|
||||
interface EmitEvent {
|
||||
(e: 'node-click', node: TreeNode, type: EventType): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TreeEntityProps>(), {
|
||||
showCheckbox: false,
|
||||
})
|
||||
const emit = defineEmits<EmitEvent>()
|
||||
|
||||
/**
|
||||
* 是否设置短线
|
||||
* @param node
|
||||
*/
|
||||
const renderLineShort = (node: TreeNode): boolean => {
|
||||
return (
|
||||
// 没有下一个节点
|
||||
(node._nextSibling === null &&
|
||||
node._parentNode &&
|
||||
// 外层最后一个
|
||||
(node._parentNode._nextSibling === null ||
|
||||
//上一层父级有延伸线
|
||||
(node._parentNode._nextSibling &&
|
||||
!node._parentNode._nextSibling.children))) as boolean
|
||||
)
|
||||
}
|
||||
/**
|
||||
* 展开收起 icon样式
|
||||
* @param node
|
||||
*/
|
||||
const nodeIconType = (node: TreeNode): string => {
|
||||
return !node.spread ? 'layui-icon-addition' : 'layui-icon-subtraction'
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点Icon点击
|
||||
* @param node
|
||||
*/
|
||||
function handleIconClick(node: TreeNode) {
|
||||
emit('node-click', node, 'icon')
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点点击
|
||||
* @param node
|
||||
*/
|
||||
function handleNodeClick(node: TreeNode) {
|
||||
emit('node-click', node, 'node')
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归emit事件
|
||||
* @param node
|
||||
* @param type
|
||||
*/
|
||||
function innerClick(node: TreeNode, type: EventType, e?: Event) {
|
||||
emit('node-click', node, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* checkbox click
|
||||
* @param arg
|
||||
* @param node
|
||||
*/
|
||||
function handleCheckboxChange(
|
||||
arg: { checked: boolean; value: string },
|
||||
node: TreeNode
|
||||
) {
|
||||
props.updateCheckedByNode(node)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<template v-if="node.children && node.children.length > 0">
|
||||
<div
|
||||
class="layui-tree-set"
|
||||
:class="{
|
||||
'layui-tree-setLineShort': renderLineShort(node),
|
||||
'layui-tree-setHide': !node.parentId,
|
||||
}"
|
||||
>
|
||||
<div class="layui-tree-entry">
|
||||
<div class="layui-tree-main">
|
||||
<span class="layui-tree-iconClick layui-tree-icon">
|
||||
<LayIcon
|
||||
:type="nodeIconType(node)"
|
||||
@click.prevent.stop="handleIconClick(node, 'icon')"
|
||||
/>
|
||||
</span>
|
||||
<LayCheckbox
|
||||
v-if="showCheckbox"
|
||||
v-model:checked="node._checked"
|
||||
name="name"
|
||||
skin="primary"
|
||||
@change="
|
||||
(args) => {
|
||||
handleCheckboxChange(args, node)
|
||||
}
|
||||
"
|
||||
>
|
||||
<!-- {{ node.title }} || {{node.id}}-->
|
||||
</LayCheckbox>
|
||||
<span
|
||||
class="layui-tree-txt"
|
||||
@click.prevent.stop="handleNodeClick(node, 'node')"
|
||||
>
|
||||
{{ node.title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-show="node.spread"
|
||||
class="layui-tree-pack layui-tree-showLine"
|
||||
style="display: block"
|
||||
>
|
||||
<LayTreeEntity
|
||||
v-for="(item, index) in node.children"
|
||||
:key="index"
|
||||
:node="item"
|
||||
:show-checkbox="showCheckbox"
|
||||
:update-checked-by-node="updateCheckedByNode"
|
||||
@node-click="innerClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
class="layui-tree-set"
|
||||
:class="{
|
||||
'layui-tree-setLineShort': renderLineShort(node),
|
||||
}"
|
||||
>
|
||||
<div class="layui-tree-entry">
|
||||
<div class="layui-tree-main">
|
||||
<span class="layui-tree-iconClick">
|
||||
<LayIcon
|
||||
type="layui-icon-file"
|
||||
@click.prevent.stop="handleIconClick(node, 'icon')"
|
||||
/>
|
||||
</span>
|
||||
<LayCheckbox
|
||||
v-if="showCheckbox"
|
||||
v-model:checked="node._checked"
|
||||
name="name"
|
||||
skin="primary"
|
||||
label="1"
|
||||
@change="
|
||||
(args) => {
|
||||
handleCheckboxChange(args, node)
|
||||
}
|
||||
"
|
||||
>
|
||||
</LayCheckbox>
|
||||
<span
|
||||
class="layui-tree-txt"
|
||||
@click.prevent.stop="handleNodeClick(node, 'node')"
|
||||
>
|
||||
{{ node.title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<style scoped lang="less"></style>
|
@ -5,13 +5,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { StringOrNumber } from '/@src/module/tree/new-tree/tree.type'
|
||||
import { StringOrNumber } from '/@src/module/tree/tree.type'
|
||||
import { Nullable } from '/@src/module/type'
|
||||
|
||||
import LayIcon from '../../icon'
|
||||
import LayCheckbox from '../../checkbox'
|
||||
import { nextTick, Ref } from 'vue'
|
||||
import { Tree } from '/@src/module/tree/new-tree/tree'
|
||||
import LayIcon from '../icon'
|
||||
import LayCheckbox from '../checkbox'
|
||||
import { Ref } from 'vue'
|
||||
import { Tree } from '/@src/module/tree/tree'
|
||||
|
||||
type CustomKey = string | number
|
||||
type CustomString = (() => string) | string
|
||||
@ -134,7 +134,9 @@ function handleTitleClick(node: TreeData) {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<transition>
|
||||
<transition
|
||||
name="move"
|
||||
>
|
||||
<div
|
||||
v-if="node.isLeaf.value"
|
||||
class="layui-tree-pack layui-tree-showLine"
|
||||
@ -153,4 +155,5 @@ function handleTitleClick(node: TreeData) {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
</style>
|
@ -1,6 +1,5 @@
|
||||
import type { App } from 'vue'
|
||||
// import Component from './index.vue'
|
||||
import Component from './new-tree/index.vue'
|
||||
import Component from './index.vue'
|
||||
import type { IDefineComponent } from '../type/index'
|
||||
|
||||
Component.install = (app: App) => {
|
||||
|
@ -1,202 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { VNode, VNodeChild } from 'vue'
|
||||
import TreeEntity from './TreeEntity.vue'
|
||||
import { useTreeData } from '/@src/module/tree/useTreeData'
|
||||
import { TreeNode } from '/@src/module/tree/tree.type'
|
||||
import { getEmitNode } from '/@src/module/tree/treeHelper'
|
||||
|
||||
type EditAction = 'add' | 'update' | 'del'
|
||||
|
||||
type EditType = boolean | EditAction[]
|
||||
|
||||
interface TreeData {
|
||||
/**
|
||||
* 节点唯一索引值,用于对指定节点进行各类操作
|
||||
*/
|
||||
id: string | number
|
||||
/**
|
||||
* 节点标题
|
||||
*/
|
||||
title: string | (() => string)
|
||||
/**
|
||||
* 节点字段名
|
||||
*/
|
||||
field: string | (() => string)
|
||||
/**
|
||||
* 子节点。支持设定选项同父节点
|
||||
*/
|
||||
children: TreeData[]
|
||||
/**
|
||||
* 点击节点弹出新窗口对应的 url。需开启 isJump 参数
|
||||
* 废弃,通过 on-click事件用户控制
|
||||
*/
|
||||
href?: string | URL
|
||||
/**
|
||||
* 节点是否初始展开,默认 false
|
||||
* 废弃:设置 v-model:spreadKeys
|
||||
*/
|
||||
spread?: boolean
|
||||
/**
|
||||
* 节点是否初始为选中状态(如果开启复选框的话),默认 false
|
||||
* 废弃:设置 v-model:checkedKeys
|
||||
*/
|
||||
checked?: boolean
|
||||
/**
|
||||
* 节点是否为禁用状态。默认 false
|
||||
*/
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface TreeProps {
|
||||
/**
|
||||
* 指定唯一的id
|
||||
*/
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
key?: string
|
||||
/**
|
||||
* 选中的节点
|
||||
*/
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
checkedKeys?: NonNullable<(string | number)[]>
|
||||
/**
|
||||
* 展开的节点
|
||||
*/
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
spreadKeys?: (string | number)[]
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
data?: TreeData[]
|
||||
/**
|
||||
* 是否显示复选框 默认 false
|
||||
*/
|
||||
showCheckbox?: boolean
|
||||
/**
|
||||
* 是否开启节点的操作图标。默认 false。
|
||||
若为 true,则默认显示“改删”图标
|
||||
若为 数组,则可自由配置操作图标的显示状态和顺序,目前支持的操作图标有:add、update、del,如:
|
||||
edit: ['add', 'update', 'del']
|
||||
*/
|
||||
edit?: EditType
|
||||
/**
|
||||
* 是否开启手风琴模式,默认 false
|
||||
*/
|
||||
accordion?: boolean
|
||||
/**
|
||||
* 是否仅允许节点左侧图标控制展开收缩。默认 false(即点击节点本身也可控制)。若为 true,则只能通过节点左侧图标来展开收缩
|
||||
*/
|
||||
onlyIconControl?: boolean
|
||||
/**
|
||||
* 是否允许点击节点时弹出新窗口跳转。默认 false,若开启,需在节点数据中设定 link 参数(值为 url 格式
|
||||
* 废弃:能过事件用户自行控制
|
||||
*/
|
||||
isJump?: boolean
|
||||
/**
|
||||
* 是否开启连接线。默认 true,若设为 false,则节点左侧出现三角图标。
|
||||
*/
|
||||
showLine?: boolean
|
||||
/**
|
||||
* 自定义各类默认文本,目前支持以下设定:
|
||||
*/
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
text?: {
|
||||
/**
|
||||
* 节点默认名称
|
||||
*/
|
||||
defaultNodeName?: () => string | string
|
||||
/**
|
||||
* 数据为空时的提示文本
|
||||
*/
|
||||
none?: (() => string) | string | VNode | Element
|
||||
}
|
||||
}
|
||||
|
||||
interface EmitData {
|
||||
/**
|
||||
* 当前点击的节点数据
|
||||
*/
|
||||
data: TreeData
|
||||
/**
|
||||
* 节点的展开状态
|
||||
* remove
|
||||
*/
|
||||
state?: 'open' | 'close' | 'normal'
|
||||
/**
|
||||
* 当前节点元素
|
||||
* remove
|
||||
*/
|
||||
elem?: Element | VNode | VNodeChild
|
||||
}
|
||||
|
||||
interface TreeEmits {
|
||||
/**
|
||||
* 节点被点击后触发
|
||||
* @param e 事件
|
||||
* @param treeNode
|
||||
*/
|
||||
(e: 'node-click', treeNode: EmitData): void
|
||||
/**
|
||||
* 点击复选框时触发
|
||||
* @param e 事件
|
||||
* @param treeNode
|
||||
*/
|
||||
(e: 'node-check', treeNode: EmitData): void
|
||||
// /**
|
||||
// * 操作节点的回调
|
||||
// * @param e 事件
|
||||
// * @param treeNode
|
||||
// */
|
||||
// (e: 'node-operate', treeNode: EmitData): void
|
||||
(e: 'update:spreadKeys', spreadKeys: (string | number)[]): void
|
||||
(e: 'update:checkedKeys', checkedKeys: (string | number)[]): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TreeProps>(), {
|
||||
showCheckbox: false,
|
||||
accordion: false,
|
||||
onlyIconControl: false,
|
||||
isJump: false,
|
||||
showLine: true,
|
||||
edit: () => true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<TreeEmits>()
|
||||
|
||||
const {
|
||||
innerTreeData,
|
||||
updateInnerTreeData,
|
||||
treeWrapperClass,
|
||||
updateCheckedByNode,
|
||||
} = useTreeData(props, emit)
|
||||
|
||||
function handleNodeClick(node: TreeNode, type: 'node' | 'icon') {
|
||||
// 是否只通过icon控制展开收起
|
||||
if (props.onlyIconControl) {
|
||||
type === 'icon' && updateInnerTreeData(innerTreeData.value, node)
|
||||
} else {
|
||||
updateInnerTreeData(innerTreeData.value, node)
|
||||
}
|
||||
// icon 点击不emit出事件
|
||||
const emitNode = getEmitNode(props.data!, node)
|
||||
type !== 'icon' && emit('node-click', { data: emitNode! })
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
* @Date: 2021-10-16 13:22:38
|
||||
* @LastEditors: 落小梅
|
||||
* @LastEditTime: 2021-10-16 13:53:14
|
||||
* @FilePath: \layui-vue\src\module\tree\new-tree\index.vue
|
||||
-->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayTree',
|
||||
}
|
||||
|
||||
// import { TreeEmits, TreeProps as Tp } from './tree.type'
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import TreeNode from './TreeNode.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useTree } from '/@src/module/tree/useTree'
|
||||
import { TreeData } from '/@src/module/tree/tree'
|
||||
|
||||
type StringFn = () => string
|
||||
type StringOrNumber = string | number
|
||||
type KeysType = (number | string)[]
|
||||
type EditType = boolean | ('add' | 'update' | 'delete')
|
||||
|
||||
interface OriginalTreeData {
|
||||
title: StringFn | string
|
||||
id: StringOrNumber
|
||||
field: StringFn | string
|
||||
children?: OriginalTreeData[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface TreeProps {
|
||||
checkedKeys?: KeysType
|
||||
data: OriginalTreeData
|
||||
showCheckbox?: boolean
|
||||
edit?: EditType
|
||||
accordion?: boolean
|
||||
onlyIconControl?: boolean
|
||||
showLine?: boolean
|
||||
disabled?: boolean
|
||||
replaceFields?: {
|
||||
id?: string
|
||||
children?: string
|
||||
title?: string
|
||||
}
|
||||
}
|
||||
|
||||
interface TreeEmits {
|
||||
(e: 'update:checkedKeys', keys: KeysType): void
|
||||
(e: 'update:expandKeys', keys: KeysType): void
|
||||
(e: 'node-click', node: OriginalTreeData): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TreeProps>(), {
|
||||
showCheckbox: false,
|
||||
edit: false,
|
||||
accordion: false,
|
||||
onlyIconControl: false,
|
||||
disabled: false,
|
||||
showLine: true,
|
||||
replaceFields: () => {
|
||||
return {
|
||||
id: 'id',
|
||||
children: 'children',
|
||||
title: 'title',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<TreeEmits>()
|
||||
|
||||
const className = computed(() => {
|
||||
return {
|
||||
'layui-tree': true,
|
||||
'layui-form': props.showCheckbox,
|
||||
'layui-tree-line': props.showLine,
|
||||
}
|
||||
})
|
||||
|
||||
const { tree, nodeList } = useTree(props, emit)
|
||||
|
||||
function handleClick(node: TreeData) {
|
||||
const originNode = tree.getOriginData(node.id)
|
||||
emit('node-click', originNode)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div :class="treeWrapperClass">
|
||||
<TreeEntity
|
||||
v-for="(node, index) in innerTreeData"
|
||||
:key="node.id || index"
|
||||
:node="node"
|
||||
<div :class="className">
|
||||
<tree-node
|
||||
:tree="tree"
|
||||
:node-list="nodeList"
|
||||
:show-checkbox="showCheckbox"
|
||||
:update-checked-by-node="updateCheckedByNode"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
:show-line="showLine"
|
||||
:only-icon-control="onlyIconControl"
|
||||
@node-click="handleClick"
|
||||
></tree-node>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
|
@ -1,99 +0,0 @@
|
||||
<!--
|
||||
* @Date: 2021-10-16 13:22:38
|
||||
* @LastEditors: 落小梅
|
||||
* @LastEditTime: 2021-10-16 13:53:14
|
||||
* @FilePath: \layui-vue\src\module\tree\new-tree\index.vue
|
||||
-->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayTree',
|
||||
}
|
||||
|
||||
// import { TreeEmits, TreeProps as Tp } from './tree.type'
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import TreeNode from './TreeNode.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useTree } from '/@src/module/tree/new-tree/useTree'
|
||||
import { TreeData } from '/@src/module/tree/new-tree/tree'
|
||||
|
||||
type StringFn = () => string
|
||||
type StringOrNumber = string | number
|
||||
type KeysType = (number | string)[]
|
||||
type EditType = boolean | ('add' | 'update' | 'delete')
|
||||
|
||||
interface OriginalTreeData {
|
||||
title: StringFn | string
|
||||
id: StringOrNumber
|
||||
field: StringFn | string
|
||||
children?: OriginalTreeData[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface TreeProps {
|
||||
checkedKeys?: KeysType
|
||||
data: OriginalTreeData
|
||||
showCheckbox?: boolean
|
||||
edit?: EditType
|
||||
accordion?: boolean
|
||||
onlyIconControl?: boolean
|
||||
showLine?: boolean
|
||||
disabled?: boolean
|
||||
replaceFields?: {
|
||||
id?: string
|
||||
children?: string
|
||||
title?: string
|
||||
}
|
||||
}
|
||||
|
||||
interface TreeEmits {
|
||||
(e: 'update:checkedKeys', keys: KeysType): void
|
||||
(e: 'update:expandKeys', keys: KeysType): void
|
||||
(e: 'node-click', node: OriginalTreeData): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TreeProps>(), {
|
||||
showCheckbox: false,
|
||||
edit: false,
|
||||
accordion: false,
|
||||
onlyIconControl: false,
|
||||
disabled: false,
|
||||
showLine: true,
|
||||
replaceFields: () => {
|
||||
return {
|
||||
id: 'id',
|
||||
children: 'children',
|
||||
title: 'title',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<TreeEmits>()
|
||||
|
||||
const className = computed(() => {
|
||||
return {
|
||||
'layui-tree': true,
|
||||
'layui-form': props.showCheckbox,
|
||||
'layui-tree-line': props.showLine,
|
||||
}
|
||||
})
|
||||
|
||||
const { tree, nodeList } = useTree(props, emit)
|
||||
|
||||
function handleClick(node: TreeData) {
|
||||
const originNode = tree.getOriginData(node.id)
|
||||
emit('node-click', originNode)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div :class="className">
|
||||
<tree-node
|
||||
:tree="tree"
|
||||
:node-list="nodeList"
|
||||
:show-checkbox="showCheckbox"
|
||||
:show-line="showLine"
|
||||
:only-icon-control="onlyIconControl"
|
||||
@node-click="handleClick"
|
||||
></tree-node>
|
||||
</div>
|
||||
</template>
|
@ -1,34 +0,0 @@
|
||||
export type StringFn = () => string
|
||||
export type StringOrNumber = string | number
|
||||
export type KeysType = (number | string)[]
|
||||
export type EditType = boolean | ('add' | 'update' | 'delete')
|
||||
|
||||
export interface OriginalTreeData {
|
||||
title: StringFn | string
|
||||
id: StringOrNumber
|
||||
field: StringFn | string
|
||||
children?: OriginalTreeData[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export interface TreeProps {
|
||||
checkedKeys?: KeysType
|
||||
expandKeys?: KeysType
|
||||
data: OriginalTreeData
|
||||
showCheckbox?: boolean
|
||||
edit?: EditType
|
||||
accordion?: boolean
|
||||
onlyIconControl?: boolean
|
||||
showLine?: boolean
|
||||
replaceFields?: {
|
||||
id?: string
|
||||
children?: string
|
||||
title?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TreeEmits {
|
||||
(e: 'update:checkedKeys', keys: KeysType): void
|
||||
(e: 'update:expandKeys', keys: KeysType): void
|
||||
(e: 'node-click', node: OriginalTreeData, event: Event): void
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
import {
|
||||
OriginalTreeData,
|
||||
StringFn,
|
||||
StringOrNumber,
|
||||
} from '/@src/module/tree/new-tree/tree.type'
|
||||
import { computed, ref, Ref } from 'vue'
|
||||
import { Nullable } from '/@src/module/type'
|
||||
|
||||
export interface TreeConfig {
|
||||
disabled: boolean
|
||||
showLine: boolean
|
||||
showCheckbox: boolean
|
||||
spread: boolean
|
||||
checkedKeys: StringOrNumber[]
|
||||
spreadKeys: StringOrNumber[]
|
||||
}
|
||||
|
||||
export const useTreeData = (
|
||||
treeConfig: TreeConfig,
|
||||
origin: OriginalTreeData | OriginalTreeData[]
|
||||
) => {
|
||||
const tree = computed(() => {
|
||||
const initTree = initialTree(origin)
|
||||
const treeNodes = getTreeNodes(initTree, treeConfig, '')
|
||||
return treeNodes
|
||||
})
|
||||
}
|
||||
|
||||
function getTreeNodes(
|
||||
origin: OriginalTreeData[],
|
||||
config: TreeConfig,
|
||||
parentId = ''
|
||||
) {
|
||||
const len = origin.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
const node = createTreeNode(config, origin[i], i, len, parentId)
|
||||
}
|
||||
}
|
||||
|
||||
function createTreeNode(
|
||||
config: TreeConfig,
|
||||
current: OriginalTreeData,
|
||||
i: number,
|
||||
len: number,
|
||||
parentId: string
|
||||
) {
|
||||
const treeNode = new TreeNode(current)
|
||||
const { disabled, spread } = config
|
||||
treeNode.isDisabled.value = disabled
|
||||
treeNode.isLeaf.value = spread
|
||||
if (i < len - 1) {
|
||||
treeNode.hasNextSibling = true
|
||||
}
|
||||
treeNode.parentId = parentId
|
||||
}
|
||||
|
||||
class TreeNode {
|
||||
id: StringOrNumber
|
||||
parentId: StringOrNumber
|
||||
title: StringFn | string
|
||||
children: Nullable<TreeNode[]>
|
||||
parentNode: Nullable<TreeNode>
|
||||
isDisabled: Ref<boolean>
|
||||
isLeaf: Ref<boolean>
|
||||
isChecked: Ref<boolean>
|
||||
hasNextSibling: boolean
|
||||
constructor(options: OriginalTreeData) {
|
||||
this.id = options.id
|
||||
this.title = options.title
|
||||
this.children = []
|
||||
this.parentId = ''
|
||||
this.parentNode = null
|
||||
this.isDisabled = ref(false)
|
||||
this.isLeaf = ref(false)
|
||||
this.isChecked = ref(false)
|
||||
this.hasNextSibling = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init tree.
|
||||
* if origin source is object then return Array.of(originSource)
|
||||
* if array, return
|
||||
* @param originData
|
||||
*/
|
||||
function initialTree(originData: OriginalTreeData | OriginalTreeData[]): any {
|
||||
let treeNodes = []
|
||||
if (!Array.isArray(originData)) {
|
||||
treeNodes.push(originData)
|
||||
} else {
|
||||
treeNodes = Array.of(originData)
|
||||
}
|
||||
return treeNodes
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
OriginalTreeData,
|
||||
StringOrNumber,
|
||||
} from '/@src/module/tree/new-tree/tree.type'
|
||||
} from '/@src/module/tree/tree.type'
|
||||
import { Nullable } from '/@src/module/type'
|
||||
import { Ref, ref } from 'vue'
|
||||
|
@ -1,193 +1,34 @@
|
||||
/**
|
||||
* defineProps 暂不支持外部导入的复杂类型,这里备用后续迁移。
|
||||
* @see https://github.com/vuejs/vue-next/issues/4294
|
||||
* 暂时 在index.vue内部单独实现一份
|
||||
*/
|
||||
import {
|
||||
ComputedRef,
|
||||
Ref,
|
||||
UnwrapRef,
|
||||
VNode,
|
||||
VNodeChild,
|
||||
WritableComputedRef,
|
||||
} from 'vue'
|
||||
import { Nullable, Recordable } from '/@src/module/type'
|
||||
export type StringFn = () => string
|
||||
export type StringOrNumber = string | number
|
||||
export type KeysType = (number | string)[]
|
||||
export type EditType = boolean | ('add' | 'update' | 'delete')
|
||||
|
||||
type EditAction = 'add' | 'update' | 'del'
|
||||
|
||||
type EditType = boolean | EditAction[]
|
||||
|
||||
export declare interface TreeData {
|
||||
/**
|
||||
* 节点唯一索引值,用于对指定节点进行各类操作
|
||||
*/
|
||||
id: string | number
|
||||
/**
|
||||
* 节点标题
|
||||
*/
|
||||
title: string | (() => string)
|
||||
/**
|
||||
* 节点字段名
|
||||
*/
|
||||
field: string | (() => string)
|
||||
/**
|
||||
* 子节点。支持设定选项同父节点
|
||||
*/
|
||||
children: TreeData[]
|
||||
/**
|
||||
* 点击节点弹出新窗口对应的 url。需开启 isJump 参数
|
||||
* 废弃,通过 on-click事件用户控制
|
||||
*/
|
||||
href?: string | URL
|
||||
/**
|
||||
* 节点是否初始展开,默认 false
|
||||
* 废弃:设置 v-model:spreadKeys
|
||||
*/
|
||||
spread?: boolean
|
||||
/**
|
||||
* 节点是否初始为选中状态(如果开启复选框的话),默认 false
|
||||
* 废弃:设置 v-model:checkedKeys
|
||||
*/
|
||||
checked?: boolean
|
||||
/**
|
||||
* 节点是否为禁用状态。默认 false
|
||||
*/
|
||||
export interface OriginalTreeData {
|
||||
title: StringFn | string
|
||||
id: StringOrNumber
|
||||
field: StringFn | string
|
||||
children?: OriginalTreeData[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export declare interface TreeProps {
|
||||
/**
|
||||
* 指定唯一的id
|
||||
*/
|
||||
key?: string
|
||||
/**
|
||||
* 选中的节点
|
||||
*/
|
||||
checkedKeys?: NonNullable<(string | number)[]>
|
||||
/**
|
||||
* 展开的节点
|
||||
*/
|
||||
spreadKeys?: (string | number)[]
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
data?: TreeData[]
|
||||
/**
|
||||
* 是否显示复选框 默认 false
|
||||
*/
|
||||
export interface TreeProps {
|
||||
checkedKeys?: KeysType
|
||||
expandKeys?: KeysType
|
||||
data: OriginalTreeData
|
||||
showCheckbox?: boolean
|
||||
/**
|
||||
* 是否开启节点的操作图标。默认 false。
|
||||
若为 true,则默认显示“改删”图标
|
||||
若为 数组,则可自由配置操作图标的显示状态和顺序,目前支持的操作图标有:add、update、del,如:
|
||||
edit: ['add', 'update', 'del']
|
||||
*/
|
||||
edit?: EditType
|
||||
/**
|
||||
* 是否开启手风琴模式,默认 false
|
||||
*/
|
||||
accordion?: boolean
|
||||
/**
|
||||
* 是否仅允许节点左侧图标控制展开收缩。默认 false(即点击节点本身也可控制)。若为 true,则只能通过节点左侧图标来展开收缩
|
||||
*/
|
||||
onlyIconControl?: boolean
|
||||
/**
|
||||
* 是否允许点击节点时弹出新窗口跳转。默认 false,若开启,需在节点数据中设定 link 参数(值为 url 格式
|
||||
* 废弃:能过事件用户自行控制
|
||||
*/
|
||||
isJump?: boolean
|
||||
/**
|
||||
* 是否开启连接线。默认 true,若设为 false,则节点左侧出现三角图标。
|
||||
*/
|
||||
showLine?: boolean
|
||||
/**
|
||||
* 自定义各类默认文本,目前支持以下设定:
|
||||
*/
|
||||
text?: {
|
||||
/**
|
||||
* 节点默认名称
|
||||
*/
|
||||
defaultNodeName?: () => string | string
|
||||
/**
|
||||
* 数据为空时的提示文本
|
||||
*/
|
||||
none?: (() => string) | string | VNode | Element
|
||||
replaceFields?: {
|
||||
id?: string
|
||||
children?: string
|
||||
title?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmitData {
|
||||
/**
|
||||
* 当前点击的节点数据
|
||||
*/
|
||||
data: TreeData
|
||||
/**
|
||||
* 节点的展开状态
|
||||
*/
|
||||
state?: 'open' | 'close' | 'normal'
|
||||
/**
|
||||
* 当前节点元素
|
||||
*/
|
||||
elem?: Element | VNode | VNodeChild
|
||||
}
|
||||
|
||||
export declare interface TreeEmits {
|
||||
/**
|
||||
* 节点被点击后触发
|
||||
* @param e 事件
|
||||
* @param treeNode
|
||||
*/
|
||||
(e: 'node-click', treeNode: EmitData): void
|
||||
/**
|
||||
* 点击复选框时触发
|
||||
* @param e 事件
|
||||
* @param treeNode
|
||||
*/
|
||||
(e: 'node-check', treeNode: EmitData): void
|
||||
/**
|
||||
* 操作节点的回调
|
||||
* @param e 事件
|
||||
* @param treeNode
|
||||
*/
|
||||
// (e: 'on-operate', treeNode: EmitData): void
|
||||
/**
|
||||
* update:spreadKeys
|
||||
* @param e
|
||||
* @param spreadKeys
|
||||
*/
|
||||
(e: 'update:spreadKeys', spreadKeys: (string | number)[]): void
|
||||
(e: 'update:checkedKeys', checkedKeys: (string | number)[]): void
|
||||
}
|
||||
|
||||
export interface TreeExpose {
|
||||
/**
|
||||
* 返回选中的节点数据
|
||||
* 一般来说,v-model就够使了,但不排除需要直接获取其值的时候,此时通过该方法获取
|
||||
* eg: treeRef.getChecked()
|
||||
*/
|
||||
getChecked: () => TreeData
|
||||
}
|
||||
|
||||
// 设置节点勾选 setChecked 变为v-model控制
|
||||
// 实例重载 reload 变为v-model控制
|
||||
|
||||
export interface TreeNode extends TreeData {
|
||||
parentId: string | number
|
||||
children: TreeNode[]
|
||||
_parentNode?: Nullable<TreeNode>
|
||||
_nextSibling?: Nullable<TreeNode>
|
||||
_expand?: boolean
|
||||
_checked?: boolean
|
||||
}
|
||||
|
||||
/** hook type **/
|
||||
export type UseTreeData = (
|
||||
props: TreeProps,
|
||||
emit: TreeEmits
|
||||
) => {
|
||||
innerTreeData: Ref<UnwrapRef<TreeNode[]>>
|
||||
checkedKeys: WritableComputedRef<(string | number)[]>
|
||||
spreadKeys: WritableComputedRef<(string | number)[]>
|
||||
treeWrapperClass: ComputedRef<Recordable>
|
||||
updateInnerTreeData: (treeData: TreeData[], node: TreeData) => void
|
||||
updateCheckedByNode: (treeNode: TreeNode) => void
|
||||
export interface TreeEmits {
|
||||
(e: 'update:checkedKeys', keys: KeysType): void
|
||||
(e: 'update:expandKeys', keys: KeysType): void
|
||||
(e: 'node-click', node: OriginalTreeData, event: Event): void
|
||||
}
|
||||
|
@ -1,217 +0,0 @@
|
||||
import { TreeData, TreeNode } from '/@src/module/tree/tree.type'
|
||||
import { Nullable } from '/@src/module/type'
|
||||
import { WritableComputedRef } from 'vue'
|
||||
|
||||
/**
|
||||
* 添加父级parentId
|
||||
* @param data
|
||||
* @param parentId
|
||||
*/
|
||||
export const generatorTreeData = (
|
||||
data: TreeData[] | TreeNode[],
|
||||
parentId: TreeNode['parentId'] = ''
|
||||
): TreeNode[] => {
|
||||
const innerTreeData: TreeNode[] = []
|
||||
const len = data.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
const item = data[i]
|
||||
const inner = {
|
||||
...item,
|
||||
parentId: parentId,
|
||||
spread: item.spread || false,
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
inner.children = generatorTreeData(item.children, item.id)
|
||||
}
|
||||
innerTreeData.push(inner as TreeNode)
|
||||
}
|
||||
return innerTreeData
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前节点的下一个节点nextSibling,没有为null
|
||||
* @param data
|
||||
*/
|
||||
export const setNextSiblings = (data: TreeNode[]): void => {
|
||||
const len = data.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
data[i]._nextSibling = i + 1 < len ? data[i + 1] : null
|
||||
if (data[i].children && data[i].children.length > 0) {
|
||||
setNextSiblings(data[i].children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前节点的父节点,没有为null
|
||||
* @param data
|
||||
* @param parentNode
|
||||
*/
|
||||
export const setParentNode = (
|
||||
data: TreeNode[],
|
||||
parentNode?: TreeNode
|
||||
): void => {
|
||||
const len = data.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
data[i]._parentNode = parentNode ? parentNode : null
|
||||
if (data[i].children && data[i].children.length > 0) {
|
||||
setParentNode(data[i].children, data[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化内部tree结构
|
||||
* @param data
|
||||
* @param checkedKeys
|
||||
*/
|
||||
export const initialTreeData = (
|
||||
data: TreeData[],
|
||||
checkedKeys: WritableComputedRef<(string | number)[]>
|
||||
): TreeNode[] => {
|
||||
const innerTree = generatorTreeData(data, '')
|
||||
setNextSiblings(innerTree)
|
||||
setParentNode(innerTree)
|
||||
patchCheckedKeys(innerTree, checkedKeys)
|
||||
return innerTree
|
||||
}
|
||||
|
||||
/**
|
||||
* 树展开的节点
|
||||
* v-model:spreadKeys 或者
|
||||
* data.item.spreadKeys
|
||||
* @param data
|
||||
*/
|
||||
export const getTreeSpreadKeys = (data: TreeData[]): (string | number)[] => {
|
||||
let keys: (string | number)[] = []
|
||||
const len = data.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
const id: number | string = data[i].id
|
||||
if (data[i].spread) {
|
||||
keys.push(id)
|
||||
}
|
||||
if (data[i].children && data[i].children.length > 0) {
|
||||
keys = [...keys, ...getTreeSpreadKeys(data[i].children)]
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* checkedKes分发到node
|
||||
* @param tree
|
||||
* @param checkedKeys
|
||||
* @param checked
|
||||
*/
|
||||
export const patchCheckedKeys = (
|
||||
tree: TreeNode[],
|
||||
checkedKeys: WritableComputedRef<(string | number)[]>,
|
||||
checked = false
|
||||
): void => {
|
||||
if (!checkedKeys.value) {
|
||||
return
|
||||
}
|
||||
const len = tree.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
tree[i]._checked = checked
|
||||
const node = tree[i]
|
||||
// 该节点是checked
|
||||
if (checkedKeys.value.indexOf(node.id) > -1) {
|
||||
node._checked = true
|
||||
if (node.children && node.children.length > 0) {
|
||||
patchCheckedKeys(node.children, checkedKeys, true)
|
||||
}
|
||||
} else {
|
||||
if (node.children && node.children.length > 0) {
|
||||
patchCheckedKeys(node.children, checkedKeys, false)
|
||||
// 判断children是否为都选中,如果是都选中的情况下,父组件也得是选中
|
||||
const allChildrenChecked = node.children.every((it) => it._checked)
|
||||
if (allChildrenChecked) {
|
||||
node._checked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateChildren(data: TreeNode[], flag: boolean) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i]._checked = flag
|
||||
if (data[i].children && data[i].children.length > 0) {
|
||||
updateChildren(data[i].children, flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新树
|
||||
* @param data
|
||||
* @param clickNode
|
||||
* @param parentNode
|
||||
*/
|
||||
export function updateInnerTreeDataChecked(
|
||||
data: TreeNode[],
|
||||
clickNode: TreeNode,
|
||||
parentNode?: TreeNode
|
||||
) {
|
||||
const len = data.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
const currentNode = data[i]
|
||||
// 找到当前更新的节点
|
||||
if (currentNode.id === clickNode.id) {
|
||||
// 如果当前节点有子节点,更新子节点
|
||||
if (currentNode.children && currentNode.children.length > 0) {
|
||||
updateChildren(data[i].children, currentNode._checked!)
|
||||
}
|
||||
} else {
|
||||
if (currentNode.children && currentNode.children.length > 0) {
|
||||
updateInnerTreeDataChecked(currentNode.children, clickNode, currentNode)
|
||||
}
|
||||
}
|
||||
// 当前节点有选中,父节点一定选中
|
||||
if (currentNode.children && currentNode.children.length > 0) {
|
||||
currentNode._checked = currentNode.children.some((it) => it._checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getCheckedKeys(tree: TreeNode[]): (string | number)[] {
|
||||
let keys: (string | number)[] = []
|
||||
const len = tree.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
const current = tree[i]
|
||||
if (current._checked) {
|
||||
keys.push(current.id)
|
||||
if (current.children && current.children.length > 0) {
|
||||
keys = [...keys, ...getCheckedKeys(current.children)]
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { TreeEmits, TreeProps } from '/@src/module/tree/new-tree/tree.type'
|
||||
import { TreeEmits, TreeProps } from '/@src/module/tree/tree.type'
|
||||
import { computed, ComputedRef, watch } from 'vue'
|
||||
import { Tree, TreeData } from '/@src/module/tree/new-tree/tree'
|
||||
import { Tree, TreeData } from '/@src/module/tree/tree'
|
||||
|
||||
export declare type UseTree = (
|
||||
props: TreeProps,
|
@ -1,102 +0,0 @@
|
||||
import { TreeData, TreeNode, UseTreeData } from '/@src/module/tree/tree.type'
|
||||
import { computed, ref, unref, watch } from 'vue'
|
||||
import {
|
||||
getCheckedKeys,
|
||||
getTreeSpreadKeys,
|
||||
initialTreeData,
|
||||
updateInnerTreeDataChecked,
|
||||
} from '/@src/module/tree/treeHelper'
|
||||
import { Recordable } from '/@src/module/type'
|
||||
|
||||
export const useTreeData: UseTreeData = (props, emit) => {
|
||||
const spreadKeys = computed({
|
||||
get: () => {
|
||||
if (!props.data) return []
|
||||
if (props.spreadKeys && props.spreadKeys.length > 0) {
|
||||
return props.spreadKeys as (string | number)[]
|
||||
}
|
||||
return getTreeSpreadKeys(props.data)
|
||||
},
|
||||
set: (value: (string | number)[]) => {
|
||||
emit('update:spreadKeys', value)
|
||||
},
|
||||
})
|
||||
|
||||
const checkedKeys = computed({
|
||||
get: () => {
|
||||
return props.checkedKeys!
|
||||
},
|
||||
set: (value: (string | number)[]) => {
|
||||
emit('update:checkedKeys', value)
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* 渲染的data
|
||||
*/
|
||||
const innerTreeData = ref<TreeNode[]>([])
|
||||
watch(
|
||||
() => props.data,
|
||||
(treeData) => {
|
||||
if (treeData) {
|
||||
innerTreeData.value = initialTreeData(treeData, checkedKeys)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/**
|
||||
* watch innerTreeDate to emit checkedKeys
|
||||
* @param treeData
|
||||
* @param node
|
||||
*/
|
||||
watch(
|
||||
innerTreeData,
|
||||
(tree) => {
|
||||
const emitCheckedKeys = getCheckedKeys(tree)
|
||||
checkedKeys.value = emitCheckedKeys
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
function updateInnerTreeData(treeData: TreeData[], node: TreeData): void {
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
if (treeData[i].id === node.id) {
|
||||
treeData[i].spread = !treeData[i].spread
|
||||
break
|
||||
}
|
||||
if (treeData[i].children && treeData[i].children.length > 0) {
|
||||
updateInnerTreeData(treeData[i].children, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tree wrapper class
|
||||
*/
|
||||
const treeWrapperClass = computed((): Recordable => {
|
||||
const { showCheckbox, showLine } = unref(props)
|
||||
return {
|
||||
'layui-tree': true,
|
||||
'layui-form': showCheckbox,
|
||||
'layui-tree-line': showLine,
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 更新checked状态到node中
|
||||
* @param node
|
||||
*/
|
||||
function updateCheckedByNode(node: TreeNode) {
|
||||
updateInnerTreeDataChecked(innerTreeData.value, node)
|
||||
}
|
||||
|
||||
return {
|
||||
spreadKeys,
|
||||
checkedKeys,
|
||||
innerTreeData,
|
||||
updateInnerTreeData,
|
||||
treeWrapperClass,
|
||||
updateCheckedByNode,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user