Commit ab23505331d7c6bcbd8dda1aebea65b7da286bc7

Authored by qiang.tian
1 parent 162a87b6

refactor: base-condition

1 @import '~@qx/ui/src/style/variable.less'; 1 @import '~@qx/ui/src/style/variable.less';
2 2
3 .qx-base-condition-item { 3 .qx-base-condition-item {
  4 + margin-bottom: 16px;
  5 + flex-grow: 1;
  6 +
4 &__header { 7 &__header {
5 display: flex; 8 display: flex;
6 align-items: center; 9 align-items: center;
7 justify-content: space-between; 10 justify-content: space-between;
8 font-size: 14px; 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 &__header-icon { 18 &__header-icon {
@@ -28,7 +34,7 @@ @@ -28,7 +34,7 @@
28 color: @N8; 34 color: @N8;
29 35
30 &:hover { 36 &:hover {
31 - color: @E3 37 + color: @E3;
32 } 38 }
33 } 39 }
34 40
@@ -44,7 +50,35 @@ @@ -44,7 +50,35 @@
44 display: flex; 50 display: flex;
45 align-items: center; 51 align-items: center;
46 justify-content: center; 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 import { ControlOutlined } from '@ant-design/icons'; 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 import React, { useState } from 'react'; 4 import React, { useState } from 'react';
4 import type { QxBaseConditionField } from '../qx-base-condition'; 5 import type { QxBaseConditionField } from '../qx-base-condition';
5 import { QxBaseIcon } from '../qx-base-icon'; 6 import { QxBaseIcon } from '../qx-base-icon';
@@ -7,6 +8,18 @@ import { QxFieldSetter } from '../qx-field-setter'; @@ -7,6 +8,18 @@ import { QxFieldSetter } from '../qx-field-setter';
7 8
8 import './index.less'; 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,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 export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => { 669 export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => {
640 let iconType: string = ''; 670 let iconType: string = '';
641 switch (widgetName) { 671 switch (widgetName) {
@@ -762,15 +792,16 @@ export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => { @@ -762,15 +792,16 @@ export const WidgetsIcon = ({ widgetName }: { widgetName: string }) => {
762 792
763 export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ 793 export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({
764 value, 794 value,
765 - isMultiple = false,  
766 - isRange = false,  
767 field, 795 field,
768 remove, 796 remove,
769 onChange, 797 onChange,
  798 + mode = 'condition',
770 ValueAssignmentPopup, 799 ValueAssignmentPopup,
771 }) => { 800 }) => {
772 const [CustomValueRender, setCustomValueRender] = 801 const [CustomValueRender, setCustomValueRender] =
773 - useState<React.ReactNode>(null); 802 + useState<() => React.ReactNode>();
  803 +
  804 + const [open, setOpen] = useState(false);
774 805
775 const handleChange = (val: any) => { 806 const handleChange = (val: any) => {
776 onChange?.({ 807 onChange?.({
@@ -781,40 +812,89 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ @@ -781,40 +812,89 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({
781 }; 812 };
782 813
783 const handleFilerChange = (val: string) => { 814 const handleFilerChange = (val: string) => {
784 - onChange?.({ 815 + const newValue = {
785 ...(value || {}), 816 ...(value || {}),
786 opt: val, 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 const handleDefaultValueSettingChange = ( 844 const handleDefaultValueSettingChange = (
791 val: any, 845 val: any,
792 - valueRender: React.ReactNode, 846 + valueRender: () => React.ReactNode,
793 ) => { 847 ) => {
794 handleChange(val); 848 handleChange(val);
795 - setCustomValueRender(valueRender); 849 + setCustomValueRender(() => valueRender);
796 }; 850 };
797 851
  852 + const handleOpenChange = setOpen;
  853 +
798 const RenderContent = ( 854 const RenderContent = (
799 <> 855 <>
800 - {CustomValueRender && ( 856 + {typeof CustomValueRender === 'function' && (
801 <div className="qx-base-condition-item__content-value-render"> 857 <div className="qx-base-condition-item__content-value-render">
802 - {CustomValueRender} 858 + {CustomValueRender()}
803 </div> 859 </div>
804 )} 860 )}
805 <QxFieldSetter 861 <QxFieldSetter
806 value={value?.valuesObj} 862 value={value?.valuesObj}
807 fieldGroupType={value?.fieldGroupType} 863 fieldGroupType={value?.fieldGroupType}
808 onChange={handleChange} 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 return ( 898 return (
819 <div className="qx-base-condition-item"> 899 <div className="qx-base-condition-item">
820 <div className="qx-base-condition-item__header"> 900 <div className="qx-base-condition-item__header">
@@ -828,14 +908,7 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ @@ -828,14 +908,7 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({
828 </div> 908 </div>
829 <div className="qx-base-condition-item__header-right"> 909 <div className="qx-base-condition-item__header-right">
830 <span className="qx-base-condition-item__header-filter"> 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 </span> 912 </span>
840 <span className="qx-base-condition-item__header-delete"> 913 <span className="qx-base-condition-item__header-delete">
841 <Tooltip title="删除"> 914 <Tooltip title="删除">
@@ -848,12 +921,13 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ @@ -848,12 +921,13 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({
848 </div> 921 </div>
849 </div> 922 </div>
850 <div className="qx-base-condition-item__content"> 923 <div className="qx-base-condition-item__content">
851 - {ValueAssignmentPopup ? ( 924 + {typeof ValueAssignmentPopup !== 'undefined' ? (
852 <ValueAssignmentPopup 925 <ValueAssignmentPopup
  926 + open={open}
853 value={value} 927 value={value}
854 - isMultiple={isMultiple}  
855 field={field} 928 field={field}
856 onChange={handleDefaultValueSettingChange} 929 onChange={handleDefaultValueSettingChange}
  930 + onOpenChange={handleOpenChange}
857 > 931 >
858 {RenderContent} 932 {RenderContent}
859 </ValueAssignmentPopup> 933 </ValueAssignmentPopup>
@@ -868,15 +942,16 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({ @@ -868,15 +942,16 @@ export const QxBaseConditionItem: React.FC<QxBaseConditionItemProps> = ({
868 export interface ValueAssignmentPopupProps 942 export interface ValueAssignmentPopupProps
869 extends Omit<QxBaseConditionItemProps, 'onChange'> { 943 extends Omit<QxBaseConditionItemProps, 'onChange'> {
870 children?: React.ReactNode; 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 export interface QxBaseConditionItemProps { 950 export interface QxBaseConditionItemProps {
875 field: QxBaseConditionField; 951 field: QxBaseConditionField;
876 value?: any; 952 value?: any;
877 - isMultiple?: boolean;  
878 - isRange?: boolean;  
879 onChange?: (val: any) => void; 953 onChange?: (val: any) => void;
880 remove?: (field: QxBaseConditionField) => void; 954 remove?: (field: QxBaseConditionField) => void;
881 ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps>; 955 ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps>;
  956 + mode?: string;
882 } 957 }
1 @import '~@qx/ui/src/style/variable.less'; 1 @import '~@qx/ui/src/style/variable.less';
2 2
3 -.qx-condition-list { 3 +.qx-base-condition-list {
  4 +
4 &-item { 5 &-item {
5 display: flex; 6 display: flex;
6 align-items: center; 7 align-items: center;
7 8
  9 +
8 &__idx { 10 &__idx {
9 color: @N9; 11 color: @N9;
10 margin-right: 4px; 12 margin-right: 4px;
@@ -12,9 +14,4 @@ @@ -12,9 +14,4 @@
12 display: inline-block; 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,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 <API id="QxBaseCondition"></API> 122 <API id="QxBaseCondition"></API>
@@ -9,11 +9,9 @@ export enum FieldBaseType { @@ -9,11 +9,9 @@ export enum FieldBaseType {
9 YEAR_SEC = 'DATE', 9 YEAR_SEC = 'DATE',
10 } 10 }
11 11
12 -export const rangeType = ['DOUBLE']  
13 -export const multipleType = ['STRING']  
14 12
15 export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { 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 const getDefaultConditionOptions = (item: QxBaseConditionField) => ({ 16 const getDefaultConditionOptions = (item: QxBaseConditionField) => ({
19 ...item, 17 ...item,
@@ -38,6 +36,7 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { @@ -38,6 +36,7 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => {
38 const handleDelete = (key: number) => { 36 const handleDelete = (key: number) => {
39 localOptions.splice(key, 1); 37 localOptions.splice(key, 1);
40 setLocalOptions([...localOptions]); 38 setLocalOptions([...localOptions]);
  39 + props.onChange?.([...localOptions])
41 }; 40 };
42 41
43 useEffect(() => { 42 useEffect(() => {
@@ -45,29 +44,28 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => { @@ -45,29 +44,28 @@ export const QxBaseCondition: React.FC<QxBaseConditionProps> = (props) => {
45 }, [props.options]); 44 }, [props.options]);
46 45
47 const RenderCondition = localOptions?.map((item, key) => ( 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 {(props?.showIdx ?? true) && ( 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 <QxBaseConditionItem 51 <QxBaseConditionItem
53 key={item.code || key} 52 key={item.code || key}
54 - isMultiple={multipleType.includes(item.fieldType)}  
55 - isRange={rangeType.includes(item.fieldType)} 53 + mode={props.mode}
56 {...item} 54 {...item}
57 value={props.value?.[key] || getDefaultConditionOptions(item)} 55 value={props.value?.[key] || getDefaultConditionOptions(item)}
58 field={item} 56 field={item}
59 onChange={(val) => handleItemChange(val, key)} 57 onChange={(val) => handleItemChange(val, key)}
60 remove={() => handleDelete(key)} 58 remove={() => handleDelete(key)}
61 ValueAssignmentPopup={ 59 ValueAssignmentPopup={
62 - item.showValueAssignmentPopup ? props.ValueAssignmentPopup : undefined 60 + (item.showValueAssignmentPopup ?? true) ? props.ValueAssignmentPopup : undefined
63 } 61 }
64 /> 62 />
65 </div> 63 </div>
66 )); 64 ));
67 65
68 return ( 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 </div> 69 </div>
72 ); 70 );
73 }; 71 };
@@ -76,6 +74,7 @@ export interface QxBaseConditionField { @@ -76,6 +74,7 @@ export interface QxBaseConditionField {
76 field: string; 74 field: string;
77 fieldType: string; 75 fieldType: string;
78 fieldName: string; 76 fieldName: string;
  77 + fieldGroupType: string;
79 qxProps?: string; 78 qxProps?: string;
80 extract?: any; 79 extract?: any;
81 } 80 }
@@ -87,10 +86,13 @@ export interface QxBaseConditionOptionsProps extends QxBaseConditionField { @@ -87,10 +86,13 @@ export interface QxBaseConditionOptionsProps extends QxBaseConditionField {
87 [key: string]: any; 86 [key: string]: any;
88 } 87 }
89 88
  89 +export type QxBaseConditionMode = 'condition' | 'variable'
  90 +
90 export interface QxBaseConditionProps { 91 export interface QxBaseConditionProps {
91 showIdx?: boolean; 92 showIdx?: boolean;
  93 + mode: QxBaseConditionMode;
92 options: QxBaseConditionOptionsProps[]; 94 options: QxBaseConditionOptionsProps[];
93 value: any[]; 95 value: any[];
94 onChange?: (val: any[]) => void; 96 onChange?: (val: any[]) => void;
95 - ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps> 97 + ValueAssignmentPopup?: React.FC<ValueAssignmentPopupProps>;
96 } 98 }