Commit ab23505331d7c6bcbd8dda1aebea65b7da286bc7

Authored by qiang.tian
1 parent 162a87b6

refactor: base-condition

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 }
... ...