fix: 图表添加级联选择 (#522)
<!-- Note --> <!-- This is a template for submitting a new feature. Use the bug fix template if you're submitting a bug fix pull request by adding `template=bug_fix.md` to your pull request URL. --> # Description <!-- Describe the new feature or modification to an existing feature clearly and consciously. --> # Motivation <!-- Explain the reason for adding or modifying this feature. --> # Key changes <!-- Provide a technically detailed description of the key changes made. --> - Frontend - Backend # Test plan ## Suggestions <!-- Provide any suggestions or recommendations for improvements in the testing plan. --> ## Underlying risk <!-- Identify any potential risks or issues that may arise from the new feature or modification. --> # Showcase <!-- Including any screenshots of the new feature or modification. --> Reviewed-on: daoyoucloud/tachycode#522 Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
eb0ba1c96b
commit
08adf2fe40
@ -24,38 +24,88 @@ const AssociationCascader = connect((props) => {
|
||||
});
|
||||
|
||||
const options = useMemo(() => {
|
||||
const dict: { [key: string]: string[] } =
|
||||
data?.data.reduce((acc, item) => {
|
||||
if (item[associationField]) {
|
||||
const key = item[associationField][joinTitleField];
|
||||
acc[key] ??= [];
|
||||
acc[key].push(item[titleField]);
|
||||
if (props.chartCascader) {
|
||||
const dataOptions = {};
|
||||
const options = [];
|
||||
data?.data.forEach((item) => {
|
||||
if (item?.[associationField]) {
|
||||
const field = [];
|
||||
if (Array.isArray(item[associationField]) && item[associationField].length) {
|
||||
field.push(...item[associationField]);
|
||||
} else {
|
||||
field.push({ ...item[associationField] });
|
||||
}
|
||||
field.forEach((fieldItem) => {
|
||||
if (!Object.keys(fieldItem).length) return;
|
||||
if (dataOptions[fieldItem[joinTitleField]]) {
|
||||
dataOptions[fieldItem[joinTitleField]].push({
|
||||
label: item[titleField],
|
||||
value: item[titleField],
|
||||
});
|
||||
} else {
|
||||
dataOptions[fieldItem[joinTitleField]] = [
|
||||
{
|
||||
label: item[titleField],
|
||||
value: item[titleField],
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, {}) || {};
|
||||
const options = Object.entries(dict).map(([key, values]) => ({
|
||||
[fieldNames.label]: key,
|
||||
[fieldNames.value]: key,
|
||||
children: values.map((value) => ({ [fieldNames.label]: value, [fieldNames.value]: value })),
|
||||
}));
|
||||
return options;
|
||||
});
|
||||
for (const key in dataOptions) {
|
||||
options.push({
|
||||
label: key,
|
||||
value: key,
|
||||
children: [...dataOptions[key]],
|
||||
});
|
||||
}
|
||||
return options;
|
||||
} else {
|
||||
const dict: { [key: string]: string[] } =
|
||||
data?.data.reduce((acc, item) => {
|
||||
if (item[associationField]) {
|
||||
const key = item[associationField][joinTitleField];
|
||||
acc[key] ??= [];
|
||||
acc[key].push(item[titleField]);
|
||||
}
|
||||
return acc;
|
||||
}, {}) || {};
|
||||
const options = Object.entries(dict).map(([key, values]) => ({
|
||||
[fieldNames.label]: key,
|
||||
[fieldNames.value]: key,
|
||||
children: values.map((value) => ({ [fieldNames.label]: value, [fieldNames.value]: value })),
|
||||
}));
|
||||
return options;
|
||||
}
|
||||
}, [associationField, joinTitleField, titleField, data?.data]);
|
||||
return <SingleValueCascader options={options} {...props} showSearch />;
|
||||
|
||||
return (
|
||||
<SingleValueCascader
|
||||
options={options}
|
||||
{...props}
|
||||
showSearch
|
||||
titleField={titleField}
|
||||
joinTitleField={joinTitleField}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const SingleValueCascader = (props) => {
|
||||
const { value, options, onChange, fieldNames } = props;
|
||||
const { value, options, onChange, fieldNames, chartCascader, titleField, joinTitleField } = props;
|
||||
const fieldLabel = fieldNames ? fieldNames.value : titleField;
|
||||
const joinFieldLabel = fieldNames ? fieldNames.value : 'value';
|
||||
const arrayValue = value && options ? [] : undefined;
|
||||
if (arrayValue) {
|
||||
for (const option of options) {
|
||||
if (option[fieldNames.value] === value) {
|
||||
arrayValue.push(option[fieldNames.value]);
|
||||
if (option[fieldLabel] === value) {
|
||||
arrayValue.push(option[fieldLabel]);
|
||||
break;
|
||||
}
|
||||
for (const subOption of option.children) {
|
||||
if (subOption[fieldNames.value] === value) {
|
||||
arrayValue.push(option[fieldNames.value]);
|
||||
arrayValue.push(subOption[fieldNames.value]);
|
||||
if (subOption[joinFieldLabel] === value) {
|
||||
arrayValue.push(option[joinFieldLabel]);
|
||||
arrayValue.push(subOption[joinFieldLabel]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
useDesignable,
|
||||
useGlobalTheme,
|
||||
useSchemaInitializerItem,
|
||||
useCompile,
|
||||
} from '@nocobase/client';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Alert, ConfigProvider, Typography } from 'antd';
|
||||
@ -35,7 +36,7 @@ const { Paragraph, Text } = Typography;
|
||||
const FieldComponentProps: React.FC = observer(
|
||||
(props) => {
|
||||
const form = useForm();
|
||||
const schema = getPropsSchemaByComponent(form.values.component);
|
||||
const schema = getPropsSchemaByComponent(form.values.component, props['allCollection']);
|
||||
return schema ? <SchemaComponent schema={schema} {...props} /> : null;
|
||||
},
|
||||
{ displayName: 'FieldComponentProps' },
|
||||
@ -127,12 +128,31 @@ export const ChartFilterCustomItemInitializer: React.FC<{
|
||||
const dm = useDataSourceManager();
|
||||
const sourceFields = useChartFilterSourceFields();
|
||||
const { options: fieldComponents, values: fieldComponentValues } = useFieldComponents();
|
||||
const { collections, getCollectionFields } = useCollectionManager_deprecated();
|
||||
const compile = useCompile();
|
||||
const allCollection = collections.map((collection) => {
|
||||
return {
|
||||
label: collection.getOption('title'),
|
||||
value: collection.getOption('name'),
|
||||
};
|
||||
});
|
||||
const handleClick = useCallback(async () => {
|
||||
const values = await FormDialog(
|
||||
t('Add custom field'),
|
||||
() => (
|
||||
<SchemaComponentOptions
|
||||
scope={{ ...scope, useChartFilterSourceFields }}
|
||||
scope={{
|
||||
...scope,
|
||||
useChartFilterSourceFields,
|
||||
useCollectionField(collection) {
|
||||
return getCollectionFields(collection)?.map((field) => {
|
||||
return {
|
||||
label: compile(field.uiSchema.title),
|
||||
value: field.name,
|
||||
};
|
||||
});
|
||||
},
|
||||
}}
|
||||
components={{ ...components, FieldComponentProps }}
|
||||
>
|
||||
<FormLayout layout={'vertical'}>
|
||||
@ -176,6 +196,9 @@ export const ChartFilterCustomItemInitializer: React.FC<{
|
||||
type: 'object',
|
||||
title: t('Component properties'),
|
||||
'x-component': 'FieldComponentProps',
|
||||
'x-component-props': {
|
||||
allCollection,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
@ -242,6 +265,7 @@ export const ChartFilterCustomItemInitializer: React.FC<{
|
||||
'x-component-props': {
|
||||
...(defaultSchema['x-component-props'] || {}),
|
||||
...props,
|
||||
chartCascader: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -55,7 +55,7 @@ export const getOptionsSchema = () => {
|
||||
return options;
|
||||
};
|
||||
|
||||
export const getPropsSchemaByComponent = (component: string) => {
|
||||
export const getPropsSchemaByComponent = (component: string, allCollection) => {
|
||||
const showTime = {
|
||||
type: 'boolean',
|
||||
'x-content': '{{ t("Show time") }}',
|
||||
@ -135,6 +135,34 @@ export const getPropsSchemaByComponent = (component: string) => {
|
||||
options,
|
||||
},
|
||||
},
|
||||
AssociationCascader: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
title: '{{t("Field collection")}}',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
enum: allCollection,
|
||||
},
|
||||
associationField: {
|
||||
type: 'string',
|
||||
title: '{{t("Field Association")}}',
|
||||
'x-visible': false,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': {
|
||||
dependencies: ['component', '.collection'],
|
||||
fulfill: {
|
||||
schema: {
|
||||
'x-visible': "{{$deps[0]=== 'AssociationCascader' && $deps[1]}}",
|
||||
enum: '{{useCollectionField($deps[1])}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return propsSchema[component];
|
||||
};
|
||||
|
@ -507,6 +507,7 @@ export const useFieldComponents = () => {
|
||||
{ label: t('Select'), value: 'Select' },
|
||||
{ label: t('Radio group'), value: 'Radio.Group' },
|
||||
{ label: t('Checkbox group'), value: 'Checkbox.Group' },
|
||||
{ label: t('AssociationCascader'), value: 'AssociationCascader' },
|
||||
// { label: t('China region'), value: 'chinaRegion' },
|
||||
];
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user