mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
Feat/tree cxd 4.0 (#3536)
* feat:tree ui级联同数据级联分离,行为对齐社区 * feat:云舍4.0样适配 * feat:先取消拖拽 * 问题修改 * chore:变量名修改 * 解决冲突 & 修改文档 * 修改文档 Co-authored-by: pianruijie <pianruijie@baidu.com>
This commit is contained in:
parent
2a29366403
commit
47c1c35058
@ -159,7 +159,7 @@ order: 59
|
||||
|
||||
## 选中父节点是否自动选中子节点
|
||||
|
||||
默认选中父节点会自动选中子节点,可以设置`"cascade": true`,不自动选中子节点
|
||||
`autoCheckChildren`默认为true,选中父节点会自动选中子节点,可以设置`"autoCheckChildren": false`,不自动选中子节点
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -209,6 +209,92 @@ order: 59
|
||||
"name": "tree2",
|
||||
"label": "不自动选中子节点",
|
||||
"multiple": true,
|
||||
"autoCheckChildren": false,
|
||||
"options": [
|
||||
{
|
||||
"label": "A",
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"label": "B",
|
||||
"value": "b",
|
||||
"children": [
|
||||
{
|
||||
"label": "B-1",
|
||||
"value": "b-1"
|
||||
},
|
||||
{
|
||||
"label": "B-2",
|
||||
"value": "b-2"
|
||||
},
|
||||
{
|
||||
"label": "B-3",
|
||||
"value": "b-3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "C",
|
||||
"value": "c"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 选中父节点自动选中子节点,数据是否包含父子节点的值
|
||||
`cascade`默认为false,子节点禁止反选,值不包含子节点值,配置`"cascade": true`,子节点可以反选,值包含父子节点值
|
||||
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree1",
|
||||
"label": "默认子节点禁止反选,值不包含子节点值",
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{
|
||||
"label": "A",
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"label": "B",
|
||||
"value": "b",
|
||||
"children": [
|
||||
{
|
||||
"label": "B-1",
|
||||
"value": "b-1"
|
||||
},
|
||||
{
|
||||
"label": "B-2",
|
||||
"value": "b-2"
|
||||
},
|
||||
{
|
||||
"label": "B-3",
|
||||
"value": "b-3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "C",
|
||||
"value": "c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree2",
|
||||
"label": "子节点可以反选,值包含父子节点值",
|
||||
"multiple": true,
|
||||
"cascade": true,
|
||||
"options": [
|
||||
{
|
||||
@ -243,9 +329,7 @@ order: 59
|
||||
}
|
||||
```
|
||||
|
||||
## 选中父节点,值是否包含子节点
|
||||
|
||||
默认选中父节点,是不会带上子节点的值,想要自动带上子节点的值,那么配置`"withChildren": true`
|
||||
`withChildren`默认为false,子节点禁止反选,值包含父子节点值,配置`withChildren": true`,子节点禁止反选,值包含父子节点值
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -256,7 +340,7 @@ order: 59
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree1",
|
||||
"label": "默认不自动带上子节点的值",
|
||||
"label": "默认不包含子节点的值",
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{
|
||||
@ -340,7 +424,7 @@ order: 59
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree1",
|
||||
"label": "默认不自动带上子节点的值",
|
||||
"label": "默认不包含子节点的值",
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{
|
||||
@ -825,9 +909,10 @@ true false true [{label: 'A/B/C', value: 'a/b/c'},{label: 'A
|
||||
| showRadio | `boolean` | `false` | 是否显示单选按钮,`multiple` 为 `false` 是有效。 |
|
||||
| initiallyOpen | `boolean` | `true` | 设置是否默认展开所有层级。 |
|
||||
| unfoldedLevel | `number` | `0` | 设置默认展开的级数,只有`initiallyOpen`不是`true`时生效。 |
|
||||
| cascade | `boolean` | `false` | 当选中父节点时不自动选择子节点。 |
|
||||
| withChildren | `boolean` | `false` | 选中父节点时,值里面将包含子节点的值,否则只会保留父节点的值。 |
|
||||
| onlyChildren | `boolean` | `false` | 多选时,选中父节点时,是否只将其子节点加入到值中。 |
|
||||
| autoCheckChildren | `boolean` | `true` | 当选中父节点时级联选择子节点。 |
|
||||
| cascade | `boolean` | `false` | autoCheckChildren为true时生效;默认行为:子节点禁用,值只包含父节点值;设置为true时,子节点可反选,值包含父子节点值。 |
|
||||
| withChildren | `boolean` | `false` | cascade为false时生效,选中父节点时,值里面将包含父子节点的值,否则只会保留父节点的值。
|
||||
| onlyChildren | `boolean` | `false` | autoCheckChildren为true时生效,不受cascade影响;onlyChildren为true,ui行为级联选中子节点,子节点可反选,值只包含子节点的值。 |
|
||||
| rootCreatable | `boolean` | `false` | 是否可以创建顶级节点 |
|
||||
| rootCreateTip | `string` | `"添加一级节点"` | 创建顶级节点的悬浮提示 |
|
||||
| minLength | `number` | | 最少选中的节点数 |
|
||||
|
@ -1495,8 +1495,10 @@
|
||||
--TabsTransfer-border-color: #e8e9eb;
|
||||
|
||||
--Tree-indent: var(--gap-md);
|
||||
--Tree-icon-gap: var(--gap-xs);
|
||||
--Tree-inputHeight: calc(var(--Form-input-height) * 0.85);
|
||||
--Tree-item-onHover-bg: rgba(0, 126, 255, 0.08);
|
||||
--Tree-item-onHover-bg-pure: rgba(0, 126, 255, 0.08);
|
||||
--Tree-itemArrowWidth: #{px2rem(12px)};
|
||||
--Tree-itemHeight: #{px2rem(30px)};
|
||||
--Tree-itemLabel--onChecked-color: var(--Form-selectValue-color);
|
||||
|
@ -50,7 +50,7 @@
|
||||
top: 2px;
|
||||
width: px2rem(16px);
|
||||
height: px2rem(16px);
|
||||
margin-left: var(--gap-xs);
|
||||
margin-left: var(--Tree-icon-gap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,26 +87,35 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: var(--Tree-item-onHover-bg);
|
||||
user-select: none;
|
||||
margin-bottom: px2rem(4px);
|
||||
padding-left: var(--Tree-icon-gap);
|
||||
& > * {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
> .#{$ns}Checkbox {
|
||||
display: inline-flex;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--Tree-item-onHover-bg-pure);
|
||||
}
|
||||
}
|
||||
|
||||
// & > * {
|
||||
// position: relative;
|
||||
// z-index: 2;
|
||||
// }
|
||||
|
||||
// &:hover:after {
|
||||
// position: absolute;
|
||||
// content: '';
|
||||
// z-index: 1;
|
||||
// background: var(--Tree-item-onHover-bg);
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
// left: -99999px;
|
||||
// }
|
||||
&-item {
|
||||
.is-checked {
|
||||
background: var(--Tree-item-onChekced-bg);
|
||||
border-radius: var(--Tree-item-onChekced-bg-borderRadius);
|
||||
}
|
||||
.is-disabled {
|
||||
color: var(--text--muted-color);
|
||||
background: none;
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-draggable &-itemLabel:hover::after {
|
||||
@ -120,12 +129,12 @@
|
||||
vertical-align: top;
|
||||
height: var(--Tree-itemHeight);
|
||||
line-height: var(--Tree-itemHeight);
|
||||
padding-right: var(--gap-xs);
|
||||
padding-right: var(--Tree-icon-gap);
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: var(--gap-xs);
|
||||
margin-left: var(--Tree-icon-gap);
|
||||
cursor: pointer;
|
||||
|
||||
> svg {
|
||||
@ -146,7 +155,7 @@
|
||||
> a {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-left: var(--gap-sm);
|
||||
margin-left: var(--Tree-icon-gap);
|
||||
color: var(--icon-color);
|
||||
|
||||
&:hover {
|
||||
@ -205,7 +214,7 @@
|
||||
width: $svgSize;
|
||||
height: $svgSize;
|
||||
top: $svgSize * 0.125;
|
||||
margin-right: var(--gap-xs);
|
||||
margin-right: var(--Tree-icon-gap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +222,7 @@
|
||||
cursor: pointer;
|
||||
width: var(--Tree-itemArrowWidth);
|
||||
display: inline-flex;
|
||||
margin-right: var(--gap-xs);
|
||||
margin-right: var(--Tree-icon-gap);
|
||||
|
||||
// &:before {
|
||||
// font-style: normal;
|
||||
@ -236,7 +245,7 @@
|
||||
|
||||
&-itemArrowPlaceholder {
|
||||
display: inline-block;
|
||||
width: calc(var(--Tree-itemArrowWidth) + var(--gap-xs));
|
||||
width: calc(var(--Tree-itemArrowWidth) + var(--Tree-icon-gap));
|
||||
}
|
||||
|
||||
&-itemDrager {
|
||||
@ -245,12 +254,12 @@
|
||||
}
|
||||
|
||||
&-spinner {
|
||||
margin-right: var(--gap-xs);
|
||||
margin-right: var(--Tree-icon-gap);
|
||||
}
|
||||
|
||||
&-itemIcon {
|
||||
display: inline-flex;
|
||||
margin-right: var(--gap-xs);
|
||||
margin-right: var(--Tree-icon-gap);
|
||||
}
|
||||
|
||||
&-rootIcon,
|
||||
@ -262,30 +271,16 @@
|
||||
height: px2rem(14px);
|
||||
}
|
||||
}
|
||||
|
||||
&-itemLabel {
|
||||
user-select: none;
|
||||
|
||||
&.is-checked,
|
||||
&.is-children-checked {
|
||||
color: var(--Tree-itemLabel--onChecked-color);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
color: var(--text--muted-color);
|
||||
}
|
||||
|
||||
> .#{$ns}Checkbox {
|
||||
display: inline-flex;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&-itemText {
|
||||
cursor: pointer;
|
||||
flex: 1 auto;
|
||||
color: var(--Form-input-color);
|
||||
line-height: var(--lineHeightBase);
|
||||
padding: px2rem(4px) 0;
|
||||
}
|
||||
.is-disabled &-itemText {
|
||||
color: var(--text--muted-color);
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
@ -313,7 +308,7 @@
|
||||
|
||||
&--hover {
|
||||
border-radius: 0;
|
||||
background-color: var(--Tree-item-onHover-bg);
|
||||
background-color: var(--Tree-item-onHover-bg-pure);
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
|
@ -632,12 +632,17 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
||||
|
||||
--Satus-icon-width: #{px2rem(14px)};
|
||||
|
||||
--Tree-itemHeight: #{px2rem(32px)};
|
||||
|
||||
--Progress-borderRadius: #{$R7};
|
||||
|
||||
--Rating-inactive-color: #{$G9};
|
||||
|
||||
// tree
|
||||
--Tree-item-onHover-bg-pure: #{$G10};
|
||||
--Tree-item-onChekced-bg: var(--Form-select-menu-onHover-bg);
|
||||
--Tree-item-onChekced-bg-borderRadius: #{px2rem(2px)};
|
||||
--Tree-itemHeight: #{px2rem(24px)};
|
||||
--Tree-icon-gap: var(--gap-sm);
|
||||
|
||||
// Formula
|
||||
--InputFormula-code-bgColor: #{$G10};
|
||||
|
||||
|
@ -88,14 +88,21 @@ interface TreeSelectorProps extends ThemeProps, LocaleProps {
|
||||
pathSeparator?: string;
|
||||
// 已选择节点路径
|
||||
nodePath: any[];
|
||||
// ui级联关系,true代表级联选中,false代表不级联,默认为true
|
||||
autoCheckChildren: boolean;
|
||||
|
||||
// 这个配置名字没取好,目前的含义是,如果这个配置成true,点父级的时候,子级点不会自选中。
|
||||
// 否则点击父级,子节点选中。
|
||||
/*
|
||||
* 该属性代表数据级联关系,autoCheckChildren为true时生效,默认为false,具体数据级联关系如下:
|
||||
* 1.casacde为false,ui行为为级联选中子节点,子节点禁用;值只包含父节点的值
|
||||
* 2.cascade为false,withChildren为true,ui行为为级联选中子节点,子节点禁用;值包含父子节点的值
|
||||
* 3.cascade为true,ui行为级联选中子节点,子节点可反选,值包含父子节点的值,此时withChildren属性失效
|
||||
* 4.cascade不论为true还是false,onlyChildren为true,ui行为级联选中子节点,子节点可反选,值只包含子节点的值
|
||||
*/
|
||||
cascade?: boolean;
|
||||
|
||||
selfDisabledAffectChildren?: boolean;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
|
||||
// 是否为内建 增、改、删。当有复杂表单的时候直接抛出去让外层能统一处理
|
||||
bultinCUD?: boolean;
|
||||
rootCreatable?: boolean;
|
||||
@ -157,6 +164,7 @@ export class TreeSelector extends React.Component<
|
||||
hideRoot: true,
|
||||
rootLabel: 'Tree.root',
|
||||
rootValue: 0,
|
||||
autoCheckChildren: true,
|
||||
cascade: false,
|
||||
selfDisabledAffectChildren: true,
|
||||
rootCreateTip: 'Tree.addRoot',
|
||||
@ -182,7 +190,6 @@ export class TreeSelector extends React.Component<
|
||||
|
||||
constructor(props: TreeSelectorProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: value2array(
|
||||
props.value,
|
||||
@ -412,15 +419,12 @@ export class TreeSelector extends React.Component<
|
||||
const props = this.props;
|
||||
const value = this.state.value.concat();
|
||||
const idx = value.indexOf(item);
|
||||
const onlyChildren = props.onlyChildren;
|
||||
|
||||
const {onlyChildren, withChildren, cascade, autoCheckChildren} = props;
|
||||
if (checked) {
|
||||
~idx || value.push(item);
|
||||
|
||||
// cascade 为 true 表示父节点跟子节点没有级联关系。
|
||||
if (!props.cascade) {
|
||||
if (autoCheckChildren) {
|
||||
const children = item.children ? item.children.concat([]) : [];
|
||||
|
||||
if (onlyChildren) {
|
||||
// 父级选中的时候,子节点也都选中,但是自己不选中
|
||||
!~idx && children.length && value.pop();
|
||||
@ -445,7 +449,7 @@ export class TreeSelector extends React.Component<
|
||||
value.splice(index, 1);
|
||||
}
|
||||
|
||||
if (props.withChildren) {
|
||||
if (withChildren || cascade) {
|
||||
value.push(child);
|
||||
}
|
||||
|
||||
@ -464,7 +468,7 @@ export class TreeSelector extends React.Component<
|
||||
if (
|
||||
parent.children.every((child: any) => ~value.indexOf(child))
|
||||
) {
|
||||
if (!props.withChildren) {
|
||||
if (!cascade && !withChildren) {
|
||||
parent.children.forEach((child: any) => {
|
||||
const index = value.indexOf(child);
|
||||
if (~index) {
|
||||
@ -483,19 +487,18 @@ export class TreeSelector extends React.Component<
|
||||
}
|
||||
} else {
|
||||
~idx && value.splice(idx, 1);
|
||||
|
||||
if (!props.cascade && (props.withChildren || onlyChildren)) {
|
||||
const children = item.children ? item.children.concat([]) : [];
|
||||
while (children.length) {
|
||||
let child = children.shift();
|
||||
let index = value.indexOf(child);
|
||||
|
||||
if (~index) {
|
||||
value.splice(index, 1);
|
||||
}
|
||||
|
||||
if (child.children && child.children.length) {
|
||||
children.push.apply(children, child.children);
|
||||
if (autoCheckChildren) {
|
||||
if (cascade || withChildren || onlyChildren) {
|
||||
const children = item.children ? item.children.concat([]) : [];
|
||||
while (children.length) {
|
||||
let child = children.shift();
|
||||
let index = value.indexOf(child);
|
||||
if (~index) {
|
||||
value.splice(index, 1);
|
||||
}
|
||||
if (child.children && child.children.length) {
|
||||
children.push.apply(children, child.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -664,7 +667,6 @@ export class TreeSelector extends React.Component<
|
||||
@autobind
|
||||
getDropInfo(e: React.DragEvent<Element>, node: Option): IDropInfo {
|
||||
let rect = e.currentTarget.getBoundingClientRect();
|
||||
|
||||
const dragNode = this.dragNode;
|
||||
const deltaX = Math.min(50, rect.width * 0.3);
|
||||
const gap = node?.children?.length ? 0 : 16;
|
||||
@ -676,7 +678,6 @@ export class TreeSelector extends React.Component<
|
||||
let top = targetOffset.top - offset.top;
|
||||
|
||||
let {clientX, clientY} = e;
|
||||
|
||||
let position: IDropInfo['position'] =
|
||||
clientY >= rect.top + rect.height / 2 ? 'bottom' : 'top';
|
||||
let indicator;
|
||||
@ -788,6 +789,7 @@ export class TreeSelector extends React.Component<
|
||||
valueField,
|
||||
iconField,
|
||||
disabledField,
|
||||
autoCheckChildren,
|
||||
cascade,
|
||||
selfDisabledAffectChildren,
|
||||
onlyChildren,
|
||||
@ -818,7 +820,6 @@ export class TreeSelector extends React.Component<
|
||||
if (!isVisible(item as any, options)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checked = !!~value.indexOf(item);
|
||||
const selfDisabled = item[disabledField];
|
||||
let selfChecked = !!uncheckable || checked;
|
||||
@ -829,16 +830,15 @@ export class TreeSelector extends React.Component<
|
||||
childrenItems = this.renderList(
|
||||
item.children,
|
||||
value,
|
||||
cascade
|
||||
? false
|
||||
: uncheckable ||
|
||||
(selfDisabledAffectChildren ? selfDisabled : false) ||
|
||||
(multiple && checked)
|
||||
(!autoCheckChildren || cascade)
|
||||
? false: (uncheckable
|
||||
|| (selfDisabledAffectChildren ? selfDisabled : false)
|
||||
|| (multiple && checked))
|
||||
);
|
||||
selfChildrenChecked = !!childrenItems.childrenChecked;
|
||||
if (
|
||||
!selfChecked &&
|
||||
onlyChildren &&
|
||||
onlyChildren && autoCheckChildren &&
|
||||
item.children.length === childrenItems.childrenChecked
|
||||
) {
|
||||
selfChecked = true;
|
||||
@ -864,7 +864,7 @@ export class TreeSelector extends React.Component<
|
||||
<Checkbox
|
||||
size="sm"
|
||||
disabled={nodeDisabled}
|
||||
checked={selfChecked || (!cascade && selfChildrenChecked)}
|
||||
checked={selfChecked || (autoCheckChildren && selfChildrenChecked)}
|
||||
partial={!selfChecked}
|
||||
onChange={this.handleCheck.bind(this, item, !selfChecked)}
|
||||
/>
|
||||
|
@ -39,7 +39,16 @@ export interface TreeControlSchema extends FormOptionsControl {
|
||||
showIcon?: boolean;
|
||||
|
||||
/**
|
||||
* 父子之间是否完全独立。
|
||||
* ui级联关系,true代表级联选中,false代表不级联,默认为true
|
||||
*/
|
||||
autoCheckChildren?: boolean;
|
||||
|
||||
/**
|
||||
* 该属性代表数据级联关系,autoCheckChildren为true时生效,默认为false,具体数据级联关系如下:
|
||||
* 1.casacde为false,ui行为为级联选中子节点,子节点禁用;值只包含父节点的值
|
||||
* 2.cascade为false,withChildren为true,ui行为为级联选中子节点,子节点禁用;值包含父子节点的值
|
||||
* 3.cascade为true,ui行为级联选中子节点,子节点可反选,值包含父子节点的值,此时withChildren属性失效
|
||||
* 4.cascade不论为true还是false,onlyChildren为true,ui行为级联选中子节点,子节点可反选,值只包含子节点的值
|
||||
*/
|
||||
cascade?: boolean;
|
||||
|
||||
@ -166,6 +175,7 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
loading,
|
||||
hideRoot,
|
||||
rootLabel,
|
||||
autoCheckChildren,
|
||||
cascade,
|
||||
rootValue,
|
||||
showIcon,
|
||||
@ -222,6 +232,7 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
showIcon={showIcon}
|
||||
showRadio={showRadio}
|
||||
showOutline={showOutline}
|
||||
autoCheckChildren={autoCheckChildren}
|
||||
cascade={cascade}
|
||||
foldedField="collapsed"
|
||||
value={value || ''}
|
||||
|
Loading…
Reference in New Issue
Block a user