Skip to content

Commit

Permalink
feat: condition-builder支持selectMode为chained的选项层级显示 (#7120)
Browse files Browse the repository at this point in the history
* feat: condition-builder支持selectMode为chained的选项层级显示
  • Loading branch information
sqzhou committed Jun 12, 2023
1 parent 125d860 commit 75952bd
Show file tree
Hide file tree
Showing 14 changed files with 625 additions and 171 deletions.
88 changes: 84 additions & 4 deletions docs/zh-CN/components/form/condition-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ type Value = ValueGroup;
"label": "条件组件",
"name": "conditions",
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
"source": "/api/condition-fields?a=${a}&waitSeconds=2"
"source": "/api/condition-fields/custom?a=${a}&waitSeconds=2"
}
]
}
Expand All @@ -500,7 +500,7 @@ type Value = ValueGroup;

> 2.3.0 及以上版本
通过 selectMode 配置组合条件左侧选项类型,可配置项为`list``tree`,默认为`list`两者数据格式相同,只是下拉框展示方式不同,当存在多层 children 嵌套时,建议使用`tree`
通过 selectMode 配置组合条件左侧选项类型,可配置项为`list``tree``chained`,默认为`list`这三个数据格式基本类似,只是下拉框展示方式不同,`tree`是树形下拉,`chained`为多个级联的下拉。当存在多层 children 嵌套时,建议使用`tree`

selectMode 为`list`

