lodash#reduce TypeScript Examples

The following examples show how to use lodash#reduce. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From erda-ui with GNU Affero General Public License v3.0 7 votes vote down vote up
getNodeById = ({ treeData, id, recycled = true }: IById): TEST_SET.TestSetNode | any =>
  reduce(
    treeData,
    (result, single: any) => {
      if (result) {
        // 已经查出来了
        return result;
      }
      if (id === single.id && recycled === single.recycled) {
        // 找到了
        return single;
      }
      // 继续在子级查找
      return getNodeById({ treeData: single.children, id, recycled });
    },
    null,
  )
Example #2
Source File: utils.ts    From erda-ui with GNU Affero General Public License v3.0 7 votes vote down vote up
getNodeByPath = ({ treeData, eventKey, valueKey }: IFunc): any => {
  if (!eventKey) {
    return '';
  }
  const list = eventKey.split('-').join(' children ').split(' ');
  if (valueKey) list.push(valueKey);
  return reduce(
    list,
    (result, singleKey) => {
      if (!result) {
        // 第一次
        const firstKey = get(treeData, [0, 'key']);
        if (firstKey === singleKey) {
          return get(treeData, [0]);
        }
      } else if (singleKey === 'children') {
        // 子节点则继续返回
        return get(result, ['children']);
      } else if (isArray(result)) {
        // 获取children中匹配的node
        return find(result, ({ id }: any) => `${id}` === singleKey);
      }
      // 最终单个节点的某个字段,比如key、title、id等
      return get(result, [singleKey]);
    },
    '',
  );
}
Example #3
Source File: headerUtil.ts    From S2 with MIT License 6 votes vote down vote up
generateSheetConfig = (
  sheet: SpreadSheet,
  result: SwitcherResult,
) => {
  // 交叉表需要过滤掉被隐藏的字段,而明细表不需要,明细表需要将隐藏的字段通过hiddenColumnFields返回给options
  const isTableSheet = sheet instanceof TableSheet;

  const fields = SWITCHER_FIELDS.reduce((fields, fieldKey) => {
    const items = result[fieldKey]?.items ?? [];
    const hideItems = result[fieldKey]?.hideItems ?? [];

    fields[fieldKey] = filter(
      items,
      (item) => !hideItems.find((hide) => !isTableSheet && hide.id === item.id),
    ).map((i) => i.id);

    return fields;
  }, {} as Fields);

  const hiddenColumnFields = isTableSheet
    ? result[FieldType.Cols].hideItems.map((i) => i.id)
    : undefined;

  return { fields, hiddenColumnFields };
}
Example #4
Source File: api-editor.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getConf = (data: Record<string, any>, key: string | string[]) => {
  return isArray(key)
    ? reduce(
        key,
        (obj, k) => {
          return { ...obj, [k]: data[k] };
        },
        {},
      )
    : data[key];
}
Example #5
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
topNMap = reduce(
  topNConfig,
  (prev, next) => {
    return {
      ...prev,
      [`service-list@${next.key}`]: {
        op: {
          clickRow: (item: CP_DATA_RANK.IItem) => {
            listDetail(item.id, item.name);
          },
        },
        props: {
          theme: [
            {
              titleIcon: next.icon,
              color: next.color,
            },
          ],
        },
      },
    };
  },
  {},
)
Example #6
Source File: overview.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
topNMap = reduce(
  topNConfig,
  (prev, next) => {
    return {
      ...prev,
      [`service-overview@${next.key}`]: {
        props: {
          theme: [
            {
              titleIcon: next.icon,
              color: next.color,
            },
          ],
        },
      },
    };
  },
  {},
)
Example #7
Source File: paramsSerializer.ts    From linkedin-private-api with MIT License 6 votes vote down vote up
paramsSerializer = (params: Record<string, string | Record<string, string>>): string => {
  const encodedParams = mapValues(params, value => {
    if (!isArray(value) && !isPlainObject(value)) {
      return value.toString();
    }

    if (isArray(value)) {
      return `List(${value.join(',')})`;
    }

    const encodedList = reduce(
      value as Record<string, string>,
      (res, filterVal, filterKey) => `${res}${res ? ',' : ''}${encodeFilter(filterVal, filterKey)}`,
      '',
    );

    return `List(${encodedList})`;
  });

  return stringify(encodedParams, undefined, undefined, {
    encodeURIComponent: uri => uri,
  });
}
Example #8
Source File: apiConfig.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
multiGroupsAndDimensionDataHandler =
  (dataKeys: string[], tagLength?: number) => (originData: IOriginData) => {
    if (isEmpty(originData)) return {};
    const { time = [], results = [] } = originData || {};
    const data = get(results, '[0].data') || [];
    const parsedData = reduce(
      data,
      (result: any, value) => {
        const reData: any[] = [];
        const ip = value[dataKeys[0]].tag.slice(0, tagLength || 8);
        forEach(dataKeys, (item) => {
          const dataItem: any = value[item];
          if (dataItem) {
            dataItem.tag = dataItem.name;
            reData.push(dataItem);
          }
        });
        return { ...result, [ip]: reData };
      },
      {},
    );

    return { time, results: parsedData };
  }
Example #9
Source File: login.ts    From linkedin-private-api with MIT License 6 votes vote down vote up
private setRequestHeaders({ cookies }: { cookies: AuthCookies }): void {
    const cookieStr = reduce(cookies, (res, v, k) => `${res}${k}="${v}"; `, '');

    this.client.request.setHeaders({
      ...requestHeaders,
      cookie: cookieStr,
      'csrf-token': cookies.JSESSIONID!,
    });
  }
Example #10
Source File: headerUtil.ts    From S2 with MIT License 6 votes vote down vote up
getSwitcherFields = (result: SwitcherResult) => {
  return reduce(
    result,
    (cfg, value, field) => {
      cfg.fields[field] = map(value.items, 'id');
      cfg.hiddenFields.push(...map(value.hideItems, 'id'));
      return cfg;
    },
    {
      fields: {},
      hiddenFields: [],
    } as { fields: Fields; hiddenFields: string[] },
  );
}
Example #11
Source File: headerUtil.ts    From S2 with MIT License 6 votes vote down vote up
generateSwitcherFields = (
  sheet: SpreadSheet,
  { fields = {}, meta = [] } = {} as Pick<S2DataConfig, 'fields' | 'meta'>,
  hiddenColumnFields: string[] = [],
) => {
  return SWITCHER_FIELDS.reduce((config, fieldKey) => {
    const values = fields[fieldKey];
    if (isEmpty(values)) {
      return config;
    }
    const items = map(values, (id) => {
      const target = find(meta, ['field', id]);
      return {
        id,
        displayName: target?.name,
        checked: !hiddenColumnFields.includes(id),
      };
    });

    config[fieldKey] = { items, ...getSwitcherFieldCfg(sheet, fieldKey) };
    return config;
  }, {} as SwitcherFields);
}
Example #12
Source File: pivot-data-set.ts    From S2 with MIT License 6 votes vote down vote up
/**
 * 获取查询结果中的纬度值
 * @param dimensions [province, city]
 * @param query { province: '四川省', city: '成都市', type: '办公用品' }
 * @returns ['四川省', '成都市']
 */
