fix: improve the display of the title field (#1741)
* fix: fix the displaying problem of Select * fix: fix error * fix: filter out fields * feat: format date * feat: optimize * fix: fix error * feat: use the default title field * fix: fix bug
This commit is contained in:
		
							parent
							
								
									7b892cec50
								
							
						
					
					
						commit
						71af0f16a9
					
				@ -42,6 +42,12 @@ const titlePrompt = 'Default title for each record';
 | 
			
		||||
const expectTypes = ['string', 'integer', 'bigInt', 'float', 'double', 'decimal', 'date', 'dateonly', 'time'];
 | 
			
		||||
const excludeInterfaces = ['icon'];
 | 
			
		||||
 | 
			
		||||
// 是否可以作为标题字段
 | 
			
		||||
export const isTitleField = (field) => {
 | 
			
		||||
  if (!field) return false;
 | 
			
		||||
  return !field.isForeignKey && expectTypes.includes(field.type) && !excludeInterfaces.includes(field.interface);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CurrentFields = (props) => {
 | 
			
		||||
  const compile = useCompile();
 | 
			
		||||
  const { getInterface } = useCollectionManager();
 | 
			
		||||
@ -91,7 +97,7 @@ const CurrentFields = (props) => {
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return expectTypes.includes(record.type) && !excludeInterfaces.includes(record.interface) ? (
 | 
			
		||||
        return isTitleField(record) ? (
 | 
			
		||||
          <Tooltip title={t(titlePrompt)} placement="right" overlayInnerStyle={{ textAlign: 'center' }}>
 | 
			
		||||
            <Switch
 | 
			
		||||
              size="small"
 | 
			
		||||
@ -201,7 +207,7 @@ const InheritFields = (props) => {
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return expectTypes.includes(record.type) && !excludeInterfaces.includes(record.interface) ? (
 | 
			
		||||
        return isTitleField(record) ? (
 | 
			
		||||
          <Tooltip title={t(titlePrompt)} placement="right" overlayInnerStyle={{ textAlign: 'center' }}>
 | 
			
		||||
            <Switch
 | 
			
		||||
              size="small"
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,11 @@ export const linkTo: IField = {
 | 
			
		||||
  },
 | 
			
		||||
  availableTypes: ['belongsToMany'],
 | 
			
		||||
  schemaInitialize(schema: ISchema, { readPretty, block, targetCollection }) {
 | 
			
		||||
    if (targetCollection?.titleField && schema['x-component-props']) {
 | 
			
		||||
      schema['x-component-props'].fieldNames = schema['x-component-props'].fieldNames || { value: 'id' };
 | 
			
		||||
      schema['x-component-props'].fieldNames.label = targetCollection.titleField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (targetCollection?.template === 'file') {
 | 
			
		||||
      const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
 | 
			
		||||
      fieldNames.label = 'preview';
 | 
			
		||||
 | 
			
		||||
@ -51,8 +51,13 @@ export const m2m: IField = {
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  availableTypes:['belongsToMany'],
 | 
			
		||||
  availableTypes: ['belongsToMany'],
 | 
			
		||||
  schemaInitialize(schema: ISchema, { readPretty, block, targetCollection }) {
 | 
			
		||||
    if (targetCollection?.titleField && schema['x-component-props']) {
 | 
			
		||||
      schema['x-component-props'].fieldNames = schema['x-component-props'].fieldNames || { value: 'id' };
 | 
			
		||||
      schema['x-component-props'].fieldNames.label = targetCollection.titleField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (targetCollection?.template === 'file') {
 | 
			
		||||
      const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
 | 
			
		||||
      fieldNames.label = 'preview';
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,11 @@ export const m2o: IField = {
 | 
			
		||||
  },
 | 
			
		||||
  availableTypes:['belongsTo'],
 | 
			
		||||
  schemaInitialize(schema: ISchema, { block, readPretty, targetCollection }) {
 | 
			
		||||
    if (targetCollection?.titleField && schema['x-component-props']) {
 | 
			
		||||
      schema['x-component-props'].fieldNames = schema['x-component-props'].fieldNames || { value: 'id' };
 | 
			
		||||
      schema['x-component-props'].fieldNames.label = targetCollection.titleField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (targetCollection?.template === 'file') {
 | 
			
		||||
      const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
 | 
			
		||||
      fieldNames.label = 'preview';
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,12 @@ export const o2m: IField = {
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  availableTypes: ['hasMany'],
 | 
			
		||||
  schemaInitialize(schema: ISchema, { field, block, readPretty }) {
 | 
			
		||||
  schemaInitialize(schema: ISchema, { field, block, readPretty, targetCollection }) {
 | 
			
		||||
    if (targetCollection?.titleField && schema['x-component-props']) {
 | 
			
		||||
      schema['x-component-props'].fieldNames = schema['x-component-props'].fieldNames || { value: 'id' };
 | 
			
		||||
      schema['x-component-props'].fieldNames.label = targetCollection.titleField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (block === 'Form') {
 | 
			
		||||
      if (schema['x-component'] === 'TableField') {
 | 
			
		||||
        const association = `${field.collectionName}.${field.name}`;
 | 
			
		||||
 | 
			
		||||
@ -475,6 +475,11 @@ export const obo: IField = {
 | 
			
		||||
      schema['x-component-props']['size'] = 'small';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (targetCollection?.titleField && schema['x-component-props']) {
 | 
			
		||||
      schema['x-component-props'].fieldNames = schema['x-component-props'].fieldNames || { value: 'id' };
 | 
			
		||||
      schema['x-component-props'].fieldNames.label = targetCollection.titleField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (targetCollection?.template === 'file') {
 | 
			
		||||
      const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
 | 
			
		||||
      fieldNames.label = 'preview';
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ import {
 | 
			
		||||
  useCollectionManager,
 | 
			
		||||
  useSortFields,
 | 
			
		||||
} from '../../../collection-manager';
 | 
			
		||||
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
 | 
			
		||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
			
		||||
import { useCompile, useDesignable, useFieldComponentOptions, useFieldTitle } from '../../hooks';
 | 
			
		||||
import { removeNullCondition } from '../filter';
 | 
			
		||||
@ -91,7 +92,7 @@ interface AssociationSelectInterface {
 | 
			
		||||
 | 
			
		||||
export const AssociationSelect = InternalAssociationSelect as unknown as AssociationSelectInterface;
 | 
			
		||||
 | 
			
		||||
AssociationSelect.Designer = () => {
 | 
			
		||||
AssociationSelect.Designer = function Designer() {
 | 
			
		||||
  const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
 | 
			
		||||
  const { getField, template } = useCollection();
 | 
			
		||||
  const { form } = useFormBlockContext();
 | 
			
		||||
@ -99,7 +100,6 @@ AssociationSelect.Designer = () => {
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const tk = useFilterByTk();
 | 
			
		||||
  const {} = useCollection();
 | 
			
		||||
  const { dn, refresh, insertAdjacent } = useDesignable();
 | 
			
		||||
  const compile = useCompile();
 | 
			
		||||
  const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
 | 
			
		||||
@ -134,7 +134,7 @@ AssociationSelect.Designer = () => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const options = targetFields
 | 
			
		||||
    .filter((field) => !field?.target && field.type !== 'boolean')
 | 
			
		||||
    .filter((field) => isTitleField(field))
 | 
			
		||||
    .map((field) => ({
 | 
			
		||||
      value: field?.name,
 | 
			
		||||
      label: compile(field?.uiSchema?.title) || field?.name,
 | 
			
		||||
@ -691,7 +691,7 @@ AssociationSelect.Designer = () => {
 | 
			
		||||
 * 用于筛选表单区块
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
AssociationSelect.FilterDesigner = () => {
 | 
			
		||||
AssociationSelect.FilterDesigner = function FilterDesigner() {
 | 
			
		||||
  const { getCollectionFields, getInterface, getCollectionJoinField } = useCollectionManager();
 | 
			
		||||
  const { getField } = useCollection();
 | 
			
		||||
  const { form } = useFormBlockContext();
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider';
 | 
			
		||||
import { BlockRequestContext, useFilterByTk, useFormBlockContext } from '../../../block-provider';
 | 
			
		||||
import { Collection, useCollection, useCollectionManager } from '../../../collection-manager';
 | 
			
		||||
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
 | 
			
		||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
			
		||||
import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks';
 | 
			
		||||
import { BlockItem } from '../block-item';
 | 
			
		||||
@ -70,7 +71,7 @@ export const FormItem: any = observer((props: any) => {
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
FormItem.Designer = () => {
 | 
			
		||||
FormItem.Designer = function Designer() {
 | 
			
		||||
  const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
 | 
			
		||||
  const { getField } = useCollection();
 | 
			
		||||
  const tk = useFilterByTk();
 | 
			
		||||
@ -97,11 +98,14 @@ FormItem.Designer = () => {
 | 
			
		||||
    initialValue['required'] = field.required;
 | 
			
		||||
  }
 | 
			
		||||
  const options = targetFields
 | 
			
		||||
    .filter((field) => !field?.target && field.type !== 'boolean')
 | 
			
		||||
    .filter((field) => {
 | 
			
		||||
      return isTitleField(field);
 | 
			
		||||
    })
 | 
			
		||||
    .map((field) => ({
 | 
			
		||||
      value: field?.name,
 | 
			
		||||
      label: compile(field?.uiSchema?.title) || field?.name,
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
  let readOnlyMode = 'editable';
 | 
			
		||||
  if (fieldSchema['x-disabled'] === true) {
 | 
			
		||||
    readOnlyMode = 'readonly';
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
import { LoadingOutlined } from '@ant-design/icons';
 | 
			
		||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
 | 
			
		||||
import { SelectProps } from 'antd';
 | 
			
		||||
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react';
 | 
			
		||||
import { SelectProps, Tag } from 'antd';
 | 
			
		||||
import moment from 'moment';
 | 
			
		||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
 | 
			
		||||
import { ResourceActionOptions, useRequest } from '../../../api-client';
 | 
			
		||||
import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
 | 
			
		||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
 | 
			
		||||
import { useCompile } from '../../hooks';
 | 
			
		||||
import { Select, defaultFieldNames } from '../select';
 | 
			
		||||
import { ReadPretty } from './ReadPretty';
 | 
			
		||||
@ -15,6 +17,7 @@ export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
 | 
			
		||||
  wait?: number;
 | 
			
		||||
  manual?: boolean;
 | 
			
		||||
  mapOptions?: (data: any) => RemoteSelectProps['fieldNames'];
 | 
			
		||||
  targetField?: any;
 | 
			
		||||
  service: ResourceActionOptions<P>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -28,10 +31,73 @@ const InternalRemoteSelect = connect(
 | 
			
		||||
      objectValue,
 | 
			
		||||
      manual = true,
 | 
			
		||||
      mapOptions,
 | 
			
		||||
      targetField: _targetField,
 | 
			
		||||
      ...others
 | 
			
		||||
    } = props;
 | 
			
		||||
    const compile = useCompile();
 | 
			
		||||
    const firstRun = useRef(false);
 | 
			
		||||
    const fieldSchema = useFieldSchema();
 | 
			
		||||
    const { getField } = useCollection();
 | 
			
		||||
    const { getCollectionJoinField } = useCollectionManager();
 | 
			
		||||
    const collectionField = getField(fieldSchema.name);
 | 
			
		||||
    const targetField =
 | 
			
		||||
      _targetField ||
 | 
			
		||||
      (collectionField?.target &&
 | 
			
		||||
        fieldNames?.label &&
 | 
			
		||||
        getCollectionJoinField(`${collectionField.target}.${fieldNames.label}`));
 | 
			
		||||
 | 
			
		||||
    const mapOptionsToTags = useCallback(
 | 
			
		||||
      (options) => {
 | 
			
		||||
        try {
 | 
			
		||||
          return options.map((option) => {
 | 
			
		||||
            let label = option[fieldNames.label];
 | 
			
		||||
 | 
			
		||||
            if (targetField?.uiSchema?.enum) {
 | 
			
		||||
              if (Array.isArray(label)) {
 | 
			
		||||
                label = label
 | 
			
		||||
                  .map((item, index) => {
 | 
			
		||||
                    const option = targetField.uiSchema.enum.find((i) => i.value === item);
 | 
			
		||||
                    if (option) {
 | 
			
		||||
                      return (
 | 
			
		||||
                        <Tag key={index} color={option.color} style={{ marginRight: 3 }}>
 | 
			
		||||
                          {option?.label || item}
 | 
			
		||||
                        </Tag>
 | 
			
		||||
                      );
 | 
			
		||||
                    } else {
 | 
			
		||||
                      return <Tag>{item}</Tag>;
 | 
			
		||||
                    }
 | 
			
		||||
                  })
 | 
			
		||||
                  .reverse();
 | 
			
		||||
              } else {
 | 
			
		||||
                const item = targetField.uiSchema.enum.find((i) => i.value === label);
 | 
			
		||||
                if (item) {
 | 
			
		||||
                  label = <Tag color={item.color}>{item.label}</Tag>;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (targetField?.type === 'date') {
 | 
			
		||||
              label = moment(label).format('YYYY-MM-DD');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (mapOptions) {
 | 
			
		||||
              return mapOptions({
 | 
			
		||||
                [fieldNames.label]: label,
 | 
			
		||||
                [fieldNames.value]: option[fieldNames.value],
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
            return {
 | 
			
		||||
              [fieldNames.label]: label || option[fieldNames.value],
 | 
			
		||||
              [fieldNames.value]: option[fieldNames.value],
 | 
			
		||||
            };
 | 
			
		||||
          });
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
          console.error(err);
 | 
			
		||||
          return options;
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      [targetField?.uiSchema, fieldNames],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const { data, run, loading } = useRequest(
 | 
			
		||||
      {
 | 
			
		||||
@ -82,11 +148,8 @@ const InternalRemoteSelect = connect(
 | 
			
		||||
 | 
			
		||||
    const getOptionsByFieldNames = useCallback(
 | 
			
		||||
      (item) => {
 | 
			
		||||
        if (mapOptions) {
 | 
			
		||||
          return mapOptions(item);
 | 
			
		||||
        }
 | 
			
		||||
        return Object.keys(fieldNames).reduce((obj, key) => {
 | 
			
		||||
          const value = item[fieldNames[key]];
 | 
			
		||||
          let value = item[fieldNames[key]];
 | 
			
		||||
          if (value) {
 | 
			
		||||
            // support hidden, disabled, etc.
 | 
			
		||||
            obj[['label', 'value', 'options'].includes(key) ? fieldNames[key] : key] =
 | 
			
		||||
@ -138,7 +201,7 @@ const InternalRemoteSelect = connect(
 | 
			
		||||
        value={value}
 | 
			
		||||
        {...others}
 | 
			
		||||
        loading={loading}
 | 
			
		||||
        options={options}
 | 
			
		||||
        options={mapOptionsToTags(options)}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -21,9 +21,10 @@ export const FormDataTemplates = observer((props: any) => {
 | 
			
		||||
  const { useProps } = props;
 | 
			
		||||
  const { defaultValues, collectionName } = useProps();
 | 
			
		||||
  const { collectionList, getEnableFieldTree } = useCollectionState(collectionName);
 | 
			
		||||
  const { getCollection } = useCollectionManager();
 | 
			
		||||
  const { getCollection, getCollectionField } = useCollectionManager();
 | 
			
		||||
  const collection = getCollection(collectionName);
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const field = getCollectionField(`${collectionName}.${collection?.titleField || 'id'}`);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SchemaComponent
 | 
			
		||||
@ -83,16 +84,24 @@ export const FormDataTemplates = observer((props: any) => {
 | 
			
		||||
                        multiple: false,
 | 
			
		||||
                        objectValue: false,
 | 
			
		||||
                        manual: false,
 | 
			
		||||
                        mapOptions: (item) => {
 | 
			
		||||
                          return {
 | 
			
		||||
                            ...item,
 | 
			
		||||
                            [collection.titleField || 'label']: `#${item.id} ${item[collection.titleField] || ''}`,
 | 
			
		||||
                            value: item.id,
 | 
			
		||||
                          };
 | 
			
		||||
                        targetField: field,
 | 
			
		||||
                        mapOptions(option) {
 | 
			
		||||
                          try {
 | 
			
		||||
                            const label = collection?.titleField || 'id';
 | 
			
		||||
                            option[label] = (
 | 
			
		||||
                              <>
 | 
			
		||||
                                #{option.id} {option[label]}
 | 
			
		||||
                              </>
 | 
			
		||||
                            );
 | 
			
		||||
                            return option;
 | 
			
		||||
                          } catch (error) {
 | 
			
		||||
                            console.error(error);
 | 
			
		||||
                            return option;
 | 
			
		||||
                          }
 | 
			
		||||
                        },
 | 
			
		||||
                        fieldNames: {
 | 
			
		||||
                          label: collection.titleField || 'label',
 | 
			
		||||
                          value: 'value',
 | 
			
		||||
                          label: collection?.titleField || 'id',
 | 
			
		||||
                          value: 'id',
 | 
			
		||||
                        },
 | 
			
		||||
                      },
 | 
			
		||||
                      'x-reactions': [
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user