mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-07 22:49:26 +08:00
349 lines
11 KiB
TypeScript
349 lines
11 KiB
TypeScript
/**
|
|
* 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 { ArrayField } from '@formily/core';
|
|
import { ISchema, Schema, useForm } from '@formily/react';
|
|
import {
|
|
CollectionFieldOptions,
|
|
DEFAULT_DATA_SOURCE_KEY,
|
|
useACLRoleContext,
|
|
useDataSourceManager,
|
|
} from '@nocobase/client';
|
|
import { useContext, useMemo } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { ChartConfigContext } from '../configure';
|
|
import formatters from '../configure/formatters';
|
|
import { useChartsTranslation } from '../locale';
|
|
import { ChartRendererContext } from '../renderer';
|
|
import { getField, getSelectedFields, parseField, processData } from '../utils';
|
|
|
|
export type FieldOption = {
|
|
value: string;
|
|
label: string;
|
|
key: string;
|
|
alias?: string;
|
|
name?: string;
|
|
type?: string;
|
|
interface?: string;
|
|
uiSchema?: ISchema;
|
|
target?: string;
|
|
targetFields?: FieldOption[];
|
|
};
|
|
|
|
export const useChartDataSource = (dataSource?: string) => {
|
|
const { current } = useContext(ChartConfigContext);
|
|
const { dataSource: currentDataSource, collection } = current || {};
|
|
dataSource = dataSource || currentDataSource || DEFAULT_DATA_SOURCE_KEY;
|
|
const dm = useDataSourceManager();
|
|
const ds = dm.getDataSource(dataSource);
|
|
const fim = dm.collectionFieldInterfaceManager;
|
|
const cm = ds?.collectionManager;
|
|
return { cm, fim, collection };
|
|
};
|
|
|
|
export const useFields = (
|
|
collectionFields: CollectionFieldOptions[],
|
|
): (CollectionFieldOptions & {
|
|
key: string;
|
|
label: string;
|
|
value: string;
|
|
})[] => {
|
|
const fields = (collectionFields || [])
|
|
.filter((field) => {
|
|
return field.interface;
|
|
})
|
|
.map((field) => ({
|
|
...field,
|
|
key: field.name,
|
|
label: field.uiSchema?.title || field.name,
|
|
value: field.name,
|
|
}));
|
|
return fields;
|
|
};
|
|
|
|
export const useFieldsWithAssociation = (dataSource?: string, collection?: string) => {
|
|
const { t } = useTranslation();
|
|
const { cm, fim, collection: _collection } = useChartDataSource(dataSource);
|
|
const collectionFields = cm.getCollectionFields(collection || _collection);
|
|
const fields = useFields(collectionFields);
|
|
return useMemo(
|
|
() =>
|
|
fields.map((field) => {
|
|
const filterable = fim.getFieldInterface(field.interface)?.filterable;
|
|
const label = Schema.compile(field.uiSchema?.title || field.name, { t });
|
|
if (!(filterable && (filterable?.nested || filterable?.children?.length))) {
|
|
return { ...field, label };
|
|
}
|
|
let targetFields = [];
|
|
if (filterable?.nested) {
|
|
const nestedFields = (cm.getCollectionFields(field.target) || [])
|
|
.filter((targetField) => {
|
|
return targetField.interface;
|
|
})
|
|
.map((targetField) => ({
|
|
...targetField,
|
|
key: `${field.name}.${targetField.name}`,
|
|
label: `${label} / ${Schema.compile(targetField.uiSchema?.title || targetField.name, { t })}`,
|
|
value: `${field.name}.${targetField.name}`,
|
|
}));
|
|
targetFields = [...targetFields, ...nestedFields];
|
|
}
|
|
|
|
if (filterable?.children?.length) {
|
|
const children = filterable.children.map((child: any) => ({
|
|
...child,
|
|
key: `${field.name}.${child.name}`,
|
|
label: `${label} / ${Schema.compile(child.schema?.title || child.title || child.name, { t })}`,
|
|
value: `${field.name}.${child.name}`,
|
|
}));
|
|
targetFields = [...targetFields, ...children];
|
|
}
|
|
|
|
return {
|
|
...field,
|
|
label,
|
|
targetFields,
|
|
};
|
|
}),
|
|
[fields],
|
|
);
|
|
};
|
|
|
|
export const useChartFields = (fields: FieldOption[]) => (field: any) => {
|
|
const query = field.query('query').get('value') || {};
|
|
const selectedFields = getSelectedFields(fields, query);
|
|
field.dataSource = selectedFields;
|
|
};
|
|
|
|
export const useFormatters = (fields: FieldOption[]) => (field: any) => {
|
|
const selectedField = field.query('.field').get('value');
|
|
if (!selectedField) {
|
|
field.dataSource = [];
|
|
return;
|
|
}
|
|
let options = [];
|
|
const fieldInterface = getField(fields, selectedField)?.interface;
|
|
switch (fieldInterface) {
|
|
case 'datetime':
|
|
case 'datetimeTz':
|
|
case 'unixTimestamp':
|
|
case 'datetimeNoTz':
|
|
case 'createdAt':
|
|
case 'updatedAt':
|
|
options = formatters.datetime;
|
|
break;
|
|
case 'date':
|
|
options = formatters.date;
|
|
break;
|
|
case 'time':
|
|
options = formatters.time;
|
|
break;
|
|
default:
|
|
options = [];
|
|
}
|
|
field.dataSource = options;
|
|
};
|
|
|
|
export const useCollectionOptions = () => {
|
|
const { t } = useTranslation();
|
|
const dm = useDataSourceManager();
|
|
const { parseAction } = useACLRoleContext();
|
|
const allCollections = dm.getAllCollections({
|
|
filterCollection: (collection) => {
|
|
const params = parseAction(`${collection.name}:list`);
|
|
return params;
|
|
},
|
|
});
|
|
const options = allCollections
|
|
.filter(({ key, isDBInstance }) => key === DEFAULT_DATA_SOURCE_KEY || isDBInstance)
|
|
.map(({ key, displayName, collections }) => ({
|
|
value: key,
|
|
label: displayName,
|
|
children: collections.map((collection) => ({
|
|
value: collection.name,
|
|
label: collection.title,
|
|
})),
|
|
}));
|
|
return useMemo(() => Schema.compile(options, { t }), [options]);
|
|
};
|
|
|
|
export const useOrderFieldsOptions = (defaultOptions: any[], fields: FieldOption[]) => (field: any) => {
|
|
const query = field.query('query').get('value') || {};
|
|
const { measures = [] } = query;
|
|
const hasAgg = measures.some((measure: { aggregation?: string }) => measure.aggregation);
|
|
if (!hasAgg) {
|
|
field.componentProps.fieldNames = {
|
|
label: 'title',
|
|
value: 'name',
|
|
children: 'children',
|
|
};
|
|
field.dataSource = defaultOptions;
|
|
return;
|
|
}
|
|
const selectedFields = getSelectedFields(fields, query);
|
|
field.componentProps.fieldNames = {};
|
|
field.dataSource = selectedFields;
|
|
};
|
|
|
|
export const useOrderReaction = (defaultOptions: any[], fields: FieldOption[]) => (field: ArrayField) => {
|
|
const query = field.query('query').get('value') || {};
|
|
const { measures = [] } = query;
|
|
const hasAgg = measures.some((measure: { aggregation?: string }) => measure.aggregation);
|
|
let dataSource = defaultOptions;
|
|
const allValues = [];
|
|
if (hasAgg) {
|
|
dataSource = getSelectedFields(fields, query);
|
|
dataSource.forEach((field) => {
|
|
allValues.push(field.value);
|
|
});
|
|
} else {
|
|
dataSource.forEach((field) => {
|
|
const children = field.children || [];
|
|
if (!children.length) {
|
|
allValues.push(field.value || field.name);
|
|
}
|
|
children.forEach((child: any) => {
|
|
allValues.push(`${field.name}.${child.name}`);
|
|
});
|
|
});
|
|
}
|
|
|
|
const orders = field.value || [];
|
|
const newOrders = orders.reduce((newOrders: any[], item: any) => {
|
|
const { alias } = parseField(item.field);
|
|
if (!item.field || allValues.includes(alias)) {
|
|
newOrders.push(item);
|
|
}
|
|
return newOrders;
|
|
}, []);
|
|
field.setValue(newOrders);
|
|
};
|
|
|
|
export const useData = (data?: any[], dataSource?: string, collection?: string) => {
|
|
const { t } = useChartsTranslation();
|
|
const { service, query } = useContext(ChartRendererContext);
|
|
const fields = useFieldsWithAssociation(dataSource, collection);
|
|
const form = useForm();
|
|
const selectedFields = getSelectedFields(fields, form?.values?.query || query);
|
|
return processData(selectedFields, service?.data || data || [], { t });
|
|
};
|
|
|
|
export const useCollectionFieldsOptions = (dataSource: string, collectionName: string, maxDepth = 2, excludes = []) => {
|
|
const { cm, fim, collection } = useChartDataSource(dataSource);
|
|
const collectionFields = cm.getCollectionFields(collectionName || collection);
|
|
const fields = collectionFields.filter((v) => !excludes.includes(v.interface));
|
|
|
|
const field2option = (field, depth, prefix?) => {
|
|
if (!field.interface) {
|
|
return;
|
|
}
|
|
const fieldInterface = fim.getFieldInterface(field.interface);
|
|
if (!fieldInterface?.filterable) {
|
|
return;
|
|
}
|
|
const { nested, children } = fieldInterface.filterable;
|
|
const value = prefix ? `${prefix}.${field.name}` : field.name;
|
|
const option = {
|
|
...field,
|
|
name: field.name,
|
|
title: field?.uiSchema?.title || field.name,
|
|
schema: field?.uiSchema,
|
|
key: value,
|
|
};
|
|
if (field.target && depth > maxDepth) {
|
|
return;
|
|
}
|
|
if (depth > maxDepth) {
|
|
return option;
|
|
}
|
|
if (children?.length) {
|
|
option['children'] = children.map((v) => {
|
|
return {
|
|
...v,
|
|
key: `${field.name}.${v.name}`,
|
|
};
|
|
});
|
|
}
|
|
if (nested) {
|
|
const targetFields = cm.getCollectionFields(field.target).filter((v) => !excludes.includes(v.interface));
|
|
const options = getOptions(targetFields, depth + 1, field.name).filter(Boolean);
|
|
option['children'] = option['children'] || [];
|
|
option['children'].push(...options);
|
|
}
|
|
return option;
|
|
};
|
|
const getOptions = (fields, depth, prefix?) => {
|
|
const options = [];
|
|
fields.forEach((field) => {
|
|
const option = field2option(field, depth, prefix);
|
|
if (option) {
|
|
options.push(option);
|
|
}
|
|
});
|
|
return options;
|
|
};
|
|
const options = getOptions(fields, 1);
|
|
return options;
|
|
};
|
|
|
|
export const useCollectionFilterOptions = (dataSource: string, collection: string) => {
|
|
const { cm, fim, collection: _collection } = useChartDataSource(dataSource);
|
|
return useMemo(() => {
|
|
const fields = cm.getCollectionFields(collection || _collection);
|
|
const field2option = (field, depth) => {
|
|
if (!field.interface) {
|
|
return;
|
|
}
|
|
const fieldInterface = fim.getFieldInterface(field.interface);
|
|
if (!fieldInterface?.filterable) {
|
|
return;
|
|
}
|
|
const { nested, children, operators } = fieldInterface.filterable;
|
|
const option = {
|
|
name: field.name,
|
|
title: field?.uiSchema?.title || field.name,
|
|
schema: field?.uiSchema,
|
|
operators:
|
|
operators?.filter?.((operator) => {
|
|
return !operator?.visible || operator.visible(field);
|
|
}) || [],
|
|
interface: field.interface,
|
|
};
|
|
if (field.target && depth > 2) {
|
|
return;
|
|
}
|
|
if (depth > 2) {
|
|
return option;
|
|
}
|
|
if (children?.length) {
|
|
option['children'] = children;
|
|
}
|
|
if (nested) {
|
|
const targetFields = cm.getCollectionFields(field.target);
|
|
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
|
option['children'] = option['children'] || [];
|
|
option['children'].push(...options);
|
|
}
|
|
return option;
|
|
};
|
|
const getOptions = (fields, depth) => {
|
|
const options = [];
|
|
fields.forEach((field) => {
|
|
const option = field2option(field, depth);
|
|
if (option) {
|
|
options.push(option);
|
|
}
|
|
});
|
|
return options;
|
|
};
|
|
const options = getOptions(fields, 1);
|
|
return options;
|
|
}, [collection, cm, fim]);
|
|
};
|