From 1575b270954aec9ce593ad80cbf0df84276ac8bd Mon Sep 17 00:00:00 2001 From: jianglinjun Date: Sat, 28 Sep 2024 11:41:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A0=91=E5=9C=A8?= =?UTF-8?q?simple=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=A0=91=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=9C=89=E5=BE=AA=E7=8E=AF=E8=B0=83=E7=94=A8=E6=97=B6=E4=BA=A7?= =?UTF-8?q?=E7=94=9F=E6=AD=BB=E5=BE=AA=E7=8E=AF=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++ src/control/tree/el-tree-util.ts | 83 +++++++++++++++++++++++++++++++- src/control/tree/tree.tsx | 47 +++++++++++------- 3 files changed, 115 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a70460f90..625c0c36e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ ## [Unreleased] +### Fixed + +- 修复树在simple模式下树节点有循环调用时产生死循环的异常 + ## [0.7.38-alpha.12] - 2024-09-27 ### Fixed diff --git a/src/control/tree/el-tree-util.ts b/src/control/tree/el-tree-util.ts index ba1ccf73b..deb92ea94 100644 --- a/src/control/tree/el-tree-util.ts +++ b/src/control/tree/el-tree-util.ts @@ -1,9 +1,48 @@ import { RuntimeError } from '@ibiz-template/core'; -import { ITreeController, ITreeNodeData } from '@ibiz-template/runtime'; +import { + IControlProvider, + ITreeController, + ITreeNodeData, + TreeController, +} from '@ibiz-template/runtime'; +import { IDETree } from '@ibiz/model-core'; import { ElTree } from 'element-plus'; import { NodeDropType } from 'element-plus/es/components/tree/src/tree.type'; import { debounce } from 'lodash-es'; -import { Ref } from 'vue'; +import { Ref, watch } from 'vue'; + +/** + * 树props接口 + * + * @export + * @interface IGridProps + */ +export interface ITreeProps { + // 模型 + modelData: IDETree; + // 上下文 + context: IContext; + // 视图参数 + params: IParams; + // 适配器 + provider?: IControlProvider; + // 部件行数据默认激活模式 + mdctrlActiveMode?: number; + // 是否单选 + singleSelect?: boolean; + // 是否是导航的 + navigational?: boolean; + // 默认展开节点 + defaultExpandedKeys?: string[]; + // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法 + checkStrictly?: boolean; + // 是否是本地数据模式 + isSimple?: boolean; + // 本地数据模式data + data?: Array; + // 默认加载 + loadDefault: boolean; +} /** * 根据id查找树节点数据对象 @@ -121,3 +160,43 @@ export function formatNodeDropType( ); } } + +export function useAppTreeBase(c: TreeController, props: ITreeProps): void { + // 设置默认展开 + if (props.defaultExpandedKeys) { + c.state.defaultExpandedKeys = props.defaultExpandedKeys; + } + + // 初始化数据 + const initSimpleData = (): void => { + if (!props.data) { + return; + } + const root = props.data.find((item: ITreeNodeData) => { + return (item as IData).isRoot === true; + }); + if (root) { + c.state.rootNodes = props.data; + c.state.items = props.data; + } + }; + + c.evt.on('onCreated', async () => { + if (props.isSimple) { + initSimpleData(); + c.state.isLoaded = true; + } + }); + + watch( + () => props.data, + () => { + if (props.isSimple) { + initSimpleData(); + } + }, + { + deep: true, + }, + ); +} diff --git a/src/control/tree/tree.tsx b/src/control/tree/tree.tsx index ac1a532ca..8f519d7bf 100644 --- a/src/control/tree/tree.tsx +++ b/src/control/tree/tree.tsx @@ -14,7 +14,7 @@ import { } from 'vue'; import { MenuItem } from '@imengyu/vue3-context-menu'; import { createUUID } from 'qx-util'; -import { debounce } from 'lodash-es'; +import { cloneDeep, debounce } from 'lodash-es'; import { IDETBGroupItem, IDETBRawItem, @@ -42,6 +42,7 @@ import { isNil } from 'ramda'; import { findNodeData, formatNodeDropType, + useAppTreeBase, useElTreeUtil, } from './el-tree-util'; @@ -75,13 +76,16 @@ export const TreeControl = defineComponent({ loadDefault: { type: Boolean, default: true }, checkStrictly: { type: Boolean, default: true }, isSimple: { type: Boolean, default: false }, - data: { type: Array, default: () => [] }, + data: { type: Array, required: false }, }, - setup(props) { + emits: ['nodeClick'], + setup(props, { emit }) { const c = useControlController( (...args) => new TreeController(...args), ); + useAppTreeBase(c, props); + const cascadeSelect = ref(false); const counterData = reactive({}); const fn = (counter: IData) => { @@ -238,16 +242,23 @@ export const TreeControl = defineComponent({ // simple模式 let nodes: ITreeNodeData[] = []; if (item.level === 0) { - const tempNodes = props.data.find((_item: IData) => { + const tempNodes = c.state.items.find((_item: IData) => { return _item.isRoot; }); if (tempNodes) { - nodes = [tempNodes as ITreeNodeData]; + nodes = [tempNodes]; } } else { - nodes = props.data.filter((_item: IData) => { - return _item._pid.includes(item.data._uuid); - }) as ITreeNodeData[]; + nodes = c.state.items + .filter((_item: IData) => { + return _item._pid.includes(item.data._uuid); + }) + .map((_item: IData) => { + const tempItem = cloneDeep(_item); + // 避免树死循环,需要给一个随机值,节点真实数据id用_uuid保存 + tempItem._id = createUUID(); + return tempItem; + }) as ITreeNodeData[]; } item._children = nodes; callback(toElNodes(nodes)); @@ -321,6 +332,10 @@ export const TreeControl = defineComponent({ */ const onNodeClick = (nodeData: ITreeNodeData, evt: MouseEvent) => { evt.stopPropagation(); + // 设计态并且是simple模式时向上抛出节点点击事件 + if (props.isSimple && c.context.srfrunmode === 'DESIGN') { + emit('nodeClick', { nodeData, evt }); + } if (nodeData._disableSelect) return; if (forbidClick) { return; @@ -576,20 +591,18 @@ export const TreeControl = defineComponent({ }; const updateNodeExpand = (data: IData, expanded: boolean) => { - let nodeData; - if (props.isSimple) { - nodeData = props.data.find((item: IData) => { - return item._uuid === data._uuid; - }); - } else { - nodeData = findNodeData(data._uuid, c); - } + const nodeData = findNodeData(data._uuid, c); if (!nodeData) { throw new RuntimeError( ibiz.i18n.t('control.common.noFoundNode', { id: data._uuid }), ); } - c.onExpandChange(nodeData as ITreeNodeData, expanded); + const tempData = cloneDeep(nodeData); + if (props.isSimple && c.context.srfrunmode === 'DESIGN') { + // 之前处理过节点数据的_id,避免循环嵌套进入死循环,这里需要对_id在再次进行处理保证选中展开效果 + tempData._id = data._id; + } + c.onExpandChange(tempData as ITreeNodeData, expanded); }; const debounceSearch = debounce(() => { -- Gitee From 399f7a28507430ea86368cc8440bfc61bbf7e4c6 Mon Sep 17 00:00:00 2001 From: jianglinjun Date: Sun, 29 Sep 2024 10:08:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?style:=20=E6=9B=B4=E6=96=B0=E6=A0=91?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/control/tree/el-tree-util.ts | 8 ++++++++ src/control/tree/tree.tsx | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/control/tree/el-tree-util.ts b/src/control/tree/el-tree-util.ts index deb92ea94..0c543fd7a 100644 --- a/src/control/tree/el-tree-util.ts +++ b/src/control/tree/el-tree-util.ts @@ -161,6 +161,14 @@ export function formatNodeDropType( } } + +/** + * 初始化树数据 + * + * @export + * @param {TreeController} c + * @param {ITreeProps} props + */ export function useAppTreeBase(c: TreeController, props: ITreeProps): void { // 设置默认展开 if (props.defaultExpandedKeys) { diff --git a/src/control/tree/tree.tsx b/src/control/tree/tree.tsx index 8f519d7bf..c050feda5 100644 --- a/src/control/tree/tree.tsx +++ b/src/control/tree/tree.tsx @@ -333,7 +333,7 @@ export const TreeControl = defineComponent({ const onNodeClick = (nodeData: ITreeNodeData, evt: MouseEvent) => { evt.stopPropagation(); // 设计态并且是simple模式时向上抛出节点点击事件 - if (props.isSimple && c.context.srfrunmode === 'DESIGN') { + if (c.context.srfrunmode === 'DESIGN') { emit('nodeClick', { nodeData, evt }); } if (nodeData._disableSelect) return; @@ -598,7 +598,7 @@ export const TreeControl = defineComponent({ ); } const tempData = cloneDeep(nodeData); - if (props.isSimple && c.context.srfrunmode === 'DESIGN') { + if (props.isSimple) { // 之前处理过节点数据的_id,避免循环嵌套进入死循环,这里需要对_id在再次进行处理保证选中展开效果 tempData._id = data._id; } -- Gitee