refactor(tree): 树组件重构完成
This commit is contained in:
parent
f8a8d865bf
commit
11dc8f1e21
@ -8,6 +8,7 @@
|
|||||||
:showCheckbox="showCheckbox"
|
:showCheckbox="showCheckbox"
|
||||||
v-model:checkedKeys="checkedKeys"
|
v-model:checkedKeys="checkedKeys"
|
||||||
@node-click="handleClick"
|
@node-click="handleClick"
|
||||||
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
</lay-tree>
|
</lay-tree>
|
||||||
<br/>
|
<br/>
|
||||||
@ -32,6 +33,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
{{ clickNode }}
|
{{ clickNode }}
|
||||||
</pre>
|
</pre>
|
||||||
|
<br/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -202,6 +204,7 @@ const showLine = ref(true);
|
|||||||
const clickNode = ref(null);
|
const clickNode = ref(null);
|
||||||
const showCheckbox = ref(true);
|
const showCheckbox = ref(true);
|
||||||
const checkedKeys = ref([1]);
|
const checkedKeys = ref([1]);
|
||||||
|
const disabled = ref(false);
|
||||||
|
|
||||||
function handleClick(node) {
|
function handleClick(node) {
|
||||||
clickNode.value = node
|
clickNode.value = node
|
||||||
@ -216,12 +219,22 @@ function handleClick(node) {
|
|||||||
|
|
||||||
| Name | Description | Accepted Values |
|
| Name | Description | Accepted Values |
|
||||||
| -------- | ---- | ----------------------- |
|
| -------- | ---- | ----------------------- |
|
||||||
| name | 原始属性 name | -- |
|
| data | 树型组件数据,类型 TreeData \| TreeData[] | null |
|
||||||
| data | 树型组件数据,类型TreeData[] | null |
|
|
||||||
| showCheckbox | 是否显示复选框 | false |
|
| showCheckbox | 是否显示复选框 | false |
|
||||||
| onlyIconControl | 是否仅允许节点左侧图标控制展开收缩 | false |
|
| onlyIconControl | 是否仅允许节点左侧图标控制展开收缩 | false |
|
||||||
| showLine | 是否开启连接线 | true |
|
| showLine | 是否开启连接线 | true |
|
||||||
| checkedKeys(v-model:checkedKeys) | 开启选中后选中的节点 | - |
|
| checkedKeys(v-model:checkedKeys) | 开启showCheckbox后, 选中的节点 | [] |
|
||||||
|
|
||||||
|
::: field TreeData
|
||||||
|
|
||||||
|
:::
|
||||||
|
| Name | Description | Accepted Values |
|
||||||
|
| -------- | ---- | ----------------------- |
|
||||||
|
| id | 唯一值 | - |
|
||||||
|
| title | 节点名称 | - |
|
||||||
|
| children | 子节点 | [] |
|
||||||
|
| disabled | 该节点是否禁用 | false |
|
||||||
|
| spread | 该节点是否展开 | false | - |
|
||||||
|
|
||||||
|
|
||||||
::: field tree events
|
::: field tree events
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
import Component from './index.vue'
|
// import Component from './index.vue'
|
||||||
// import Component from './new-tree/index.vue'
|
import Component from './new-tree/index.vue'
|
||||||
import type { IDefineComponent } from '../type/index'
|
import type { IDefineComponent } from '../type/index'
|
||||||
|
|
||||||
Component.install = (app: App) => {
|
Component.install = (app: App) => {
|
||||||
|
@ -11,6 +11,7 @@ import { Nullable } from '/@src/module/type'
|
|||||||
import LayIcon from '../../icon'
|
import LayIcon from '../../icon'
|
||||||
import LayCheckbox from '../../checkbox'
|
import LayCheckbox from '../../checkbox'
|
||||||
import { nextTick, Ref } from 'vue'
|
import { nextTick, Ref } from 'vue'
|
||||||
|
import { Tree } from "/@src/module/tree/new-tree/tree";
|
||||||
|
|
||||||
type CustomKey = string | number
|
type CustomKey = string | number
|
||||||
type CustomString = (() => string) | string
|
type CustomString = (() => string) | string
|
||||||
@ -20,7 +21,7 @@ interface TreeData {
|
|||||||
title: CustomString
|
title: CustomString
|
||||||
children: TreeData[]
|
children: TreeData[]
|
||||||
parentKey: Nullable<StringOrNumber>
|
parentKey: Nullable<StringOrNumber>
|
||||||
isRoot: Ref<boolean>
|
isRoot: boolean
|
||||||
isChecked: Ref<boolean>
|
isChecked: Ref<boolean>
|
||||||
isDisabled: Ref<boolean>
|
isDisabled: Ref<boolean>
|
||||||
isLeaf: Ref<boolean>
|
isLeaf: Ref<boolean>
|
||||||
@ -29,13 +30,15 @@ interface TreeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TreeNodeProps {
|
interface TreeNodeProps {
|
||||||
|
tree: Tree
|
||||||
nodeList: TreeData[]
|
nodeList: TreeData[]
|
||||||
showCheckbox: boolean
|
showCheckbox: boolean
|
||||||
showLine: boolean
|
showLine: boolean
|
||||||
|
onlyIconControl: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TreeNodeEmits {
|
interface TreeNodeEmits {
|
||||||
(e: 'node-click', event: Event): void
|
(e: 'node-click', node: TreeData): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<TreeNodeProps>()
|
const props = defineProps<TreeNodeProps>()
|
||||||
@ -68,60 +71,26 @@ const nodeIconType = (node: TreeData): string => {
|
|||||||
return 'layui-icon-file'
|
return 'layui-icon-file'
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNodeClick(
|
|
||||||
args: { label: string; value: string },
|
|
||||||
node: TreeData
|
|
||||||
) {
|
|
||||||
emit('node-click', args, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
function recursiveNodeClick(
|
function recursiveNodeClick(
|
||||||
args: { label: string; value: string },
|
|
||||||
node: TreeData
|
node: TreeData
|
||||||
) {
|
) {
|
||||||
emit('node-click', args, node)
|
emit('node-click', node)
|
||||||
}
|
|
||||||
|
|
||||||
function setChildrenChecked(checked: boolean, nodes: TreeData[]) {
|
|
||||||
const len = nodes.length
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
nodes[i].isChecked.value = checked
|
|
||||||
nodes[i].children &&
|
|
||||||
nodes[i].children.length > 0 &&
|
|
||||||
setChildrenChecked(checked, nodes[i].children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setParentChecked(checked: boolean, parent: TreeData) {
|
|
||||||
if (!parent) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parent.isChecked.value = checked
|
|
||||||
const pChild = parent.children
|
|
||||||
const pChildChecked = pChild.some((c) => c.isChecked.value)
|
|
||||||
if (pChildChecked) {
|
|
||||||
parent.isChecked.value = true
|
|
||||||
}
|
|
||||||
if (parent.parentNode) {
|
|
||||||
setParentChecked(checked, parent.parentNode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChange(checked: boolean, node: TreeData) {
|
function handleChange(checked: boolean, node: TreeData) {
|
||||||
node.isChecked.value = checked
|
props.tree.setCheckedKeys(checked, node)
|
||||||
// 处理上级
|
|
||||||
if (node.parentNode) {
|
|
||||||
setParentChecked(checked, node.parentNode)
|
|
||||||
}
|
|
||||||
// 处理下级
|
|
||||||
if (node.children) {
|
|
||||||
setChildrenChecked(checked, node.children)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleIconClick(node: TreeData) {
|
function handleIconClick(node: TreeData) {
|
||||||
node.isLeaf.value = !node.isLeaf.value
|
node.isLeaf.value = !node.isLeaf.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTitleClick(node: TreeData) {
|
||||||
|
if (!props.onlyIconControl) {
|
||||||
|
handleIconClick(node)
|
||||||
|
}
|
||||||
|
emit('node-click', node)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -161,6 +130,7 @@ function handleIconClick(node: TreeData) {
|
|||||||
'layui-tree-txt': true,
|
'layui-tree-txt': true,
|
||||||
'layui-disabled': node.isDisabled.value,
|
'layui-disabled': node.isDisabled.value,
|
||||||
}"
|
}"
|
||||||
|
@click="handleTitleClick(node)"
|
||||||
>
|
>
|
||||||
{{ node.title }}
|
{{ node.title }}
|
||||||
</span>
|
</span>
|
||||||
@ -177,6 +147,8 @@ function handleIconClick(node: TreeData) {
|
|||||||
:show-checkbox="showCheckbox"
|
:show-checkbox="showCheckbox"
|
||||||
:show-line="showLine"
|
:show-line="showLine"
|
||||||
@node-click="recursiveNodeClick"
|
@node-click="recursiveNodeClick"
|
||||||
|
:tree="tree"
|
||||||
|
:only-icon-control="onlyIconControl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
@ -15,6 +15,7 @@ export default {
|
|||||||
import TreeNode from './TreeNode.vue'
|
import TreeNode from './TreeNode.vue'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useTree } from '/@src/module/tree/new-tree/useTree'
|
import { useTree } from '/@src/module/tree/new-tree/useTree'
|
||||||
|
import { TreeData } from '/@src/module/tree/new-tree/tree'
|
||||||
|
|
||||||
type StringFn = () => string
|
type StringFn = () => string
|
||||||
type StringOrNumber = string | number
|
type StringOrNumber = string | number
|
||||||
@ -37,6 +38,7 @@ interface TreeProps {
|
|||||||
accordion?: boolean
|
accordion?: boolean
|
||||||
onlyIconControl?: boolean
|
onlyIconControl?: boolean
|
||||||
showLine?: boolean
|
showLine?: boolean
|
||||||
|
disabled?: boolean
|
||||||
replaceFields?: {
|
replaceFields?: {
|
||||||
id?: string
|
id?: string
|
||||||
children?: string
|
children?: string
|
||||||
@ -46,7 +48,8 @@ interface TreeProps {
|
|||||||
|
|
||||||
interface TreeEmits {
|
interface TreeEmits {
|
||||||
(e: 'update:checkedKeys', keys: KeysType): void
|
(e: 'update:checkedKeys', keys: KeysType): void
|
||||||
(e: 'node-click', args: any, node: OriginalTreeData, event: Event): void
|
(e: 'update:expandKeys', keys: KeysType): void
|
||||||
|
(e: 'node-click', node: OriginalTreeData): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<TreeProps>(), {
|
const props = withDefaults(defineProps<TreeProps>(), {
|
||||||
@ -54,6 +57,7 @@ const props = withDefaults(defineProps<TreeProps>(), {
|
|||||||
edit: false,
|
edit: false,
|
||||||
accordion: false,
|
accordion: false,
|
||||||
onlyIconControl: false,
|
onlyIconControl: false,
|
||||||
|
disabled: false,
|
||||||
showLine: true,
|
showLine: true,
|
||||||
replaceFields: () => {
|
replaceFields: () => {
|
||||||
return {
|
return {
|
||||||
@ -63,6 +67,7 @@ const props = withDefaults(defineProps<TreeProps>(), {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<TreeEmits>()
|
const emit = defineEmits<TreeEmits>()
|
||||||
|
|
||||||
const className = computed(() => {
|
const className = computed(() => {
|
||||||
@ -73,21 +78,21 @@ const className = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const { tree, nodeList } = useTree(props, emit)
|
||||||
nodeList,
|
|
||||||
handleCheckbox
|
|
||||||
} = useTree(props, emit)
|
|
||||||
|
|
||||||
function handleClick (args, node) {
|
function handleClick(node: TreeData) {
|
||||||
handleCheckbox(node)
|
const originNode = tree.getOriginData(node.id)
|
||||||
|
emit('node-click', originNode)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div :class="className">
|
<div :class="className">
|
||||||
<tree-node
|
<tree-node
|
||||||
:nodeList="nodeList"
|
:tree="tree"
|
||||||
:showCheckbox="showCheckbox"
|
:node-list="nodeList"
|
||||||
:showLine="showLine"
|
:show-checkbox="showCheckbox"
|
||||||
|
:show-line="showLine"
|
||||||
|
:only-icon-control="onlyIconControl"
|
||||||
@node-click="handleClick"
|
@node-click="handleClick"
|
||||||
></tree-node>
|
></tree-node>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,82 +28,16 @@ interface ReplaceFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TreeConfig {
|
interface TreeConfig {
|
||||||
disabled: boolean
|
|
||||||
showCheckbox: boolean
|
showCheckbox: boolean
|
||||||
checkedKeys: StringOrNumber[]
|
checkedKeys: StringOrNumber[]
|
||||||
|
expandKeys: StringOrNumber[]
|
||||||
nodeMap: Map<StringOrNumber, TreeData>
|
nodeMap: Map<StringOrNumber, TreeData>
|
||||||
|
originMap: Map<StringOrNumber, OriginalTreeData>
|
||||||
replaceFields: ReplaceFields
|
replaceFields: ReplaceFields
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNode(
|
|
||||||
config: TreeConfig,
|
|
||||||
origin: OriginalTreeData,
|
|
||||||
parentKey: StringOrNumber,
|
|
||||||
hasNextSibling: boolean
|
|
||||||
): TreeData {
|
|
||||||
const {
|
|
||||||
disabled,
|
|
||||||
checkedKeys,
|
|
||||||
showCheckbox,
|
|
||||||
nodeMap,
|
|
||||||
replaceFields: { children, id, title },
|
|
||||||
} = config
|
|
||||||
|
|
||||||
const nodeKey = Reflect.get(origin, id)
|
|
||||||
const nodeTitle = Reflect.get(origin, title)
|
|
||||||
const nodeChildren = Reflect.get(origin, children)
|
|
||||||
const nodeDisabled = !!Reflect.get(origin, 'disabled')
|
|
||||||
const nodeIsLeaf = !!Reflect.get(origin, 'spread')
|
|
||||||
|
|
||||||
const parent = nodeMap.get(parentKey)
|
|
||||||
if (parent) {
|
|
||||||
console.log(parent.isChecked.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log((parent && parent.isChecked.value) || checkedKeys.includes(nodeKey))
|
|
||||||
|
|
||||||
// const isCheckedValue = (parent && parent.isChecked.value) || checkedKeys.includes(nodeKey)
|
|
||||||
|
|
||||||
const node = Object.assign({}, origin, {
|
|
||||||
id: nodeKey,
|
|
||||||
title: nodeTitle,
|
|
||||||
children: nodeChildren ? nodeChildren : [],
|
|
||||||
parentKey: parentKey,
|
|
||||||
isRoot: parentKey === '',
|
|
||||||
isDisabled: ref(nodeDisabled),
|
|
||||||
isChecked: showCheckbox ? ref(checkedKeys.includes(nodeKey)) : ref(false),
|
|
||||||
// isChecked: ref(isCheckedValue),
|
|
||||||
isLeaf: ref(nodeIsLeaf),
|
|
||||||
hasNextSibling: hasNextSibling,
|
|
||||||
parentNode: null,
|
|
||||||
})
|
|
||||||
// 如果全局设置了disabled,则全部至为true
|
|
||||||
if (disabled) {
|
|
||||||
node.isDisabled.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nodeMap.has(nodeKey)) {
|
|
||||||
nodeMap.set(nodeKey, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setParentNode(
|
|
||||||
nodes: TreeData[],
|
|
||||||
parentNode: Nullable<TreeData> = null
|
|
||||||
) {
|
|
||||||
const len = nodes.length
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
Reflect.set(nodes[i], 'parentNode', parentNode)
|
|
||||||
if (nodes[i].children && nodes[i].children.length > 0) {
|
|
||||||
setParentNode(nodes[i].children, nodes[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tree {
|
class Tree {
|
||||||
private readonly config: TreeConfig
|
protected config: TreeConfig
|
||||||
protected treeData: TreeData[]
|
protected treeData: TreeData[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -111,7 +45,13 @@ class Tree {
|
|||||||
origin: OriginalTreeData | OriginalTreeData[]
|
origin: OriginalTreeData | OriginalTreeData[]
|
||||||
) {
|
) {
|
||||||
this.config = config
|
this.config = config
|
||||||
this.treeData = this.createTree(origin)
|
this.treeData = []
|
||||||
|
this.init(origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(origin: OriginalTreeData | OriginalTreeData[]): void {
|
||||||
|
const tree = this.createTree(origin)
|
||||||
|
this.treeData = tree
|
||||||
}
|
}
|
||||||
|
|
||||||
createTree(
|
createTree(
|
||||||
@ -120,15 +60,7 @@ class Tree {
|
|||||||
): TreeData[] {
|
): TreeData[] {
|
||||||
let data
|
let data
|
||||||
if (!Array.isArray(origin)) {
|
if (!Array.isArray(origin)) {
|
||||||
data = Array.of(
|
data = Array.of(Object.assign({}, origin))
|
||||||
Object.assign({}, origin, {
|
|
||||||
// isRoot: true,
|
|
||||||
// isChecked: ref(false),
|
|
||||||
// isExpand: ref(false),
|
|
||||||
// isDisabled: ref(false),
|
|
||||||
// isLeaf: ref(false),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
data = origin
|
data = origin
|
||||||
}
|
}
|
||||||
@ -137,7 +69,7 @@ class Tree {
|
|||||||
|
|
||||||
const len = data.length
|
const len = data.length
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const node = getNode(this.config, data[i], parentKey, i < len - 1)
|
const node = this.getNode(data[i], parentKey, i < len - 1)
|
||||||
const nodeChildren = Reflect.get(node, children)
|
const nodeChildren = Reflect.get(node, children)
|
||||||
const nodeHasChildren = !!Reflect.get(node, children)
|
const nodeHasChildren = !!Reflect.get(node, children)
|
||||||
|
|
||||||
@ -150,13 +82,117 @@ class Tree {
|
|||||||
return nodeList
|
return nodeList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNode(
|
||||||
|
origin: OriginalTreeData,
|
||||||
|
parentKey: StringOrNumber,
|
||||||
|
hasNextSibling: boolean
|
||||||
|
): TreeData {
|
||||||
|
const {
|
||||||
|
nodeMap,
|
||||||
|
originMap,
|
||||||
|
checkedKeys,
|
||||||
|
expandKeys,
|
||||||
|
replaceFields: { children, id, title },
|
||||||
|
} = this.config
|
||||||
|
|
||||||
|
const nodeKey = Reflect.get(origin, id)
|
||||||
|
const nodeTitle = Reflect.get(origin, title)
|
||||||
|
const nodeChildren = Reflect.get(origin, children)
|
||||||
|
const nodeDisabled = !!Reflect.get(origin, 'disabled')
|
||||||
|
const nodeIsLeaf = !!Reflect.get(origin, 'spread')
|
||||||
|
|
||||||
|
const parentNode = nodeMap.get(parentKey)
|
||||||
|
|
||||||
|
const node = Object.assign({}, origin, {
|
||||||
|
id: nodeKey,
|
||||||
|
title: nodeTitle,
|
||||||
|
children: nodeChildren ? nodeChildren : [],
|
||||||
|
parentKey: parentKey,
|
||||||
|
isRoot: parentKey === '',
|
||||||
|
isDisabled: ref(false),
|
||||||
|
isChecked: ref(false),
|
||||||
|
isLeaf: ref(false),
|
||||||
|
hasNextSibling: hasNextSibling,
|
||||||
|
parentNode: parentNode || null,
|
||||||
|
})
|
||||||
|
|
||||||
|
node.isDisabled.value = nodeDisabled
|
||||||
|
node.isChecked.value = parentNode ? parentNode.isChecked.value : checkedKeys.includes(nodeKey)
|
||||||
|
node.isLeaf.value = parentNode ? parentNode.isLeaf.value : expandKeys.includes(nodeKey)
|
||||||
|
node.isLeaf.value = nodeIsLeaf
|
||||||
|
|
||||||
|
if (!nodeMap.has(nodeKey)) {
|
||||||
|
nodeMap.set(nodeKey, node)
|
||||||
|
}
|
||||||
|
if (!originMap.has(nodeKey)) {
|
||||||
|
originMap.set(nodeKey, origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenChecked(checked: boolean, nodes: TreeData[]) {
|
||||||
|
const len = nodes.length
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
nodes[i].isChecked.value = checked
|
||||||
|
nodes[i].children &&
|
||||||
|
nodes[i].children.length > 0 &&
|
||||||
|
this.setChildrenChecked(checked, nodes[i].children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setParentChecked(checked: boolean, parent: TreeData) {
|
||||||
|
if (!parent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parent.isChecked.value = checked
|
||||||
|
const pChild = parent.children
|
||||||
|
const pChildChecked = pChild.some((c) => c.isChecked.value)
|
||||||
|
if (pChildChecked) {
|
||||||
|
parent.isChecked.value = true
|
||||||
|
}
|
||||||
|
if (parent.parentNode) {
|
||||||
|
this.setParentChecked(checked, parent.parentNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckedKeys(checked: boolean, node: TreeData) {
|
||||||
|
node.isChecked.value = checked
|
||||||
|
// 处理上级
|
||||||
|
if (node.parentNode) {
|
||||||
|
this.setParentChecked(checked, node.parentNode)
|
||||||
|
}
|
||||||
|
// 处理下级
|
||||||
|
if (node.children) {
|
||||||
|
this.setChildrenChecked(checked, node.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getData() {
|
getData() {
|
||||||
return this.treeData
|
return this.treeData
|
||||||
}
|
}
|
||||||
|
|
||||||
setChecked(node: TreeData) {
|
getKeys () {
|
||||||
const item = this.config.nodeMap.get(node.id)
|
const checkedKeys = []
|
||||||
console.log(item)
|
const expandKeys = []
|
||||||
|
const iterator = this.config.nodeMap[Symbol.iterator]()
|
||||||
|
let next = iterator.next()
|
||||||
|
while (!next.done) {
|
||||||
|
const [, node] = next.value
|
||||||
|
const id = Reflect.get(node, this.config.replaceFields.id)
|
||||||
|
if (node.isChecked.value) {
|
||||||
|
checkedKeys.push(id)
|
||||||
|
}
|
||||||
|
if (node.isLeaf.value) {
|
||||||
|
expandKeys.push(id)
|
||||||
|
}
|
||||||
|
next = iterator.next()
|
||||||
|
}
|
||||||
|
return { checkedKeys, expandKeys }
|
||||||
|
}
|
||||||
|
|
||||||
|
getOriginData (key: StringOrNumber): OriginalTreeData {
|
||||||
|
return this.config.originMap.get(key)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
/*
|
|
||||||
* @Date: 2021-10-16 13:07:34
|
|
||||||
* @LastEditors: 落小梅
|
|
||||||
* @LastEditTime: 2021-10-16 13:29:06
|
|
||||||
* @FilePath: \layui-vue\src\module\tree\new-tree\tree.type.ts
|
|
||||||
*/
|
|
||||||
export type StringFn = () => string
|
export type StringFn = () => string
|
||||||
export type StringOrNumber = string | number
|
export type StringOrNumber = string | number
|
||||||
export type KeysType = (number | string)[]
|
export type KeysType = (number | string)[]
|
||||||
@ -19,6 +13,7 @@ export interface OriginalTreeData {
|
|||||||
|
|
||||||
export interface TreeProps {
|
export interface TreeProps {
|
||||||
checkedKeys?: KeysType
|
checkedKeys?: KeysType
|
||||||
|
expandKeys?: KeysType
|
||||||
data: OriginalTreeData
|
data: OriginalTreeData
|
||||||
showCheckbox?: boolean
|
showCheckbox?: boolean
|
||||||
edit?: EditType
|
edit?: EditType
|
||||||
@ -34,11 +29,7 @@ export interface TreeProps {
|
|||||||
|
|
||||||
export interface TreeEmits {
|
export interface TreeEmits {
|
||||||
(e: 'update:checkedKeys', keys: KeysType): void
|
(e: 'update:checkedKeys', keys: KeysType): void
|
||||||
|
(e: 'update:expandKeys', keys: KeysType): void
|
||||||
(e: 'node-click', node: OriginalTreeData, event: Event): void
|
(e: 'node-click', node: OriginalTreeData, event: Event): void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface Tree {}
|
|
||||||
|
@ -1,51 +1,49 @@
|
|||||||
import { TreeEmits, TreeProps } from '/@src/module/tree/new-tree/tree.type'
|
import { TreeEmits, TreeProps } from '/@src/module/tree/new-tree/tree.type'
|
||||||
import { computed, ComputedRef, watch } from 'vue'
|
import { computed, ComputedRef, watch } from 'vue'
|
||||||
import { setParentNode, Tree, TreeData } from '/@src/module/tree/new-tree/tree'
|
import { Tree, TreeData } from '/@src/module/tree/new-tree/tree'
|
||||||
|
|
||||||
export declare type UseTree = (
|
export declare type UseTree = (
|
||||||
props: TreeProps,
|
props: TreeProps,
|
||||||
emit: TreeEmits
|
emit: TreeEmits
|
||||||
) => {
|
) => {
|
||||||
|
tree: Tree
|
||||||
nodeList: ComputedRef<TreeData[]>
|
nodeList: ComputedRef<TreeData[]>
|
||||||
handleCheckbox: (node: TreeData) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTree: UseTree = (props: TreeProps, emit: TreeEmits) => {
|
export const useTree: UseTree = (props: TreeProps, emit: TreeEmits) => {
|
||||||
const tree = computed(() => {
|
const tree = new Tree(
|
||||||
return new Tree(
|
{
|
||||||
{
|
nodeMap: new Map(),
|
||||||
disabled: false,
|
originMap: new Map(),
|
||||||
nodeMap: new Map(),
|
replaceFields: {
|
||||||
replaceFields: {
|
id: 'id',
|
||||||
id: 'id',
|
title: 'title',
|
||||||
title: 'title',
|
children: 'children',
|
||||||
children: 'children',
|
|
||||||
},
|
|
||||||
showCheckbox: props.showCheckbox ?? false,
|
|
||||||
checkedKeys: props.checkedKeys ?? [],
|
|
||||||
},
|
},
|
||||||
props.data
|
showCheckbox: props.showCheckbox ?? false,
|
||||||
)
|
checkedKeys: props.checkedKeys ?? [],
|
||||||
})
|
expandKeys: props.expandKeys ?? [],
|
||||||
|
},
|
||||||
|
props.data
|
||||||
|
)
|
||||||
|
|
||||||
const nodeList = computed(() => {
|
const nodeList = computed(() => {
|
||||||
const nodes = tree.value.getData()
|
const nodes = tree.getData()
|
||||||
setParentNode(nodes)
|
|
||||||
return nodes
|
return nodes
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => nodeList,
|
() => nodeList,
|
||||||
(list) => {
|
(list) => {
|
||||||
|
const { checkedKeys, expandKeys } = tree.getKeys()
|
||||||
|
emit('update:checkedKeys', checkedKeys)
|
||||||
|
// emit('update:expandKeys', expandKeys)
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
function handleCheckbox(node: TreeData) {
|
|
||||||
tree.value.setChecked(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
tree,
|
||||||
nodeList,
|
nodeList,
|
||||||
handleCheckbox,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
src/module/tree/new-tree/useTreeData.ts
Normal file
83
src/module/tree/new-tree/useTreeData.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user