feat: display association fields (#512)
* feat: association field features * fix: remove comments * fix: association field in creation form will trigger error * fix: column decorator title * fix: column designer title * fix: association field in table * feat: adjust documents * fix: remove m2o subfield mode * fix: adjust title field display condition * fix: relation field title bug * fix: o2m multiple is true * feat: association fields are loaded on demand * fix: support sub field * feat: remove FormField require config * fix: two lines in Columns config menu of table block * fix: could not find schema node * fix: add form context to internal table block * fix(client): non-empty judgment * feat: translations * fix: add / edit field title compile * fix: unique * fix: association feature bugs * feat: add oho & o2m selector filter * fix: add field added logic in FormField and TableField * fix: remove updateAssociationValues middleware * feat: recordprovider in association fields * feat: add kanban association appends Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
95d0b524d0
commit
32e744319e
@ -9,6 +9,7 @@ import { KanbanBlockProvider, useKanbanBlockProps } from './KanbanBlockProvider'
|
|||||||
import { TableBlockProvider, useTableBlockProps } from './TableBlockProvider';
|
import { TableBlockProvider, useTableBlockProps } from './TableBlockProvider';
|
||||||
import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider';
|
import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider';
|
||||||
import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider';
|
import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider';
|
||||||
|
import { FormFieldProvider, useFormFieldProps } from './FormFieldProvider';
|
||||||
|
|
||||||
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||||
return (
|
return (
|
||||||
@ -19,6 +20,7 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
TableBlockProvider,
|
TableBlockProvider,
|
||||||
TableSelectorProvider,
|
TableSelectorProvider,
|
||||||
FormBlockProvider,
|
FormBlockProvider,
|
||||||
|
FormFieldProvider,
|
||||||
DetailsBlockProvider,
|
DetailsBlockProvider,
|
||||||
KanbanBlockProvider,
|
KanbanBlockProvider,
|
||||||
RecordLink,
|
RecordLink,
|
||||||
@ -30,6 +32,7 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
useParamsFromRecord,
|
useParamsFromRecord,
|
||||||
useCalendarBlockProps,
|
useCalendarBlockProps,
|
||||||
useFormBlockProps,
|
useFormBlockProps,
|
||||||
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { useField } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
@ -29,6 +29,7 @@ const InternalFormBlockProvider = (props) => {
|
|||||||
field,
|
field,
|
||||||
service,
|
service,
|
||||||
resource,
|
resource,
|
||||||
|
updateAssociationValues: [],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
import { createForm, onFieldReact, onFormInputChange, onFormValuesChange } from '@formily/core';
|
||||||
|
import { ArrayField, Field, ObjectField } from '@formily/core';
|
||||||
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
|
import { RecordProvider } from '../record-provider';
|
||||||
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
|
import { useFormBlockContext } from './FormBlockProvider';
|
||||||
|
|
||||||
|
export const FormFieldContext = createContext<any>({});
|
||||||
|
|
||||||
|
const InternalFormFieldProvider = (props) => {
|
||||||
|
const { action, readPretty, fieldName } = props;
|
||||||
|
const formBlockCtx = useFormBlockContext();
|
||||||
|
|
||||||
|
if (!formBlockCtx?.updateAssociationValues?.includes(fieldName)) {
|
||||||
|
formBlockCtx?.updateAssociationValues?.push(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const field = useField();
|
||||||
|
|
||||||
|
const form = useMemo(
|
||||||
|
() =>
|
||||||
|
createForm({
|
||||||
|
effects() {
|
||||||
|
onFormValuesChange((form) => {
|
||||||
|
formBlockCtx?.form?.setValuesIn(fieldName, form.values);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
readPretty,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { resource, service } = useBlockRequestContext();
|
||||||
|
if (service.loading) {
|
||||||
|
return <Spin />;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('InternalFormFieldProvider', fieldName);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecordProvider record={service?.data?.data}>
|
||||||
|
<FormFieldContext.Provider
|
||||||
|
value={{
|
||||||
|
action,
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
service,
|
||||||
|
resource,
|
||||||
|
fieldName,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</FormFieldContext.Provider>
|
||||||
|
</RecordProvider>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithoutFormFieldResource = createContext(null);
|
||||||
|
|
||||||
|
export const FormFieldProvider = (props) => {
|
||||||
|
console.log('FormFieldProvider', props);
|
||||||
|
return (
|
||||||
|
<WithoutFormFieldResource.Provider value={false}>
|
||||||
|
<BlockProvider block={'FormField'} {...props}>
|
||||||
|
<InternalFormFieldProvider {...props} />
|
||||||
|
</BlockProvider>
|
||||||
|
</WithoutFormFieldResource.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFormFieldContext = () => {
|
||||||
|
return useContext(FormFieldContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFormFieldProps = () => {
|
||||||
|
const ctx = useFormFieldContext();
|
||||||
|
useEffect(() => {
|
||||||
|
ctx?.form?.setInitialValues(ctx.service?.data?.data);
|
||||||
|
}, []);
|
||||||
|
return {
|
||||||
|
form: ctx.form,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { useField } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
|
import uniq from 'lodash/uniq';
|
||||||
import React, { createContext, useContext, useEffect } from 'react';
|
import React, { createContext, useContext, useEffect } from 'react';
|
||||||
import { useACLRoleContext } from '../acl';
|
import { useACLRoleContext } from '../acl';
|
||||||
import { useCollection } from '../collection-manager';
|
import { useCollection, useCollectionManager } from '../collection-manager';
|
||||||
import { toColumns } from '../schema-component/antd/kanban/Kanban';
|
import { toColumns } from '../schema-component/antd/kanban/Kanban';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
|
|
||||||
@ -48,9 +49,61 @@ const InternalKanbanBlockProvider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const recursiveProperties = (schema: Schema, component = 'CollectionField', associationFields, appends = []) => {
|
||||||
|
schema.mapProperties((s) => {
|
||||||
|
const name = s.name.toString();
|
||||||
|
if (s['x-component'] === component && !appends.includes(name)) {
|
||||||
|
// 关联字段和关联的关联字段
|
||||||
|
const [firstName] = name.split('.');
|
||||||
|
if (associationFields.has(name)) {
|
||||||
|
appends.push(name);
|
||||||
|
} else if (associationFields.has(firstName) && !appends.includes(firstName)) {
|
||||||
|
appends.push(firstName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
recursiveProperties(s, component, associationFields, appends);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAssociationNames = (collection) => {
|
||||||
|
const { getCollectionFields } = useCollectionManager();
|
||||||
|
const collectionFields = getCollectionFields(collection);
|
||||||
|
const associationFields = new Set();
|
||||||
|
for (const collectionField of collectionFields) {
|
||||||
|
if (collectionField.target) {
|
||||||
|
associationFields.add(collectionField.name);
|
||||||
|
const fields = getCollectionFields(collectionField.target);
|
||||||
|
for (const field of fields) {
|
||||||
|
if (field.target) {
|
||||||
|
associationFields.add(`${collectionField.name}.${field.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const kanbanSchema = fieldSchema.reduceProperties((buf, schema) => {
|
||||||
|
if (schema['x-component'] === 'KanbanV2') {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}, new Schema({}));
|
||||||
|
const gridSchema = kanbanSchema?.properties?.card?.properties?.grid;
|
||||||
|
const appends = [];
|
||||||
|
recursiveProperties(gridSchema, 'CollectionField', associationFields, appends);
|
||||||
|
console.log('useAssociationNames', appends);
|
||||||
|
return uniq(appends);
|
||||||
|
};
|
||||||
|
|
||||||
export const KanbanBlockProvider = (props) => {
|
export const KanbanBlockProvider = (props) => {
|
||||||
|
const params = { ...props.params };
|
||||||
|
const appends = useAssociationNames(props.collection);
|
||||||
|
console.log('KanbanBlockProvider', appends);
|
||||||
|
if (!Object.keys(params).includes('appends')) {
|
||||||
|
params['appends'] = appends;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BlockProvider {...props}>
|
<BlockProvider {...props} params={params}>
|
||||||
<InternalKanbanBlockProvider {...props} />
|
<InternalKanbanBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
import { ArrayField, Field } from '@formily/core';
|
import { ArrayField, Field } from '@formily/core';
|
||||||
import { useField } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import React, { createContext, useContext, useEffect } from 'react';
|
import React, { createContext, useContext, useEffect } from 'react';
|
||||||
import { APIClient } from '../api-client';
|
import { APIClient } from '../api-client';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
|
import { useFormBlockContext } from './FormBlockProvider';
|
||||||
|
import { useFormFieldContext } from './FormFieldProvider';
|
||||||
|
|
||||||
export const TableFieldContext = createContext<any>({});
|
export const TableFieldContext = createContext<any>({});
|
||||||
|
|
||||||
const InternalTableFieldProvider = (props) => {
|
const InternalTableFieldProvider = (props) => {
|
||||||
const { params = {}, showIndex, dragSort } = props;
|
const { params = {}, showIndex, dragSort, fieldName } = props;
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { resource, service } = useBlockRequestContext();
|
const { resource, service } = useBlockRequestContext();
|
||||||
|
|
||||||
|
const formBlockCtx = useFormBlockContext();
|
||||||
|
const formFieldCtx = useFormFieldContext();
|
||||||
|
|
||||||
|
const fullFieldName = formFieldCtx && formFieldCtx.fieldName ? `${formFieldCtx.fieldName}.${fieldName}` : fieldName;
|
||||||
|
|
||||||
|
if (!formBlockCtx?.updateAssociationValues?.includes(fullFieldName)) {
|
||||||
|
formBlockCtx?.updateAssociationValues?.push(fullFieldName);
|
||||||
|
}
|
||||||
// if (service.loading) {
|
// if (service.loading) {
|
||||||
// return <Spin />;
|
// return <Spin />;
|
||||||
// }
|
// }
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { useField } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import React, { createContext, useContext, useEffect } from 'react';
|
import React, { createContext, useContext, useEffect } from 'react';
|
||||||
import { useCollectionManager } from '../collection-manager';
|
import { useCollection, useCollectionManager } from '../collection-manager';
|
||||||
|
import { useRecord } from '../record-provider';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
|
import { useFormBlockContext } from './FormBlockProvider';
|
||||||
|
|
||||||
export const TableSelectorContext = createContext<any>({});
|
export const TableSelectorContext = createContext<any>({});
|
||||||
|
|
||||||
@ -36,7 +38,23 @@ const useAssociationNames = (collection) => {
|
|||||||
return names;
|
return names;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const recursiveParent = (schema: Schema, component) => {
|
||||||
|
return schema['x-component'] === component
|
||||||
|
? schema
|
||||||
|
: (schema.parent ? recursiveParent(schema.parent, component) : null);
|
||||||
|
}
|
||||||
|
|
||||||
export const TableSelectorProvider = (props) => {
|
export const TableSelectorProvider = (props) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const ctx = useFormBlockContext()
|
||||||
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
|
const record = useRecord();
|
||||||
|
|
||||||
|
const collectionFieldSchema = recursiveParent(fieldSchema, 'CollectionField');
|
||||||
|
// const value = ctx.form.query(collectionFieldSchema?.name).value();
|
||||||
|
const collectionField = getCollectionJoinField(collectionFieldSchema?.['x-collection-field']);
|
||||||
|
|
||||||
|
console.log('TableSelectorProvider', collectionFieldSchema, collectionField, record);
|
||||||
const params = { ...props.params };
|
const params = { ...props.params };
|
||||||
const appends = useAssociationNames(props.collection);
|
const appends = useAssociationNames(props.collection);
|
||||||
if (props.dragSort) {
|
if (props.dragSort) {
|
||||||
@ -45,6 +63,23 @@ export const TableSelectorProvider = (props) => {
|
|||||||
if (appends?.length) {
|
if (appends?.length) {
|
||||||
params['appends'] = appends;
|
params['appends'] = appends;
|
||||||
}
|
}
|
||||||
|
if (collectionField) {
|
||||||
|
if (['oho', 'o2m'].includes(collectionField.interface)) {
|
||||||
|
params['filter'] = {
|
||||||
|
$or: [{
|
||||||
|
[collectionField.foreignKey]: {
|
||||||
|
$is: null,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
[collectionField.foreignKey]: {
|
||||||
|
$eq: record?.[collectionField.sourceKey],
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (['obo'].includes(collectionField.interface)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BlockProvider {...props} params={params}>
|
<BlockProvider {...props} params={params}>
|
||||||
<InternalTableSelectorProvider {...props} params={params} />
|
<InternalTableSelectorProvider {...props} params={params} />
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Schema as SchemaCompiler } from '@formily/json-schema';
|
import { Schema as SchemaCompiler } from '@formily/json-schema';
|
||||||
import { useField, useFieldSchema, useForm } from '@formily/react';
|
import { useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
|
import { useFormBlockContext } from '@nocobase/client';
|
||||||
import { message, Modal } from 'antd';
|
import { message, Modal } from 'antd';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -73,7 +74,7 @@ function getFormValues(filterByTk, field, form, fieldNames, getField, resource)
|
|||||||
if (field.added && !field.added.has(key)) {
|
if (field.added && !field.added.has(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (['subTable', 'o2m'].includes(collectionField.interface)) {
|
if (['subTable', 'o2m', 'o2o', 'oho', 'obo', 'm2o'].includes(collectionField.interface)) {
|
||||||
values[key] = form.values[key];
|
values[key] = form.values[key];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -304,9 +305,11 @@ export const useUpdateActionProps = () => {
|
|||||||
const { setVisible } = useActionContext();
|
const { setVisible } = useActionContext();
|
||||||
const actionSchema = useFieldSchema();
|
const actionSchema = useFieldSchema();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const record = useRecord();
|
||||||
const { fields, getField } = useCollection();
|
const { fields, getField } = useCollection();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const actionField = useField();
|
const actionField = useField();
|
||||||
|
const { updateAssociationValues } = useFormBlockContext();
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { assignedValues, onSuccess, overwriteValues, skipValidator } = actionSchema?.['x-action-settings'] ?? {};
|
const { assignedValues, onSuccess, overwriteValues, skipValidator } = actionSchema?.['x-action-settings'] ?? {};
|
||||||
@ -315,7 +318,7 @@ export const useUpdateActionProps = () => {
|
|||||||
await form.submit();
|
await form.submit();
|
||||||
}
|
}
|
||||||
const fieldNames = fields.map((field) => field.name);
|
const fieldNames = fields.map((field) => field.name);
|
||||||
const values = getFormValues(filterByTk, field, form, fieldNames, getField, resource);
|
let values = getFormValues(filterByTk, field, form, fieldNames, getField, resource);
|
||||||
actionField.data = field.data || {};
|
actionField.data = field.data || {};
|
||||||
actionField.data.loading = true;
|
actionField.data.loading = true;
|
||||||
try {
|
try {
|
||||||
@ -326,6 +329,7 @@ export const useUpdateActionProps = () => {
|
|||||||
...overwriteValues,
|
...overwriteValues,
|
||||||
...assignedValues,
|
...assignedValues,
|
||||||
},
|
},
|
||||||
|
updateAssociationValues
|
||||||
});
|
});
|
||||||
actionField.data.loading = false;
|
actionField.data.loading = false;
|
||||||
if (!(resource instanceof TableFieldResource)) {
|
if (!(resource instanceof TableFieldResource)) {
|
||||||
|
@ -7,4 +7,5 @@ export * from './KanbanBlockProvider';
|
|||||||
export * from './TableBlockProvider';
|
export * from './TableBlockProvider';
|
||||||
export * from './TableFieldProvider';
|
export * from './TableFieldProvider';
|
||||||
export * from './TableSelectorProvider';
|
export * from './TableSelectorProvider';
|
||||||
|
export * from './FormFieldProvider';
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import { useCollectionField } from './hooks';
|
|||||||
// TODO: 初步适配
|
// TODO: 初步适配
|
||||||
const InternalField: React.FC = (props) => {
|
const InternalField: React.FC = (props) => {
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
|
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { name, interface: interfaceType, uiSchema } = useCollectionField();
|
const { name, interface: interfaceType, uiSchema } = useCollectionField();
|
||||||
const component = useComponent(uiSchema?.['x-component']);
|
const component = useComponent(uiSchema?.['x-component']);
|
||||||
@ -68,13 +67,15 @@ const InternalField: React.FC = (props) => {
|
|||||||
if (!uiSchema) {
|
if (!uiSchema) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return React.createElement(component, props, props.children);
|
return React.createElement(component, props, props.children);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CollectionField = connect((props) => {
|
export const CollectionField = connect((props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = fieldSchema?.['x-component-props']?.['field'];
|
||||||
return (
|
return (
|
||||||
<CollectionFieldProvider name={fieldSchema.name}>
|
<CollectionFieldProvider name={fieldSchema.name} field={field}>
|
||||||
<InternalField {...props} />
|
<InternalField {...props} />
|
||||||
</CollectionFieldProvider>
|
</CollectionFieldProvider>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { SchemaKey } from '@formily/react';
|
import { SchemaKey, useFieldSchema } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CollectionFieldContext } from './context';
|
import { CollectionFieldContext } from './context';
|
||||||
import { useCollection } from './hooks';
|
import { useCollection, useCollectionManager } from './hooks';
|
||||||
import { CollectionFieldOptions } from './types';
|
import { CollectionFieldOptions } from './types';
|
||||||
|
|
||||||
export const CollectionFieldProvider: React.FC<{ name?: SchemaKey; field?: CollectionFieldOptions }> = (props) => {
|
export const CollectionFieldProvider: React.FC<{ name?: SchemaKey; field?: CollectionFieldOptions }> = (props) => {
|
||||||
const { name, field, children } = props;
|
const { name, field, children } = props;
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
const { getField } = useCollection();
|
const { getField } = useCollection();
|
||||||
const value = field || getField(field?.name || name);
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
|
const value = field || getField(field?.name || name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ const getSchema = (schema: IField, record: any, compile): ISchema => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
title: `${record.title} - ${compile('{{ t("Add field") }}')}`,
|
title: `${compile(record.title)} - ${compile('{{ t("Add field") }}')}`,
|
||||||
properties: {
|
properties: {
|
||||||
summary: {
|
summary: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
@ -37,7 +37,7 @@ const getSchema = (schema: IField, record: any, compile): ISchema => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
title: `${record.__parent?.title} - ${compile('{{ t("Edit field") }}')}`,
|
title: `${compile(record.__parent?.title)} - ${compile('{{ t("Edit field") }}')}`,
|
||||||
properties: {
|
properties: {
|
||||||
summary: {
|
summary: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
@ -45,7 +45,13 @@ export const linkTo: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty }) {
|
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
||||||
|
if (block === 'Form') {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
if (readPretty) {
|
if (readPretty) {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
@ -53,7 +59,8 @@ export const linkTo: IField = {
|
|||||||
} else {
|
} else {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
selector: cloneDeep(recordPickerSelector),
|
selector: cloneDeep(recordPickerSelector),
|
||||||
};
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialize: (values: any) => {
|
initialize: (values: any) => {
|
||||||
|
@ -46,6 +46,12 @@ export const m2m: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
||||||
|
if (block === 'Form') {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
if (readPretty) {
|
if (readPretty) {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
@ -53,7 +59,8 @@ export const m2m: IField = {
|
|||||||
} else {
|
} else {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
selector: cloneDeep(recordPickerSelector),
|
selector: cloneDeep(recordPickerSelector),
|
||||||
};
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (['Table', 'Kanban'].includes(block)) {
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
|
@ -44,7 +44,53 @@ export const m2o: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
schemaInitialize(schema: ISchema, { field, block, readPretty, action }) {
|
||||||
|
if (block === 'Form' && schema['x-component'] === 'FormField') {
|
||||||
|
const association = `${field.collectionName}.${field.name}`;
|
||||||
|
schema.type = 'void';
|
||||||
|
schema.properties = {
|
||||||
|
block: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormFieldProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: field.target,
|
||||||
|
association: association,
|
||||||
|
resource: association,
|
||||||
|
action,
|
||||||
|
fieldName: field.name,
|
||||||
|
readPretty
|
||||||
|
},
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-component-props': {
|
||||||
|
bordered: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
[field.name]: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-component-props': {
|
||||||
|
useProps: '{{ useFormFieldProps }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
__form_grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'FormItemInitializers',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schema.type = 'string';
|
||||||
|
if (block === 'Form') {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
if (readPretty) {
|
if (readPretty) {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
@ -52,7 +98,9 @@ export const m2o: IField = {
|
|||||||
} else {
|
} else {
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
selector: cloneDeep(recordPickerSelector),
|
selector: cloneDeep(recordPickerSelector),
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (['Table', 'Kanban'].includes(block)) {
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { relationshipType } from './properties';
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
|
||||||
export const o2m: IField = {
|
export const o2m: IField = {
|
||||||
@ -15,14 +16,14 @@ export const o2m: IField = {
|
|||||||
// name,
|
// name,
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
// title,
|
// title,
|
||||||
'x-component': 'TableField',
|
'x-component': 'RecordPicker',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
// mode: 'tags',
|
// mode: 'tags',
|
||||||
// multiple: true,
|
multiple: true,
|
||||||
// fieldNames: {
|
fieldNames: {
|
||||||
// label: 'id',
|
label: 'id',
|
||||||
// value: 'id',
|
value: 'id',
|
||||||
// },
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reverseField: {
|
reverseField: {
|
||||||
@ -43,10 +44,10 @@ export const o2m: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { field, readPretty, block }) {
|
schemaInitialize(schema: ISchema, { field, block, readPretty }) {
|
||||||
|
if (block === 'Form' && schema['x-component'] === 'TableField') {
|
||||||
const association = `${field.collectionName}.${field.name}`;
|
const association = `${field.collectionName}.${field.name}`;
|
||||||
schema['type'] = 'void';
|
schema['type'] = 'void';
|
||||||
schema['x-component'] = 'TableField';
|
|
||||||
schema['properties'] = {
|
schema['properties'] = {
|
||||||
block: {
|
block: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -61,6 +62,7 @@ export const o2m: IField = {
|
|||||||
},
|
},
|
||||||
showIndex: true,
|
showIndex: true,
|
||||||
dragSort: false,
|
dragSort: false,
|
||||||
|
fieldName: field.name,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
actions: {
|
actions: {
|
||||||
@ -83,7 +85,27 @@ export const o2m: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
schema['x-component'] = 'CollectionField';
|
||||||
|
schema.type = 'object';
|
||||||
|
|
||||||
|
if (block === 'Form') {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (readPretty) {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
schema['properties'] = {
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// if (readPretty) {
|
// if (readPretty) {
|
||||||
// schema['properties'] = {
|
// schema['properties'] = {
|
||||||
// viewer: cloneDeep(recordPickerViewer),
|
// viewer: cloneDeep(recordPickerViewer),
|
||||||
|
@ -3,6 +3,66 @@ import { cloneDeep } from 'lodash';
|
|||||||
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
|
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
|
||||||
|
const internalSchameInitialize = (schema: ISchema, { field, block, readPretty, action }) => {
|
||||||
|
if (block === 'Form' && schema['x-component'] === 'FormField') {
|
||||||
|
const association = `${field.collectionName}.${field.name}`;
|
||||||
|
schema.type = 'void';
|
||||||
|
schema.properties = {
|
||||||
|
block: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormFieldProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: field.target,
|
||||||
|
association: association,
|
||||||
|
resource: association,
|
||||||
|
action: action,
|
||||||
|
fieldName: field.name,
|
||||||
|
readPretty,
|
||||||
|
},
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-component-props': {
|
||||||
|
bordered: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
[field.name]: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-component-props': {
|
||||||
|
useProps: '{{ useFormFieldProps }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
__form_grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'FormItemInitializers',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schema.type = 'string';
|
||||||
|
if (block === 'Form') {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (readPretty) {
|
||||||
|
schema['properties'] = {
|
||||||
|
viewer: cloneDeep(recordPickerViewer),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
schema['properties'] = {
|
||||||
|
selector: cloneDeep(recordPickerSelector),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const o2o: IField = {
|
export const o2o: IField = {
|
||||||
name: 'o2o',
|
name: 'o2o',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@ -45,16 +105,8 @@ export const o2o: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
schemaInitialize(schema: ISchema, { field, block, readPretty, action }) {
|
||||||
if (readPretty) {
|
internalSchameInitialize(schema, { field, block, readPretty, action });
|
||||||
schema['properties'] = {
|
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
schema['properties'] = {
|
|
||||||
selector: cloneDeep(recordPickerSelector),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (['Table', 'Kanban'].includes(block)) {
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
@ -217,16 +269,8 @@ export const oho: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
schemaInitialize(schema: ISchema, { field, block, readPretty, action }) {
|
||||||
if (readPretty) {
|
internalSchameInitialize(schema, { field, block, readPretty, action });
|
||||||
schema['properties'] = {
|
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
schema['properties'] = {
|
|
||||||
selector: cloneDeep(recordPickerSelector),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (['Table', 'Kanban'].includes(block)) {
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
@ -388,16 +432,8 @@ export const obo: IField = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schemaInitialize(schema: ISchema, { readPretty, block }) {
|
schemaInitialize(schema: ISchema, { field, block, readPretty, action }) {
|
||||||
if (readPretty) {
|
internalSchameInitialize(schema, { field, block, readPretty, action });
|
||||||
schema['properties'] = {
|
|
||||||
viewer: cloneDeep(recordPickerViewer),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
schema['properties'] = {
|
|
||||||
selector: cloneDeep(recordPickerSelector),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (['Table', 'Kanban'].includes(block)) {
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
|
@ -79,7 +79,7 @@ export default {
|
|||||||
"Add new": "Add new",
|
"Add new": "Add new",
|
||||||
"Add record": "Add record",
|
"Add record": "Add record",
|
||||||
"Custom field display name": "Custom field display name",
|
"Custom field display name": "Custom field display name",
|
||||||
"Display fields": "Display fields",
|
"Display fields": "Display collection fields",
|
||||||
"Edit record": "Edit record",
|
"Edit record": "Edit record",
|
||||||
"Delete menu item": "Delete menu item",
|
"Delete menu item": "Delete menu item",
|
||||||
"Add page": "Add page",
|
"Add page": "Add page",
|
||||||
@ -541,5 +541,10 @@ export default {
|
|||||||
"One to one (has one)": "One to one (has one)",
|
"One to one (has one)": "One to one (has one)",
|
||||||
"One to one (belongs to)": "One to one (belongs to)",
|
"One to one (belongs to)": "One to one (belongs to)",
|
||||||
"Use the same time zone (GMT) for all users": "Use the same time zone (GMT) for all users",
|
"Use the same time zone (GMT) for all users": "Use the same time zone (GMT) for all users",
|
||||||
|
"Block title": "Block title",
|
||||||
|
"Edit block title": "Edit block title",
|
||||||
"Province/city/area name": "Province/city/area name",
|
"Province/city/area name": "Province/city/area name",
|
||||||
|
"Field component": "Field component",
|
||||||
|
"Subtable": "Subtable",
|
||||||
|
"Subform": "Subform",
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,11 @@ export default {
|
|||||||
"Many to one description": "用于创建多对一关系,比如一个城市只能属于一个国家,一个国家可以有多个城市。作为字段存在时,它是一个下拉选择用于选择目标数据表的数据。创建后,会在目标数据表里自动生成一个多对一字段。",
|
"Many to one description": "用于创建多对一关系,比如一个城市只能属于一个国家,一个国家可以有多个城市。作为字段存在时,它是一个下拉选择用于选择目标数据表的数据。创建后,会在目标数据表里自动生成一个多对一字段。",
|
||||||
"Many to many description": "用于创建多对多关系,比如一个学生会有多个老师,一个老师也会有多个学生。作为字段存在时,它是一个下拉选择用于选择目标数据表的数据。",
|
"Many to many description": "用于创建多对多关系,比如一个学生会有多个老师,一个老师也会有多个学生。作为字段存在时,它是一个下拉选择用于选择目标数据表的数据。",
|
||||||
"Generated automatically if left blank": "留空时,自动生成中间表",
|
"Generated automatically if left blank": "留空时,自动生成中间表",
|
||||||
"References fields of associated table": "引用关联表的字段",
|
"Display association fields": "显示关联表的字段",
|
||||||
|
"Field component": "字段组件",
|
||||||
|
"Subtable": "子表格",
|
||||||
|
"Subform": "子表单",
|
||||||
|
"Record picker": "数据选择器",
|
||||||
"Toggles the subfield mode": "切换子字段模式",
|
"Toggles the subfield mode": "切换子字段模式",
|
||||||
"Selector mode": "选择器模式",
|
"Selector mode": "选择器模式",
|
||||||
"Subtable mode": "子表格模式",
|
"Subtable mode": "子表格模式",
|
||||||
|
@ -2,15 +2,26 @@ import { css } from '@emotion/css';
|
|||||||
import { FormItem as Item } from '@formily/antd';
|
import { FormItem as Item } from '@formily/antd';
|
||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCompile, useDesignable } from '../..';
|
import { useCompile, useDesignable } from '../..';
|
||||||
import { useFormBlockContext } from '../../../block-provider';
|
import { useFilterByTk, useFormBlockContext } from '../../../block-provider';
|
||||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||||
import { BlockItem } from '../block-item';
|
import { BlockItem } from '../block-item';
|
||||||
import { HTMLEncode } from '../input/shared';
|
import { HTMLEncode } from '../input/shared';
|
||||||
|
|
||||||
|
const divWrap = (schema: ISchema) => {
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'div',
|
||||||
|
properties: {
|
||||||
|
[schema.name || uid()]: schema,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const FormItem: any = (props) => {
|
export const FormItem: any = (props) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
return (
|
return (
|
||||||
@ -36,18 +47,21 @@ export const FormItem: any = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
FormItem.Designer = () => {
|
FormItem.Designer = (props) => {
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const { getCollectionFields, getCollection, getInterface, getCollectionJoinField } = useCollectionManager();
|
||||||
const { getField } = useCollection();
|
const { getField } = useCollection();
|
||||||
|
const tk = useFilterByTk();
|
||||||
const { form } = useFormBlockContext();
|
const { form } = useFormBlockContext();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { dn, refresh } = useDesignable();
|
const { dn, refresh, insertAdjacent, insertBeforeBegin } = useDesignable();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const collectionField = getField(fieldSchema['name']);
|
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
|
||||||
|
const interfaceConfig = getInterface(collectionField?.interface);
|
||||||
const originalTitle = collectionField?.uiSchema?.title;
|
const originalTitle = collectionField?.uiSchema?.title;
|
||||||
const targetFields = collectionField?.target ? getCollectionFields(collectionField.target) : [];
|
const targetFields = collectionField?.target ? getCollectionFields(collectionField.target) : [];
|
||||||
|
const isSubFormAssocitionField = field.address.segments.includes('__form_grid');
|
||||||
const initialValue = {
|
const initialValue = {
|
||||||
title: field.title === originalTitle ? undefined : field.title,
|
title: field.title === originalTitle ? undefined : field.title,
|
||||||
};
|
};
|
||||||
@ -168,7 +182,7 @@ FormItem.Designer = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!field.readPretty && (
|
{!field.readPretty && fieldSchema['x-component'] !== 'FormField' && (
|
||||||
<SchemaSettings.SwitchItem
|
<SchemaSettings.SwitchItem
|
||||||
key="required"
|
key="required"
|
||||||
title={t('Required')}
|
title={t('Required')}
|
||||||
@ -187,6 +201,53 @@ FormItem.Designer = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{form && !isSubFormAssocitionField && ['o2o', 'oho', 'obo', 'o2m'].includes(collectionField?.interface) && (
|
||||||
|
<SchemaSettings.SelectItem
|
||||||
|
title={t('Field component')}
|
||||||
|
options={
|
||||||
|
collectionField?.interface === 'o2m'
|
||||||
|
? [
|
||||||
|
{ label: t('Record picker'), value: 'CollectionField' },
|
||||||
|
{ label: t('Subtable'), value: 'TableField' },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{ label: t('Record picker'), value: 'CollectionField' },
|
||||||
|
{ label: t('Subform'), value: 'FormField' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
value={fieldSchema['x-component']}
|
||||||
|
onChange={(v) => {
|
||||||
|
const schema: ISchema = {
|
||||||
|
name: collectionField.name,
|
||||||
|
type: 'void',
|
||||||
|
// title: compile(collectionField.uiSchema?.title),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-designer': 'FormItem.Designer',
|
||||||
|
'x-component': v,
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-collection-field': fieldSchema['x-collection-field'],
|
||||||
|
};
|
||||||
|
|
||||||
|
interfaceConfig?.schemaInitialize?.(schema, {
|
||||||
|
field: collectionField,
|
||||||
|
block: 'Form',
|
||||||
|
readPretty: field.readPretty,
|
||||||
|
action: tk ? 'get' : null,
|
||||||
|
});
|
||||||
|
|
||||||
|
insertAdjacent('beforeBegin', divWrap(schema), {
|
||||||
|
onSuccess: () => {
|
||||||
|
dn.remove(null, {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn: {
|
||||||
|
'x-component': 'Grid',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{form && !form?.readPretty && collectionField?.interface !== 'o2m' && (
|
{form && !form?.readPretty && collectionField?.interface !== 'o2m' && (
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.SelectItem
|
||||||
key="pattern"
|
key="pattern"
|
||||||
@ -198,7 +259,6 @@ FormItem.Designer = () => {
|
|||||||
]}
|
]}
|
||||||
value={readOnlyMode}
|
value={readOnlyMode}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
console.log('v', v);
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
};
|
};
|
||||||
@ -219,7 +279,6 @@ FormItem.Designer = () => {
|
|||||||
schema['x-read-pretty'] = true;
|
schema['x-read-pretty'] = true;
|
||||||
schema['x-disabled'] = false;
|
schema['x-disabled'] = false;
|
||||||
field.readPretty = true;
|
field.readPretty = true;
|
||||||
// field.disabled = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@ -232,6 +291,7 @@ FormItem.Designer = () => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dn.emit('patch', {
|
dn.emit('patch', {
|
||||||
schema,
|
schema,
|
||||||
});
|
});
|
||||||
@ -240,7 +300,7 @@ FormItem.Designer = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{collectionField?.target && (
|
{collectionField?.target && fieldSchema['x-component'] === 'CollectionField' && (
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.SelectItem
|
||||||
key="title-field"
|
key="title-field"
|
||||||
title={t('Title field')}
|
title={t('Title field')}
|
||||||
@ -254,12 +314,15 @@ FormItem.Designer = () => {
|
|||||||
...field.componentProps.fieldNames,
|
...field.componentProps.fieldNames,
|
||||||
label,
|
label,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if (fieldSchema['x-component-props']?.['field']?.['uiSchema']?.['x-component-props']) {
|
||||||
|
// fieldSchema['x-component-props']['field']['uiSchema']['x-component-props']['fieldNames'] = fieldNames;
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// }
|
||||||
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
||||||
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
|
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
|
||||||
field.componentProps.fieldNames = fieldNames;
|
schema['x-component-props'] = fieldSchema['x-component-props'];
|
||||||
schema['x-component-props'] = {
|
|
||||||
fieldNames,
|
|
||||||
};
|
|
||||||
dn.emit('patch', {
|
dn.emit('patch', {
|
||||||
schema,
|
schema,
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { observer, useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useFormBlockContext } from '../../../block-provider';
|
||||||
|
import { useCollection } from '../../../collection-manager';
|
||||||
|
import { useCompile } from '../../hooks';
|
||||||
|
|
||||||
|
export const FormField: any = observer((props) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { getField } = useCollection();
|
||||||
|
const field = useField();
|
||||||
|
const collectionField = getField(fieldSchema.name);
|
||||||
|
const compile = useCompile();
|
||||||
|
const ctx = useFormBlockContext();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!field.title) {
|
||||||
|
field.title = compile(collectionField?.uiSchema?.title);
|
||||||
|
}
|
||||||
|
if (ctx?.field) {
|
||||||
|
ctx.field.added = ctx.field.added || new Set();
|
||||||
|
ctx.field.added.add(fieldSchema.name);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return <div>{props.children}</div>;
|
||||||
|
});
|
@ -5,3 +5,4 @@ FormV2.Designer = FormDesigner;
|
|||||||
FormV2.ReadPrettyDesigner = ReadPrettyFormDesigner;
|
FormV2.ReadPrettyDesigner = ReadPrettyFormDesigner;
|
||||||
|
|
||||||
export { FormV2, DetailsDesigner };
|
export { FormV2, DetailsDesigner };
|
||||||
|
export * from './FormField';
|
||||||
|
@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useAPIClient } from '../../../api-client';
|
import { useAPIClient } from '../../../api-client';
|
||||||
import { createDesignable, useDesignable } from '../../../schema-component';
|
import { createDesignable, useDesignable } from '../../../schema-component';
|
||||||
import { SchemaInitializer } from '../../../schema-initializer';
|
import { SchemaInitializer } from '../../../schema-initializer';
|
||||||
import { useFormItemInitializerFields } from '../../../schema-initializer/utils';
|
import { useAssociatedFormItemInitializerFields, useFormItemInitializerFields } from '../../../schema-initializer/utils';
|
||||||
|
|
||||||
const titleCss = css`
|
const titleCss = css`
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -93,6 +93,14 @@ export const KanbanCardDesigner = (props: any) => {
|
|||||||
title: t('Display fields'),
|
title: t('Display fields'),
|
||||||
children: fields,
|
children: fields,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: t('Display association fields'),
|
||||||
|
children: useAssociatedFormItemInitializerFields({readPretty: true, block: 'KanbanV2'}),
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
component={<MenuOutlined style={{ cursor: 'pointer', fontSize: 12 }} />}
|
component={<MenuOutlined style={{ cursor: 'pointer', fontSize: 12 }} />}
|
||||||
/>
|
/>
|
||||||
@ -101,3 +109,4 @@ export const KanbanCardDesigner = (props: any) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RecursionField, useFieldSchema } from '@formily/react';
|
import { RecursionField, useFieldSchema } from '@formily/react';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||||
import { useTableSelectorProps as useTsp } from '../../../block-provider/TableSelectorProvider';
|
import { useTableSelectorProps as useTsp } from '../../../block-provider/TableSelectorProvider';
|
||||||
import { CollectionProvider, useCollection } from '../../../collection-manager';
|
import { CollectionProvider, useCollection } from '../../../collection-manager';
|
||||||
import { FormProvider, SchemaComponentOptions } from '../../core';
|
import { FormProvider, SchemaComponentOptions } from '../../core';
|
||||||
@ -60,15 +60,31 @@ export const InputRecordPicker: React.FC<any> = (props) => {
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const collectionField = useAssociation(props);
|
const collectionField = useAssociation(props);
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const options = (Array.isArray(value) ? value : value ? [value] : []).map(option => {
|
|
||||||
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
const [options, setOptions] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value) {
|
||||||
|
const opts = (Array.isArray(value) ? value : value ? [value] : []).map(option => {
|
||||||
const label = option[fieldNames.label];
|
const label = option[fieldNames.label];
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
[fieldNames.label]: compile(label),
|
[fieldNames.label]: compile(label),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const [selectedRows, setSelectedRows] = useState(options);
|
setOptions(opts);
|
||||||
const values = options?.map((option) => option[fieldNames.value]);
|
setSelectedRows(opts);
|
||||||
|
}
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
useEffect(() => {})
|
||||||
|
|
||||||
|
const getValue = () => {
|
||||||
|
if (multiple == null) return null;
|
||||||
|
|
||||||
|
return multiple ? (Array.isArray(value) ? value?.map(v => v[fieldNames.value]) : []) : value?.[fieldNames.value];
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<Select
|
||||||
@ -93,7 +109,7 @@ export const InputRecordPicker: React.FC<any> = (props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
value={multiple ? values : values?.[0]}
|
value={getValue()}
|
||||||
open={false}
|
open={false}
|
||||||
/>
|
/>
|
||||||
<RecordPickerContext.Provider value={{ multiple, onChange, selectedRows, setSelectedRows }}>
|
<RecordPickerContext.Provider value={{ multiple, onChange, selectedRows, setSelectedRows }}>
|
||||||
@ -101,7 +117,13 @@ export const InputRecordPicker: React.FC<any> = (props) => {
|
|||||||
<ActionContext.Provider value={{ openMode: 'drawer', visible, setVisible }}>
|
<ActionContext.Provider value={{ openMode: 'drawer', visible, setVisible }}>
|
||||||
<FormProvider>
|
<FormProvider>
|
||||||
<SchemaComponentOptions scope={{ useTableSelectorProps, usePickActionProps }}>
|
<SchemaComponentOptions scope={{ useTableSelectorProps, usePickActionProps }}>
|
||||||
<RecursionField schema={fieldSchema} onlyRenderProperties />
|
<RecursionField
|
||||||
|
schema={fieldSchema}
|
||||||
|
onlyRenderProperties
|
||||||
|
filterProperties={(s) => {
|
||||||
|
return s['x-component'] === 'RecordPicker.Selector';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</SchemaComponentOptions>
|
</SchemaComponentOptions>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
|
@ -3,8 +3,8 @@ import { observer, RecursionField, useField, useFieldSchema } from '@formily/rea
|
|||||||
import { toArr } from '@formily/shared';
|
import { toArr } from '@formily/shared';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider';
|
import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider';
|
||||||
import { CollectionProvider, useCollection } from '../../../collection-manager';
|
import { CollectionProvider, useCollection, useCollectionManager } from '../../../collection-manager';
|
||||||
import { RecordProvider } from '../../../record-provider';
|
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||||
import { FormProvider } from '../../core';
|
import { FormProvider } from '../../core';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
import { ActionContext } from '../action';
|
import { ActionContext } from '../action';
|
||||||
@ -17,14 +17,17 @@ interface IEllipsisWithTooltipRef {
|
|||||||
export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
||||||
const { ellipsis } = props;
|
const { ellipsis } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
|
const recordCtx = useRecord();
|
||||||
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const fieldNames = useFieldNames(props);
|
const fieldNames = useFieldNames(props);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [popoverVisible, setPopoverVisible] = useState<boolean>();
|
const [popoverVisible, setPopoverVisible] = useState<boolean>();
|
||||||
const { getField } = useCollection();
|
const { getField } = useCollection();
|
||||||
const collectionField = getField(fieldSchema.name);
|
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||||
const [record, setRecord] = useState({});
|
const [record, setRecord] = useState({});
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
|
||||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||||
const renderRecords = () =>
|
const renderRecords = () =>
|
||||||
toArr(field.value).map((record, index, arr) => {
|
toArr(field.value).map((record, index, arr) => {
|
||||||
@ -47,6 +50,37 @@ export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const renderWithoutTableFieldResourceProvider = () => (
|
||||||
|
<WithoutTableFieldResource.Provider value={true}>
|
||||||
|
<FormProvider>
|
||||||
|
<RecursionField
|
||||||
|
schema={fieldSchema}
|
||||||
|
onlyRenderProperties
|
||||||
|
filterProperties={(s) => {
|
||||||
|
return s['x-component'] === 'RecordPicker.Viewer';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormProvider>
|
||||||
|
</WithoutTableFieldResource.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderRecordProvider = () => {
|
||||||
|
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||||
|
|
||||||
|
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||||
|
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||||
|
<RecordProvider record={record}>
|
||||||
|
{renderWithoutTableFieldResourceProvider()}
|
||||||
|
</RecordProvider>
|
||||||
|
</RecordProvider>
|
||||||
|
) : (
|
||||||
|
<RecordProvider record={record}>
|
||||||
|
{renderWithoutTableFieldResourceProvider()}
|
||||||
|
</RecordProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return collectionField ? (
|
return collectionField ? (
|
||||||
<div>
|
<div>
|
||||||
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
||||||
@ -55,13 +89,7 @@ export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
|||||||
{renderRecords()}
|
{renderRecords()}
|
||||||
</EllipsisWithTooltip>
|
</EllipsisWithTooltip>
|
||||||
<ActionContext.Provider value={{ visible, setVisible, openMode: 'drawer' }}>
|
<ActionContext.Provider value={{ visible, setVisible, openMode: 'drawer' }}>
|
||||||
<RecordProvider record={record}>
|
{renderRecordProvider()}
|
||||||
<WithoutTableFieldResource.Provider value={true}>
|
|
||||||
<FormProvider>
|
|
||||||
<RecursionField schema={fieldSchema} onlyRenderProperties />
|
|
||||||
</FormProvider>
|
|
||||||
</WithoutTableFieldResource.Provider>
|
|
||||||
</RecordProvider>
|
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
</CollectionProvider>
|
</CollectionProvider>
|
||||||
</BlockAssociationContext.Provider>
|
</BlockAssociationContext.Provider>
|
||||||
|
@ -2,6 +2,8 @@ import { useFieldSchema } from "@formily/react";
|
|||||||
|
|
||||||
export const useFieldNames = (props) => {
|
export const useFieldNames = (props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const fieldNames = fieldSchema?.['x-component-props']?.['fieldNames'] || props.fieldNames;
|
const fieldNames = fieldSchema['x-component-props']?.['field']?.['uiSchema']?.['x-component-props']?.['fieldNames']
|
||||||
|
|| fieldSchema?.['x-component-props']?.['fieldNames']
|
||||||
|
|| props.fieldNames;
|
||||||
return { label: 'label', value: 'value', ...fieldNames };
|
return { label: 'label', value: 'value', ...fieldNames };
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import React, { useLayoutEffect } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { SortableItem, useCollection, useCompile, useDesignable, useDesigner } from '../../../';
|
import { SortableItem, useCollection, useCollectionManager, useCompile, useDesignable, useDesigner } from '../../../';
|
||||||
import { designerCss } from './Table.Column.ActionBar';
|
import { designerCss } from './Table.Column.ActionBar';
|
||||||
|
|
||||||
export const useColumnSchema = () => {
|
export const useColumnSchema = () => {
|
||||||
const { getField } = useCollection();
|
const { getField } = useCollection();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const columnSchema = useFieldSchema();
|
const columnSchema = useFieldSchema();
|
||||||
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
const fieldSchema = columnSchema.reduceProperties((buf, s) => {
|
const fieldSchema = columnSchema.reduceProperties((buf, s) => {
|
||||||
if (s['x-component'] === 'CollectionField') {
|
if (s['x-component'] === 'CollectionField') {
|
||||||
return s;
|
return s;
|
||||||
@ -16,7 +17,8 @@ export const useColumnSchema = () => {
|
|||||||
if (!fieldSchema) {
|
if (!fieldSchema) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const collectionField = getField(fieldSchema.name);
|
|
||||||
|
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||||
return { columnSchema, fieldSchema, collectionField, uiSchema: compile(collectionField?.uiSchema) };
|
return { columnSchema, fieldSchema, collectionField, uiSchema: compile(collectionField?.uiSchema) };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ export const TableColumnDecorator = (props) => {
|
|||||||
<SortableItem className={designerCss}>
|
<SortableItem className={designerCss}>
|
||||||
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField}/>
|
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField}/>
|
||||||
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
|
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
|
||||||
{field.title || compile(uiSchema?.title)}
|
{field?.title || compile(uiSchema?.title)}
|
||||||
{/* <div
|
{/* <div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
field.title = uid();
|
field.title = uid();
|
||||||
|
@ -24,6 +24,7 @@ const useLabelFields = (collectionName?: any) => {
|
|||||||
|
|
||||||
export const TableColumnDesigner = (props) => {
|
export const TableColumnDesigner = (props) => {
|
||||||
const { uiSchema, fieldSchema, collectionField } = props;
|
const { uiSchema, fieldSchema, collectionField } = props;
|
||||||
|
const { getInterface } = useCollectionManager();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const columnSchema = useFieldSchema();
|
const columnSchema = useFieldSchema();
|
||||||
@ -31,6 +32,8 @@ export const TableColumnDesigner = (props) => {
|
|||||||
const fieldNames =
|
const fieldNames =
|
||||||
fieldSchema?.['x-component-props']?.['fieldNames'] || uiSchema?.['x-component-props']?.['fieldNames'];
|
fieldSchema?.['x-component-props']?.['fieldNames'] || uiSchema?.['x-component-props']?.['fieldNames'];
|
||||||
const options = useLabelFields(collectionField?.target);
|
const options = useLabelFields(collectionField?.target);
|
||||||
|
const intefaceCfg = getInterface(collectionField?.interface);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GeneralSchemaDesigner disableInitializer>
|
<GeneralSchemaDesigner disableInitializer>
|
||||||
<SchemaSettings.ModalItem
|
<SchemaSettings.ModalItem
|
||||||
@ -43,7 +46,7 @@ export const TableColumnDesigner = (props) => {
|
|||||||
title: {
|
title: {
|
||||||
// title: t('Column title'),
|
// title: t('Column title'),
|
||||||
default: columnSchema?.title,
|
default: columnSchema?.title,
|
||||||
description: `${t('Original title: ')}${collectionField?.uiSchema?.title}`,
|
description: `${t('Original title: ')}${collectionField?.uiSchema?.title || fieldSchema?.title}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
@ -65,6 +68,8 @@ export const TableColumnDesigner = (props) => {
|
|||||||
dn.refresh();
|
dn.refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{
|
||||||
|
intefaceCfg && intefaceCfg.sortable === true && (
|
||||||
<SchemaSettings.SwitchItem
|
<SchemaSettings.SwitchItem
|
||||||
title={t('Sortable')}
|
title={t('Sortable')}
|
||||||
checked={field.componentProps.sorter}
|
checked={field.componentProps.sorter}
|
||||||
@ -74,16 +79,18 @@ export const TableColumnDesigner = (props) => {
|
|||||||
};
|
};
|
||||||
columnSchema['x-component-props'] = {
|
columnSchema['x-component-props'] = {
|
||||||
...columnSchema['x-component-props'],
|
...columnSchema['x-component-props'],
|
||||||
sorter: v,
|
sorter: v
|
||||||
};
|
}
|
||||||
schema['x-component-props'] = columnSchema['x-component-props'];
|
schema['x-component-props'] = columnSchema['x-component-props'];
|
||||||
field.componentProps.sorter = v;
|
field.componentProps.sorter = v;
|
||||||
dn.emit('patch', {
|
dn.emit('patch', {
|
||||||
schema,
|
schema
|
||||||
});
|
});
|
||||||
dn.refresh();
|
dn.refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
{['linkTo', 'm2m', 'm2o', 'o2m', 'obo', 'oho'].includes(collectionField?.interface) && (
|
{['linkTo', 'm2m', 'm2o', 'o2m', 'obo', 'oho'].includes(collectionField?.interface) && (
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.SelectItem
|
||||||
title={t('Title field')}
|
title={t('Title field')}
|
||||||
|
@ -38,6 +38,7 @@ const useTableColumns = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
const dataIndex = collectionFields?.length > 0 ? collectionFields[0].name : s.name;
|
const dataIndex = collectionFields?.length > 0 ? collectionFields[0].name : s.name;
|
||||||
|
console.log('useTableColumns', s.name, s, field.value);
|
||||||
return {
|
return {
|
||||||
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||||
dataIndex,
|
dataIndex,
|
||||||
@ -46,7 +47,8 @@ const useTableColumns = () => {
|
|||||||
// width: 300,
|
// width: 300,
|
||||||
render: (v, record) => {
|
render: (v, record) => {
|
||||||
const index = field.value?.indexOf(record);
|
const index = field.value?.indexOf(record);
|
||||||
console.log((Date.now() - start) / 1000);
|
// console.log((Date.now() - start) / 1000);
|
||||||
|
console.log('useTableColumns.index', index, record);
|
||||||
return (
|
return (
|
||||||
<RecordIndexProvider index={index}>
|
<RecordIndexProvider index={index}>
|
||||||
<RecordProvider record={record}>
|
<RecordProvider record={record}>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { observer, useField, useFieldSchema, useForm } from '@formily/react';
|
import { observer, useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { useFormBlockContext } from '../../../block-provider';
|
||||||
import { useCollection } from '../../../collection-manager';
|
import { useCollection } from '../../../collection-manager';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
import { ActionBar } from '../action';
|
import { ActionBar } from '../action';
|
||||||
@ -10,10 +11,15 @@ export const TableField: any = observer((props) => {
|
|||||||
const field = useField();
|
const field = useField();
|
||||||
const collectionField = getField(fieldSchema.name);
|
const collectionField = getField(fieldSchema.name);
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
const ctx = useFormBlockContext();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!field.title) {
|
if (!field.title) {
|
||||||
field.title = compile(collectionField?.uiSchema?.title);
|
field.title = compile(collectionField?.uiSchema?.title);
|
||||||
}
|
}
|
||||||
|
if (ctx?.field) {
|
||||||
|
ctx.field.added = ctx.field.added || new Set();
|
||||||
|
ctx.field.added.add(fieldSchema.name);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,34 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaInitializer } from '../SchemaInitializer';
|
import { SchemaInitializer } from '../SchemaInitializer';
|
||||||
import { itemsMerge, useTableColumnInitializerFields } from '../utils';
|
import { itemsMerge, useAssociatedTableColumnInitializerFields, useTableColumnInitializerFields } from '../utils';
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
export const TableColumnInitializers = (props: any) => {
|
export const TableColumnInitializers = (props: any) => {
|
||||||
const { items = [] } = props;
|
const { items = [] } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const associatedFields = useAssociatedTableColumnInitializerFields();
|
||||||
|
const fieldItems: any[] = [{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: t('Display fields'),
|
||||||
|
children: useTableColumnInitializerFields(),
|
||||||
|
}];
|
||||||
|
if (associatedFields?.length > 0) {
|
||||||
|
fieldItems.push({
|
||||||
|
type: 'divider',
|
||||||
|
}, {
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: t('Display association fields'),
|
||||||
|
children: associatedFields,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fieldItems.push({
|
||||||
|
type: 'divider',
|
||||||
|
}, {
|
||||||
|
type: 'item',
|
||||||
|
title: t('Action column'),
|
||||||
|
component: 'TableActionColumnInitializer',
|
||||||
|
})
|
||||||
return (
|
return (
|
||||||
<SchemaInitializer.Button
|
<SchemaInitializer.Button
|
||||||
insertPosition={'beforeEnd'}
|
insertPosition={'beforeEnd'}
|
||||||
@ -28,21 +50,7 @@ export const TableColumnInitializers = (props: any) => {
|
|||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
items={itemsMerge(
|
items={itemsMerge(
|
||||||
[
|
fieldItems,
|
||||||
{
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: t('Display fields'),
|
|
||||||
children: useTableColumnInitializerFields(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'item',
|
|
||||||
title: t('Action column'),
|
|
||||||
component: 'TableActionColumnInitializer',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
items,
|
items,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -82,7 +82,8 @@ export const useTableColumnInitializerFields = () => {
|
|||||||
'x-collection-field': `${name}.${field.name}`,
|
'x-collection-field': `${name}.${field.name}`,
|
||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
'x-component-props': {},
|
'x-component-props': {
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// interfaceConfig?.schemaInitialize?.(schema, { field, readPretty: true, block: 'Table' });
|
// interfaceConfig?.schemaInitialize?.(schema, { field, readPretty: true, block: 'Table' });
|
||||||
return {
|
return {
|
||||||
@ -100,6 +101,57 @@ export const useTableColumnInitializerFields = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useAssociatedTableColumnInitializerFields = () => {
|
||||||
|
const { name, fields } = useCollection();
|
||||||
|
const { getInterface, getCollectionFields } = useCollectionManager();
|
||||||
|
|
||||||
|
const groups = fields
|
||||||
|
?.filter((field) => {
|
||||||
|
return ['o2o', 'oho', 'obo', 'm2o'].includes(field.interface);
|
||||||
|
})
|
||||||
|
?.map((field) => {
|
||||||
|
const subFields = getCollectionFields(field.target);
|
||||||
|
const items = subFields
|
||||||
|
// ?.filter((subField) => subField?.interface && !['o2o', 'oho', 'obo', 'o2m', 'm2o', 'subTable', 'linkTo'].includes(subField?.interface))
|
||||||
|
?.filter((subField) => subField?.interface && !['subTable'].includes(subField?.interface))
|
||||||
|
?.map((subField) => {
|
||||||
|
const interfaceConfig = getInterface(subField.interface);
|
||||||
|
const schema = {
|
||||||
|
// type: 'string',
|
||||||
|
name: `${field.name}.${subField.name}`,
|
||||||
|
// title: subField?.uiSchema?.title || subField.name,
|
||||||
|
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-collection-field': `${name}.${field.name}.${subField.name}`,
|
||||||
|
'x-component-props': {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'item',
|
||||||
|
title: subField?.uiSchema?.title || subField.name,
|
||||||
|
component: 'TableCollectionFieldInitializer',
|
||||||
|
find: findTableColumn,
|
||||||
|
remove: removeTableColumn,
|
||||||
|
schemaInitialize: (s) => {
|
||||||
|
interfaceConfig?.schemaInitialize?.(s, { field: subField, readPretty: true, block: 'Table' });
|
||||||
|
},
|
||||||
|
field: subField,
|
||||||
|
schema,
|
||||||
|
} as SchemaInitializerItemOptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'subMenu',
|
||||||
|
title: field.uiSchema?.title,
|
||||||
|
children: items,
|
||||||
|
} as SchemaInitializerItemOptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
export const useFormItemInitializerFields = (options?: any) => {
|
export const useFormItemInitializerFields = (options?: any) => {
|
||||||
const { name, fields } = useCollection();
|
const { name, fields } = useCollection();
|
||||||
const { getInterface } = useCollectionManager();
|
const { getInterface } = useCollectionManager();
|
||||||
@ -110,14 +162,17 @@ export const useFormItemInitializerFields = (options?: any) => {
|
|||||||
?.filter((field) => field?.interface)
|
?.filter((field) => field?.interface)
|
||||||
?.map((field) => {
|
?.map((field) => {
|
||||||
const interfaceConfig = getInterface(field.interface);
|
const interfaceConfig = getInterface(field.interface);
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: field.name,
|
name: field.name,
|
||||||
// title: field?.uiSchema?.title || field.name,
|
// title: field?.uiSchema?.title || field.name,
|
||||||
'x-designer': 'FormItem.Designer',
|
'x-designer': 'FormItem.Designer',
|
||||||
'x-component': 'CollectionField',
|
'x-component': field.interface === 'o2m' ? 'TableField' : 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-collection-field': `${name}.${field.name}`,
|
'x-collection-field': `${name}.${field.name}`,
|
||||||
|
'x-component-props': {
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// interfaceConfig?.schemaInitialize?.(schema, { field, block: 'Form', readPretty: form.readPretty });
|
// interfaceConfig?.schemaInitialize?.(schema, { field, block: 'Form', readPretty: form.readPretty });
|
||||||
return {
|
return {
|
||||||
@ -133,6 +188,55 @@ export const useFormItemInitializerFields = (options?: any) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useAssociatedFormItemInitializerFields = (options?: any) => {
|
||||||
|
const { name, fields } = useCollection();
|
||||||
|
const { getInterface, getCollectionFields } = useCollectionManager();
|
||||||
|
const form = useForm();
|
||||||
|
const { readPretty = form.readPretty, block = 'Form' } = options || {};
|
||||||
|
|
||||||
|
const groups = fields
|
||||||
|
?.filter((field) => {
|
||||||
|
return ['o2o', 'oho', 'obo', 'm2o'].includes(field.interface);
|
||||||
|
})
|
||||||
|
?.map((field) => {
|
||||||
|
const subFields = getCollectionFields(field.target);
|
||||||
|
const items = subFields
|
||||||
|
?.filter((subField) => subField?.interface && !['subTable'].includes(subField?.interface))
|
||||||
|
?.map((subField) => {
|
||||||
|
const interfaceConfig = getInterface(subField.interface);
|
||||||
|
const schema = {
|
||||||
|
type: 'string',
|
||||||
|
name: `${field.name}.${subField.name}`,
|
||||||
|
// title: subField?.uiSchema?.title || subField.name,
|
||||||
|
'x-designer': 'FormItem.Designer',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-component-props': {
|
||||||
|
},
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': `${name}.${field.name}.${subField.name}`,
|
||||||
|
};
|
||||||
|
// interfaceConfig?.schemaInitialize?.(schema, { field, block: 'Form', readPretty: form.readPretty });
|
||||||
|
return {
|
||||||
|
type: 'item',
|
||||||
|
title: subField?.uiSchema?.title || subField.name,
|
||||||
|
component: 'CollectionFieldInitializer',
|
||||||
|
remove: removeGridFormItem,
|
||||||
|
schemaInitialize: (s) => {
|
||||||
|
interfaceConfig?.schemaInitialize?.(s, { field: subField, block, readPretty });
|
||||||
|
},
|
||||||
|
schema,
|
||||||
|
} as SchemaInitializerItemOptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'subMenu',
|
||||||
|
title: field.uiSchema?.title,
|
||||||
|
children: items,
|
||||||
|
} as SchemaInitializerItemOptions;
|
||||||
|
});
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
export const useCustomFormItemInitializerFields = (options?: any) => {
|
export const useCustomFormItemInitializerFields = (options?: any) => {
|
||||||
const { name, fields } = useCollection();
|
const { name, fields } = useCollection();
|
||||||
const { getInterface } = useCollectionManager();
|
const { getInterface } = useCollectionManager();
|
||||||
@ -168,6 +272,7 @@ export const useCustomFormItemInitializerFields = (options?: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const findSchema = (schema: Schema, key: string, action: string) => {
|
const findSchema = (schema: Schema, key: string, action: string) => {
|
||||||
|
if (!Schema.isSchemaInstance(schema)) return null;
|
||||||
return schema.reduceProperties((buf, s) => {
|
return schema.reduceProperties((buf, s) => {
|
||||||
if (s[key] === action) {
|
if (s[key] === action) {
|
||||||
return s;
|
return s;
|
||||||
@ -420,7 +525,6 @@ export const createFormBlockSchema = (options) => {
|
|||||||
template,
|
template,
|
||||||
...others
|
...others
|
||||||
} = options;
|
} = options;
|
||||||
console.log('createFormBlockSchema', options);
|
|
||||||
const resourceName = resource || association || collection;
|
const resourceName = resource || association || collection;
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Collection } from '@nocobase/database';
|
|
||||||
import { Plugin } from '@nocobase/server';
|
import { Plugin } from '@nocobase/server';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -101,41 +100,41 @@ export class CollectionManagerPlugin extends Plugin {
|
|||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.resourcer.use(async (ctx, next) => {
|
// this.app.resourcer.use(async (ctx, next) => {
|
||||||
const { resourceName, actionName } = ctx.action;
|
// const { resourceName, actionName } = ctx.action;
|
||||||
if (actionName === 'update') {
|
// if (actionName === 'update') {
|
||||||
const { updateAssociationValues = [] } = ctx.action.params;
|
// const { updateAssociationValues = [] } = ctx.action.params;
|
||||||
const [collectionName, associationName] = resourceName.split('.');
|
// const [collectionName, associationName] = resourceName.split('.');
|
||||||
if (!associationName) {
|
// if (!associationName) {
|
||||||
const collection: Collection = ctx.db.getCollection(collectionName);
|
// const collection: Collection = ctx.db.getCollection(collectionName);
|
||||||
if (collection) {
|
// if (collection) {
|
||||||
for (const [, field] of collection.fields) {
|
// for (const [, field] of collection.fields) {
|
||||||
if (['subTable', 'o2m'].includes(field.options.interface)) {
|
// if (['subTable', 'o2m'].includes(field.options.interface)) {
|
||||||
updateAssociationValues.push(field.name);
|
// updateAssociationValues.push(field.name);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
const association = ctx.db.getCollection(collectionName)?.getField?.(associationName);
|
// const association = ctx.db.getCollection(collectionName)?.getField?.(associationName);
|
||||||
if (association?.target) {
|
// if (association?.target) {
|
||||||
const collection: Collection = ctx.db.getCollection(association?.target);
|
// const collection: Collection = ctx.db.getCollection(association?.target);
|
||||||
if (collection) {
|
// if (collection) {
|
||||||
for (const [, field] of collection.fields) {
|
// for (const [, field] of collection.fields) {
|
||||||
if (['subTable', 'o2m'].includes(field.options.interface)) {
|
// if (['subTable', 'o2m'].includes(field.options.interface)) {
|
||||||
updateAssociationValues.push(field.name);
|
// updateAssociationValues.push(field.name);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (updateAssociationValues.length) {
|
// if (updateAssociationValues.length) {
|
||||||
ctx.action.mergeParams({
|
// ctx.action.mergeParams({
|
||||||
updateAssociationValues,
|
// updateAssociationValues,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
await next();
|
// await next();
|
||||||
});
|
// });
|
||||||
|
|
||||||
this.app.acl.allow('collections', 'list', 'loggedIn');
|
this.app.acl.allow('collections', 'list', 'loggedIn');
|
||||||
this.app.acl.allow('collections', ['create', 'update', 'destroy'], 'allowConfigure');
|
this.app.acl.allow('collections', ['create', 'update', 'destroy'], 'allowConfigure');
|
||||||
|
Loading…
Reference in New Issue
Block a user