export function getQueryDimValues(
  dimensions: string[],
  query: DataType,
): string[] {
  return reduce(
    dimensions,
    (res: string[], dimension: string) => {
      // push undefined when not exist
      res.push(query[dimension]);
      return res;
    },
    [],
  );
}
Example #13
Source File: data-set-operate.ts    From S2 with MIT License 6 votes vote down vote up
flattenDeep = (data: Record<any, any>[] | Record<any, any>) =>
  keys(data)?.reduce((pre, next) => {
    const item = get(data, next);
    if (Array.isArray(item)) {
      pre = pre.concat(flattenDeep(item));
    } else {
      pre?.push(item);
    }

    return pre;
  }, [])
Example #14
Source File: data-set-operate.ts    From S2 with MIT License 6 votes vote down vote up
/**
 * split total data from origin list data.
 */
export function splitTotal(rawData: Data[], fields: Fields): Data[] {
  const { rows, columns } = fields;

  return reduce(
    rawData,
    (result: Data[], data: Data) => {
      if (isTotalData([].concat(rows).concat(columns), data)) {
        result.push(data);
      }
      return result;
    },
    [],
  );
}
Example #15
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
public getViewCellHeights(layoutResult: LayoutResult) {
    const { rowLeafNodes } = layoutResult;

    const heights = reduce(
      rowLeafNodes,
      (result: number[], node: Node) => {
        result.push(last(result) + node.height);
        return result;
      },
      [0],
    );

    return {
      getTotalHeight: () => {
        return last(heights);
      },

      getCellOffsetY: (index: number) => {
        return heights[index];
      },

      getTotalLength: () => {
        // 多了一个数据 [0]
        return heights.length - 1;
      },

      getIndexRange: (minHeight: number, maxHeight: number) => {
        return getIndexRangeWithOffsets(heights, minHeight, maxHeight);
      },
    };
  }
Example #16
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
/**
   * @description Auto calculate row no-leaf node's height and y coordinate
   * @param rowLeafNodes
   */
  private autoCalculateRowNodeHeightAndY(rowLeafNodes: Node[]) {
    // 3、in grid type, all no-leaf node's height, y are auto calculated
    let prevRowParent = null;
    const leafNodes = rowLeafNodes.slice(0);
    while (leafNodes.length) {
      const node = leafNodes.shift();
      const parent = node.parent;
      if (prevRowParent !== parent && parent) {
        leafNodes.push(parent);
        // parent's y = first child's y
        parent.y = parent.children[0].y;
        // parent's height = all children's height
        parent.height = parent.children
          .map((value) => value.height)
          .reduce((sum, current) => sum + current, 0);
        prevRowParent = parent;
      }
    }
  }
Example #17
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
/**
   * Auto Auto Auto column no-leaf node's width and x coordinate
   * @param colLeafNodes
   */
  private autoCalculateColNodeWidthAndX(colLeafNodes: Node[]) {
    let prevColParent = null;
    const leafNodes = colLeafNodes.slice(0);
    while (leafNodes.length) {
      const node = leafNodes.shift();
      const parent = node.parent;
      if (prevColParent !== parent && parent) {
        leafNodes.push(parent);
        // parent's x = first child's x
        parent.x = parent.children[0].x;
        // parent's width = all children's width
        parent.width = parent.children
          .map((value: Node) => value.width)
          .reduce((sum, current) => sum + current, 0);
        prevColParent = parent;
      }
    }
  }
Example #18
Source File: base-facet.ts    From S2 with MIT License 6 votes vote down vote up
calculateCellWidthHeight = () => {
    const { colLeafNodes } = this.layoutResult;
    const widths = reduce(
      colLeafNodes,
      (result: number[], node: Node) => {
        result.push(last(result) + node.width);
        return result;
      },
      [0],
    );

    this.viewCellWidths = widths;
    this.viewCellHeights = this.getViewCellHeights(this.layoutResult);
  };
