From 1f38fde1dcb84328c0ec4c181db447784d6e4cf7 Mon Sep 17 00:00:00 2001 From: sqzhou Date: Mon, 17 Jul 2023 10:49:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20transfer=E7=BB=84=E4=BB=B6disabled?= =?UTF-8?q?=E7=9A=84=E9=80=89=E9=A1=B9=20=E5=9C=A8=E5=85=A8=E9=80=89?= =?UTF-8?q?=E7=88=B6=E7=BA=A7=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C=E4=BB=8D?= =?UTF-8?q?=E7=84=B6=E5=8F=AF=E4=BB=A5=E8=A2=AB=E9=80=89=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-ui/src/components/Tree.tsx | 39 +++++-- .../amis/__tests__/renderers/Tree.test.tsx | 101 ++++++++++++++++++ 2 files changed, 132 insertions(+), 8 deletions(-) diff --git a/packages/amis-ui/src/components/Tree.tsx b/packages/amis-ui/src/components/Tree.tsx index 41fb35dbf..e3e444482 100644 --- a/packages/amis-ui/src/components/Tree.tsx +++ b/packages/amis-ui/src/components/Tree.tsx @@ -492,14 +492,37 @@ export class TreeSelector extends React.Component< // 父级选中的时候,子节点也都选中,但是自己不选中 !~idx && children.length && value.pop(); - while (children.length) { - let child = children.shift(); - let index = value.indexOf(child); + // 取消下选择 + if ( + flattenTree(children) + .filter(item => !item?.disabled) + .some(v => ~value.indexOf(v)) + ) { + while (children.length) { + let child = children.shift(); + let index = value.indexOf(child); - if (child.children && child.children.length) { - children.push.apply(children, child.children); - } else if (!~index && child.value !== 'undefined') { - value.push(child); + if (child.children && child.children.length) { + children.push.apply(children, child.children); + } + if (~index && children.value !== 'undefined' && !child.disabled) { + value.splice(index, 1); + } + } + } else { + while (children.length) { + let child = children.shift(); + let index = value.indexOf(child); + + if (child.children && child.children.length) { + children.push.apply(children, child.children); + } else if ( + !~index && + child.value !== 'undefined' && + !child?.disabled + ) { + value.push(child); + } } } } else { @@ -557,7 +580,7 @@ export class TreeSelector extends React.Component< while (children.length) { let child = children.shift(); let index = value.indexOf(child); - if (~index) { + if (~index && !child?.disabled) { value.splice(index, 1); } if (child.children && child.children.length) { diff --git a/packages/amis/__tests__/renderers/Tree.test.tsx b/packages/amis/__tests__/renderers/Tree.test.tsx index d0d92dee2..8ccc26bb5 100644 --- a/packages/amis/__tests__/renderers/Tree.test.tsx +++ b/packages/amis/__tests__/renderers/Tree.test.tsx @@ -430,3 +430,104 @@ test('Tree: add child & cancel', async () => { expect(!!container.querySelector('[icon="close"]')).toBeFalsy() ); }); + +test('Tree: item disabled', async () => { + const onSubmit = jest.fn(); + const {container, findByText, findByPlaceholderText} = render( + amisRender( + { + "type": "form", + "api": "/api/mock2/form/saveForm", + "body": [ + { + "label": "树型展示", + "type": "transfer", + "name": "transfer", + "selectMode": "tree", + "searchable": true, + "options": [ + { + "label": "法师", + "children": [ + { + "label": "诸葛亮", + "value": "zhugeliang" + } + ] + }, + { + "label": "战士", + "children": [ + { + "label": "曹操", + "value": "caocao" + }, + { + "label": "曹操1", + "value": "caocao1", + "children": [ + { + "label": "李白1", + "value": "libai1" + }, + { + "label": "韩信1", + "value": "hanxin1" + }, + { + "label": "云中君1", + "value": "yunzhongjun1" + } + ] + }, + { + "disabled": true, + "label": "钟无艳", + "value": "zhongwuyan" + } + ] + }, + { + "label": "打野", + "children": [ + { + "label": "李白", + "value": "libai" + }, + { + "label": "韩信", + "value": "hanxin" + }, + { + "label": "云中君", + "value": "yunzhongjun" + } + ] + } + ] + } + ] + }, + {onSubmit}, + makeEnv({}) + ) + ); + const node = await findByText('战士'); + const submitBtn = await findByText('提交'); + fireEvent.click(node); + fireEvent.click(submitBtn); + + await wait(100); + + expect(onSubmit.mock.calls[0][0]).toEqual({ + transfer: 'caocao,libai1,hanxin1,yunzhongjun1' + }); + + + fireEvent.click(node); + fireEvent.click(submitBtn); + await wait(100); + expect(onSubmit.mock.calls[1][0]).toEqual({ + transfer: '' + }); +}); From 0a8c80827fb15e45110cc2adb0e14f57f9ecfd05 Mon Sep 17 00:00:00 2001 From: sqzhou Date: Mon, 17 Jul 2023 13:28:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20transfer=E7=BB=84=E4=BB=B6disabled?= =?UTF-8?q?=E7=9A=84=E9=80=89=E9=A1=B9=20=E5=9C=A8=E5=85=A8=E9=80=89?= =?UTF-8?q?=E7=88=B6=E7=BA=A7=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C=E4=BB=8D?= =?UTF-8?q?=E7=84=B6=E5=8F=AF=E4=BB=A5=E8=A2=AB=E9=80=89=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-ui/src/components/Tree.tsx | 73 +++++++++++-------- .../amis/__tests__/renderers/Tree.test.tsx | 1 - 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/packages/amis-ui/src/components/Tree.tsx b/packages/amis-ui/src/components/Tree.tsx index e3e444482..315e09f93 100644 --- a/packages/amis-ui/src/components/Tree.tsx +++ b/packages/amis-ui/src/components/Tree.tsx @@ -26,7 +26,8 @@ import { hasAbility, getTreeParent, getTreeAncestors, - flattenTree + flattenTree, + flattenTreeWithLeafNodes } from 'amis-core'; import {Option, Options, value2array} from './Select'; import {themeable, ThemeProps, highlight} from 'amis-core'; @@ -488,55 +489,65 @@ export class TreeSelector extends React.Component< // cascade 为 true 表示父节点跟子节点没有级联关系。 if (autoCheckChildren) { const children = item.children ? item.children.concat([]) : []; + const hasDisabled = flattenTree(children).some(item => item?.disabled); + if (onlyChildren) { // 父级选中的时候,子节点也都选中,但是自己不选中 !~idx && children.length && value.pop(); - // 取消下选择 - if ( - flattenTree(children) - .filter(item => !item?.disabled) - .some(v => ~value.indexOf(v)) - ) { - while (children.length) { - let child = children.shift(); - let index = value.indexOf(child); + // 这个 isAllChecked 主要是判断如果有disabled的item项,这时父节点还是选中的话,针对性的处理逻辑 + const isAllChecked = flattenTreeWithLeafNodes(children) + .filter(item => !item?.disabled) + .every(v => ~value.indexOf(v)); - if (child.children && child.children.length) { - children.push.apply(children, child.children); - } - if (~index && children.value !== 'undefined' && !child.disabled) { - value.splice(index, 1); - } + while (children.length) { + let child = children.shift(); + let index = value.indexOf(child); + + if (child.children && child.children.length) { + children.push.apply(children, child.children); + continue; } - } else { - while (children.length) { - let child = children.shift(); - let index = value.indexOf(child); - if (child.children && child.children.length) { - children.push.apply(children, child.children); - } else if ( - !~index && - child.value !== 'undefined' && + if (hasDisabled && isAllChecked) { + if ( + ~index && + children.value !== 'undefined' && !child?.disabled ) { - value.push(child); + value.splice(index, 1); } + continue; + } + + if (!~index && child.value !== 'undefined' && !child?.disabled) { + value.push(child); } } } else { + // 这个 isAllChecked 主要是判断如果有disabled的item项,这时父节点还是选中的话,针对性的处理逻辑 + const isAllChecked = flattenTree(children) + .filter(item => !item?.disabled) + .every(v => ~value.indexOf(v)); + // 只要父节点选择了,子节点就不需要了,全部去掉勾选. withChildren时相反 while (children.length) { let child = children.shift(); let index = value.indexOf(child); - if (~index) { - value.splice(index, 1); - } + if (!child?.disabled) { + // 判断下下面是否有禁用项 + if (!hasDisabled) { + if (~index) { + value.splice(index, 1); + } - if (withChildren || cascade) { - value.push(child); + if (withChildren || cascade) { + value.push(child); + } + } else { + isAllChecked ? value.splice(index, 1) : value.push(child); + } } if (child.children && child.children.length) { diff --git a/packages/amis/__tests__/renderers/Tree.test.tsx b/packages/amis/__tests__/renderers/Tree.test.tsx index 8ccc26bb5..2e248d3aa 100644 --- a/packages/amis/__tests__/renderers/Tree.test.tsx +++ b/packages/amis/__tests__/renderers/Tree.test.tsx @@ -523,7 +523,6 @@ test('Tree: item disabled', async () => { transfer: 'caocao,libai1,hanxin1,yunzhongjun1' }); - fireEvent.click(node); fireEvent.click(submitBtn); await wait(100);