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:
被雨水过滤的空气-Rairn 2023-04-22 11:04:10 +08:00 committed by GitHub
parent 7b892cec50
commit 71af0f16a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 26 deletions

View File

@ -42,6 +42,12 @@ const titlePrompt = 'Default title for each record';
const expectTypes = ['string', 'integer', 'bigInt', 'float', 'double', 'decimal', 'date', 'dateonly', 'time']; const expectTypes = ['string', 'integer', 'bigInt', 'float', 'double', 'decimal', 'date', 'dateonly', 'time'];
const excludeInterfaces = ['icon']; 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 CurrentFields = (props) => {
const compile = useCompile(); const compile = useCompile();
const { getInterface } = useCollectionManager(); 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' }}> <Tooltip title={t(titlePrompt)} placement="right" overlayInnerStyle={{ textAlign: 'center' }}>
<Switch <Switch
size="small" 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' }}> <Tooltip title={t(titlePrompt)} placement="right" overlayInnerStyle={{ textAlign: 'center' }}>
<Switch <Switch
size="small" size="small"

View File

@ -47,6 +47,11 @@ export const linkTo: IField = {
}, },
availableTypes: ['belongsToMany'], availableTypes: ['belongsToMany'],
schemaInitialize(schema: ISchema, { readPretty, block, targetCollection }) { 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') { if (targetCollection?.template === 'file') {
const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' }; const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
fieldNames.label = 'preview'; fieldNames.label = 'preview';

View File

@ -51,8 +51,13 @@ export const m2m: IField = {
}, },
}, },
}, },
availableTypes:['belongsToMany'], availableTypes: ['belongsToMany'],
schemaInitialize(schema: ISchema, { readPretty, block, targetCollection }) { 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') { if (targetCollection?.template === 'file') {
const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' }; const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
fieldNames.label = 'preview'; fieldNames.label = 'preview';

View File

@ -52,6 +52,11 @@ export const m2o: IField = {
}, },
availableTypes:['belongsTo'], availableTypes:['belongsTo'],
schemaInitialize(schema: ISchema, { block, readPretty, targetCollection }) { 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') { if (targetCollection?.template === 'file') {
const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' }; const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
fieldNames.label = 'preview'; fieldNames.label = 'preview';

View File

@ -51,7 +51,12 @@ export const o2m: IField = {
}, },
}, },
availableTypes: ['hasMany'], 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 (block === 'Form') {
if (schema['x-component'] === 'TableField') { if (schema['x-component'] === 'TableField') {
const association = `${field.collectionName}.${field.name}`; const association = `${field.collectionName}.${field.name}`;

View File

@ -475,6 +475,11 @@ export const obo: IField = {
schema['x-component-props']['size'] = 'small'; 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') { if (targetCollection?.template === 'file') {
const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' }; const fieldNames = schema['x-component-props']['fieldNames'] || { label: 'preview', value: 'id' };
fieldNames.label = 'preview'; fieldNames.label = 'preview';

View File

@ -13,6 +13,7 @@ import {
useCollectionManager, useCollectionManager,
useSortFields, useSortFields,
} from '../../../collection-manager'; } from '../../../collection-manager';
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings'; import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { useCompile, useDesignable, useFieldComponentOptions, useFieldTitle } from '../../hooks'; import { useCompile, useDesignable, useFieldComponentOptions, useFieldTitle } from '../../hooks';
import { removeNullCondition } from '../filter'; import { removeNullCondition } from '../filter';
@ -91,7 +92,7 @@ interface AssociationSelectInterface {
export const AssociationSelect = InternalAssociationSelect as unknown as AssociationSelectInterface; export const AssociationSelect = InternalAssociationSelect as unknown as AssociationSelectInterface;
AssociationSelect.Designer = () => { AssociationSelect.Designer = function Designer() {
const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager(); const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
const { getField, template } = useCollection(); const { getField, template } = useCollection();
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();
@ -99,7 +100,6 @@ AssociationSelect.Designer = () => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { t } = useTranslation(); const { t } = useTranslation();
const tk = useFilterByTk(); const tk = useFilterByTk();
const {} = useCollection();
const { dn, refresh, insertAdjacent } = useDesignable(); const { dn, refresh, insertAdjacent } = useDesignable();
const compile = useCompile(); const compile = useCompile();
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
@ -134,7 +134,7 @@ AssociationSelect.Designer = () => {
} }
const options = targetFields const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean') .filter((field) => isTitleField(field))
.map((field) => ({ .map((field) => ({
value: field?.name, value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name, label: compile(field?.uiSchema?.title) || field?.name,
@ -691,7 +691,7 @@ AssociationSelect.Designer = () => {
* *
* @returns * @returns
*/ */
AssociationSelect.FilterDesigner = () => { AssociationSelect.FilterDesigner = function FilterDesigner() {
const { getCollectionFields, getInterface, getCollectionJoinField } = useCollectionManager(); const { getCollectionFields, getInterface, getCollectionJoinField } = useCollectionManager();
const { getField } = useCollection(); const { getField } = useCollection();
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();

View File

@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider'; import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider';
import { BlockRequestContext, useFilterByTk, useFormBlockContext } from '../../../block-provider'; import { BlockRequestContext, useFilterByTk, useFormBlockContext } from '../../../block-provider';
import { Collection, useCollection, useCollectionManager } from '../../../collection-manager'; import { Collection, useCollection, useCollectionManager } from '../../../collection-manager';
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings'; import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks'; import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks';
import { BlockItem } from '../block-item'; 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 { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
const { getField } = useCollection(); const { getField } = useCollection();
const tk = useFilterByTk(); const tk = useFilterByTk();
@ -97,11 +98,14 @@ FormItem.Designer = () => {
initialValue['required'] = field.required; initialValue['required'] = field.required;
} }
const options = targetFields const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean') .filter((field) => {
return isTitleField(field);
})
.map((field) => ({ .map((field) => ({
value: field?.name, value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name, label: compile(field?.uiSchema?.title) || field?.name,
})); }));
let readOnlyMode = 'editable'; let readOnlyMode = 'editable';
if (fieldSchema['x-disabled'] === true) { if (fieldSchema['x-disabled'] === true) {
readOnlyMode = 'readonly'; readOnlyMode = 'readonly';

View File

@ -1,9 +1,11 @@
import { LoadingOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons';
import { connect, mapProps, mapReadPretty } from '@formily/react'; import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react';
import { SelectProps } from 'antd'; import { SelectProps, Tag } from 'antd';
import moment from 'moment';
import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { ResourceActionOptions, useRequest } from '../../../api-client'; import { ResourceActionOptions, useRequest } from '../../../api-client';
import { mergeFilter } from '../../../block-provider/SharedFilterProvider'; import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { Select, defaultFieldNames } from '../select'; import { Select, defaultFieldNames } from '../select';
import { ReadPretty } from './ReadPretty'; import { ReadPretty } from './ReadPretty';
@ -15,6 +17,7 @@ export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
wait?: number; wait?: number;
manual?: boolean; manual?: boolean;
mapOptions?: (data: any) => RemoteSelectProps['fieldNames']; mapOptions?: (data: any) => RemoteSelectProps['fieldNames'];
targetField?: any;
service: ResourceActionOptions<P>; service: ResourceActionOptions<P>;
}; };
@ -28,10 +31,73 @@ const InternalRemoteSelect = connect(
objectValue, objectValue,
manual = true, manual = true,
mapOptions, mapOptions,
targetField: _targetField,
...others ...others
} = props; } = props;
const compile = useCompile(); const compile = useCompile();
const firstRun = useRef(false); 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( const { data, run, loading } = useRequest(
{ {
@ -82,11 +148,8 @@ const InternalRemoteSelect = connect(
const getOptionsByFieldNames = useCallback( const getOptionsByFieldNames = useCallback(
(item) => { (item) => {
if (mapOptions) {
return mapOptions(item);
}
return Object.keys(fieldNames).reduce((obj, key) => { return Object.keys(fieldNames).reduce((obj, key) => {
const value = item[fieldNames[key]]; let value = item[fieldNames[key]];
if (value) { if (value) {
// support hidden, disabled, etc. // support hidden, disabled, etc.
obj[['label', 'value', 'options'].includes(key) ? fieldNames[key] : key] = obj[['label', 'value', 'options'].includes(key) ? fieldNames[key] : key] =
@ -138,7 +201,7 @@ const InternalRemoteSelect = connect(
value={value} value={value}
{...others} {...others}
loading={loading} loading={loading}
options={options} options={mapOptionsToTags(options)}
/> />
); );
}, },

View File

@ -21,9 +21,10 @@ export const FormDataTemplates = observer((props: any) => {
const { useProps } = props; const { useProps } = props;
const { defaultValues, collectionName } = useProps(); const { defaultValues, collectionName } = useProps();
const { collectionList, getEnableFieldTree } = useCollectionState(collectionName); const { collectionList, getEnableFieldTree } = useCollectionState(collectionName);
const { getCollection } = useCollectionManager(); const { getCollection, getCollectionField } = useCollectionManager();
const collection = getCollection(collectionName); const collection = getCollection(collectionName);
const { t } = useTranslation(); const { t } = useTranslation();
const field = getCollectionField(`${collectionName}.${collection?.titleField || 'id'}`);
return ( return (
<SchemaComponent <SchemaComponent
@ -83,16 +84,24 @@ export const FormDataTemplates = observer((props: any) => {
multiple: false, multiple: false,
objectValue: false, objectValue: false,
manual: false, manual: false,
mapOptions: (item) => { targetField: field,
return { mapOptions(option) {
...item, try {
[collection.titleField || 'label']: `#${item.id} ${item[collection.titleField] || ''}`, const label = collection?.titleField || 'id';
value: item.id, option[label] = (
}; <>
#{option.id} {option[label]}
</>
);
return option;
} catch (error) {
console.error(error);
return option;
}
}, },
fieldNames: { fieldNames: {
label: collection.titleField || 'label', label: collection?.titleField || 'id',
value: 'value', value: 'id',
}, },
}, },
'x-reactions': [ 'x-reactions': [