Showing
5 changed files
with
223 additions
and
47 deletions
1 | 1 | @import '~@qx/ui/src/style/variable.less'; |
2 | 2 | |
3 | 3 | .qx-base-condition-item { |
4 | + margin-bottom: 16px; | |
5 | + flex-grow: 1; | |
6 | + | |
4 | 7 | &__header { |
5 | 8 | display: flex; |
6 | 9 | align-items: center; |
7 | 10 | justify-content: space-between; |
8 | 11 | font-size: 14px; |
9 | - color: @N9 | |
12 | + color: @N9; | |
13 | + height: 22px; | |
14 | + margin-bottom: 4px; | |
15 | + overflow: hidden; | |
10 | 16 | } |
11 | 17 | |
12 | 18 | &__header-icon { |
... | ... | @@ -28,7 +34,7 @@ |
28 | 34 | color: @N8; |
29 | 35 | |
30 | 36 | &:hover { |
31 | - color: @E3 | |
37 | + color: @E3; | |
32 | 38 | } |
33 | 39 | } |
34 | 40 | |
... | ... | @@ -44,7 +50,35 @@ |
44 | 50 | display: flex; |
45 | 51 | align-items: center; |
46 | 52 | justify-content: center; |
47 | - color: @N7 | |
53 | + color: @N7; | |
54 | + } | |
55 | + | |
56 | + &-value-render { | |
57 | + position: absolute; | |
58 | + background-color: #fff; | |
59 | + width: calc(100% - 33px); | |
60 | + height: 30px; | |
61 | + display: flex; | |
62 | + align-items: center; | |
63 | + top: 1px; | |
64 | + left: 8px; | |
65 | + z-index: 1; | |
66 | + } | |
67 | + } | |
68 | + | |
69 | + &__header-filter { | |
70 | + margin-right: 8px; | |
71 | + } | |
72 | + | |
73 | + &__header-right { | |
74 | + transition: all 0.2s ease; | |
75 | + transform: translateX(20px); | |
76 | + text-align: right; | |
77 | + } | |
78 | + | |
79 | + &:hover { | |
80 | + .qx-base-condition-item__header-right { | |
81 | + transform: translateX(0); | |
48 | 82 | } |
49 | 83 | } |
50 | 84 | } | ... | ... |
1 | 1 | import { ControlOutlined } from '@ant-design/icons'; |
2 | -import { Select, Tooltip } from 'antd'; | |
2 | +import { Select, Tooltip, Input } from 'antd'; | |
3 | +import { size } from 'lodash-es'; | |
3 | 4 | import React, { useState } from 'react'; |
4 | 5 | import type { QxBaseConditionField } from '../qx-base-condition'; |
5 | 6 | import { QxBaseIcon } from '../qx-base-icon'; |
... | ... | @@ -7,6 +8,18 @@ import { QxFieldSetter } from '../qx-field-setter'; |
7 | 8 | |
8 | 9 | import './index.less'; |
9 | 10 | |
11 | +export const multipleType = [ | |
12 | + 'STRING', | |
13 | + 'REL_MULTI', | |
14 | + 'ENUM', | |
15 | + 'ENUM_MULTI', | |
16 | + 'TEXT', | |
17 | + // 'TREE', | |
18 | + // 'USER', | |
19 | + 'USER_MULTI', | |
20 | + // 'ORG', | |
21 | + 'ORG_MULTI', | |
22 | +]; | |
10 | 23 | /** |
11 | 24 | * 过滤条件运算符及默认选项 |
12 | 25 | */ |
... | ... | @@ -636,6 +649,23 @@ export const ConditionCol: any = { |
636 | 649 | }, |
637 | 650 | }; |
638 | 651 | |
652 | +const optValTypeCheck = { | |
653 | + // 条件是否为"为空"、"不为空" | |
654 | + isEmptyType: (opt: any) => { | |
655 | + return ['IS_NULL', 'NOT_NULL'].includes(opt || ''); | |
656 | + }, | |
657 | + // 范围类型 | |
658 | + isRangeType: (opt: any) => { | |
659 | + return ['IN', 'NOT_IN'].includes(opt || ''); | |
660 | + }, | |
661 | + // 流程条件多选优化 | |
662 | + isMultiType: (opt: any) => { | |
663 | + return ['BELONG', 'NOT_BELONG', 'INCLUDE', 'NOT_INCLUDE'].includes( | |
664 | + opt || '', | |
665 | + ); | |
666 | + }, | |
667 | +}; | |
668 | + | |
639 | 669 | export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => { |
640 | 670 | let iconType: string = ''; |
641 | 671 | switch (widgetName) { |
... | ... | @@ -762,15 +792,16 @@ export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => { |
762 | 792 | |
763 | 793 | export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
764 | 794 | value, |
765 | - isMultiple = false, | |
766 | - isRange = false, | |
767 | 795 | field, |
768 | 796 | remove, |
769 | 797 | onChange, |
798 | + mode = 'condition', | |
770 | 799 | ValueAssignmentPopup, |
771 | 800 | }) => { |
772 | 801 | const [CustomValueRender, setCustomValueRender] = |
773 | - useState<React.ReactNode>(null); | |
802 | + useState<() => React.ReactNode>(); | |
803 | + | |
804 | + const [open, setOpen] = useState(false); | |
774 | 805 | |
775 | 806 | const handleChange = (val: any) => { |
776 | 807 | onChange?.({ |
... | ... | @@ -781,40 +812,89 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
781 | 812 | }; |
782 | 813 | |
783 | 814 | const handleFilerChange = (val: string) => { |
784 | - onChange?.({ | |
815 | + const newValue = { | |
785 | 816 | ...(value || {}), |
786 | 817 | opt: val, |
787 | - }); | |
818 | + }; | |
819 | + | |
820 | + // "为空"、"不为空"条件时,置空"值" | |
821 | + if (optValTypeCheck.isEmptyType(newValue.opt)) { | |
822 | + newValue.valuesObj = []; | |
823 | + } | |
824 | + | |
825 | + // 切换到非'范围'类型时,删除第2个值 | |
826 | + if (optValTypeCheck.isRangeType(newValue?.opt)) { | |
827 | + newValue.valuesObj = (newValue.valuesObj || []).filter( | |
828 | + (it) => it?.isRange, | |
829 | + ); | |
830 | + } else if ( | |
831 | + optValTypeCheck.isMultiType(newValue?.opt) || | |
832 | + multipleType.includes(newValue.fieldGroupType) | |
833 | + ) { | |
834 | + } else { | |
835 | + const vals = (newValue.valuesObj || []).map((it) => it?.isRange); | |
836 | + if (size(vals) > 1) { | |
837 | + newValue.valuesObj = [newValue.valuesObj[0]]; | |
838 | + } | |
839 | + } | |
840 | + | |
841 | + onChange?.(newValue); | |
788 | 842 | }; |
789 | 843 | |
790 | 844 | const handleDefaultValueSettingChange = ( |
791 | 845 | val: any, |
792 | - valueRender: React.ReactNode, | |
846 | + valueRender: () => React.ReactNode, | |
793 | 847 | ) => { |
794 | 848 | handleChange(val); |
795 | - setCustomValueRender(valueRender); | |
849 | + setCustomValueRender(() => valueRender); | |
796 | 850 | }; |
797 | 851 | |
852 | + const handleOpenChange = setOpen; | |
853 | + | |
798 | 854 | const RenderContent = ( |
799 | 855 | <> |
800 | - {CustomValueRender && ( | |
856 | + {typeof CustomValueRender === 'function' && ( | |
801 | 857 | <div className="qx-base-condition-item__content-value-render"> |
802 | - {CustomValueRender} | |
858 | + {CustomValueRender()} | |
803 | 859 | </div> |
804 | 860 | )} |
805 | 861 | <QxFieldSetter |
806 | 862 | value={value?.valuesObj} |
807 | 863 | fieldGroupType={value?.fieldGroupType} |
808 | 864 | onChange={handleChange} |
809 | - isMultiple={isMultiple} | |
810 | - isRange={isRange} | |
865 | + isMultiple={multipleType.includes(value?.fieldGroupType)} | |
866 | + isRange={optValTypeCheck.isRangeType(value?.opt)} | |
867 | + disabled={optValTypeCheck.isEmptyType(value?.opt)} | |
811 | 868 | /> |
812 | - {ValueAssignmentPopup && ( | |
813 | - <ControlOutlined className="qx-base-condition-item__content-suffix" /> | |
869 | + {typeof ValueAssignmentPopup !== 'undefined' && ( | |
870 | + <ControlOutlined | |
871 | + onClick={() => { | |
872 | + handleOpenChange(!open); | |
873 | + }} | |
874 | + className="qx-base-condition-item__content-suffix" | |
875 | + /> | |
814 | 876 | )} |
815 | 877 | </> |
816 | 878 | ); |
817 | 879 | |
880 | + const RenderFilter = () => { | |
881 | + switch (mode) { | |
882 | + case 'condition': | |
883 | + return ( | |
884 | + <Select | |
885 | + value={value?.opt} | |
886 | + className="qx-base-condition-item__header-filter__select" | |
887 | + bordered={false} | |
888 | + fieldNames={{ label: 'text', value: 'key' }} | |
889 | + options={ConditionCol[value.fieldGroupType]?.operations || []} | |
890 | + onChange={handleFilerChange} | |
891 | + /> | |
892 | + ); | |
893 | + default: | |
894 | + return <a>设为</a>; | |
895 | + } | |
896 | + }; | |
897 | + | |
818 | 898 | return ( |
819 | 899 | <div className="qx-base-condition-item"> |
820 | 900 | <div className="qx-base-condition-item__header"> |
... | ... | @@ -828,14 +908,7 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
828 | 908 | </div> |
829 | 909 | <div className="qx-base-condition-item__header-right"> |
830 | 910 | <span className="qx-base-condition-item__header-filter"> |
831 | - <Select | |
832 | - value={value?.opt} | |
833 | - className="qx-base-condition-item__header-filter__select" | |
834 | - bordered={false} | |
835 | - fieldNames={{ label: 'text', value: 'key' }} | |
836 | - options={ConditionCol[value.fieldGroupType]?.operations || []} | |
837 | - onChange={handleFilerChange} | |
838 | - /> | |
911 | + <RenderFilter /> | |
839 | 912 | </span> |
840 | 913 | <span className="qx-base-condition-item__header-delete"> |
841 | 914 | <Tooltip title="删除"> |
... | ... | @@ -848,12 +921,13 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
848 | 921 | </div> |
849 | 922 | </div> |
850 | 923 | <div className="qx-base-condition-item__content"> |
851 | - {ValueAssignmentPopup ? ( | |
924 | + {typeof ValueAssignmentPopup !== 'undefined' ? ( | |
852 | 925 | <ValueAssignmentPopup |
926 | + open={open} | |
853 | 927 | value={value} |
854 | - isMultiple={isMultiple} | |
855 | 928 | field={field} |
856 | 929 | onChange={handleDefaultValueSettingChange} |
930 | + onOpenChange={handleOpenChange} | |
857 | 931 | > |
858 | 932 | {RenderContent} |
859 | 933 | </ValueAssignmentPopup> |
... | ... | @@ -868,15 +942,16 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ |
868 | 942 | export interface ValueAssignmentPopupProps |
869 | 943 | extends Omit<QxBaseConditionItemProps, 'onChange'> { |
870 | 944 | children?: React.ReactNode; |
871 | - onChange?: (val: any, valueRender: React.ReactNode) => void; | |
945 | + onChange?: (val: any, valueRender: () => React.ReactNode) => void; | |
946 | + open?: boolean; | |
947 | + onOpenChange?: (open: boolean) => void; | |
872 | 948 | } |
873 | 949 | |
874 | 950 | export interface QxBaseConditionItemProps { |
875 | 951 | field: QxBaseConditionField; |
876 | 952 | value?: any; |
877 | - isMultiple?: boolean; | |
878 | - isRange?: boolean; | |
879 | 953 | onChange?: (val: any) => void; |
880 | 954 | remove?: (field: QxBaseConditionField) => void; |
881 | 955 | ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps>; |
956 | + mode?: string; | |
882 | 957 | } | ... | ... |
1 | 1 | @import '~@qx/ui/src/style/variable.less'; |
2 | 2 | |
3 | -.qx-condition-list { | |
3 | +.qx-base-condition-list { | |
4 | + | |
4 | 5 | &-item { |
5 | 6 | display: flex; |
6 | 7 | align-items: center; |
7 | 8 | |
9 | + | |
8 | 10 | &__idx { |
9 | 11 | color: @N9; |
10 | 12 | margin-right: 4px; |
... | ... | @@ -12,9 +14,4 @@ |
12 | 14 | display: inline-block; |
13 | 15 | } |
14 | 16 | } |
15 | - | |
16 | - .qx-base-condition-item { | |
17 | - margin-bottom: 16px; | |
18 | - flex-grow: 1; | |
19 | - } | |
20 | 17 | } | ... | ... |
... | ... | @@ -51,4 +51,72 @@ export default () => { |
51 | 51 | }; |
52 | 52 | ``` |
53 | 53 | |
54 | +```tsx | |
55 | +import { QxBaseCondition } from '@qx/common'; | |
56 | +import { Dropdown, Form, Select, Tag } from 'antd'; | |
57 | + | |
58 | +const ValueAssignmentPopup = (props) => { | |
59 | + const handleChange = (val) => { | |
60 | + props.onChange?.([{ | |
61 | + type: '111', | |
62 | + value: val | |
63 | + }], () => <Tag>111111</Tag>) | |
64 | + } | |
65 | + return ( | |
66 | + <Dropdown | |
67 | + {...props} | |
68 | + dropdownRender={() => ( | |
69 | + <Select | |
70 | + onChange={handleChange} | |
71 | + options={[ | |
72 | + { | |
73 | + label: '标题', | |
74 | + value: 'title', | |
75 | + }, | |
76 | + ]} | |
77 | + /> | |
78 | + )} | |
79 | + > | |
80 | + {props.children} | |
81 | + </Dropdown> | |
82 | + ); | |
83 | +}; | |
84 | + | |
85 | +export default () => { | |
86 | + return ( | |
87 | + <div> | |
88 | + <Form> | |
89 | + <Form.Item name="condition"> | |
90 | + <QxBaseCondition | |
91 | + ValueAssignmentPopup={ValueAssignmentPopup} | |
92 | + showIdx={false} | |
93 | + mode="variable" | |
94 | + options={[ | |
95 | + { | |
96 | + field: 'text', | |
97 | + fieldType: 'STRING', | |
98 | + fieldName: '文本', | |
99 | + extract: { widget: 'qxInput' }, | |
100 | + }, | |
101 | + { | |
102 | + field: 'num_cagoif', | |
103 | + fieldType: 'DOUBLE', | |
104 | + fieldName: '数值', | |
105 | + extract: { widget: 'qxNumber' }, | |
106 | + }, | |
107 | + { | |
108 | + field: 'date_uvpapz', | |
109 | + fieldType: 'YEAR_SEC', | |
110 | + fieldName: '日期', | |
111 | + extract: { widget: 'qxDatetime' }, | |
112 | + }, | |
113 | + ]} | |
114 | + /> | |
115 | + </Form.Item> | |
116 | + </Form> | |
117 | + </div> | |
118 | + ); | |
119 | +}; | |
120 | +``` | |
121 | + | |
54 | 122 | <API id="QxBaseCondition"></API> | ... | ... |
... | ... | @@ -9,11 +9,9 @@ export enum FieldBaseType { |
9 | 9 | YEAR_SEC = 'DATE', |
10 | 10 | } |
11 | 11 | |
12 | -export const rangeType = ['DOUBLE'] | |
13 | -export const multipleType = ['STRING'] | |
14 | 12 | |
15 | 13 | export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { |
16 | - const [localOptions, setLocalOptions] = useState(props.options || []); | |
14 | + const [localOptions, setLocalOptions] = useState(props.value || props.options || []); | |
17 | 15 | |
18 | 16 | const getDefaultConditionOptions = (item: QxBaseConditionField) => ({ |
19 | 17 | ...item, |
... | ... | @@ -38,6 +36,7 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { |
38 | 36 | const handleDelete = (key: number) => { |
39 | 37 | localOptions.splice(key, 1); |
40 | 38 | setLocalOptions([...localOptions]); |
39 | + props.onChange?.([...localOptions]) | |
41 | 40 | }; |
42 | 41 | |
43 | 42 | useEffect(() => { |
... | ... | @@ -45,29 +44,28 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { |
45 | 44 | }, [props.options]); |
46 | 45 | |
47 | 46 | const RenderCondition = localOptions?.map((item, key) => ( |
48 | - <div className="qx-condition-list-item" key={item.code || key}> | |
47 | + <div className="qx-base-condition-list-item" key={item.code || key}> | |
49 | 48 | {(props?.showIdx ?? true) && ( |
50 | - <span className="qx-condition-list-item__idx">{key + 1}.</span> | |
49 | + <span className="qx-base-condition-list-item__idx">{key + 1}.</span> | |
51 | 50 | )} |
52 | 51 | <QxBaseConditionItem |
53 | 52 | key={item.code || key} |
54 | - isMultiple={multipleType.includes(item.fieldType)} | |
55 | - isRange={rangeType.includes(item.fieldType)} | |
53 | + mode={props.mode} | |
56 | 54 | {...item} |
57 | 55 | value={props.value?.[key] || getDefaultConditionOptions(item)} |
58 | 56 | field={item} |
59 | 57 | onChange={(val) => handleItemChange(val, key)} |
60 | 58 | remove={() => handleDelete(key)} |
61 | 59 | ValueAssignmentPopup={ |
62 | - item.showValueAssignmentPopup ? props.ValueAssignmentPopup : undefined | |
60 | + (item.showValueAssignmentPopup ?? true) ? props.ValueAssignmentPopup : undefined | |
63 | 61 | } |
64 | 62 | /> |
65 | 63 | </div> |
66 | 64 | )); |
67 | 65 | |
68 | 66 | return ( |
69 | - <div className="qx-condition"> | |
70 | - <div className="qx-condition-list">{RenderCondition}</div> | |
67 | + <div className="qx-base-condition"> | |
68 | + <div className="qx-base-condition-list">{RenderCondition}</div> | |
71 | 69 | </div> |
72 | 70 | ); |
73 | 71 | }; |
... | ... | @@ -76,6 +74,7 @@ export interface QxBaseConditionField { |
76 | 74 | field: string; |
77 | 75 | fieldType: string; |
78 | 76 | fieldName: string; |
77 | + fieldGroupType: string; | |
79 | 78 | qxProps?: string; |
80 | 79 | extract?: any; |
81 | 80 | } |
... | ... | @@ -87,10 +86,13 @@ export interface QxBaseConditionOptionsProps extends QxBaseConditionField { |
87 | 86 | [key: string]: any; |
88 | 87 | } |
89 | 88 | |
89 | +export type QxBaseConditionMode = 'condition' | 'variable' | |
90 | + | |
90 | 91 | export interface QxBaseConditionProps { |
91 | 92 | showIdx?: boolean; |
93 | + mode: QxBaseConditionMode; | |
92 | 94 | options: QxBaseConditionOptionsProps[]; |
93 | 95 | value: any[]; |
94 | 96 | onChange?: (val: any[]) => void; |
95 | - ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps> | |
97 | + ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps>; | |
96 | 98 | } | ... | ... |