Showing
9 changed files
with
351 additions
and
27 deletions
... | ... | @@ -684,6 +684,7 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
684 | 684 | subset, |
685 | 685 | params, |
686 | 686 | fieldGroupType, |
687 | + showAssignment = true, | |
687 | 688 | isMixValue, |
688 | 689 | }) => { |
689 | 690 | const valuesObj = value?.valuesObj?.[0] || {}; |
... | ... | @@ -760,12 +761,15 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
760 | 761 | widget={field.extract?.widget} |
761 | 762 | isMixValue={isMixValue} |
762 | 763 | /> |
763 | - <ControlOutlined | |
764 | - onClick={() => { | |
765 | - setOpen(!open); | |
766 | - }} | |
767 | - className="qx-base-condition-item__content-suffix" | |
768 | - /> | |
764 | + | |
765 | + {showAssignment ? ( | |
766 | + <ControlOutlined | |
767 | + onClick={() => { | |
768 | + setOpen(!open); | |
769 | + }} | |
770 | + className="qx-base-condition-item__content-suffix" | |
771 | + /> | |
772 | + ) : null} | |
769 | 773 | </> |
770 | 774 | ); |
771 | 775 | |
... | ... | @@ -817,18 +821,22 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
817 | 821 | </div> |
818 | 822 | </div> |
819 | 823 | <div className="qx-base-condition-item__content"> |
820 | - <QxFlowNodeFieldSelector | |
821 | - mode="variable" | |
822 | - node={node!} | |
823 | - nodes={nodes!} | |
824 | - open={open} | |
825 | - value={valuesObj?.value} | |
826 | - onChange={handleAssignment} | |
827 | - limitTypes={[field.extract?.fieldType]} | |
828 | - subset={subset} | |
829 | - > | |
830 | - {RenderContent} | |
831 | - </QxFlowNodeFieldSelector> | |
824 | + {showAssignment ? ( | |
825 | + <QxFlowNodeFieldSelector | |
826 | + mode="variable" | |
827 | + node={node!} | |
828 | + nodes={nodes!} | |
829 | + open={open} | |
830 | + value={valuesObj?.value} | |
831 | + onChange={handleAssignment} | |
832 | + limitTypes={[field.extract?.fieldType]} | |
833 | + subset={subset} | |
834 | + > | |
835 | + {RenderContent} | |
836 | + </QxFlowNodeFieldSelector> | |
837 | + ) : ( | |
838 | + RenderContent | |
839 | + )} | |
832 | 840 | </div> |
833 | 841 | </div> |
834 | 842 | ); |
... | ... | @@ -848,7 +856,7 @@ export interface QxBaseConditionItemProps { |
848 | 856 | value?: any; |
849 | 857 | onChange?: (val: any) => void; |
850 | 858 | remove?: (field: QxBaseConditionField) => void; |
851 | - ValueAssignment?: React.FC<ValueAssignmentPopupProps>; | |
859 | + showAssignment?: boolean; | |
852 | 860 | mode?: string; |
853 | 861 | node?: INode; |
854 | 862 | nodes?: INode[]; | ... | ... |
... | ... | @@ -3,7 +3,7 @@ import { QxBaseConditionItem } from '../qx-base-condition-item'; |
3 | 3 | import { INode } from '../qx-flow-node-selector'; |
4 | 4 | import './index.less'; |
5 | 5 | |
6 | -const FieldBaseType = { | |
6 | +export const FieldBaseType = { | |
7 | 7 | STRING: 'TEXT', |
8 | 8 | DOUBLE: 'NUM', |
9 | 9 | NUMBER: 'NUM', |
... | ... | @@ -68,6 +68,7 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { |
68 | 68 | nodes={props.nodes} |
69 | 69 | node={props.node} |
70 | 70 | subset={props.subset} |
71 | + showAssignment={props.showAssignment} | |
71 | 72 | onChange={(val) => handleItemChange(val, key)} |
72 | 73 | remove={() => handleDelete(key)} |
73 | 74 | /> |
... | ... | @@ -107,13 +108,21 @@ export interface QxBaseConditionOptionsProps extends QxBaseConditionField { |
107 | 108 | |
108 | 109 | export type QxBaseConditionMode = 'condition' | 'variable'; |
109 | 110 | |
111 | +export interface QxBaseConditionValueType { | |
112 | + code: string; | |
113 | + type: string; | |
114 | + opt: string; | |
115 | + mappingValues: string[]; | |
116 | + [key: string]: any | |
117 | +} | |
118 | + | |
110 | 119 | export interface QxBaseConditionProps { |
111 | 120 | showIdx?: boolean; |
112 | 121 | mode: QxBaseConditionMode; |
113 | - options: QxBaseConditionOptionsProps[]; | |
114 | - value: any[]; | |
122 | + value?: QxBaseConditionValueType[]; | |
115 | 123 | onChange?: (val: any[]) => void; |
116 | 124 | nodes?: INode[]; |
117 | 125 | node?: INode; |
118 | 126 | subset?: boolean; |
127 | + showAssignment?: boolean; | |
119 | 128 | } | ... | ... |
src/qx-condition-sql/index.less
0 → 100644
1 | +@import '~@qx/ui/src/style/variable.less'; | |
2 | +@prefix: ~'qx-condition-sql'; | |
3 | + | |
4 | +.@{prefix} { | |
5 | + | |
6 | + &__header { | |
7 | + display: flex; | |
8 | + align-items: center; | |
9 | + justify-content: space-between; | |
10 | + border-top: 1px solid @N4; | |
11 | + } | |
12 | + | |
13 | + &__title { | |
14 | + color: @N9; | |
15 | + } | |
16 | + | |
17 | + &__select { | |
18 | + color: @B8; | |
19 | + width: 60px !important; | |
20 | + text-align: right; | |
21 | + :global { | |
22 | + .ant-select-suffix { | |
23 | + color: @B8; | |
24 | + } | |
25 | + } | |
26 | + } | |
27 | + | |
28 | + &__input-textarea { | |
29 | + display: block; | |
30 | + } | |
31 | +} | ... | ... |
src/qx-condition-sql/index.tsx
0 → 100644
1 | +import { FunctionOutlined } from '@ant-design/icons'; | |
2 | +import { Input, Select } from 'antd'; | |
3 | +import cx from 'classnames'; | |
4 | +import React, { useEffect, useState } from 'react'; | |
5 | +import './index.less'; | |
6 | + | |
7 | +const prefix = 'qx-condition-sql'; | |
8 | + | |
9 | +/** | |
10 | + * 条件表达式运算符 | |
11 | + */ | |
12 | +export const OperatorCol = [ | |
13 | + { | |
14 | + key: 'AND', | |
15 | + text: '且', | |
16 | + }, | |
17 | + { | |
18 | + key: 'OR', | |
19 | + text: '或', | |
20 | + }, | |
21 | + { | |
22 | + key: 'CUSTOM', | |
23 | + text: 'CUSTOM', | |
24 | + }, | |
25 | +]; | |
26 | + | |
27 | +const options = OperatorCol.map((item) => ({ | |
28 | + value: item.key, | |
29 | + label: item.key === 'CUSTOM' ? <FunctionOutlined /> : item.text, | |
30 | +})); | |
31 | + | |
32 | +/** | |
33 | + * 组合条件 | |
34 | + */ | |
35 | +export const QxConditionSql: React.FC<ConditionSqlProps> = ({ | |
36 | + onChange, | |
37 | + onBlur, | |
38 | + value, | |
39 | +}) => { | |
40 | + const [state, setState] = useState<ConditionSqlState>( | |
41 | + value || { | |
42 | + sqlType: 'AND', | |
43 | + }, | |
44 | + ); | |
45 | + | |
46 | + const handleInputValueChange = ( | |
47 | + e: React.ChangeEvent<HTMLTextAreaElement>, | |
48 | + ) => { | |
49 | + const expression = e.target.value; | |
50 | + setState({ ...state, expression }); | |
51 | + }; | |
52 | + | |
53 | + useEffect(() => { | |
54 | + onChange?.(state); | |
55 | + }, [state]); | |
56 | + | |
57 | + useEffect(() => { | |
58 | + onChange?.({ expression: '', sqlType: 'AND' }); | |
59 | + }, []); | |
60 | + | |
61 | + return ( | |
62 | + <div className={prefix}> | |
63 | + <div className={`${prefix}__header`}> | |
64 | + <span className={`${prefix}__title`}>添加组合方式</span> | |
65 | + <Select | |
66 | + value={state.sqlType || value?.sqlType} | |
67 | + bordered={false} | |
68 | + className={`${prefix}__select`} | |
69 | + options={options} | |
70 | + onChange={(value) => { | |
71 | + setState({ | |
72 | + sqlType: value, | |
73 | + expression: | |
74 | + value !== 'CUSTOM' | |
75 | + ? `1 ${value.toLocaleLowerCase()} 2` | |
76 | + : state.expression, | |
77 | + }); | |
78 | + }} | |
79 | + /> | |
80 | + </div> | |
81 | + {state.sqlType === 'CUSTOM' ? ( | |
82 | + <div className={`${prefix}__input`}> | |
83 | + <Input.TextArea | |
84 | + className={cx(`${prefix}__input-textarea`, 'qx-input')} | |
85 | + defaultValue={state.expression} | |
86 | + onChange={handleInputValueChange} | |
87 | + onBlur={(e) => { | |
88 | + handleInputValueChange(e); | |
89 | + onBlur?.({ ...state, expression: e.target.value }); | |
90 | + }} | |
91 | + placeholder="请使用序号、小括号(英文)、逻辑符(and,or) 编写表达式例如:(1 and 2) or 3" | |
92 | + /> | |
93 | + </div> | |
94 | + ) : null} | |
95 | + </div> | |
96 | + ); | |
97 | +}; | |
98 | + | |
99 | +interface ConditionSqlProps { | |
100 | + onChange?: (val: ConditionSqlState) => void; | |
101 | + onBlur?: (val: ConditionSqlState) => void; | |
102 | + value?: ConditionSqlState; | |
103 | +} | |
104 | + | |
105 | +export interface ConditionSqlState { | |
106 | + expression?: string; | |
107 | + sqlType: SqlType; | |
108 | +} | |
109 | + | |
110 | +export type SqlType = 'AND' | 'OR' | 'CUSTOM'; | ... | ... |
src/qx-condition/index.less
0 → 100644
src/qx-condition/index.md
0 → 100644
1 | +--- | |
2 | +nav: | |
3 | + path: /component | |
4 | + title: 组件 | |
5 | + order: 1 | |
6 | +group: | |
7 | + path: /common | |
8 | + title: 条件配置 | |
9 | + order: 0 | |
10 | +--- | |
11 | + | |
12 | +## QxBaseCondition 条件配置 | |
13 | + | |
14 | +### 条件配置 | |
15 | + | |
16 | +```tsx | |
17 | +import { QxCondition } from '@qx/common'; | |
18 | +import { Form } from 'antd'; | |
19 | + | |
20 | +export default () => { | |
21 | + return ( | |
22 | + <div> | |
23 | + <Form | |
24 | + initialValues={{ | |
25 | + condition: { | |
26 | + sqlType: 'AND', | |
27 | + operators: [ | |
28 | + { | |
29 | + type: 'STRING', | |
30 | + name: '文本', | |
31 | + mappingValues: [], | |
32 | + field: { | |
33 | + code: 'text', | |
34 | + name: '文本', | |
35 | + extract: { widget: 'qxInput', fieldType: 'STRING' }, | |
36 | + }, | |
37 | + }, | |
38 | + { | |
39 | + type: 'STRING', | |
40 | + name: '文本', | |
41 | + mappingValues: [], | |
42 | + field: { | |
43 | + code: 'text', | |
44 | + name: '文本', | |
45 | + extract: { widget: 'qxInput', fieldType: 'STRING' }, | |
46 | + }, | |
47 | + }, | |
48 | + ], | |
49 | + }, | |
50 | + }} | |
51 | + onValuesChange={(value, values) => { | |
52 | + console.log('values', values) | |
53 | + }} | |
54 | + > | |
55 | + <Form.Item name="condition"> | |
56 | + <QxCondition showAssignment={false} /> | |
57 | + </Form.Item> | |
58 | + </Form> | |
59 | + </div> | |
60 | + ); | |
61 | +}; | |
62 | +``` | |
63 | + | |
64 | +<API id="QxCondition"></API> | ... | ... |
src/qx-condition/index.tsx
0 → 100644
1 | +import React, { useEffect } from 'react'; | |
2 | +import { | |
3 | + QxBaseCondition, | |
4 | + QxBaseConditionValueType, | |
5 | +} from '../qx-base-condition'; | |
6 | +import { QxConditionSql, SqlType } from '../qx-condition-sql'; | |
7 | +import { INode } from '../qx-flow-node-selector'; | |
8 | + | |
9 | +export const QxCondition: React.FC<QxConditionProps> = ({ | |
10 | + value, | |
11 | + className, | |
12 | + style, | |
13 | + enabled = true, | |
14 | + showAssignment = true, | |
15 | + onChange, | |
16 | + node, | |
17 | + nodes | |
18 | +}) => { | |
19 | + const handleChange = (opt: Partial<QxConditionValueType>) => { | |
20 | + onChange?.(Object.assign({}, value, opt, { enabled })); | |
21 | + }; | |
22 | + | |
23 | + return ( | |
24 | + <div className={`qx-condition ${className}`} style={style}> | |
25 | + <QxBaseCondition | |
26 | + mode="condition" | |
27 | + nodes={nodes} | |
28 | + node={node} | |
29 | + showAssignment={showAssignment} | |
30 | + value={value?.operators} | |
31 | + onChange={(condition) => { | |
32 | + handleChange({ | |
33 | + operators: condition, | |
34 | + }); | |
35 | + }} | |
36 | + /> | |
37 | + | |
38 | + {(value?.operators?.length || 0) >= 2 ? ( | |
39 | + <QxConditionSql value={value} onChange={handleChange} /> | |
40 | + ) : null} | |
41 | + </div> | |
42 | + ); | |
43 | +}; | |
44 | + | |
45 | +export interface QxConditionValueType { | |
46 | + /** | |
47 | + * 是否启用条件 | |
48 | + */ | |
49 | + enabled?: boolean; | |
50 | + /** | |
51 | + * 条件组合类型 | |
52 | + */ | |
53 | + sqlType: SqlType; | |
54 | + /** | |
55 | + * 自定义条件表达式 | |
56 | + */ | |
57 | + expression: string; | |
58 | + /** | |
59 | + * 算子 | |
60 | + */ | |
61 | + operators: OperatorsType[]; | |
62 | +} | |
63 | + | |
64 | +export type OperatorsType = QxBaseConditionValueType; | |
65 | + | |
66 | +export interface QxConditionProps { | |
67 | + [key: string]: any; | |
68 | + value?: QxConditionValueType; | |
69 | + onChange?: (value: QxConditionValueType) => void; | |
70 | + className?: string; | |
71 | + style?: React.CSSProperties; | |
72 | + node?: INode | |
73 | + nodes?: INode[] | |
74 | + showAssignment?: boolean | |
75 | +} | ... | ... |
... | ... | @@ -95,7 +95,6 @@ export const useNodeFieldDisplay = ({ |
95 | 95 | ) || [], |
96 | 96 | ); |
97 | 97 | |
98 | - // const [targetParentNodes, setTargetParentNodes] = useState(sourceParentNodes) | |
99 | 98 | const [inputDisplay, setInputDisplay] = useState<React.ReactNode>(); |
100 | 99 | const [optionalNodes, setOptionalNodes] = useState<INode[]>([]); // 根据 fieldType 过滤后的 nodes |
101 | 100 | |
... | ... | @@ -109,9 +108,9 @@ export const useNodeFieldDisplay = ({ |
109 | 108 | return null; |
110 | 109 | }; |
111 | 110 | |
112 | - const genDisplayDom = (value: string, nodes: INode[]) => { | |
111 | + const getDisplayConfig = (value: string, nodes: INode[] = optionalNodes) => { | |
113 | 112 | const itemId = getId(value); |
114 | - if (!itemId) return; | |
113 | + if (!itemId) return []; | |
115 | 114 | let displayConfig: any[] = []; |
116 | 115 | let n = true; |
117 | 116 | let index = 0; |
... | ... | @@ -151,6 +150,12 @@ export const useNodeFieldDisplay = ({ |
151 | 150 | n = false; |
152 | 151 | } |
153 | 152 | } |
153 | + | |
154 | + return displayConfig | |
155 | + } | |
156 | + | |
157 | + const genDisplayDom = (value: string, nodes: INode[] = optionalNodes) => { | |
158 | + const displayConfig = getDisplayConfig(value, nodes) | |
154 | 159 | return ( |
155 | 160 | <> |
156 | 161 | {displayConfig?.map((item, idx) => ( |
... | ... | @@ -185,7 +190,10 @@ export const useNodeFieldDisplay = ({ |
185 | 190 | ); |
186 | 191 | }; |
187 | 192 | |
188 | - const renderInputDisplay = (nodes: INode[] = optionalNodes, val: string = value || '', ) => { | |
193 | + const renderInputDisplay = ( | |
194 | + nodes: INode[] = optionalNodes, | |
195 | + val: string = value || '', | |
196 | + ) => { | |
189 | 197 | setInputDisplay( |
190 | 198 | <Tag bordered={false} className="qx-node-select-input__content"> |
191 | 199 | {genDisplayDom(val, nodes)} |
... | ... | @@ -345,8 +353,8 @@ export const useNodeFieldDisplay = ({ |
345 | 353 | return { |
346 | 354 | genDisplayDom, |
347 | 355 | optionalNodes, |
348 | - // targetParentNodes, | |
349 | 356 | renderInputDisplay, |
357 | + getDisplayConfig, | |
350 | 358 | inputDisplay, |
351 | 359 | }; |
352 | 360 | }; | ... | ... |