Expand Down Expand Up @@ -640,6 +640,84 @@ selectMode 为`tree`时
}
```

> 3.2.0 及以上版本
selectMode 为`chained`时,使用`fields`字段

```schema: scope="body"
{
"type": "form",
"debug": true,
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"name": "conditions",
"selectMode": "chained",
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
"fields": [
{
"label": "文本",
"type": "text",
"name": "text"
},
{
"label": "数字",
"type": "number",
"name": "number"
},
{
"label": "布尔",
"type": "boolean",
"name": "boolean"
},
{
"label": "链式结构",
"name": "chained",
"children": [
{
"label": "Folder A",
"name": "Folder_A",
"children": [
{
"label": "file A",
"name": "file_A",
"type": "number"
},
{
"label": "file B",
"name": "file_B",
"type": "text"
}
]
}
]
}
]
}
]
}
```

selectMode 为`chained`时,使用`source`字段

```schema: scope="body"
{
"type": "form",
"debug": true,
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"name": "conditions",
"selectMode": "chained",
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
"source": "/api/condition-fields/chained"
}
]
}
```

## 简易模式

通过 builderMode 配置为简易模式,在这个模式下将不开启树形分组功能,输出结果只有一层,方便后端实现简单的 SQL 生成。
Expand Down Expand Up @@ -918,7 +996,7 @@ selectMode 为`tree`时

## 属性表

| 属性名 | 类型 | 默认值 | 说明 |
| 属性名 | 类型 | 默认值 | 说明 |
| -------------- | ------------------ | -------- | ------------------------------ |
| className | `string` | | 外层 dom 类名 |
| fieldClassName | `string` | | 输入字段的类名 |
Expand All @@ -929,4 +1007,6 @@ selectMode 为`tree`时
| showANDOR | `boolean` | | 用于 simple 模式下显示切换按钮 |
| showNot | `boolean` | | 是否显示「非」按钮 |
| searchable | `boolean` | | 字段是否可搜索 |
| selectMode | `'list'``'tree'` | `'list'` | 组合条件左侧选项类型 |
| selectMode | `'list'` \| `'tree'` \| `'chained'` | `'list'` | 组合条件左侧选项类型。`'chained'`模式需要`3.2.0及以上版本` |
| addBtnVisibleOn | `string` | | 表达式:控制按钮“添加条件”的显示。参数为`depth``breadth`,分别代表深度、长度。表达式需要返回`boolean`类型`3.2.0及以上版本` |
| addGroupBtnVisibleOn | `string` | | 表达式:控制按钮“添加条件组”的显示。参数为`depth``breadth`,分别代表深度、长度。表达式需要返回`boolean`类型`3.2.0及以上版本` |
44 changes: 44 additions & 0 deletions mock/cfc/mock/condition-fields/chained.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"status": 0,
"msg": "",
"data": {
"fields": [
{
"label": "文本",
"type": "text",
"name": "text"
},
{
"label": "数字",
"type": "number",
"name": "number"
},
{
"label": "布尔",
"type": "boolean",
"name": "boolean"
},
{
"label": "日期",
"name": "date",
"children": [
{
"label": "日期1",
"type": "date",
"name": "date1"
},
{
"label": "时间2",
"type": "time",
"name": "time2"
},
{
"label": "日期时间3",
"type": "datetime",
"name": "datetime"
}
]
}
]
}
}
File renamed without changes.
35 changes: 35 additions & 0 deletions packages/amis-ui/scss/components/form/_selection.scss
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,38 @@
cursor: pointer;
}
}

.#{$ns}ChainedDropdownSelection {
display: inline-block;

&-item {
display: inline-block;
}
}

.#{$ns}DropDownSelection {
position: relative;
display: inline-block;
margin: 0.1875rem;
vertical-align: middle;

&-caret {
transition: transform var(--animation-duration) ease-out;
margin: 5px;
display: flex;
color: var(--Form-select-caret-iconColor);
&:hover {
color: var(--Form-select-caret-onHover-iconColor);
}

> svg {
width: px2rem(10px);
height: px2rem(10px);
top: 0;
}
}

&-input.is-active &-caret {
transform: rotate(180deg);
}
}
141 changes: 141 additions & 0 deletions packages/amis-ui/src/components/ChainedDropdownSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
import omit from 'lodash/omit';
import {
uncontrollable,
autobind,
ThemeProps,
themeable,
localeable,
LocaleProps
} from 'amis-core';

import {Options} from './Select';
import {BaseSelection, BaseSelectionProps} from './Selection';

import DropDownSelection from './DropDownSelection';

export interface ChainedDropDownSelectionProps
extends ThemeProps,
LocaleProps,
BaseSelectionProps {
options: Array<any>;
value: any;
onChange: (value: any) => void;
disabled?: boolean;
searchable?: boolean;
popOverContainer?: any;
}

interface ChainedDropdownSelectionState {
stacks: Array<Options>;
values: Array<string>;
}

export class ChainedDropdownSelection extends BaseSelection<
ChainedDropDownSelectionProps,
ChainedDropdownSelectionState
> {
constructor(props: ChainedDropDownSelectionProps) {
super(props);

this.state = this.computed(props.value, props.options);
}

componentDidUpdate(prevProps: ChainedDropDownSelectionProps) {
const {options, value} = this.props;
if (options !== prevProps.options || prevProps.value !== value) {
this.setState(this.computed(value, options));
}
}

computed(value: string, options: Options) {
const {valueField} = this.props;
let values: Array<string> = [];
const getValues = (opts: Options, arr: Array<string> = []) => {
opts.forEach(item => {
const cValue = valueField ? item[valueField] : item?.value ?? '';
if (cValue === value) {
values = [...arr, cValue];
} else if (item.children) {
getValues(item.children, [...arr, cValue]);
}
});
};
getValues(options);
return {
values,
stacks: this.computedStask(values)
};
}

getFlatOptions(options: Options) {
return options.map(item => omit(item, 'children'));
}

@autobind
handleSelect(index: number, value: string) {
// 当前层级点击时,需要重新设置下values的值,以及重新计算stacks列表
const {values} = this.state;
values.splice(index, values.length - index);
value && values.push(value);
const stacks = this.computedStask(values);
this.setState(
{
stacks,
values
},
() => {
this.props?.onChange?.(value);
}
);
}

// 根据树结构层级,寻找最后一层
computedStask(values: string[]) {
const {options, valueField} = this.props;
const getDeep = (opts: Options, index: number, tems: Array<Options>) => {
tems.push(this.getFlatOptions(opts));
opts.forEach(op => {
const cValue = valueField ? op[valueField] : op?.value ?? '';
if (
cValue === values[index] &&
op.children &&
values.length - 1 >= index
) {
getDeep(op.children, index + 1, tems);
}
});
return tems;
};

return getDeep(options, 0, []);
}

render() {
const {stacks, values} = this.state;
const {className, classnames: cx} = this.props;

return (
<div className={cx('ChainedDropdownSelection', className)}>
{stacks.map((item, index) => (
<div className={cx('ChainedDropdownSelection-item')} key={index}>
<DropDownSelection
{...this.props}
value={values[index]}
options={item}
onChange={value => this.handleSelect(index, value)}
/>
</div>
))}
</div>
);
}
}

export default themeable(
localeable(
uncontrollable(ChainedDropdownSelection, {
value: 'onChange'
})
)
);
Loading

0 comments on commit 75952bd

Please sign in to comment.