Example #19
Source File: semantic-model-provider.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
// Load the library files from the file system.
// To save the libraries to the file system use downloadLibraries.
function loadLibraries(version: TestModelVersion): Record<string, Json> {
  const inputFolder = getModelFolder(version);
  const files = readdirSync(inputFolder);
  const LIBFILE_SUFFIX = ".designtime.api.json";
  const libFiles = filter(files, (_) => _.endsWith(LIBFILE_SUFFIX));
  const libToFileContent = reduce(
    libFiles,
    (libToFileContentMap, file) => {
      const libName = file.substring(0, file.length - LIBFILE_SUFFIX.length);
      libToFileContentMap[libName] = readJsonSync(resolve(inputFolder, file));
      return libToFileContentMap;
    },
    Object.create(null)
  );
  return libToFileContent;
}
Example #20
Source File: convert.ts    From ui5-language-assistant with Apache License 2.0 5 votes vote down vote up
export function convertToSemanticModel(
  libraries: Record<string, Json>,
  jsonSymbols: Record<string, apiJson.ConcreteSymbol>,
  strict: boolean,
  printValidationErrors: boolean
): model.UI5SemanticModel {
  const model: model.UI5SemanticModel = {
    version: "",
    includedLibraries: [],
    classes: newMap(),
    enums: newMap(),
    functions: newMap(),
    namespaces: newMap(),
    typedefs: newMap(),
    interfaces: newMap(),
  };

  // Convert to array (with deterministic order) to ensure consistency when inserting to maps
  const libsArray = map(libraries, (fileContent, libraryName) => ({
    libraryName,
    fileContent,
  }));
  const sortedLibs = sortBy(libsArray, "libraryName");
  model.includedLibraries = map(sortedLibs, (_) => _.libraryName);

  reduce(
    sortedLibs,
    (model, { libraryName, fileContent }) => {
      fixLibrary(libraryName, fileContent);
      if (
        isLibraryFile(libraryName, fileContent, strict, printValidationErrors)
      ) {
        const libSemanticModel = convertLibraryToSemanticModel(
          libraryName,
          fileContent,
          jsonSymbols,
          strict
        );
        addLibraryToModel(libSemanticModel, model);
      } else if (strict) {
        throw new Error(`Entry for ${libraryName} is not a valid library file`);
      }
      return model;
    },
    model
  );

  return model;
}
Example #21
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
selectArr = reduce(
  cloudAccountArr,
  (arr: any[], { name, val: value }) => {
    if (name && value) arr.push({ value, name });
    return arr;
  },
  [],
)
Example #22
Source File: table-drawer.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
TableDrawer = (props) => {
  const [tableRowClassNameMap, setTableRowClassNameMap] = React.useState({});

  const initTableRowClassNameMap = (_centerRelatedGroup: any) => {
    const COLOR_KEYS = ['purple', 'pink', 'green', 'purple-2', 'blue', 'red', 'green-2', 'orange'];
    const result = reduce(
      _centerRelatedGroup,
      (acc, value) => ({ ...acc, [COLOR_KEYS.pop() || '']: value.map((item: any) => item.relAttr) }),
      {},
    );
    setTableRowClassNameMap(result);
  };

  const {
    drawerVisible,
    closeDrawer,
    isFetching,
    tableAttrsList,
    tableAttrsPaging,
    centerRelatedGroup,
    getTableAttrs,
    selectedItem,
  } = props;
  const { pageNo: current, total } = tableAttrsPaging;

  React.useEffect(() => {
    centerRelatedGroup && initTableRowClassNameMap(centerRelatedGroup);
    !isEmpty(selectedItem) && getTableAttrs({ filePath: get(selectedItem, 'file'), pageNo: 1 });
  }, [centerRelatedGroup, getTableAttrs, selectedItem]);

  const getRelatedGroupClassName = (enName: string) => {
    return findKey(tableRowClassNameMap, (item: string[]) => item.includes(enName));
  };

  const onTableSearch = (searchKey: string) => {
    getTableAttrs({ filePath: get(selectedItem, 'file'), searchKey, pageNo: 1 });
  };

  const onPageChange = (pageNo: number) => {
    getTableAttrs({ filePath: get(selectedItem, 'file'), pageNo });
  };

  return (
    <Drawer
      destroyOnClose
      title={i18n.t('dop:class catalog')}
      width="50%"
      visible={drawerVisible}
      onClose={closeDrawer}
    >
      <Spin spinning={isFetching}>
        <SearchTable
          placeholder={i18n.t('dop:search by chinese/english name of attribute')}
          onSearch={onTableSearch}
          needDebounce
        >
          <Table
            columns={columns}
            rowKey="enName"
            rowClassName={({ enName }) =>
              centerRelatedGroup ? `with-group-tag group-tag-${getRelatedGroupClassName(enName)}` : ''
            }
            dataSource={tableAttrsList}
            pagination={{
              current,
              pageSize: PAGINATION.pageSize,
              total,
              onChange: onPageChange,
            }}
            scroll={{ x: '100%' }}
          />
        </SearchTable>
      </Spin>
    </Drawer>
  );
}
Example #23
Source File: chart.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
Chart = ({ type, extraQuery = {} }: IProps) => {
  const [{ layout, boardConfig, timeSpan, loading }, updater] = useUpdate<IState>({
    layout: [],
    boardConfig: [],
    timeSpan: getTimeSpan(),
    loading: false,
  });
  React.useEffect(() => {
    updater.loading(true);
    getDashboard({ type })
      .then(({ data }: any) => updater.boardConfig(data.viewConfig || []))
      .finally(() => {
        updater.loading(false);
      });
  }, [type, updater]);
  React.useEffect(() => {
    updater.timeSpan(getTimeSpan());
  }, [extraQuery, updater]);
  const query = React.useMemo(() => {
    return {
      ...extraQuery,
      start: timeSpan.startTimeMs,
      end: timeSpan.endTimeMs,
    };
  }, [extraQuery, timeSpan.endTimeMs, timeSpan.startTimeMs]);
  React.useEffect(() => {
    const { start, end, ...restQuery } = query;
    const flag = !isEmpty(restQuery) && values(restQuery).every((t) => !!t);
    if (!flag) {
      return;
    }
    const _layout = boardConfig.map((viewItem) => {
      const filters = get(viewItem, 'view.api.extraData.filters');
      const _viewItem = merge({}, viewItem, {
        view: {
          api: {
            query: {
              start,
              end,
              ...reduce(
                filters,
                (acc, { value, method, tag }) => {
                  const matchQuery = isString(value) ? getVariableStr(value) : undefined;
                  return {
                    ...acc,
                    [`${method}_${tag}`]: matchQuery ? restQuery[matchQuery] : value.split(','),
                  };
                },
                {},
              ),
            },
          },
        },
      });
      const { api, chartType } = _viewItem.view as any;

      return merge({}, viewItem, { view: { loadData: createLoadDataFn(api, chartType) } });
    });
    updater.layout(_layout);
  }, [boardConfig, query, updater]);

  return (
    <Spin spinning={loading}>
      <CommonRangePicker
        className="mb-3"
        defaultTime={[timeSpan.startTimeMs, timeSpan.endTimeMs]}
        onOk={(v) => updater.timeSpan(v)}
      />
      <BoardGrid.Pure layout={layout} />
    </Spin>
  );
}
Example #24
Source File: form-editor.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
FormEditor = React.forwardRef((props: IProps, ref: any) => {
  const { field, allField, fieldConfig = {}, onChange, Form = DefaultForm } = props;
  const form = React.useRef();

  React.useEffect(() => {
    ref && (ref.current = form.current);
  }, [ref]);

  const onFieldChange = (data: any) => {
    onChange(data, field);
  };

  // 整合一份总的field传递给Form,同时给field通过category分类,在Tab中使用
  const fields = reduce(
    fieldConfig,
    (sum: any[], item, fKey) => {
      return sum.concat(map(item.fields, (fItem) => ({ ...fItem, category: fKey })));
    },
    [],
  );

  return (
    <div className="dice-form-editor">
      <h4>{i18n.t('common:Form edit')}</h4>
      <div className="content">
        {isEmpty(field) ? (
          <div className="tip">
            <div className="tip-title mb-4">{i18n.t('common:how to edit a form')}</div>
            <div className="tip-desc mb-4">1、{i18n.t('common:add form items on the left')}</div>
            <div className="tip-desc mb-4">2、{i18n.t('common:click edit to complete the form')}</div>
          </div>
        ) : (
          <Form
            key={field.key}
            value={field}
            formRef={form}
            onChange={onFieldChange}
            fields={fields}
            formRender={({ RenderFields, form: formRef, fields: totalFields }: any) => {
              return (
                <Tabs
                  tabs={map(fieldConfig, (item) => {
                    // tab根据fieldConfig分类,再根据key对应上面注入的category收集对应分类下的field,这样达到同一个formRef控制多个RenderField
                    const { key, name } = item;
                    const curField = filter(totalFields, (fItem) => fItem.category === key);
                    return {
                      key,
                      name,
                      content: <RenderFields key={key} allField={allField} form={formRef} fields={curField} />,
                    };
                  })}
                />
              );
            }}
          />
        )}
      </div>
    </div>
  );
})
Example #25
Source File: summary.tsx    From S2 with MIT License 5 votes vote down vote up
TooltipSummary: React.FC<SummaryProps> = React.memo((props) => {
  const { summaries = [] } = props;

  const renderSelected = () => {
    const count = reduce(
      summaries,
      (pre, next) => pre + size(next?.selectedData),
      0,
    );
    return (
      <div className={`${TOOLTIP_PREFIX_CLS}-summary-item`}>
        <span className={`${TOOLTIP_PREFIX_CLS}-selected`}>
          {count} {i18n('项')}
        </span>
        {i18n('已选择')}
      </div>
    );
  };

  const renderSummary = () => {
    return summaries?.map((item) => {
      const { name = '', value } = item || {};
      if (!name && !value) {
        return;
      }

      return (
        <div
          key={`${name}-${value}`}
          className={`${TOOLTIP_PREFIX_CLS}-summary-item`}
        >
          <span className={`${TOOLTIP_PREFIX_CLS}-summary-key`}>
            {name}({i18n('总和')})
          </span>
          <span
            className={cls(
              `${TOOLTIP_PREFIX_CLS}-summary-val`,
              `${TOOLTIP_PREFIX_CLS}-bold`,
            )}
          >
            {value}
          </span>
        </div>
      );
    });
  };

  return (
    <div className={`${TOOLTIP_PREFIX_CLS}-summary`}>
      {renderSelected()}
      {renderSummary()}
    </div>
  );
})
Example #26
Source File: login.ts    From linkedin-private-api with MIT License 5 votes vote down vote up
parseCookies = <T>(cookies: string[]): Partial<T> =>
  cookies.reduce((res, c) => {
    let parsedCookie = parseCookie(c);

    parsedCookie = pickBy(parsedCookie, (v, k) => k === Object.keys(parsedCookie)[0]);

    return merge(res, parsedCookie);
  }, {})
