From 11dc8f1e2121e2df78d0e6246f331c81698313d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=90=BD=E5=B0=8F=E6=A2=85?= <425605679@qq.com>
Date: Tue, 19 Oct 2021 18:22:46 +0800
Subject: [PATCH] =?UTF-8?q?refactor(tree):=20=E6=A0=91=E7=BB=84=E4=BB=B6?=
=?UTF-8?q?=E9=87=8D=E6=9E=84=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/docs/zh-CN/components/tree.md | 19 ++-
src/module/tree/index.ts | 4 +-
src/module/tree/new-tree/TreeNode.vue | 62 ++------
src/module/tree/new-tree/index.vue | 25 +--
src/module/tree/new-tree/tree.ts | 202 ++++++++++++++----------
src/module/tree/new-tree/tree.type.ts | 13 +-
src/module/tree/new-tree/useTree.ts | 46 +++---
src/module/tree/new-tree/useTreeData.ts | 83 ++++++++++
8 files changed, 276 insertions(+), 178 deletions(-)
create mode 100644 src/module/tree/new-tree/useTreeData.ts
diff --git a/docs/docs/zh-CN/components/tree.md b/docs/docs/zh-CN/components/tree.md
index 3d48176a..bac6b099 100644
--- a/docs/docs/zh-CN/components/tree.md
+++ b/docs/docs/zh-CN/components/tree.md
@@ -8,6 +8,7 @@
:showCheckbox="showCheckbox"
v-model:checkedKeys="checkedKeys"
@node-click="handleClick"
+ :disabled="disabled"
>
@@ -32,6 +33,7 @@
{{ clickNode }}
+
@@ -161,6 +130,7 @@ function handleIconClick(node: TreeData) {
'layui-tree-txt': true,
'layui-disabled': node.isDisabled.value,
}"
+ @click="handleTitleClick(node)"
>
{{ node.title }}
@@ -177,6 +147,8 @@ function handleIconClick(node: TreeData) {
:show-checkbox="showCheckbox"
:show-line="showLine"
@node-click="recursiveNodeClick"
+ :tree="tree"
+ :only-icon-control="onlyIconControl"
/>
diff --git a/src/module/tree/new-tree/index.vue b/src/module/tree/new-tree/index.vue
index 51bc47e6..2507962f 100644
--- a/src/module/tree/new-tree/index.vue
+++ b/src/module/tree/new-tree/index.vue
@@ -15,6 +15,7 @@ export default {
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
@@ -37,6 +38,7 @@ interface TreeProps {
accordion?: boolean
onlyIconControl?: boolean
showLine?: boolean
+ disabled?: boolean
replaceFields?: {
id?: string
children?: string
@@ -46,7 +48,8 @@ interface TreeProps {
interface TreeEmits {
(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(), {
@@ -54,6 +57,7 @@ const props = withDefaults(defineProps(), {
edit: false,
accordion: false,
onlyIconControl: false,
+ disabled: false,
showLine: true,
replaceFields: () => {
return {
@@ -63,6 +67,7 @@ const props = withDefaults(defineProps(), {
}
},
})
+
const emit = defineEmits()
const className = computed(() => {
@@ -73,21 +78,21 @@ const className = computed(() => {
}
})
-const {
- nodeList,
- handleCheckbox
-} = useTree(props, emit)
+const { tree, nodeList } = useTree(props, emit)
-function handleClick (args, node) {
- handleCheckbox(node)
+function handleClick(node: TreeData) {
+ const originNode = tree.getOriginData(node.id)
+ emit('node-click', originNode)
}
diff --git a/src/module/tree/new-tree/tree.ts b/src/module/tree/new-tree/tree.ts
index 53e93507..272fd5eb 100644
--- a/src/module/tree/new-tree/tree.ts
+++ b/src/module/tree/new-tree/tree.ts
@@ -28,82 +28,16 @@ interface ReplaceFields {
}
interface TreeConfig {
- disabled: boolean
showCheckbox: boolean
checkedKeys: StringOrNumber[]
+ expandKeys: StringOrNumber[]
nodeMap: Map
+ originMap: Map
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 = 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 {
- private readonly config: TreeConfig
+ protected config: TreeConfig
protected treeData: TreeData[]
constructor(
@@ -111,7 +45,13 @@ class Tree {
origin: OriginalTreeData | OriginalTreeData[]
) {
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(
@@ -120,15 +60,7 @@ class Tree {
): TreeData[] {
let data
if (!Array.isArray(origin)) {
- data = Array.of(
- Object.assign({}, origin, {
- // isRoot: true,
- // isChecked: ref(false),
- // isExpand: ref(false),
- // isDisabled: ref(false),
- // isLeaf: ref(false),
- })
- )
+ data = Array.of(Object.assign({}, origin))
} else {
data = origin
}
@@ -137,7 +69,7 @@ class Tree {
const len = data.length
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 nodeHasChildren = !!Reflect.get(node, children)
@@ -150,13 +82,117 @@ class Tree {
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() {
return this.treeData
}
- setChecked(node: TreeData) {
- const item = this.config.nodeMap.get(node.id)
- console.log(item)
+ getKeys () {
+ const checkedKeys = []
+ 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)!
}
}
diff --git a/src/module/tree/new-tree/tree.type.ts b/src/module/tree/new-tree/tree.type.ts
index 2e643498..bb2715f1 100644
--- a/src/module/tree/new-tree/tree.type.ts
+++ b/src/module/tree/new-tree/tree.type.ts
@@ -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 StringOrNumber = string | number
export type KeysType = (number | string)[]
@@ -19,6 +13,7 @@ export interface OriginalTreeData {
export interface TreeProps {
checkedKeys?: KeysType
+ expandKeys?: KeysType
data: OriginalTreeData
showCheckbox?: boolean
edit?: EditType
@@ -34,11 +29,7 @@ export interface TreeProps {
export interface TreeEmits {
(e: 'update:checkedKeys', keys: KeysType): void
+ (e: 'update:expandKeys', keys: KeysType): void
(e: 'node-click', node: OriginalTreeData, event: Event): void
}
-/**
- * Tree
- */
-
-export interface Tree {}
diff --git a/src/module/tree/new-tree/useTree.ts b/src/module/tree/new-tree/useTree.ts
index 4ab7279f..d0ecaf01 100644
--- a/src/module/tree/new-tree/useTree.ts
+++ b/src/module/tree/new-tree/useTree.ts
@@ -1,51 +1,49 @@
import { TreeEmits, TreeProps } from '/@src/module/tree/new-tree/tree.type'
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 = (
props: TreeProps,
emit: TreeEmits
) => {
+ tree: Tree
nodeList: ComputedRef
- handleCheckbox: (node: TreeData) => void
}
export const useTree: UseTree = (props: TreeProps, emit: TreeEmits) => {
- const tree = computed(() => {
- return new Tree(
- {
- disabled: false,
- nodeMap: new Map(),
- replaceFields: {
- id: 'id',
- title: 'title',
- children: 'children',
- },
- showCheckbox: props.showCheckbox ?? false,
- checkedKeys: props.checkedKeys ?? [],
+ const tree = new Tree(
+ {
+ nodeMap: new Map(),
+ originMap: new Map(),
+ replaceFields: {
+ id: 'id',
+ title: 'title',
+ children: 'children',
},
- props.data
- )
- })
+ showCheckbox: props.showCheckbox ?? false,
+ checkedKeys: props.checkedKeys ?? [],
+ expandKeys: props.expandKeys ?? [],
+ },
+ props.data
+ )
+
const nodeList = computed(() => {
- const nodes = tree.value.getData()
- setParentNode(nodes)
+ const nodes = tree.getData()
return nodes
})
watch(
() => nodeList,
(list) => {
+ const { checkedKeys, expandKeys } = tree.getKeys()
+ emit('update:checkedKeys', checkedKeys)
+ // emit('update:expandKeys', expandKeys)
},
{ deep: true }
)
- function handleCheckbox(node: TreeData) {
- tree.value.setChecked(node)
- }
-
return {
+ tree,
nodeList,
- handleCheckbox,
}
}
diff --git a/src/module/tree/new-tree/useTreeData.ts b/src/module/tree/new-tree/useTreeData.ts
new file mode 100644
index 00000000..0e805987
--- /dev/null
+++ b/src/module/tree/new-tree/useTreeData.ts
@@ -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
+ parentNode: Nullable
+ isDisabled: Ref
+ isLeaf: Ref
+ isChecked: Ref
+ 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
+}