<!-- 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. --> <!-- Describe the new feature or modification to an existing feature clearly and consciously. --> <!-- Explain the reason for adding or modifying this feature. --> <!-- Provide a technically detailed description of the key changes made. --> - Frontend - Backend <!-- Provide any suggestions or recommendations for improvements in the testing plan. --> <!-- Identify any potential risks or issues that may arise from the new feature or modification. --> <!-- Including any screenshots of the new feature or modification. --> Co-authored-by: sealday <sealday@gmail.com> Co-authored-by: wjh <wwwjh0710@163.com> Co-authored-by: 吕延祥 <2256334253@qq.com> Reviewed-on: daoyoucloud/nocobase#317
145 lines
4.4 KiB
TypeScript
145 lines
4.4 KiB
TypeScript
import { useFieldSchema } from '@formily/react';
|
|
import {
|
|
CollectionManagerProvider,
|
|
MaybeCollectionProvider,
|
|
useAPIClient,
|
|
useDataSourceManager,
|
|
useRequest,
|
|
} from '@nocobase/client';
|
|
import React, { createContext, useContext } from 'react';
|
|
import { parseField, removeUnparsableFilter } from '../utils';
|
|
import { ChartDataContext } from '../block/ChartDataProvider';
|
|
import { ConfigProvider } from 'antd';
|
|
import { useChartFilter } from '../hooks';
|
|
import { ChartFilterContext } from '../filter/FilterProvider';
|
|
|
|
export type MeasureProps = {
|
|
field: string | string[];
|
|
aggregation?: string;
|
|
alias?: string;
|
|
};
|
|
|
|
export type DimensionProps = {
|
|
field: string | string[];
|
|
alias?: string;
|
|
format?: string;
|
|
};
|
|
|
|
export type TransformProps = {
|
|
field: string;
|
|
type: string;
|
|
format: string;
|
|
specific?: string;
|
|
};
|
|
|
|
export type QueryProps = Partial<{
|
|
measures: MeasureProps[];
|
|
dimensions: DimensionProps[];
|
|
orders: {
|
|
field: string;
|
|
order: 'asc' | 'desc';
|
|
}[];
|
|
filter: any;
|
|
limit: number;
|
|
sql: {
|
|
fields?: string;
|
|
clauses?: string;
|
|
};
|
|
}>;
|
|
|
|
export type ChartRendererProps = {
|
|
collection: string;
|
|
dataSource?: string;
|
|
query?: QueryProps;
|
|
config?: {
|
|
chartType: string;
|
|
general: any;
|
|
advanced: any;
|
|
};
|
|
transform?: TransformProps[];
|
|
mode?: 'builder' | 'sql';
|
|
};
|
|
|
|
export const ChartRendererContext = createContext<
|
|
{
|
|
service: any;
|
|
data?: any[];
|
|
} & ChartRendererProps
|
|
>({} as any);
|
|
ChartRendererContext.displayName = 'ChartRendererContext';
|
|
|
|
export const ChartRendererProvider: React.FC<ChartRendererProps> = (props) => {
|
|
const { query, config, collection, transform, dataSource } = props;
|
|
const { addChart } = useContext(ChartDataContext);
|
|
const { ready, form, enabled } = useContext(ChartFilterContext);
|
|
const { getFilter, hasFilter, appendFilter } = useChartFilter();
|
|
const schema = useFieldSchema();
|
|
const api = useAPIClient();
|
|
const service = useRequest(
|
|
(collection, query, manual) =>
|
|
new Promise((resolve, reject) => {
|
|
// Check if the chart is configured
|
|
if (!(collection && query?.measures?.length)) return resolve(undefined);
|
|
// If the filter block is enabled, the filter form is required to be rendered
|
|
if (enabled && !form) return resolve(undefined);
|
|
const filterValues = getFilter();
|
|
const queryWithFilter =
|
|
!manual && hasFilter({ collection, query }, filterValues)
|
|
? appendFilter({ collection, query }, filterValues)
|
|
: query;
|
|
api
|
|
.request({
|
|
url: 'charts:query',
|
|
method: 'POST',
|
|
data: {
|
|
uid: schema?.['x-uid'],
|
|
collection,
|
|
...queryWithFilter,
|
|
filter: removeUnparsableFilter(queryWithFilter.filter),
|
|
dimensions: (query?.dimensions || []).map((item: DimensionProps) => {
|
|
const dimension = { ...item };
|
|
if (item.format && !item.alias) {
|
|
const { alias } = parseField(item.field);
|
|
dimension.alias = alias;
|
|
}
|
|
return dimension;
|
|
}),
|
|
measures: (query?.measures || []).map((item: MeasureProps) => {
|
|
const measure = { ...item };
|
|
if (item.aggregation && !item.alias) {
|
|
const { alias } = parseField(item.field);
|
|
measure.alias = alias;
|
|
}
|
|
return measure;
|
|
}),
|
|
},
|
|
})
|
|
.then((res) => {
|
|
resolve(res?.data?.data);
|
|
if (!manual && schema?.['x-uid']) {
|
|
addChart(schema?.['x-uid'], { collection, service, query });
|
|
}
|
|
})
|
|
.catch(reject);
|
|
}),
|
|
{
|
|
defaultParams: [collection, query],
|
|
// Wait until ChartFilterProvider is rendered and check the status of the filter form
|
|
// since the filter parameters should be applied if the filter block is enabled
|
|
ready: ready && (!enabled || !!form),
|
|
},
|
|
);
|
|
|
|
return (
|
|
<CollectionManagerProvider dataSource={dataSource}>
|
|
<MaybeCollectionProvider collection={collection} dataSource={dataSource}>
|
|
<ConfigProvider card={{ style: { boxShadow: 'none' } }}>
|
|
<ChartRendererContext.Provider value={{ collection, config, transform, service, query }}>
|
|
{props.children}
|
|
</ChartRendererContext.Provider>
|
|
</ConfigProvider>
|
|
</MaybeCollectionProvider>
|
|
</CollectionManagerProvider>
|
|
);
|
|
};
|