Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(tree): setData improvement, and fix data update problems in Vue3 #1603

Merged
merged 3 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/web/api/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spline: data

### 可过滤的树

- 过滤动作由 `filter` 属性的变更触发,故而不能只修改传递给 `filter` 属性内部的数据,应当在条件变化时变更 `filter` 属性。清空过滤条件时,通过设置 `filter=null` 来触发树组件展示状态还原。

- `allowFoldNodeOnFilter=false` 时,过滤状态下展开的路径节点无法被收起;`allowFoldNodeOnFilter=true` 时,过滤状态下展开的节点,允许点击收起,注意这会影响到树组件当前的展开状态数据。每次变更过滤条件时,会重设节点展开状态,将命中节点的路径节点展开。当清空过滤条件时,将会还原为设置过滤条件之前时的展开状态。

{{ filter }}

### 自定义
Expand Down Expand Up @@ -85,6 +89,9 @@ spline: data

#### 展开时加载节点

默认为点击加载数据。`valueMode` 默认为 `onlyLeaf`。选中父节点时,子节点由于未加载,无法更新和获取选中状态,导致无法更新父节点的状态。
所以使用延迟加载时,推荐 `valueMode` 设置为 `all` 或者 `parentFirst`。

{{ lazy }}

<!-- ### 受控操作
Expand Down
2 changes: 1 addition & 1 deletion js/tree/tree-node-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ export class TreeNodeModel {
if (!isUndefined(dataValue)) _data.value = dataValue;
if (!isUndefined(dataLabel)) _data.label = dataLabel;
if (!isUndefined(dataDisabled)) _data.disable = dataDisabled;

Object.assign(node.data, _data);
Object.assign(node, _data);
node.update();
}
}

Expand Down
3 changes: 3 additions & 0 deletions js/tree/tree-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export class TreeNode {
// 节点隶属的树实例
public tree: TreeStore;

// 节点私有 id,不接受外部传入,确保唯一性
public [privateKey]: string;

// 节点 id ,唯一标志
public value: string;

Expand Down
70 changes: 38 additions & 32 deletions js/tree/tree-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import camelCase from 'lodash/camelCase';
import isPlainObject from 'lodash/isPlainObject';
import mitt from 'mitt';

import { TreeNode } from './tree-node';
import { TreeNode, privateKey } from './tree-node';
import {
TreeNodeValue,
TypeIdMap,
Expand All @@ -19,6 +19,7 @@ import {
TypeTreeFilterOptions,
TypeRelatedNodesOptions,
TypeTreeEventState,
TypeUpdatedMap,
} from './types';

function nextTick(fn: () => void): Promise<void> {
Expand Down Expand Up @@ -72,7 +73,7 @@ export class TreeStore {
public nodeMap: Map<TreeNodeValue, TreeNode>;

// 节点 私有 ID 映射
public privateMap: Map<string, TreeNode>;
public privateMap: Map<TreeNodeValue, TreeNode>;

// 配置选项
public config: TypeTreeStoreOptions;
Expand All @@ -81,7 +82,7 @@ export class TreeStore {
public activedMap: TypeIdMap;

// 数据被更新的节点集合
public updatedMap: TypeIdMap;
public updatedMap: TypeUpdatedMap;

// 选中节点集合
public checkedMap: TypeIdMap;
Expand Down Expand Up @@ -342,10 +343,6 @@ export class TreeStore {
* @return void
*/
public reload(list: TypeTreeNodeData[]): void {
this.expandedMap.clear();
this.checkedMap.clear();
this.activedMap.clear();
this.filterMap.clear();
this.removeAll();
this.append(list);
}
Expand Down Expand Up @@ -492,9 +489,17 @@ export class TreeStore {
* @return void
*/
public updated(node?: TreeNode): void {
if (node?.value) {
this.updatedMap.set(node.value, true);
const { updatedMap } = this;
if (node) {
// 传入节点,则为指定节点的更新
updatedMap.set(node[privateKey], 'changed');
} else {
// reflow 流程不传入节点,需要更新所有节点
this.getNodes().forEach((itemNode) => {
updatedMap.set(itemNode[privateKey], 'changed');
});
}

if (this.updateTick) return;
this.updateTick = nextTick(() => {
this.updateTick = null;
Expand All @@ -510,28 +515,23 @@ export class TreeStore {
// 以便于优化锁定检查算法
this.lockFilterPathNodes();

const updatedList = Array.from(this.updatedMap.keys());
if (updatedList.length > 0) {
// 统计需要更新状态的节点,派发更新事件
const updatedNodes = updatedList.map((value) => this.getNode(value));
this.emit('update', {
nodes: updatedNodes,
map: this.updatedMap,
});
} else if (this.shouldReflow) {
// 单纯的回流不需要更新节点状态
// 但需要触发更新事件
// 实际业务中,这个逻辑几乎无法触发,节点操作必然引发 update
// 这里代码仅仅用于边界兜底
this.emit('update', {
nodes: [],
map: this.updatedMap,
});
}
// stateId 用于单个节点状态监控
const stateId = `t${new Date().getTime()}`;
const updatedList = Array.from(updatedMap.keys());
const updatedNodes = updatedList.map((nodePrivateKey) => {
updatedMap.set(nodePrivateKey, stateId);
return this.privateMap.get(nodePrivateKey);
});

// 统计需要更新状态的节点,派发更新事件
this.emit('update', {
nodes: updatedNodes,
map: updatedMap,
});

// 每次回流检查完毕,还原检查状态
this.shouldReflow = false;
this.updatedMap.clear();
updatedMap.clear();
});
}

Expand Down Expand Up @@ -822,10 +822,16 @@ export class TreeStore {
* @return void
*/
public removeAll(): void {
const nodes = this.getNodes();
nodes.forEach((node) => {
node.remove();
});
this.expandedMap.clear();
this.checkedMap.clear();
this.activedMap.clear();
this.filterMap.clear();
this.nodeMap.clear();
this.privateMap.clear();
this.updatedMap.clear();
this.nodes = [];
this.children = [];
this.reflow();
}

/**
Expand Down
4 changes: 3 additions & 1 deletion js/tree/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,12 @@ export type TypeTreeNodeModel = TreeNodeModel<TypeTreeNodeData>

export type TypeTreeFilter = (node: TreeNodeModel<TypeTreeNodeData>) => boolean;

export type TypeUpdatedMap = Map<TreeNodeValue, string>;

export interface TypeTreeEventState {
node?: TreeNode;
nodes?: TreeNode[];
map?: TypeIdMap;
map?: TypeUpdatedMap;
data?: TypeTreeNodeData[];
}

Expand Down
Loading