refactor(tree): 树组件重构完成
This commit is contained in:
		
							parent
							
								
									f8a8d865bf
								
							
						
					
					
						commit
						11dc8f1e21
					
				@ -8,6 +8,7 @@
 | 
			
		||||
    :showCheckbox="showCheckbox"
 | 
			
		||||
    v-model:checkedKeys="checkedKeys"
 | 
			
		||||
    @node-click="handleClick"
 | 
			
		||||
    :disabled="disabled"
 | 
			
		||||
  >
 | 
			
		||||
  </lay-tree>
 | 
			
		||||
  <br/>
 | 
			
		||||
@ -32,6 +33,7 @@
 | 
			
		||||
  <pre>
 | 
			
		||||
    {{ clickNode }}
 | 
			
		||||
  </pre>
 | 
			
		||||
  <br/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
@ -202,6 +204,7 @@ const showLine = ref(true);
 | 
			
		||||
const clickNode = ref(null);
 | 
			
		||||
const showCheckbox = ref(true);
 | 
			
		||||
const checkedKeys = ref([1]);
 | 
			
		||||
const disabled = ref(false);
 | 
			
		||||
 | 
			
		||||
function handleClick(node) {
 | 
			
		||||
 clickNode.value = node
 | 
			
		||||
@ -216,12 +219,22 @@ function handleClick(node) {
 | 
			
		||||
 | 
			
		||||
| Name   | Description | Accepted Values  |
 | 
			
		||||
| -------- | ---- | ----------------------- |
 | 
			
		||||
| name       | 原始属性 name | --  |
 | 
			
		||||
| data       | 树型组件数据,类型TreeData[] | null |
 | 
			
		||||
| data       | 树型组件数据,类型 TreeData \| TreeData[] | null |
 | 
			
		||||
| showCheckbox | 是否显示复选框 | false |
 | 
			
		||||
| onlyIconControl | 是否仅允许节点左侧图标控制展开收缩 | false |
 | 
			
		||||
| 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
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import type { App } from 'vue'
 | 
			
		||||
import Component from './index.vue'
 | 
			
		||||
// import Component from './new-tree/index.vue'
 | 
			
		||||
// import Component from './index.vue'
 | 
			
		||||
import Component from './new-tree/index.vue'
 | 
			
		||||
import type { IDefineComponent } from '../type/index'
 | 
			
		||||
 | 
			
		||||
Component.install = (app: App) => {
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ 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";
 | 
			
		||||
 | 
			
		||||
type CustomKey = string | number
 | 
			
		||||
type CustomString = (() => string) | string
 | 
			
		||||
@ -20,7 +21,7 @@ interface TreeData {
 | 
			
		||||
  title: CustomString
 | 
			
		||||
  children: TreeData[]
 | 
			
		||||
  parentKey: Nullable<StringOrNumber>
 | 
			
		||||
  isRoot: Ref<boolean>
 | 
			
		||||
  isRoot: boolean
 | 
			
		||||
  isChecked: Ref<boolean>
 | 
			
		||||
  isDisabled: Ref<boolean>
 | 
			
		||||
  isLeaf: Ref<boolean>
 | 
			
		||||
@ -29,13 +30,15 @@ interface TreeData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface TreeNodeProps {
 | 
			
		||||
  tree: Tree
 | 
			
		||||
  nodeList: TreeData[]
 | 
			
		||||
  showCheckbox: boolean
 | 
			
		||||
  showLine: boolean
 | 
			
		||||
  onlyIconControl: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface TreeNodeEmits {
 | 
			
		||||
  (e: 'node-click', event: Event): void
 | 
			
		||||
  (e: 'node-click', node: TreeData): void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = defineProps<TreeNodeProps>()
 | 
			
		||||
@ -68,60 +71,26 @@ const nodeIconType = (node: TreeData): string => {
 | 
			
		||||
  return 'layui-icon-file'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleNodeClick(
 | 
			
		||||
  args: { label: string; value: string },
 | 
			
		||||
  node: TreeData
 | 
			
		||||
) {
 | 
			
		||||
  emit('node-click', args, node)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function recursiveNodeClick(
 | 
			
		||||
  args: { label: string; value: string },
 | 
			
		||||
  node: TreeData
 | 
			
		||||
) {
 | 
			
		||||
  emit('node-click', args, 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)
 | 
			
		||||
  }
 | 
			
		||||
  emit('node-click', node)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleChange(checked: boolean, node: TreeData) {
 | 
			
		||||
  node.isChecked.value = checked
 | 
			
		||||
  // 处理上级
 | 
			
		||||
  if (node.parentNode) {
 | 
			
		||||
    setParentChecked(checked, node.parentNode)
 | 
			
		||||
  }
 | 
			
		||||
  // 处理下级
 | 
			
		||||
  if (node.children) {
 | 
			
		||||
    setChildrenChecked(checked, node.children)
 | 
			
		||||
  }
 | 
			
		||||
  props.tree.setCheckedKeys(checked, node)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleIconClick(node: TreeData) {
 | 
			
		||||
  node.isLeaf.value = !node.isLeaf.value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleTitleClick(node: TreeData) {
 | 
			
		||||
  if (!props.onlyIconControl) {
 | 
			
		||||
    handleIconClick(node)
 | 
			
		||||
  }
 | 
			
		||||
  emit('node-click', node)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
@ -161,6 +130,7 @@ function handleIconClick(node: TreeData) {
 | 
			
		||||
            'layui-tree-txt': true,
 | 
			
		||||
            'layui-disabled': node.isDisabled.value,
 | 
			
		||||
          }"
 | 
			
		||||
          @click="handleTitleClick(node)"
 | 
			
		||||
        >
 | 
			
		||||
          {{ node.title }}
 | 
			
		||||
        </span>
 | 
			
		||||
@ -177,6 +147,8 @@ function handleIconClick(node: TreeData) {
 | 
			
		||||
          :show-checkbox="showCheckbox"
 | 
			
		||||
          :show-line="showLine"
 | 
			
		||||
          @node-click="recursiveNodeClick"
 | 
			
		||||
          :tree="tree"
 | 
			
		||||
          :only-icon-control="onlyIconControl"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </transition>
 | 
			
		||||
 | 
			
		||||
@ -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<TreeProps>(), {
 | 
			
		||||
@ -54,6 +57,7 @@ const props = withDefaults(defineProps<TreeProps>(), {
 | 
			
		||||
  edit: false,
 | 
			
		||||
  accordion: false,
 | 
			
		||||
  onlyIconControl: false,
 | 
			
		||||
  disabled: false,
 | 
			
		||||
  showLine: true,
 | 
			
		||||
  replaceFields: () => {
 | 
			
		||||
    return {
 | 
			
		||||
@ -63,6 +67,7 @@ const props = withDefaults(defineProps<TreeProps>(), {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<TreeEmits>()
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <div :class="className">
 | 
			
		||||
    <tree-node
 | 
			
		||||
      :nodeList="nodeList"
 | 
			
		||||
      :showCheckbox="showCheckbox"
 | 
			
		||||
      :showLine="showLine"
 | 
			
		||||
      :tree="tree"
 | 
			
		||||
      :node-list="nodeList"
 | 
			
		||||
      :show-checkbox="showCheckbox"
 | 
			
		||||
      :show-line="showLine"
 | 
			
		||||
      :only-icon-control="onlyIconControl"
 | 
			
		||||
      @node-click="handleClick"
 | 
			
		||||
    ></tree-node>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
@ -28,82 +28,16 @@ interface ReplaceFields {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface TreeConfig {
 | 
			
		||||
  disabled: boolean
 | 
			
		||||
  showCheckbox: boolean
 | 
			
		||||
  checkedKeys: StringOrNumber[]
 | 
			
		||||
  expandKeys: StringOrNumber[]
 | 
			
		||||
  nodeMap: Map<StringOrNumber, TreeData>
 | 
			
		||||
  originMap: Map<StringOrNumber, OriginalTreeData>
 | 
			
		||||
  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 {
 | 
			
		||||
  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)!
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 {}
 | 
			
		||||
 | 
			
		||||
@ -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<TreeData[]>
 | 
			
		||||
  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,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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