mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 10:42:19 +08:00
Refactor/2.0 association (#7136)
* refactor: code improve * refactor: code improve * refactor: code improve * refactor: code improve * refactor: code improve
This commit is contained in:
parent
a33a91a091
commit
28baf20755
@ -46,24 +46,37 @@ export const titleField = defineAction({
|
||||
},
|
||||
},
|
||||
defaultParams: (ctx: any) => {
|
||||
const { target } = ctx.model.collectionField.options;
|
||||
const collectionManager = ctx.model.collectionField.collection.collectionManager;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
const targetCollection = ctx.model.targetCollection;
|
||||
const filterKey = getUniqueKeyFromCollection(targetCollection.options as any);
|
||||
return {
|
||||
label: ctx.model.props.fieldNames?.label || targetCollection.options.titleField || filterKey,
|
||||
};
|
||||
},
|
||||
handler(ctx: any, params) {
|
||||
const { target } = ctx.model.collectionField.options;
|
||||
const collectionManager = ctx.model.collectionField.collection.collectionManager;
|
||||
ctx.model.setStepParams;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
async handler(ctx: any, params) {
|
||||
const target = ctx.model.collectionField.target;
|
||||
const targetCollection = ctx.model.collectionField.targetCollection;
|
||||
console.log(ctx.model.collectionField);
|
||||
const filterKey = getUniqueKeyFromCollection(targetCollection.options as any);
|
||||
const label = params.label || targetCollection.options.titleField || filterKey;
|
||||
const newFieldNames = {
|
||||
value: filterKey,
|
||||
label: params.label || targetCollection.options.titleField || filterKey,
|
||||
label,
|
||||
};
|
||||
ctx.model.setComponentProps({ fieldNames: newFieldNames });
|
||||
const targetCollectionField = targetCollection.getField(label);
|
||||
const use = targetCollectionField.getFirstSubclassNameOf('ReadPrettyFieldModel') || 'ReadPrettyFieldModel';
|
||||
const model = ctx.model.setSubModel('field', {
|
||||
use,
|
||||
stepParams: {
|
||||
default: {
|
||||
step1: {
|
||||
dataSourceKey: ctx.model.collectionField.dataSourceKey,
|
||||
collectionName: target,
|
||||
fieldPath: newFieldNames.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await model.applyAutoFlows();
|
||||
},
|
||||
});
|
||||
|
@ -9,8 +9,7 @@
|
||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||
import { Select } from 'antd';
|
||||
import React from 'react';
|
||||
import { FlowModelRenderer, useFlowEngine, useFlowModel, reactive } from '@nocobase/flow-engine';
|
||||
import { useCompile } from '../../../../../schema-component';
|
||||
import { useFlowModel, FlowModel } from '@nocobase/flow-engine';
|
||||
import { tval } from '@nocobase/utils/client';
|
||||
import { AssociationFieldEditableFieldModel } from './AssociationFieldEditableFieldModel';
|
||||
|
||||
@ -34,52 +33,19 @@ function toValue(record: any | any[], fieldNames, multiple = false) {
|
||||
|
||||
return convert(record);
|
||||
}
|
||||
const modelCache = new Map<string, any>();
|
||||
|
||||
function LabelByField(props) {
|
||||
const { option, fieldNames } = props;
|
||||
const cacheKey = option[fieldNames.value] + option[fieldNames.label];
|
||||
const currentModel: any = useFlowModel();
|
||||
const flowEngine = useFlowEngine();
|
||||
if (modelCache.has(cacheKey)) {
|
||||
return option[fieldNames.label] ? <FlowModelRenderer model={modelCache.get(cacheKey)} /> : tval('N/A');
|
||||
}
|
||||
const collectionManager = currentModel.collectionField.collection.collectionManager;
|
||||
const target = currentModel.collectionField?.options?.target;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
const targetLabelField = targetCollection.getField(fieldNames.label);
|
||||
const fieldClasses = Array.from(flowEngine.filterModelClassByParent('ReadPrettyFieldModel').values()).sort(
|
||||
(a, b) => (a.meta?.sort || 0) - (b.meta?.sort || 0),
|
||||
);
|
||||
|
||||
const fieldClass = fieldClasses.find(
|
||||
(cls) => cls.supportedFieldInterfaces?.includes(targetLabelField?.options?.interface),
|
||||
);
|
||||
const model = flowEngine.createModel({
|
||||
use: fieldClass?.name || 'ReadPrettyFieldModel',
|
||||
stepParams: {
|
||||
default: {
|
||||
step1: {
|
||||
dataSourceKey: currentModel.collectionField.collection.dataSourceKey,
|
||||
collectionName: target,
|
||||
fieldPath: fieldNames.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
model.setSharedContext({
|
||||
...currentModel.getSharedContext(),
|
||||
value: option[fieldNames.label],
|
||||
const currentModel = useFlowModel();
|
||||
const field = currentModel.subModels.field as FlowModel;
|
||||
const key = option[fieldNames.value];
|
||||
const fieldModel = field.createFork({}, key);
|
||||
fieldModel.setSharedContext({
|
||||
value: option?.[fieldNames.label],
|
||||
currentRecord: option,
|
||||
});
|
||||
|
||||
model.setParent(currentModel.parent);
|
||||
modelCache.set(cacheKey, model);
|
||||
|
||||
return (
|
||||
<span key={option[fieldNames.value]}>
|
||||
{option[fieldNames.label] ? <FlowModelRenderer model={model} uid={option[fieldNames.value]} /> : tval('N/A')}
|
||||
</span>
|
||||
);
|
||||
return <span key={option[fieldNames.value]}>{option[fieldNames.label] ? fieldModel.render() : tval('N/A')}</span>;
|
||||
}
|
||||
|
||||
function LazySelect(props) {
|
||||
|
@ -11,13 +11,14 @@ import { Input } from '@formily/antd-v5';
|
||||
import React from 'react';
|
||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||
import { EditableFieldModel } from '../EditableFieldModel';
|
||||
import { useParseMarkdown } from './util';
|
||||
import { useParseMarkdown, convertToText } from './util';
|
||||
import { useMarkdownStyles } from './style';
|
||||
|
||||
const MarkdownReadPretty = (props) => {
|
||||
export const MarkdownReadPretty = (props) => {
|
||||
const { textOnly } = props;
|
||||
const markdownClass = useMarkdownStyles();
|
||||
const { html = '' } = useParseMarkdown(props.value);
|
||||
|
||||
const text = convertToText(html);
|
||||
const value = (
|
||||
<div
|
||||
className={` ${markdownClass} nb-markdown nb-markdown-default nb-markdown-table`}
|
||||
@ -25,7 +26,7 @@ const MarkdownReadPretty = (props) => {
|
||||
/>
|
||||
);
|
||||
|
||||
return value;
|
||||
return <>{textOnly ? text : value}</>;
|
||||
};
|
||||
|
||||
const Markdown: any = connect(
|
||||
|
@ -13,20 +13,3 @@ import { ReadPrettyFieldModel } from '../ReadPrettyFieldModel';
|
||||
export class AssociationReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
targetCollection;
|
||||
}
|
||||
|
||||
AssociationReadPrettyFieldModel.registerFlow({
|
||||
key: 'AssociationReadPrettyFieldDefault',
|
||||
auto: true,
|
||||
sort: 150,
|
||||
steps: {
|
||||
step1: {
|
||||
handler(ctx, params) {
|
||||
const { collectionField } = ctx.model;
|
||||
const { target } = collectionField?.options || {};
|
||||
const collectionManager = collectionField.collection.collectionManager;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
ctx.model.targetCollection = targetCollection;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -8,10 +8,11 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { castArray } from 'lodash';
|
||||
import { Button } from 'antd';
|
||||
import { tval } from '@nocobase/utils/client';
|
||||
import { AssociationReadPrettyFieldModel } from './AssociationReadPrettyFieldModel';
|
||||
import { FlowEngineProvider, reactive } from '@nocobase/flow-engine';
|
||||
import { reactive, FlowModel } from '@nocobase/flow-engine';
|
||||
import { getUniqueKeyFromCollection } from '../../../../../collection-manager/interfaces/utils';
|
||||
|
||||
const LinkToggleWrapper = ({ enableLink, children, currentRecord, ...props }) => {
|
||||
@ -46,70 +47,43 @@ export class AssociationSelectReadPrettyFieldModel extends AssociationReadPretty
|
||||
set onClick(fn) {
|
||||
this.setProps({ ...this.props, onClick: fn });
|
||||
}
|
||||
private fieldModelCache: Record<string, FlowModel> = {};
|
||||
@reactive
|
||||
public render() {
|
||||
const { fieldNames, enableLink = true } = this.props;
|
||||
const value = this.getValue();
|
||||
if (!this.collectionField || !value) {
|
||||
return;
|
||||
}
|
||||
const { target } = this.collectionField?.options || {};
|
||||
const collectionManager = this.collectionField.collection.collectionManager;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
const targetLabelField = targetCollection.getField(fieldNames.label);
|
||||
const fieldClasses = Array.from(this.flowEngine.filterModelClassByParent('ReadPrettyFieldModel').values())?.sort(
|
||||
(a, b) => (a.meta?.sort || 0) - (b.meta?.sort || 0),
|
||||
);
|
||||
const fieldInterfaceName = targetLabelField?.options?.interface;
|
||||
const fieldClass = fieldClasses.find((fieldClass) => {
|
||||
return fieldClass.supportedFieldInterfaces?.includes(fieldInterfaceName);
|
||||
});
|
||||
const model = this.flowEngine.createModel({
|
||||
use: fieldClass?.name || 'ReadPrettyFieldModel',
|
||||
stepParams: {
|
||||
default: {
|
||||
step1: {
|
||||
dataSourceKey: this.collectionField.collection.dataSourceKey,
|
||||
collectionName: target,
|
||||
fieldPath: fieldNames.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
dataSource: targetLabelField.enum,
|
||||
...targetLabelField.getComponentProps(),
|
||||
},
|
||||
});
|
||||
model.setSharedContext({
|
||||
...this.ctx.shared,
|
||||
value: value?.[fieldNames.label],
|
||||
currentRecord: value,
|
||||
});
|
||||
model.setParent(this.parent);
|
||||
if (Array.isArray(value)) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 2 }}>
|
||||
{value.map((v, idx) => {
|
||||
const mol = model.createFork({}, `${idx}`);
|
||||
mol.setSharedContext({ ...this.ctx.shared, index: idx, value: v?.[fieldNames.label], currentRecord: v });
|
||||
return (
|
||||
<React.Fragment key={idx}>
|
||||
{idx > 0 && <span style={{ color: 'rgb(170, 170, 170)' }}>,</span>}
|
||||
<LinkToggleWrapper enableLink={enableLink} {...this.props} currentRecord={v}>
|
||||
<FlowEngineProvider engine={this.flowEngine}>
|
||||
{v?.[fieldNames.label] ? mol.render() : this.flowEngine.translate('N/A')}
|
||||
</FlowEngineProvider>
|
||||
</LinkToggleWrapper>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!value) return null;
|
||||
|
||||
const arrayValue = castArray(value);
|
||||
const field = this.subModels.field as FlowModel;
|
||||
return (
|
||||
<LinkToggleWrapper enableLink={enableLink} {...this.props} currentRecord={value}>
|
||||
<FlowEngineProvider engine={this.flowEngine}>{model.render()}</FlowEngineProvider>
|
||||
</LinkToggleWrapper>
|
||||
<>
|
||||
{arrayValue.map((v, index) => {
|
||||
const key = `${index}`;
|
||||
let fieldModel = this.fieldModelCache[v?.[fieldNames.label]];
|
||||
|
||||
if (!fieldModel) {
|
||||
fieldModel = field.createFork({}, key);
|
||||
fieldModel.setSharedContext({
|
||||
index,
|
||||
value: v?.[fieldNames.label],
|
||||
currentRecord: v,
|
||||
});
|
||||
this.fieldModelCache[v?.[fieldNames.label]] = fieldModel;
|
||||
}
|
||||
|
||||
const content = v?.[fieldNames.label] ? fieldModel.render() : this.flowEngine.translate('N/A');
|
||||
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{index > 0 && ', '}
|
||||
<LinkToggleWrapper enableLink={enableLink} {...this.props} currentRecord={v}>
|
||||
{content}
|
||||
</LinkToggleWrapper>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -123,16 +97,32 @@ AssociationSelectReadPrettyFieldModel.registerFlow({
|
||||
fieldNames: {
|
||||
use: 'titleField',
|
||||
title: tval('Title field'),
|
||||
handler(ctx, params) {
|
||||
const { target } = ctx.model.collectionField.options;
|
||||
async handler(ctx, params) {
|
||||
const { target } = ctx.model.collectionField;
|
||||
const collectionManager = ctx.model.collectionField.collection.collectionManager;
|
||||
const targetCollection = collectionManager.getCollection(target);
|
||||
const filterKey = getUniqueKeyFromCollection(targetCollection.options as any);
|
||||
const label = params.label || targetCollection.options.titleField || filterKey;
|
||||
const newFieldNames = {
|
||||
value: filterKey,
|
||||
label: params.label || targetCollection.options.titleField || filterKey,
|
||||
label,
|
||||
};
|
||||
const targetCollectionField = targetCollection.getField(label);
|
||||
const use = targetCollectionField.getFirstSubclassNameOf('ReadPrettyFieldModel') || 'ReadPrettyFieldModel';
|
||||
ctx.model.setProps({ fieldNames: newFieldNames });
|
||||
const model = ctx.model.setSubModel('field', {
|
||||
use,
|
||||
stepParams: {
|
||||
default: {
|
||||
step1: {
|
||||
dataSourceKey: ctx.model.collectionField.dataSourceKey,
|
||||
collectionName: target,
|
||||
fieldPath: newFieldNames.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await model.applyAutoFlows();
|
||||
},
|
||||
},
|
||||
enableLink: {
|
||||
|
@ -1,10 +1,21 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
import { Checkbox } from 'antd';
|
||||
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
|
||||
export class CheckboxReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['checkbox'];
|
||||
@reactive
|
||||
public render() {
|
||||
const value = this.getValue();
|
||||
const { prefix = '', suffix = '', showUnchecked } = this.props;
|
||||
|
@ -10,10 +10,12 @@
|
||||
import React from 'react';
|
||||
import { ColorPicker } from 'antd';
|
||||
import { css } from '@emotion/css';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
|
||||
export class ColorReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['color'];
|
||||
@reactive
|
||||
public render() {
|
||||
const value = this.getValue();
|
||||
|
||||
|
@ -8,11 +8,13 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
import { Icon } from '../../../../icon';
|
||||
|
||||
export class IconReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['icon'];
|
||||
@reactive
|
||||
public render() {
|
||||
const value = this.getValue();
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { cx, css } from '@emotion/css';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
|
||||
@ -19,6 +20,7 @@ const JSONClassName = css`
|
||||
|
||||
export class JsonReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['json'];
|
||||
@reactive
|
||||
public render() {
|
||||
const value = this.getValue();
|
||||
const { space, style, className } = this.props;
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { tval } from '@nocobase/utils/client';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
import { MarkdownReadPretty } from '../EditableField/MarkdownEditableFieldModel/index';
|
||||
|
||||
export class MarkdownReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['markdown'];
|
||||
@reactive
|
||||
public render() {
|
||||
const { textOnly = true } = this.props;
|
||||
const value = this.getValue();
|
||||
return <MarkdownReadPretty textOnly={textOnly} value={value} />;
|
||||
}
|
||||
}
|
||||
|
||||
MarkdownReadPrettyFieldModel.registerFlow({
|
||||
key: 'displayMode',
|
||||
title: tval('Specific properties'),
|
||||
auto: true,
|
||||
sort: 200,
|
||||
steps: {
|
||||
displayMode: {
|
||||
uiSchema: {
|
||||
textOnly: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
{ label: tval('Text only'), value: true },
|
||||
{ label: tval('Html'), value: false },
|
||||
],
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Radio.Group',
|
||||
},
|
||||
},
|
||||
title: tval('Display mode'),
|
||||
defaultParams: {
|
||||
textOnly: true,
|
||||
},
|
||||
handler(ctx, params) {
|
||||
ctx.model.setProps({ textOnly: params.textOnly });
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
@ -1,5 +1,15 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import * as math from 'mathjs';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { isNum } from '@formily/shared';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
|
||||
@ -13,6 +23,7 @@ const toValue = (value: any, callback: (v: number) => number) => {
|
||||
};
|
||||
export class PercentReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['percent'];
|
||||
@reactive
|
||||
public render() {
|
||||
const value = this.getValue();
|
||||
const { prefix = '', suffix = '' } = this.props;
|
||||
|
@ -8,23 +8,46 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { tval } from '@nocobase/utils/client';
|
||||
import { reactive } from '@nocobase/flow-engine';
|
||||
import { ReadPrettyFieldModel } from './ReadPrettyFieldModel';
|
||||
|
||||
const lineHeight142 = { lineHeight: '1.42' };
|
||||
import { MarkdownReadPretty } from '../EditableField/MarkdownEditableFieldModel/index';
|
||||
|
||||
export class RichTextReadPrettyFieldModel extends ReadPrettyFieldModel {
|
||||
public static readonly supportedFieldInterfaces = ['richText'];
|
||||
@reactive
|
||||
public render() {
|
||||
const { textOnly = true } = this.props;
|
||||
const value = this.getValue();
|
||||
const html = (
|
||||
<div
|
||||
style={lineHeight142}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: value,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return <div>{html}</div>;
|
||||
return <MarkdownReadPretty textOnly={textOnly} value={value} />;
|
||||
}
|
||||
}
|
||||
|
||||
RichTextReadPrettyFieldModel.registerFlow({
|
||||
key: 'displayMode',
|
||||
title: tval('Specific properties'),
|
||||
auto: true,
|
||||
sort: 200,
|
||||
steps: {
|
||||
displayMode: {
|
||||
uiSchema: {
|
||||
textOnly: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
{ label: tval('Text only'), value: true },
|
||||
{ label: tval('Html'), value: false },
|
||||
],
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Radio.Group',
|
||||
},
|
||||
},
|
||||
title: tval('Display mode'),
|
||||
defaultParams: {
|
||||
textOnly: true,
|
||||
},
|
||||
handler(ctx, params) {
|
||||
ctx.model.setProps({ textOnly: params.textOnly });
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -19,3 +19,4 @@ export * from './ColorReadPrettyFieldModel';
|
||||
export * from './IconReadPrettyFieldModel';
|
||||
export * from './JsonReadPrettyFieldModel';
|
||||
export * from './AssociationFieldModel';
|
||||
export * from './MarkdownReadPrettyFieldModel';
|
||||
|
Loading…
x
Reference in New Issue
Block a user