Example #27
Source File: utils.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
transformConfigRecursively = (fieldsConfig: Field[], componentMap: Map<CT, string>) => {
  const propertiesArray: SchemaField[] = map(fieldsConfig, (item) => {
    const {
      name,
      title,
      label,
      type = 'string',
      customProps,
      wrapperProps,
      defaultValue,
      component,
      required,
      validator,
      items,
      gridConfig,
      layoutConfig,
      display,
      valuePropName,
      noPropertyLayoutWrapper = false,
      componentName: _componentName,
      properties: fieldProperties,
    } = item;

    let componentName = '';
    if (componentMap.has(component)) {
      componentName = componentMap.get(component)!;
    } else {
      componentName = _componentName ?? uniqueId('component-');
      if (valuePropName) {
        componentMap.set(connect(component, mapProps({ value: valuePropName })), componentName);
      } else {
        componentMap.set(component, componentName);
      }
    }

    let _items = {}; // for array fields
    if (items) {
      const _properties = transformConfigRecursively(items, componentMap);
      _items = {
        type: 'object',
        properties: {
          layout: {
            type: 'void',
            'x-component': 'FormLayout',
            'x-component-props': { ...defaultLayoutConfig, ...layoutConfig },
            properties: {
              grid: {
                type: 'void',
                'x-component': 'FormGrid',
                'x-component-props': {
                  maxColumns: gridConfig?.minColumns ? gridConfig.minColumns : 1,
                  ...gridConfig,
                },
                properties: _properties,
              },
            },
          },
        },
      };
    }

    let _properties;
    if (fieldProperties) {
      _properties = transformConfigRecursively(fieldProperties, componentMap);
      if (!noPropertyLayoutWrapper) {
        _properties = {
          layout: {
            type: 'void',
            'x-component': 'FormLayout',
            'x-component-props': { ...defaultLayoutConfig, ...layoutConfig },
            properties: {
              grid: {
                type: 'void',
                'x-component': 'FormGrid',
                'x-component-props': {
                  maxColumns: gridConfig?.minColumns ? gridConfig.minColumns : 1,
                  ...gridConfig,
                },
                properties: _properties,
              },
            },
          },
        };
      }
    }

    return {
      name,
      title: title ?? label,
      type,
      required,
      items: _properties ? undefined : _items,
      default: defaultValue,
      'x-validator': validator,
      'x-decorator': _properties ? undefined : 'FormItem',
      'x-component': componentName,
      'x-component-props': customProps,
      'x-display': display,
      'x-decorator-props': _properties ? { ...wrapperProps } : { colon: false, ...wrapperProps },
      properties: _properties,
    };
  });

  const properties = reduce(
    propertiesArray,
    (acc, item) => {
      const { name, ...rest } = item;
      acc[name] = rest;
      return acc;
    },
    {} as Obj<Omit<SchemaField, 'name'>>,
  );
  return properties as Obj<Omit<SchemaField, 'name'>>;
}
Example #28
Source File: trainings-chart.component.tsx    From MyWay-client with MIT License 4 votes vote down vote up
export default function TrainingsChart({
  trainingGoals,
}: {
  trainingGoals: TrainingGoalDto[];
}) {
  const ref = useRef<SVGSVGElement>(null);

  // const colorGoals = (goals: ChartGoalDto[]) => {
  //   const colorGen = colorsGenerator();
  //   return goals
  //     .filter((goal) => goal.showOnChart)
  //     .filter((goal) => goal.tasks.length !== 0)
  //     .map((goal) => ({
  //       ...goal,
  //       color: colorGen.next().value,
  //     }));
  // };

  const filterShownGoals = (goals: ChartGoalDto[]) => {
    return goals.filter((goal) => goal.showOnChart);
  };

  const isAnyGoalShown = (goals: ChartGoalDto[]) => {
    return goals.some((goal) => goal.showOnChart);
  };

  const findMaxEndingTrainingInGoals = (goals: ChartGoalDto[]): number => {
    return Math.max(
      ...goals.map((goal: ChartGoalDto) =>
        findMaxEndingTrainingInTasks(goal.tasks)
      )
    );
  };

  const findMaxEndingTrainingInTasks = (
    tasks: TrainingGoalTaskDto[]
  ): number => {
    return Math.max(
      ...tasks.map((task: TrainingGoalTaskDto) => task.endingTraining)
    );
  };

  const calcTotalNumberOfTasks = (goals: ChartGoalDto[]): number => {
    return goals.reduce(
      (acc: number, e: ChartGoalDto) => acc + e.tasks.length,
      0
    );
  };

  let goals: ChartGoalDto[] = [];
  let tasksSoFar = 0;

  for (const trainingGoal of trainingGoals) {
    goals.push({
      ...trainingGoal,
      color: "#000",
      showOnChart: true,
      tasksSoFar,
    });
    tasksSoFar += trainingGoal.tasks.length;
  }

  let chartGoals = filterShownGoals(goals);
  const maxEndingTrainingInGoals = findMaxEndingTrainingInGoals(chartGoals);

  const totalNumberOfTasks = calcTotalNumberOfTasks(chartGoals);

  const draw = useCallback(
    (ref) => {
      d3.select("#x-axis").remove();

      const svgClientSize = d3
        .select(ref.current)
        .node()
        .getBoundingClientRect();

      const xScale = d3
        .scaleLinear()
        .domain([1, maxEndingTrainingInGoals])
        .range([svgClientSize.width * 0.85, 0]);

      const xAxis = d3
        .axisBottom(xScale)
        .tickValues(
          Array.from({ length: maxEndingTrainingInGoals }, (v, k) => k + 1)
        )
        .tickFormat(d3.format("d"));

      d3.select(ref.current)
        .append("g")
        .attr("id", "x-axis")
        .attr("transform", `translate(0, ${svgClientSize.height * 0.9})`)
        .call(xAxis);
    },
    [ref]
  );

  useEffect(() => {
    window.addEventListener("resize", () => draw(ref));

    draw(ref);

    return () => {
      window.removeEventListener("resize", () => draw(ref));
    };
  }, [ref]);

  return (
    <svg
      ref={ref}
      style={{ border: "1px solid black" }}
      width="100%"
      height="100%"
    >
      {isAnyGoalShown(chartGoals) == false && <NoGoalsMarkedMessage />}
      <svg>
        {chartGoals.map((goal, idx) => (
          <TrainingsChartGoalRow
            key={goal.id}
            idx={idx}
            total={chartGoals.length}
            goal={goal}
            maxEndingTraining={maxEndingTrainingInGoals}
            totalNumberOfTasks={totalNumberOfTasks}
          />
        ))}
      </svg>
    </svg>
  );
}
Example #29
Source File: basic-params-config.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
PropertyItemForm = React.memo((props: IPropertyItemForm) => {
  const { formData, onChange, formType = 'Query', isEditMode = true, index } = props;

  const [
    {
      detailVisible,
      curPropertyType,
      innerParamList,
      dataTempStorage,
      paramListTempStorage,
      paramsModalVisible,
      arrayItemDataStorage,
      errorMap,
      formErrorMap,
    },
    updater,
    update,
  ] = useUpdate({
    detailVisible: props.detailVisible || false,
    curPropertyType: formData.type || initialTypeMap[formType] || ('string' as API_SETTING.PropertyType),
    innerParamList: [],
    dataTempStorage: {},
    paramListTempStorage: [],
    paramsModalVisible: false,
    arrayItemDataStorage: null,

    errorMap: {
      name: false,
      maxLength: false,
      minLength: false,
      maximum: false,
      minimum: false,
    },
    formErrorMap: {},
  });

  const formRef = React.useRef<IFormExtendType>({} as any);
  const paramListTempStorageRef = React.useRef<any[]>([]);
  const dataTempStorageRef = React.useRef<Obj>({});

  React.useImperativeHandle(paramListTempStorageRef, () => paramListTempStorage);
  React.useImperativeHandle(dataTempStorageRef, () => dataTempStorage);

  const isCurTypeOf = React.useCallback(
    (type: BASE_DATA_TYPE) => {
      return curPropertyType === type || get(props, ['extraDataTypes', curPropertyType, 'type']) === type;
    },
    [curPropertyType, props],
  );

  const isBasicDataType = React.useMemo(() => {
    return some(BASE_DATA_TYPE, (item) => item === curPropertyType);
  }, [curPropertyType]);

  const getRefTypePath = React.useCallback((data: Obj): string => {
    return get(data, [QUOTE_PREFIX, 0, '$ref']) || get(data, [QUOTE_PREFIX_NO_EXTENDED]) || '';
  }, []);

  const getExampleData = React.useCallback(
    (data: Obj, extraTypes?: Obj) => {
      if (!data) return '';
      const _extraTypes = extraTypes || props?.extraDataTypes;

      const refTypePath = getRefTypePath(data);
      const customType = refTypePath.split('/').slice(-1)[0];
      const customTypeData = get(_extraTypes, [customType]) || customType || {};

      if (typeof customTypeData === 'string') {
        const _type =
          get(props, ['allExtraDataTypes', customType, 'type']) || get(props, ['extraDataTypes', customType, 'type']);
        return _type === 'array' ? [] : {};
      }

      const curType = data.type || customTypeData.type;

      if (curType === 'object') {
        const newExtraTypes = produce(_extraTypes, (draft) => {
          draft && (draft[customType] = null);
        });

        const newExample: Obj = refTypePath ? getExampleData(customTypeData, newExtraTypes) : {};

        const customProperties = data.properties || {};
        forEach(keys(customProperties), (pName) => {
          const propertyItem = customProperties[pName];
          newExample[pName] = getExampleData(propertyItem, newExtraTypes);
        });

        return newExample;
      } else if (curType === 'array') {
        if (refTypePath) {
          const newItemExtraTypes = produce(_extraTypes, (draft) => {
            draft && (draft[customType] = null);
          });
          return getExampleData(customTypeData, newItemExtraTypes);
        } else {
          return [getExampleData(data.items, _extraTypes)];
        }
      } else if (refTypePath && customTypeData.example !== undefined) {
        return customTypeData.example;
      } else if (data.example !== undefined) {
        return data.example;
      } else {
        return DATATYPE_EXAMPLE_MAP[curType] || '';
      }
    },
    [getRefTypePath, props],
  );

  // 表单初始化加载
  React.useEffect(() => {
    formRef.current.resetFields();
    update({
      errorMap: {
        name: false,
        maxLength: false,
        minLength: false,
        maximum: false,
        minimum: false,
      },
      formErrorMap: {},
    });

    const innerProperties = formData?.properties;
    const requiredNames = formData?.required || [];
    updater.dataTempStorage(formData);

    const tempList = map(keys(innerProperties), (pKey: string) => {
      const _temp = { ...innerProperties[pKey] };
      if (formData?.type === 'object' && Array.isArray(formData?.required)) {
        _temp[API_PROPERTY_REQUIRED] = requiredNames.includes(pKey);
      }
      _temp[API_FORM_KEY] = pKey;

      return _temp;
    });
    updater.innerParamList(tempList);
    updater.paramListTempStorage(tempList);

    let _curPropertyType = formData?.type || formData?.schema?.type || 'object';

    const tempFormData = { ...formData };

    const refTypePath = getRefTypePath(formData);
    if (refTypePath) {
      const customType = refTypePath.split('/').slice(-1)[0];
      tempFormData.type = customType;
      _curPropertyType = customType;
    }

    updater.curPropertyType(_curPropertyType);

    setTimeout(() => {
      formRef.current!.setFieldsValue(tempFormData);
      if (isEmpty(formData)) {
        const _formData = formRef.current!.getFieldsValue();
        updater.dataTempStorage(_formData);
      } else {
        updater.dataTempStorage(formData);
      }
    });
  }, [updater, formData, getRefTypePath, update]);

  const AllDataTypes = React.useMemo(() => {
    return filter(props?.allDataTypes, (item) => item !== dataTempStorage[API_FORM_KEY]) || [];
  }, [dataTempStorage, props]);

  const onToggleDetail = React.useCallback(
    (visible) => {
      if (visible) {
        const omitList = getRefTypePath(dataTempStorage) ? ['type', API_FORM_KEY] : [API_FORM_KEY];
        const tempFormData = omit(dataTempStorage, omitList);
        const example = getExampleData(tempFormData);
        setTimeout(() => formRef.current!.setFieldsValue({ ...tempFormData, example }));
      }
      updater.dataTempStorage(dataTempStorageRef.current);
      if (curPropertyType === 'array' && arrayItemDataStorage) {
        updater.dataTempStorage(arrayItemDataStorage);
      } else {
        updater.dataTempStorage(dataTempStorageRef.current);
      }
      updater.innerParamList(paramListTempStorageRef.current);
      updater.detailVisible(visible);
    },
    [arrayItemDataStorage, curPropertyType, dataTempStorage, getExampleData, getRefTypePath, updater],
  );

  const propertyNameMap = React.useMemo(() => {
    const list = props?.siblingProperties || [];
    return map(list, (item) => item[API_FORM_KEY]);
  }, [props]);

  const setFields = React.useCallback(
    (fieldProps: ISetFieldProps) => {
      const { propertyKey = '', propertyData } = fieldProps;
      if (propertyKey === API_MEDIA && props.onSetMediaType) {
        props.onSetMediaType(fieldProps as { propertyKey: string; propertyData: string });
        return;
      }
      if (propertyKey === 'operation') {
        updater.detailVisible(propertyData);
        return;
      }
      if (formRef?.current) {
        const newFormData = produce(dataTempStorageRef.current, (draft: any) => {
          if (
            curPropertyType === 'array' &&
            !['description', 'type', API_FORM_KEY, API_PROPERTY_REQUIRED].includes(propertyKey)
          ) {
            set(draft, `items.${propertyKey}`, propertyData);
          } else {
            set(draft, propertyKey, propertyData);
          }

          if (propertyKey === 'type') {
            const curType = propertyData;
            updater.curPropertyType(curType);
            unset(draft, QUOTE_PREFIX);
            unset(draft, QUOTE_PREFIX_NO_EXTENDED);
            unset(draft, 'default');
            unset(draft, 'enum');

            if (curType === 'object' || curType === 'array') {
              unset(draft, 'pattern');
              unset(draft, 'maxLength');
              unset(draft, 'minLength');
              unset(draft, 'format');
              unset(draft, 'maximum');
              unset(draft, 'minimum');

              if (curType === 'object') {
                set(draft, 'properties', {});
                set(draft, 'required', []);
                unset(draft, 'items');
              }
              if (curType === 'array') {
                const tempItemData = {
                  type: 'string',
                  example: 'Example',
                };
                tempItemData[API_FORM_KEY] = 'items';
                set(draft, 'items', tempItemData);
                unset(draft, 'properties');
                updater.innerParamList([]);
                updater.paramListTempStorage([]);
              }
            } else if (['boolean', 'string', 'number', 'integer'].includes(curType)) {
              unset(draft, 'items');
              unset(draft, 'properties');
              unset(draft, 'required');
              if (curType !== 'number') {
                unset(draft, 'format');
                unset(draft, 'maximum');
                unset(draft, 'minimum');
              }
              if (curType !== 'string') {
                unset(draft, 'pattern');
                unset(draft, 'maxLength');
                unset(draft, 'minLength');
              }
              updater.innerParamList([]);
              updater.paramListTempStorage([]);
            }
            set(draft, 'example', DATATYPE_EXAMPLE_MAP[curType]);
          }
        });

        if (propertyKey === 'type') {
          if (!DATATYPE_EXAMPLE_MAP[propertyData]) {
            const customTypeData = get(props, ['extraDataTypes', propertyData]) || {};
            const _newTypeData = {
              ...omit(dataTempStorage, [QUOTE_PREFIX, QUOTE_PREFIX_NO_EXTENDED]),
              example: customTypeData.example || getExampleData(customTypeData),
              properties: customTypeData.type === 'object' ? {} : undefined,
              required: dataTempStorage.required,
              type: customTypeData.type,
            };
            // object类型的引用类型支持可拓展编辑
            if (customTypeData.type === 'object') {
              _newTypeData[QUOTE_PREFIX] = [{ $ref: `#/components/schemas/${propertyData}` }];
            } else {
              _newTypeData[QUOTE_PREFIX_NO_EXTENDED] = `#/components/schemas/${propertyData}`;
            }

            const typeQuotePath = _newTypeData[API_FORM_KEY];

            update({
              dataTempStorage: _newTypeData,
              innerParamList: [],
              paramListTempStorage: [],
            });

            formRef.current.setFieldsValue({ ..._newTypeData, type: propertyData });
            onChange(dataTempStorage[API_FORM_KEY], _newTypeData, { typeQuotePath, quoteTypeName: propertyData });
            return;
          }
        }

        updater.dataTempStorage(newFormData);
        onChange(dataTempStorage[API_FORM_KEY], newFormData);
      }
    },
    [curPropertyType, dataTempStorage, onChange, props, update, updater],
  );

  const dataTypeOptions = React.useMemo(() => {
    if (!props?.extraDataTypes) {
      return map(BASE_DATA_TYPE, (item) => (
        <Option key={item} value={item}>
          {item.slice(0, 1).toUpperCase() + item.slice(1)}
        </Option>
      ));
    } else {
      const basicDataTypeOptions = map(BASE_DATA_TYPE, (item) => (
        <Option key={item} value={item}>
          {item.slice(0, 1).toUpperCase() + item.slice(1)}
        </Option>
      ));
      const extraOptions =
        map(keys(props.extraDataTypes), (typeName) => (
          <Option key={typeName} value={typeName}>
            {typeName}
          </Option>
        )) || [];

      return [...basicDataTypeOptions, ...extraOptions];
    }
  }, [props]);

  const updateErrorNum = React.useCallback(
    (num, name) => {
      const _formErrorMap: IFormErrorMap = {};
      _formErrorMap[name] = num;
      if (curPropertyType === 'object') {
        forEach(paramListTempStorage, (param) => {
          const pName = param[API_FORM_KEY];
          if (pName === name) {
            _formErrorMap[pName] = num;
          } else {
            _formErrorMap[pName] = formErrorMap[pName] || 0;
          }
        });
      }

      updater.formErrorMap(_formErrorMap);
      const totalError = reduce(values(_formErrorMap), (total, cur) => total + cur, 0);

      props.updateErrorNum && props.updateErrorNum(totalError, dataTempStorage[API_FORM_KEY]);
      props.onFormErrorNumChange && props.onFormErrorNumChange(totalError, dataTempStorage[API_FORM_KEY]);
    },
    [curPropertyType, dataTempStorage, formErrorMap, paramListTempStorage, props, updater],
  );

  const onChangePropertyName = React.useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const newName = e.target.value;
      const { pattern } = regRules.specialLetter;
      const nameMap = formType === 'DataType' ? AllDataTypes : propertyNameMap;
      let isErrorValue = true;

      const isSameWithBaseType = formType === 'DataType' && newName.toLocaleLowerCase() in BASE_DATA_TYPE;
      if (newName !== '' && !pattern.test(newName) && !nameMap.includes(newName) && !isSameWithBaseType) {
        const temp = produce(dataTempStorageRef.current, (draft) => {
          set(draft, API_FORM_KEY, newName);
        });
        isErrorValue = false;

        updater.dataTempStorage(temp);
        onChange(dataTempStorageRef.current[API_FORM_KEY], temp, {
          typeQuotePath: newName,
          quoteTypeName: curPropertyType,
        });
      }

      // 统计name类型错误数量
      const newErrorMap = { ...errorMap };
      if (isErrorValue && !errorMap.name) {
        newErrorMap.name = true;
        updater.errorMap(newErrorMap);
        const errorNum = reduce(values(newErrorMap), (total, cur) => (cur ? total + 1 : total), 0);

        updateErrorNum(errorNum, dataTempStorage[API_FORM_KEY]);
      } else if (!isErrorValue && errorMap.name) {
        newErrorMap.name = false;
        updater.errorMap(newErrorMap);
        const errorNum = reduce(values(newErrorMap), (total, cur) => (cur ? total + 1 : total), 0);

        updateErrorNum(errorNum, dataTempStorage[API_FORM_KEY]);
      }
    },
    [
      AllDataTypes,
      curPropertyType,
      dataTempStorage,
      errorMap,
      formType,
      onChange,
      propertyNameMap,
      updateErrorNum,
      updater,
    ],
  );

  const onChangeNumberValue = React.useCallback(
    ({ propertyKey, propertyData }: { propertyKey: string; propertyData: number }) => {
      let isErrorValue = true;

      if (propertyKey === 'minLength') {
        const maxLength = dataTempStorageRef.current?.maxLength;
        if (maxLength === undefined || maxLength >= propertyData) {
          setFields({ propertyKey, propertyData });
          isErrorValue = false;
        }
      } else if (propertyKey === 'maxLength') {
        const minLength = dataTempStorageRef.current?.minLength;
        if (minLength === undefined || minLength <= propertyData) {
          setFields({ propertyKey, propertyData });
          isErrorValue = false;
        }
      } else if (propertyKey === 'minimum') {
        const maximum = dataTempStorageRef.current?.maximum;
        if (maximum === undefined || maximum >= propertyData) {
          setFields({ propertyKey, propertyData });
          isErrorValue = false;
        }
      } else {
        const minimum = dataTempStorageRef.current?.minimum;
        if (minimum === undefined || minimum <= propertyData) {
          setFields({ propertyKey, propertyData });
          isErrorValue = false;
        }
      }

      // 统计number类型错误数量
      const newErrorMap = { ...errorMap };
      if (isErrorValue && !errorMap[propertyKey]) {
        newErrorMap[propertyKey] = true;
        updater.errorMap(newErrorMap);
        const errorNum = reduce(values(newErrorMap), (total, cur) => (cur ? total + 1 : total), 0);

        updateErrorNum(errorNum, dataTempStorage[API_FORM_KEY]);
      } else if (!isErrorValue && errorMap[propertyKey]) {
        newErrorMap[propertyKey] = false;
        updater.errorMap(newErrorMap);
        const errorNum = reduce(values(newErrorMap), (total, cur) => (cur ? total + 1 : total), 0);

        updateErrorNum(errorNum, dataTempStorage[API_FORM_KEY]);
      }
    },
    [errorMap, dataTempStorage, setFields, updater, updateErrorNum],
  );

  //  参数详情选项
  const propertyFields = React.useMemo(() => {
    const fields = formType !== 'Parameters' ? [descriptionField] : [];
    if (!getRefTypePath(dataTempStorage)) {
      const tempFields = getPropertyDetailFields({ type: curPropertyType, curPropertyType, formData: dataTempStorage });
      fields.push(...tempFields);
    } else if (get(props, `extraDataTypes.${curPropertyType}.type`)) {
      const tempFields = getPropertyDetailFields({
        type: curPropertyType,
        curPropertyType: get(props, ['extraDataTypes', curPropertyType, 'type']),
        formData: dataTempStorage,
      });
      fields.push(...tempFields);
    }

    return map(fields, (fieldItem) => {
      const tempFieldItem = produce(fieldItem, (draft) => {
        if (['minLength', 'maxLength', 'minimum', 'maximum'].includes(draft.name)) {
          set(draft, 'customProps.onChange', (e: React.ChangeEvent<HTMLInputElement> | any) => {
            const newNum = !(Object.prototype.toString.call(e) === '[object Object]') ? e : +e.target.value;
            onChangeNumberValue({ propertyKey: fieldItem?.name, propertyData: newNum });
          });
        } else {
          set(draft, 'customProps.onChange', (e: React.ChangeEvent<HTMLInputElement> | any) => {
            const newVal = !(Object.prototype.toString.call(e) === '[object Object]') ? e : e.target.value;
            setFields({ propertyKey: fieldItem?.name, propertyData: newVal });
          });
        }
        set(draft, 'customProps.disabled', !isEditMode);
      });
      return tempFieldItem;
    });
  }, [formType, getRefTypePath, dataTempStorage, props, curPropertyType, isEditMode, onChangeNumberValue, setFields]);

  const onArrayItemChange = (_formKey: string, _formData: any, extraProps?: Obj) => {
    const newExample = [getExampleData(_formData)];
    const tempData = produce(dataTempStorageRef.current, (draft) => {
      draft.items = _formData;
      draft.example = newExample;
    });
    const _extraProps = {
      quoteTypeName: extraProps?.quoteTypeName,
      typeQuotePath: extraProps?.typeQuotePath
        ? `${dataTempStorageRef.current[API_FORM_KEY]}.${extraProps.typeQuotePath}`
        : '',
    };
    props.onChange(dataTempStorageRef.current[API_FORM_KEY], tempData, _extraProps);
    updater.arrayItemDataStorage(tempData);
  };

  const updateInnerParamList = (formKey: string, _formData: any, extraProps?: Obj) => {
    const tempList = produce(paramListTempStorageRef.current, (draft) => {
      forEach(draft, (item, index) => {
        if (item[API_FORM_KEY] === formKey) {
          draft[index] = _formData;
        }
      });
    });
    const requiredNames: string[] = [];

    const refTypePath = getRefTypePath(dataTempStorage);
    const customDataType = refTypePath ? refTypePath.split('/').slice(-1)[0] : '';

    const objectExample: Obj = { ...getExampleData(get(props, ['extraDataTypes', customDataType])) };

    forEach(tempList, (item) => {
      const _example = item?.example || DATATYPE_EXAMPLE_MAP[item?.type];
      if (item[API_PROPERTY_REQUIRED]) {
        requiredNames.push(item[API_FORM_KEY]);
      }
      objectExample[item[API_FORM_KEY]] = _example;
    });

    updater.paramListTempStorage(tempList);

    if (props.onChange && tempList?.length) {
      const newProperties = {};
      forEach(tempList, (item) => {
        newProperties[item[API_FORM_KEY]] = item;
      });
      const tempData = produce(dataTempStorageRef.current, (draft) => {
        draft.properties = newProperties;
        draft.type = 'object';
        draft.required = requiredNames;
        draft.example = objectExample;
      });
      updater.dataTempStorage(tempData);
      const typeQuotePath = extraProps?.typeQuotePath
        ? `${tempData[API_FORM_KEY] || 'schema'}.properties.${extraProps?.typeQuotePath}`
        : '';
      props.onChange(dataTempStorageRef.current[API_FORM_KEY], tempData, {
        typeQuotePath,
        quoteTypeName: extraProps?.quoteTypeName,
      });
    }
  };

  const deleteParamByFormKey = (_data: Obj, index: number) => {
    const tempList = paramListTempStorage.filter((_record, i) => index !== i);

    updater.innerParamList(tempList);
    updater.paramListTempStorage(tempList);

    const tempProperties = {};
    const newExample = {};
    const requiredNames: string[] = [];

    forEach(tempList, (item) => {
      tempProperties[item[API_FORM_KEY]] = item;
      newExample[item[API_FORM_KEY]] = item?.example;
      item[API_PROPERTY_REQUIRED] && requiredNames.push(item[API_FORM_KEY]);
    });

    const newFormData = produce(dataTempStorage, (draft) => {
      set(draft, 'properties', tempProperties);
      set(draft, 'example', newExample);
      set(draft, 'required', requiredNames);
    });
    updater.dataTempStorage(newFormData);
    props?.onChange && props.onChange(newFormData[API_FORM_KEY], newFormData);
  };

  const getExistNames = React.useCallback(() => {
    const existNames = map(paramListTempStorage, (item) => item[API_FORM_KEY]);

    const refTypePath = getRefTypePath(dataTempStorage);
    const customDataType = refTypePath ? refTypePath.split('/').slice(-1)[0] : '';

    if (refTypePath && get(props, `extraDataTypes.${curPropertyType}.type`) === 'object') {
      const _extraProperties = get(props, ['extraDataTypes', customDataType, 'properties']);
      existNames.push(...keys(_extraProperties));
    }
    return existNames;
  }, [curPropertyType, dataTempStorage, getRefTypePath, paramListTempStorage, props]);

  // object类型的批量添加参数
  const addParamList = React.useCallback(
    (newList: Obj[]) => {
      const refTypePath = getRefTypePath(dataTempStorage);
      const customDataType = refTypePath ? refTypePath.split('/').slice(-1)[0] : '';

      const tempList = [...paramListTempStorage, ...newList];
      updater.innerParamList(tempList);
      updater.paramListTempStorage(tempList);

      const tempProperties = {};
      forEach(tempList, (item) => {
        tempProperties[item[API_FORM_KEY]] = item;
      });
      const newExample = refTypePath ? { ...get(props, `extraDataTypes.${customDataType}.example`) } : {};

      const requiredNames: string[] = [];
      forEach(tempList, (property) => {
        property[API_PROPERTY_REQUIRED] && requiredNames.push(property[API_FORM_KEY]);
        newExample[property[API_FORM_KEY]] = property?.example;
      });

      const newFormData = produce(dataTempStorage, (draft) => {
        set(draft, 'properties', tempProperties);
        set(draft, 'type', 'object');
        set(draft, 'example', newExample);
        set(draft, 'required', requiredNames);
      });
      updater.dataTempStorage(newFormData);
      props?.onChange && props.onChange(dataTempStorage[API_FORM_KEY], newFormData);
    },
    [dataTempStorage, getRefTypePath, paramListTempStorage, props, updater],
  );

  // object添加单个参数
  const addParam = React.useCallback(() => {
    let newPropertyName = `propertyName${innerParamList?.length + 1}`;
    const existNames = getExistNames();

    while (existNames.includes(newPropertyName)) {
      newPropertyName += '1';
    }

    const tempObj = {
      type: 'string',
      example: 'Example',
    };
    tempObj[API_PROPERTY_REQUIRED] = true;
    tempObj[API_FORM_KEY] = newPropertyName;

    addParamList([tempObj]);
  }, [addParamList, getExistNames, innerParamList]);

  // 更新设置example示例
  React.useEffect(() => {
    const tempData = isCurTypeOf(BASE_DATA_TYPE.array) && arrayItemDataStorage ? arrayItemDataStorage : dataTempStorage;
    if (tempData.example && typeof tempData.example === 'object') {
      const newExample = getExampleData(tempData);
      formRef.current.resetFields(['example']);
      formRef.current.setFieldsValue({ example: newExample });
    }
  }, [arrayItemDataStorage, curPropertyType, dataTempStorage, getExampleData, isCurTypeOf]);

  const onCloseParamsModal = () => updater.paramsModalVisible(false);

  const onImport = (importedParams: Obj[]) => {
    onCloseParamsModal();
    addParamList(importedParams);
  };

  const formFieldsSelector = React.useMemo(() => {
    const tempFields = getPropertyFormSelector({
      formType,
      dataTypeOptions,
      propertyNameMap,
      AllDataTypes,
      detailVisible,
      index,
    });
    return map(tempFields, (fieldItem: any) => {
      const tempFieldItem = produce(fieldItem, (draft: { name: string }) => {
        if (draft.name === API_FORM_KEY && ['DataType', 'Query'].includes(formType)) {
          set(draft, 'customProps.onChange', onChangePropertyName);
        } else if (draft.name === 'operation') {
          set(draft, 'customProps.onChange', onToggleDetail);
        } else {
          set(draft, 'customProps.onChange', (e: React.ChangeEvent<HTMLInputElement> | string | boolean) => {
            const newVal = typeof e === 'string' || typeof e === 'boolean' ? e : e.target.value;
            setFields({ propertyKey: fieldItem?.name, propertyData: newVal });
          });
        }
        set(draft, 'customProps.disabled', !isEditMode);
      });
      return tempFieldItem;
    });
  }, [
    formType,
    dataTypeOptions,
    propertyNameMap,
    AllDataTypes,
    detailVisible,
    isEditMode,
    onChangePropertyName,
    onToggleDetail,
    setFields,
    index,
  ]);

  const detailType = React.useMemo(() => {
    if (detailVisible) {
      if (isCurTypeOf(BASE_DATA_TYPE.object)) {
        return 'object';
      } else if (isCurTypeOf(BASE_DATA_TYPE.array)) {
        return 'array';
      } else if (!isBasicDataType) {
        return 'example';
      }
    }
    return '';
  }, [detailVisible, isBasicDataType, isCurTypeOf]);

  return (
    <FormBuilder isMultiColumn ref={formRef}>
      {props?.formType !== 'Parameters' && <Fields fields={formFieldsSelector} />}
      {detailVisible && isBasicDataType && <Fields fields={propertyFields} />}
      {detailType === 'object' && (
        <div>
          {map(innerParamList, (record, index) => {
            return (
              <div className="param-form" key={record[API_FORM_KEY]}>
                {isEditMode && (
                  <div className="param-form-operation">
                    <Popconfirm
                      title={`${i18n.t('common:confirm to delete')}?`}
                      onConfirm={() => deleteParamByFormKey(record, index)}
                    >
                      <CustomIcon type="shanchu" className="param-form-operation-btn cursor-pointer" />
                    </Popconfirm>
                  </div>
                )}
                <div className="param-form-content">
                  <FormBuilder isMultiColumn>
                    <PropertyItemForm
                      key={record[API_FORM_KEY]}
                      updateErrorNum={updateErrorNum}
                      formData={record}
                      isEditMode={isEditMode}
                      onChange={updateInnerParamList}
                      extraDataTypes={props?.extraDataTypes}
                      allExtraDataTypes={props?.allExtraDataTypes}
                      siblingProperties={filter(
                        paramListTempStorage,
                        (item) => item[API_FORM_KEY] !== record[API_FORM_KEY],
                      )}
                      index={index}
                    />
                  </FormBuilder>
                </div>
              </div>
            );
          })}
          {isEditMode && (
            <>
              <Button className="operation-btn mb-4" onClick={addParam}>
                {i18n.t('common:add parameter')}
              </Button>
              <Button className="operation-btn mb-4 ml-2" onClick={() => updater.paramsModalVisible(true)}>
                {i18n.t('dop:import parameters')}
              </Button>
            </>
          )}
          {props?.formType !== 'Parameters' && <Fields fields={[objectExampleField]} />}
        </div>
      )}
      {detailType === 'array' && (
        <>
          {isBasicDataType && (
            <div className="array-form">
              <PropertyItemForm
                formType="Array"
                updateErrorNum={updateErrorNum}
                formData={dataTempStorage.items || {}}
                detailVisible
                onChange={onArrayItemChange}
                isEditMode={isEditMode}
                extraDataTypes={props?.extraDataTypes}
                allExtraDataTypes={props?.allExtraDataTypes}
                siblingProperties={filter(
                  paramListTempStorage,
                  (item) => item[API_FORM_KEY] !== dataTempStorage.items[API_FORM_KEY],
                )}
              />
            </div>
          )}
          <Fields fields={[objectExampleField]} />
        </>
      )}
      {detailType === 'example' && <Fields fields={[objectExampleField]} />}
      <ApiParamsModal
        visible={paramsModalVisible}
        onImport={onImport}
        onClose={onCloseParamsModal}
        paramList={paramListTempStorage}
      />
    </FormBuilder>
  );
})