Merge branch 'next' into develop

This commit is contained in:
nocobase[bot] 2025-03-13 14:14:36 +00:00
commit 1c6b1916bc
20 changed files with 954 additions and 27 deletions

View File

@ -151,7 +151,6 @@ const InternalAssociationSelect = observer(
</div> </div>
); );
}; };
console.log(fieldSchema);
return ( return (
<div key={fieldSchema.name}> <div key={fieldSchema.name}>
<Space.Compact style={{ display: 'flex' }}> <Space.Compact style={{ display: 'flex' }}>

View File

@ -20,6 +20,11 @@ import React from 'react';
import { useChartsTranslation } from '../locale'; import { useChartsTranslation } from '../locale';
import { useField, useFieldSchema } from '@formily/react'; import { useField, useFieldSchema } from '@formily/react';
/**
* @deprecated
* use `chartBlockSettings` instead
*/
export const ChartV2BlockDesigner: React.FC = () => { export const ChartV2BlockDesigner: React.FC = () => {
const { t } = useChartsTranslation(); const { t } = useChartsTranslation();
const field = useField(); const field = useField();

View File

@ -112,7 +112,7 @@ export const ChartV2BlockInitializer: React.FC = () => {
type: 'void', type: 'void',
'x-component': 'ChartCardItem', 'x-component': 'ChartCardItem',
'x-use-component-props': 'useChartBlockCardProps', 'x-use-component-props': 'useChartBlockCardProps',
'x-designer': 'ChartV2BlockDesigner', 'x-settings': 'chart:block',
'x-decorator': 'ChartBlockProvider', 'x-decorator': 'ChartBlockProvider',
properties: { properties: {
actions: { actions: {

View File

@ -11,6 +11,10 @@ import { GeneralSchemaDesigner, SchemaSettingsRemove } from '@nocobase/client';
import React from 'react'; import React from 'react';
import { useChartsTranslation } from '../locale'; import { useChartsTranslation } from '../locale';
/**
* @deprecated
* use `chartFilterBlockSettings` instead
*/
export const ChartFilterBlockDesigner: React.FC = () => { export const ChartFilterBlockDesigner: React.FC = () => {
const { t } = useChartsTranslation(); const { t } = useChartsTranslation();
return ( return (

View File

@ -31,7 +31,8 @@ const createFilterSchema = () => {
'x-component-props': { 'x-component-props': {
size: 'small', size: 'small',
}, },
'x-designer': 'ChartFilterBlockDesigner', 'x-toolbar': 'ChartFilterBlockToolbar',
'x-settings': 'chart:filterForm:block',
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',

View File

@ -353,6 +353,10 @@ const EditDataScope: React.FC = () => {
); );
}; };
/**
* @deprecated
* use `chartFilterItemSettings` instead
*/
export const ChartFilterItemDesigner: React.FC = () => { export const ChartFilterItemDesigner: React.FC = () => {
const { getCollectionJoinField } = useCollectionManager_deprecated(); const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated(); const { getField } = useCollection_deprecated();

View File

@ -240,7 +240,8 @@ export const ChartFilterCustomItemInitializer: React.FC<{
title: title, title: title,
name: `custom.${name}`, name: `custom.${name}`,
required: false, required: false,
'x-designer': 'ChartFilterItemDesigner', 'x-toolbar': 'ChartFilterItemToolbar',
'x-settings': 'chart:filterForm:item',
'x-decorator': 'ChartFilterFormItem', 'x-decorator': 'ChartFilterFormItem',
'x-component-props': { 'x-component-props': {
...(defaultSchema['x-component-props'] || {}), ...(defaultSchema['x-component-props'] || {}),

View File

@ -129,18 +129,21 @@ export const useChartFilter = () => {
title, title,
name: `${name}.${field.name}`, name: `${name}.${field.name}`,
required: false, required: false,
'x-designer': 'ChartFilterItemDesigner', 'x-toolbar': 'ChartFilterItemToolbar',
'x-settings': 'chart:filterForm:item',
'x-component': 'CollectionField', 'x-component': 'CollectionField',
'x-decorator': 'ChartFilterFormItem', 'x-decorator': 'ChartFilterFormItem',
'x-data-source': dataSource, 'x-data-source': dataSource,
'x-collection-field': `${fieldName}.${field.name}`, 'x-collection-field': `${fieldName}.${field.name}`,
...defaultOperator?.schema,
'x-component-props': { 'x-component-props': {
utc: false,
underFilter: true,
...field.uiSchema?.['x-component-props'], ...field.uiSchema?.['x-component-props'],
'filter-operator': defaultOperator, 'filter-operator': defaultOperator,
'data-source': dataSource, 'data-source': dataSource,
'collection-field': `${fieldName}.${field.name}`, 'collection-field': `${fieldName}.${field.name}`,
}, },
'x-filter-operators': defaultOperator?.value,
}; };
if (field.interface === 'formula') { if (field.interface === 'formula') {
const component = getFormulaComponent(field.dataType) || 'Input'; const component = getFormulaComponent(field.dataType) || 'Input';
@ -189,18 +192,21 @@ export const useChartFilter = () => {
type: 'string', type: 'string',
name: `${name}.${child.name}`, name: `${name}.${child.name}`,
required: false, required: false,
'x-designer': 'ChartFilterItemDesigner', 'x-settings': 'chart:filterForm:item',
'x-toolbar': 'ChartFilterItemToolbar',
'x-decorator': 'ChartFilterFormItem', 'x-decorator': 'ChartFilterFormItem',
'x-data-source': dataSource, 'x-data-source': dataSource,
'x-collection-field': `${fieldName}.${child.name}`, 'x-collection-field': `${fieldName}.${child.name}`,
...child.schema, ...child.schema,
title, title,
...defaultOperator?.schema,
'x-component-props': { 'x-component-props': {
utc: false,
underFilter: true,
'filter-operator': defaultOperator, 'filter-operator': defaultOperator,
'data-source': dataSource, 'data-source': dataSource,
'collection-field': `${fieldName}.${child.name}`, 'collection-field': `${fieldName}.${child.name}`,
}, },
'x-filter-operators': defaultOperator?.value,
}; };
if (defaultOperator?.noValue) { if (defaultOperator?.noValue) {
schema = { schema = {

View File

@ -26,13 +26,20 @@ import {
chartFilterItemInitializers_deprecated, chartFilterItemInitializers_deprecated,
} from './filter'; } from './filter';
import { lang } from './locale'; import { lang } from './locale';
import { chartActionsInitializer } from './initializers/chartActions';
import { chartActionRefreshSettings } from './settings/chartActionRefresh';
import { useChartRefreshActionProps } from './initializers/RefreshAction';
import { chartBlockActionsInitializer } from './initializers/chartBlockActions';
import { useChartBlockRefreshActionProps } from './initializers/BlockRefreshAction';
import { chartBlockActionRefreshSettings } from './settings/chartBlockActionRefresh';
import { useChartBlockCardProps } from './block/ChartBlock'; import { useChartBlockCardProps } from './block/ChartBlock';
import { chartActionsInitializer } from './initializers/chartActions';
import {
chartActionRefreshSettings,
chartBlockActionRefreshSettings,
chartBlockSettings,
chartFilterBlockSettings,
chartFilterItemSettings,
chartRendererSettings,
} from './settings';
import { chartBlockActionsInitializer } from './initializers/chartBlockActions';
import { useChartRefreshActionProps } from './initializers/RefreshAction';
import { useChartBlockRefreshActionProps } from './initializers/BlockRefreshAction';
import { ChartRendererToolbar, ChartFilterBlockToolbar, ChartFilterItemToolbar } from './toolbar';
import { ChartCardItem } from './block/CardItem'; import { ChartCardItem } from './block/CardItem';
class PluginDataVisualiztionClient extends Plugin { class PluginDataVisualiztionClient extends Plugin {
@ -48,6 +55,9 @@ class PluginDataVisualiztionClient extends Plugin {
ChartV2Block, ChartV2Block,
ChartCardItem, ChartCardItem,
ChartBlockProvider, ChartBlockProvider,
ChartRendererToolbar,
ChartFilterBlockToolbar,
ChartFilterItemToolbar,
}); });
this.app.addScopes({ this.app.addScopes({
useChartBlockCardProps, useChartBlockCardProps,
@ -55,16 +65,24 @@ class PluginDataVisualiztionClient extends Plugin {
useChartBlockRefreshActionProps, useChartBlockRefreshActionProps,
}); });
this.app.schemaInitializerManager.add(chartInitializers_deprecated); this.app.schemaInitializerManager.add(
this.app.schemaInitializerManager.add(chartInitializers); chartInitializers_deprecated,
this.app.schemaInitializerManager.add(chartFilterItemInitializers_deprecated); chartInitializers,
this.app.schemaInitializerManager.add(chartFilterItemInitializers); chartFilterItemInitializers_deprecated,
this.app.schemaInitializerManager.add(chartFilterActionInitializers_deprecated); chartFilterItemInitializers,
this.app.schemaInitializerManager.add(chartFilterActionInitializers); chartFilterActionInitializers_deprecated,
this.app.schemaInitializerManager.add(chartActionsInitializer); chartFilterActionInitializers,
this.app.schemaInitializerManager.add(chartBlockActionsInitializer); chartActionsInitializer,
this.app.schemaSettingsManager.add(chartActionRefreshSettings); chartBlockActionsInitializer,
this.app.schemaSettingsManager.add(chartBlockActionRefreshSettings); );
this.app.schemaSettingsManager.add(
chartActionRefreshSettings,
chartBlockActionRefreshSettings,
chartBlockSettings,
chartRendererSettings,
chartFilterBlockSettings,
chartFilterItemSettings,
);
const blockInitializers = this.app.schemaInitializerManager.get('page:addBlock'); const blockInitializers = this.app.schemaInitializerManager.get('page:addBlock');
blockInitializers?.add('dataBlocks.chartV2', { blockInitializers?.add('dataBlocks.chartV2', {

View File

@ -25,6 +25,10 @@ import { useChartsTranslation } from '../locale';
import { createRendererSchema } from '../utils'; import { createRendererSchema } from '../utils';
import { ChartRendererContext } from './ChartRendererProvider'; import { ChartRendererContext } from './ChartRendererProvider';
/**
* @deprecated
* use `chartRendererSettings` instead
*/
export function ChartRendererDesigner() { export function ChartRendererDesigner() {
const { t } = useChartsTranslation(); const { t } = useChartsTranslation();
const { setVisible, setCurrent } = useContext(ChartConfigContext); const { setVisible, setCurrent } = useContext(ChartConfigContext);

View File

@ -0,0 +1,103 @@
/**
* 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 {
SchemaSettings,
SchemaSettingsBlockTitleItem,
SchemaSettingsSwitchItem,
useDesignable,
useToken,
} from '@nocobase/client';
import { useChartsTranslation } from '../locale';
import { useField, useFieldSchema } from '@formily/react';
export const chartBlockSettings = new SchemaSettings({
name: 'chart:block',
items: [
{
name: 'title',
Component: SchemaSettingsBlockTitleItem,
},
{
name: 'background',
Component: () => {
const { t } = useChartsTranslation();
const field = useField();
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
const { token } = useToken();
return (
<SchemaSettingsSwitchItem
title={t('Show background')}
checked={field.componentProps.style?.background !== 'none'}
onChange={(v) => {
const style = {
...field.componentProps.style,
background: v ? token.colorBgContainer : 'none',
boxShadow: v ? token.boxShadowTertiary : 'none',
};
field.componentProps.style = style;
field.componentProps.bordered = v;
fieldSchema['x-component-props'] = field.componentProps;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': field.componentProps,
},
});
dn.refresh();
}}
/>
);
},
},
{
name: 'padding',
Component: () => {
const { t } = useChartsTranslation();
const field = useField();
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
const { token } = useToken();
return (
<SchemaSettingsSwitchItem
title={t('Show padding')}
checked={field.componentProps.bodyStyle?.padding !== '5px 0 0'}
onChange={(v) => {
const style = {
...field.componentProps.bodyStyle,
padding: v ? `${token.paddingLG}px ${token.paddingLG}px 0` : '5px 0 0',
};
field.componentProps.bodyStyle = style;
fieldSchema['x-component-props'] = field.componentProps;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': field.componentProps,
},
});
dn.refresh();
}}
/>
);
},
},
{
name: 'divider',
type: 'divider',
},
{
name: 'remove',
type: 'remove',
},
],
});

View File

@ -0,0 +1,29 @@
/**
* 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 { SchemaSettings, SchemaSettingsRemove } from '@nocobase/client';
export const chartFilterBlockSettings = new SchemaSettings({
name: 'chart:filterForm:block',
items: [
{
name: 'remove',
Component: () => {
return (
<SchemaSettingsRemove
breakRemoveOn={{
'x-component': 'ChartV2Block',
}}
/>
);
},
},
],
});

View File

@ -0,0 +1,574 @@
/**
* 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, { useContext } from 'react';
import {
EditDescription,
GeneralSchemaDesigner,
SchemaSettingsItem,
SchemaSettingsDivider,
SchemaSettingsModalItem,
SchemaSettingsRemove,
VariablesContext,
useCollection_deprecated,
useCollectionManager_deprecated,
useCompile,
useDesignable,
SchemaSettingsSelectItem,
CollectionFieldOptions_deprecated,
DEFAULT_DATA_SOURCE_KEY,
useIsAssociationField,
SchemaSettingsDataScope,
removeNullCondition,
useFormBlockContext,
useLocalVariables,
SchemaSettings,
useApp,
useIsFileField,
} from '@nocobase/client';
import { useChartsTranslation } from '../locale';
import { Schema, useField, useFieldSchema, ISchema } from '@formily/react';
import { Field } from '@formily/core';
import _ from 'lodash';
import { ChartFilterContext } from '../filter/FilterProvider';
import { getPropsSchemaByComponent, setDefaultValue } from '../filter/utils';
import { ChartFilterVariableInput } from '../filter/FilterVariableInput';
import { useChartDataSource, useChartFilter, useCollectionJoinFieldTitle } from '../hooks';
import { Typography } from 'antd';
import { getFormulaInterface } from '../utils';
const { Text } = Typography;
function useFieldComponentName(): string {
const field = useField<Field>();
const isFileField = useIsFileField();
const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const fieldSchema = useFieldSchema();
const fieldName = fieldSchema.name as string;
const collectionField = getField(fieldName) || getCollectionJoinField(fieldSchema['x-collection-field']);
const map = {
// AssociationField 的 mode 默认值是 Select
AssociationField: 'Select',
};
const fieldComponentName =
fieldSchema?.['x-component-props']?.['mode'] ||
field?.componentProps?.['mode'] ||
(isFileField ? 'FileManager' : '') ||
fieldSchema?.['x-component-props']?.['component'] ||
collectionField?.uiSchema?.['x-component'];
return map[fieldComponentName] || fieldComponentName;
}
const EditTitle = () => {
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const { setField } = useContext(ChartFilterContext);
return (
<SchemaSettingsModalItem
key="edit-field-title"
title={t('Edit field title')}
schema={{
type: 'object',
title: t('Edit field title'),
properties: {
title: {
title: t('Field title'),
default: field?.title,
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {},
},
},
}}
onSubmit={({ title }) => {
if (title) {
field.title = title;
fieldSchema.title = title;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
title: fieldSchema.title,
},
});
setField(fieldSchema.name as string, { title });
}
dn.refresh();
}}
/>
);
};
const EditOperator = () => {
const compile = useCompile();
const fieldSchema = useFieldSchema();
const field = useField<Field>();
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const { setField } = useContext(ChartFilterContext);
const fieldName = fieldSchema['x-collection-field'];
const dataSource = fieldSchema['x-data-source'] || DEFAULT_DATA_SOURCE_KEY;
const { cm, fim } = useChartDataSource(dataSource);
if (!cm) {
return null;
}
const getOperators = (props: CollectionFieldOptions_deprecated) => {
let fieldInterface = props?.interface;
if (fieldInterface === 'formula') {
fieldInterface = getFormulaInterface(props.dataType) || props.dataType;
}
const interfaceConfig = fim.getFieldInterface(fieldInterface);
const operatorList = interfaceConfig?.filterable?.operators || [];
return { operatorList, interfaceConfig };
};
let props = cm.getCollectionField(fieldName);
let { operatorList, interfaceConfig } = getOperators(props);
if (!operatorList.length) {
const names = fieldName.split('.');
const name = names.pop();
if (names.length < 2) {
return null;
}
props = cm.getCollectionField(names.join('.'));
if (!props) {
return null;
}
const res = getOperators(props);
operatorList = res.operatorList;
interfaceConfig = res.interfaceConfig;
if (!interfaceConfig) {
return null;
}
const children = interfaceConfig?.filterable.children || [];
const child = children.find((item: any) => item.name === name);
operatorList = child?.operators || [];
}
if (!operatorList.length) {
return null;
}
const defaultComponent = interfaceConfig?.default?.uiSchema?.['x-component'] || 'Input';
const operator = fieldSchema['x-component-props']?.['filter-operator'];
const setOperatorComponent = (operator: any, component: any, props = {}) => {
const componentProps = field.componentProps || {};
field.component = component;
field.componentProps = {
...componentProps,
'filter-operator': operator,
...props,
};
fieldSchema['x-component'] = component;
fieldSchema['x-component-props'] = {
...fieldSchema['x-component-props'],
'filter-operator': operator,
...props,
};
fieldSchema['x-filter-operator'] = operator?.value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-component': component,
'x-component-props': {
...fieldSchema['x-component-props'],
'filter-operator': operator,
...props,
},
'x-filter-operator': operator?.value,
},
});
};
return (
<SchemaSettingsSelectItem
key="operator"
title={t('Operator')}
value={operator?.value || operatorList[0]?.value}
options={compile(operatorList)}
onChange={(op: string) => {
const operator = operatorList.find((item: any) => item.value === op);
if (operator.noValue) {
setOperatorComponent(operator, 'ChartFilterCheckbox', {
content: Schema.compile(operator.label, { t }),
});
} else if (operator.schema?.['x-component']) {
setOperatorComponent(operator, operator.schema['x-component']);
} else {
setOperatorComponent(operator, defaultComponent);
}
setField(fieldSchema.name as string, { operator });
dn.refresh();
}}
/>
);
};
const EditProps = () => {
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const propsSchema = getPropsSchemaByComponent(fieldSchema['x-component']);
return (
<SchemaSettingsModalItem
key="edit-field-props"
title={t('Edit field properties')}
schema={{
title: t('Edit field properties'),
...propsSchema,
}}
initialValues={field.componentProps}
onSubmit={(props) => {
field.reset();
field.componentProps = props;
fieldSchema['x-component-props'] = props;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-component-props': props,
},
});
dn.refresh();
}}
/>
);
};
const EditDefaultValue = () => {
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const variables = useContext(VariablesContext);
const localVariables = useLocalVariables();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { getTranslatedTitle } = useChartFilter();
const title = getTranslatedTitle(fieldSchema.title);
return (
<SchemaSettingsModalItem
key="set field default value"
title={t('Set default value')}
components={{
ChartFilterVariableInput,
}}
schema={{
type: 'void',
title: t('Set default value'),
properties: {
default: {
title,
'x-decorator': 'FormItem',
'x-component': 'ChartFilterVariableInput',
'x-component-props': {
fieldSchema,
},
},
},
}}
onSubmit={({ default: { value } }) => {
field.setInitialValue(value);
fieldSchema.default = value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
default: value,
},
});
dn.refresh();
setDefaultValue(field, variables, localVariables);
}}
/>
);
};
const EditTitleField = () => {
const { getCollectionFields, getCollectionJoinField, getInterface } = useCollectionManager_deprecated();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const compile = useCompile();
const collectionField = getCollectionJoinField(fieldSchema['x-collection-field']);
const targetFields = collectionField?.target
? getCollectionFields(collectionField?.target)
: getCollectionFields(collectionField?.targetCollection) ?? [];
const options = targetFields
.filter((field) => {
if (field?.target || field.type === 'boolean') {
return false;
}
const fieldInterface = getInterface(field?.interface);
return fieldInterface?.titleUsable;
})
.map((field) => ({
value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name,
}));
return options.length > 0 && fieldSchema['x-component'] === 'CollectionField' ? (
<SchemaSettingsSelectItem
key="title-field"
title={t('Title field')}
options={options}
value={field?.componentProps?.fieldNames?.label}
onChange={(label: string) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field.componentProps.fieldNames,
label,
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames;
dn.emit('patch', {
schema,
});
dn.refresh();
}}
/>
) : null;
};
const EditDataScope: React.FC = () => {
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const dataSource = fieldSchema['x-data-source'] || DEFAULT_DATA_SOURCE_KEY;
const { form } = useFormBlockContext();
const { cm } = useChartDataSource(dataSource);
const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
if (!collectionField) {
return null;
}
return (
<SchemaSettingsDataScope
form={form}
defaultFilter={fieldSchema?.['x-component-props']?.service?.params?.filter || {}}
collectionName={collectionField.target}
onSubmit={({ filter }) => {
filter = removeNullCondition(filter);
_.set(field.componentProps, 'service.params.filter', filter);
fieldSchema['x-component-props'] = field.componentProps;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': field.componentProps,
},
});
}}
/>
);
};
export const chartFilterItemSettings = new SchemaSettings({
name: 'chart:filterForm:item',
items: [
{
name: 'originalTitle',
Component: () => {
const { t } = useChartsTranslation();
const fieldSchema = useFieldSchema();
const fieldName = fieldSchema.name as string;
const isCustom = fieldName.startsWith('custom.');
const dataSource = fieldSchema['x-data-source'] || DEFAULT_DATA_SOURCE_KEY;
const originalTitle = useCollectionJoinFieldTitle(dataSource, fieldName);
if (isCustom) {
return null;
}
return (
<>
<SchemaSettingsItem title={fieldName}>
<Text type="secondary">
{t('Original field')}: {originalTitle}
</Text>
</SchemaSettingsItem>
<SchemaSettingsDivider />
</>
);
},
},
{
name: 'decoratorOptions',
type: 'itemGroup',
hideIfNoChildren: true,
useComponentProps() {
const { t } = useChartsTranslation();
return {
title: t('Generic properties'),
};
},
useChildren() {
return [
{
name: 'title',
Component: EditTitle,
},
{
name: 'displayTitle',
type: 'switch',
useComponentProps() {
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
return {
title: t('Display title'),
checked: fieldSchema['x-decorator-props']?.['showTitle'] ?? true,
onChange(checked) {
fieldSchema['x-decorator-props'] = fieldSchema['x-decorator-props'] || {};
fieldSchema['x-decorator-props']['showTitle'] = checked;
field.decoratorProps.showTitle = checked;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-decorator-props': {
...fieldSchema['x-decorator-props'],
showTitle: checked,
},
},
});
dn.refresh();
},
};
},
},
{
name: 'description',
Component: EditDescription,
},
{
name: 'editTooltip',
type: 'modal',
useComponentProps() {
const { t } = useChartsTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
return {
title: t('Edit tooltip'),
schema: {
type: 'object',
title: t('Edit tooltip'),
properties: {
tooltip: {
default: fieldSchema?.['x-decorator-props']?.tooltip,
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
'x-component-props': {},
},
},
} as ISchema,
onSubmit({ tooltip }) {
field.decoratorProps.tooltip = tooltip;
fieldSchema['x-decorator-props'] = fieldSchema['x-decorator-props'] || {};
fieldSchema['x-decorator-props']['tooltip'] = tooltip;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-decorator-props': fieldSchema['x-decorator-props'],
},
});
dn.refresh();
},
};
},
},
{
name: 'defaultValue',
Component: EditDefaultValue,
},
{
name: 'operator',
Component: () => {
const fieldSchema = useFieldSchema();
const fieldName = fieldSchema.name as string;
const isCustom = fieldName.startsWith('custom.');
if (isCustom) {
return null;
}
return <EditOperator />;
},
},
{
name: 'props',
Component: () => {
const fieldSchema = useFieldSchema();
const fieldName = fieldSchema.name as string;
const isCustom = fieldName.startsWith('custom.');
const hasProps = getPropsSchemaByComponent(fieldSchema['x-component']);
if (hasProps && isCustom) {
return <EditProps />;
}
return null;
},
},
];
},
},
{
name: 'componentOptions',
type: 'itemGroup',
hideIfNoChildren: true,
useComponentProps() {
const { t } = useChartsTranslation();
return {
title: t('Specific properties'),
};
},
useChildren() {
const fieldComponentNameMap = {
Select: 'FilterSelect',
};
const app = useApp();
const fieldComponentName = useFieldComponentName();
const componentSettings = app.schemaSettingsManager.get(
`fieldSettings:component:${fieldComponentNameMap[fieldComponentName] || fieldComponentName}`,
);
return componentSettings?.items || [];
},
},
{
name: 'divider',
Component: () => {
return <SchemaSettingsDivider />;
},
},
{
name: 'remove',
Component: () => {
const { t } = useChartsTranslation();
return (
<SchemaSettingsRemove
key="remove"
confirm={{
title: t('Delete field'),
}}
breakRemoveOn={{
'x-component': 'Grid',
}}
/>
);
},
},
],
});

View File

@ -0,0 +1,102 @@
/**
* 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, { useContext } from 'react';
import { useField, useFieldSchema } from '@formily/react';
import {
SchemaSettings,
SchemaSettingsItem,
SchemaSettingsRemove,
gridRowColWrap,
useDataSource,
useCollection,
useDesignable,
} from '@nocobase/client';
import { ChartDataContext } from '../block/ChartDataProvider';
import { ChartConfigContext } from '../configure';
import { useChartsTranslation } from '../locale';
import { createRendererSchema } from '../utils';
import { ChartRendererContext } from '../renderer/ChartRendererProvider';
export const chartRendererSettings = new SchemaSettings({
name: 'chart:renderer',
items: [
{
name: 'configure',
Component: () => {
const { t } = useChartsTranslation();
const { setVisible, setCurrent } = useContext(ChartConfigContext);
const { service } = useContext(ChartRendererContext);
const field = useField();
const schema = useFieldSchema();
const dataSource = useDataSource();
const { name } = useCollection();
return (
<SchemaSettingsItem
title="Configure"
key="configure"
onClick={async () => {
setCurrent({ schema, field, dataSource: dataSource.key, collection: name, service, data: service.data });
setVisible(true);
}}
>
{t('Configure')}
</SchemaSettingsItem>
);
},
},
{
name: 'duplicate',
Component: () => {
const { t } = useChartsTranslation();
const { insertAdjacent, refresh } = useDesignable();
const schema = useFieldSchema();
return (
<SchemaSettingsItem
title="Duplicate"
key="duplicate"
onClick={() => {
insertAdjacent('afterEnd', gridRowColWrap(createRendererSchema(schema?.['x-decorator-props'])));
refresh({ refreshParentSchema: true });
}}
>
{t('Duplicate')}
</SchemaSettingsItem>
);
},
},
{
name: 'divider',
type: 'divider',
},
{
name: 'remove',
Component: () => {
const { removeChart } = useContext(ChartDataContext);
const schema = useFieldSchema();
return (
<SchemaSettingsRemove
// removeParentsIfNoChildren
breakRemoveOn={{
'x-component': 'ChartV2Block',
}}
confirm={{
onOk: () => {
removeChart(schema['x-uid']);
},
}}
/>
);
},
},
],
});

View File

@ -0,0 +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.
*/
export { chartActionRefreshSettings } from './chartActionRefresh';
export { chartBlockActionRefreshSettings } from './chartBlockActionRefresh';
export { chartBlockSettings } from './chartBlock';
export { chartRendererSettings } from './chartRenderer';
export { chartFilterBlockSettings } from './chartFilterBlock';
export { chartFilterItemSettings } from './chartFilterItem';

View File

@ -0,0 +1,18 @@
/**
* 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 { SchemaToolbar } from '@nocobase/client';
import React from 'react';
import { useChartsTranslation } from '../locale';
export const ChartFilterBlockToolbar = () => {
const { t } = useChartsTranslation();
return <SchemaToolbar title={t('Filter')} />;
};

View File

@ -0,0 +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 { SchemaToolbar } from '@nocobase/client';
import React from 'react';
export const ChartFilterItemToolbar = () => {
return <SchemaToolbar showBackground={true} showBorder={false} />;
};

View File

@ -0,0 +1,17 @@
/**
* 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 { SchemaToolbar, useCollection } from '@nocobase/client';
import React from 'react';
export const ChartRendererToolbar = () => {
const { name, title } = useCollection();
return <SchemaToolbar title={title || name} />;
};

View File

@ -0,0 +1,12 @@
/**
* 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.
*/
export * from './ChartRenderer';
export * from './ChartFilterBlock';
export * from './ChartFilterItem';

View File

@ -12,8 +12,7 @@ import { uid } from '@formily/shared';
import lodash from 'lodash'; import lodash from 'lodash';
import { SelectedField } from './configure'; import { SelectedField } from './configure';
import { FieldOption } from './hooks'; import { FieldOption } from './hooks';
import { ChartRendererContext, QueryProps } from './renderer'; import { QueryProps } from './renderer';
import { useContext } from 'react';
export const createRendererSchema = (decoratorProps: any, componentProps = {}) => { export const createRendererSchema = (decoratorProps: any, componentProps = {}) => {
const { collection, config } = decoratorProps; const { collection, config } = decoratorProps;
@ -23,7 +22,8 @@ export const createRendererSchema = (decoratorProps: any, componentProps = {}) =
'x-decorator': 'ChartRendererProvider', 'x-decorator': 'ChartRendererProvider',
'x-decorator-props': decoratorProps, 'x-decorator-props': decoratorProps,
'x-acl-action': `${collection}:list`, 'x-acl-action': `${collection}:list`,
'x-designer': 'ChartRenderer.Designer', 'x-toolbar': 'ChartRendererToolbar',
'x-settings': 'chart:renderer',
'x-component': 'CardItem', 'x-component': 'CardItem',
'x-component-props': { 'x-component-props': {
size: 'small', size: 'small',