mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
Merge branch 'next' into develop
This commit is contained in:
commit
733dd4d0d3
@ -1,81 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 { processData } from '../utils';
|
|
||||||
|
|
||||||
describe('utils', () => {
|
|
||||||
describe('process data', () => {
|
|
||||||
it('should process select field', () => {
|
|
||||||
expect(
|
|
||||||
processData(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'tag',
|
|
||||||
type: 'bigInt',
|
|
||||||
interface: 'select',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
enum: [
|
|
||||||
{
|
|
||||||
value: '1',
|
|
||||||
label: 'Yes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '2',
|
|
||||||
label: 'No',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
label: 'Tag',
|
|
||||||
value: 'tag',
|
|
||||||
key: 'tag',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[{ tag: 1 }],
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
).toEqual([{ tag: 'Yes' }]);
|
|
||||||
});
|
|
||||||
it('should not process when aggregating', () => {
|
|
||||||
expect(
|
|
||||||
processData(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'tag',
|
|
||||||
type: 'bigInt',
|
|
||||||
interface: 'select',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
enum: [
|
|
||||||
{
|
|
||||||
value: '1',
|
|
||||||
label: 'Yes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '2',
|
|
||||||
label: 'No',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
label: 'Tag',
|
|
||||||
value: 'tag',
|
|
||||||
key: 'tag',
|
|
||||||
query: {
|
|
||||||
field: 'tag',
|
|
||||||
aggregation: 'count',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[{ tag: 1 }],
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
).toEqual([{ tag: 1 }]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -14,6 +14,7 @@ import {
|
|||||||
DEFAULT_DATA_SOURCE_KEY,
|
DEFAULT_DATA_SOURCE_KEY,
|
||||||
useACLRoleContext,
|
useACLRoleContext,
|
||||||
useDataSourceManager,
|
useDataSourceManager,
|
||||||
|
usePlugin,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { useContext, useMemo } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -21,7 +22,8 @@ import { ChartConfigContext } from '../configure';
|
|||||||
import formatters from '../configure/formatters';
|
import formatters from '../configure/formatters';
|
||||||
import { useChartsTranslation } from '../locale';
|
import { useChartsTranslation } from '../locale';
|
||||||
import { ChartRendererContext } from '../renderer';
|
import { ChartRendererContext } from '../renderer';
|
||||||
import { getField, getSelectedFields, parseField, processData } from '../utils';
|
import { getField, getSelectedFields, parseField } from '../utils';
|
||||||
|
import PluginDataVisualiztionClient from '..';
|
||||||
|
|
||||||
export type FieldOption = {
|
export type FieldOption = {
|
||||||
value: string;
|
value: string;
|
||||||
@ -226,12 +228,36 @@ export const useOrderReaction = (defaultOptions: any[], fields: FieldOption[]) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useData = (data?: any[], dataSource?: string, collection?: string) => {
|
export const useData = (data?: any[], dataSource?: string, collection?: string) => {
|
||||||
const { t } = useChartsTranslation();
|
|
||||||
const { service, query } = useContext(ChartRendererContext);
|
const { service, query } = useContext(ChartRendererContext);
|
||||||
|
const plugin = usePlugin(PluginDataVisualiztionClient);
|
||||||
const fields = useFieldsWithAssociation(dataSource, collection);
|
const fields = useFieldsWithAssociation(dataSource, collection);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const selectedFields = getSelectedFields(fields, form?.values?.query || query);
|
const selectedFields = getSelectedFields(fields, form?.values?.query || query) as (FieldOption & { query?: any })[];
|
||||||
return processData(selectedFields, service?.data || data || [], { t });
|
const fieldInterfaceConfigs = plugin.fieldInterfaceConfigs;
|
||||||
|
const formatters = {};
|
||||||
|
for (const field of selectedFields) {
|
||||||
|
if (field?.query?.aggregation) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const config = fieldInterfaceConfigs[field.interface];
|
||||||
|
if (!config) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { valueFormatter } = config;
|
||||||
|
formatters[field.value] = (value: any) => valueFormatter(field, value);
|
||||||
|
}
|
||||||
|
return (service?.data || data || []).map((record: any) => {
|
||||||
|
const processed = {};
|
||||||
|
Object.entries(record).forEach(([key, value]) => {
|
||||||
|
const formatter = formatters[key];
|
||||||
|
if (!formatter) {
|
||||||
|
processed[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processed[key] = formatter(value);
|
||||||
|
});
|
||||||
|
return processed;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCollectionFieldsOptions = (dataSource: string, collectionName: string, maxDepth = 2, excludes = []) => {
|
export const useCollectionFieldsOptions = (dataSource: string, collectionName: string, maxDepth = 2, excludes = []) => {
|
||||||
|
@ -41,10 +41,42 @@ import { useChartRefreshActionProps } from './initializers/RefreshAction';
|
|||||||
import { useChartBlockRefreshActionProps } from './initializers/BlockRefreshAction';
|
import { useChartBlockRefreshActionProps } from './initializers/BlockRefreshAction';
|
||||||
import { ChartRendererToolbar, ChartFilterBlockToolbar, ChartFilterItemToolbar } from './toolbar';
|
import { ChartRendererToolbar, ChartFilterBlockToolbar, ChartFilterItemToolbar } from './toolbar';
|
||||||
import { ChartCardItem } from './block/CardItem';
|
import { ChartCardItem } from './block/CardItem';
|
||||||
|
import { Schema } from '@formily/react';
|
||||||
|
|
||||||
|
type fieldInterfaceConfig = {
|
||||||
|
valueFormatter: (field: any, value: any) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const valueFormatter = (field: any, value: any) => {
|
||||||
|
const options = field.uiSchema?.enum;
|
||||||
|
const parseEnumValues = (options: { label: string; value: string }[], value: any) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((v) => parseEnumValues(options, v));
|
||||||
|
}
|
||||||
|
const option = options.find((option) => option.value === (value?.toString?.() || value));
|
||||||
|
return Schema.compile(option?.label || value, { t: lang });
|
||||||
|
};
|
||||||
|
if (!options || !Array.isArray(options)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return parseEnumValues(options, value);
|
||||||
|
};
|
||||||
|
|
||||||
class PluginDataVisualiztionClient extends Plugin {
|
class PluginDataVisualiztionClient extends Plugin {
|
||||||
public charts: ChartGroup = new ChartGroup();
|
public charts: ChartGroup = new ChartGroup();
|
||||||
|
|
||||||
|
fieldInterfaceConfigs: {
|
||||||
|
[fieldInterface: string]: fieldInterfaceConfig;
|
||||||
|
} = {
|
||||||
|
select: { valueFormatter },
|
||||||
|
multipleSelect: { valueFormatter },
|
||||||
|
radioGroup: { valueFormatter },
|
||||||
|
};
|
||||||
|
|
||||||
|
registerFieldInterfaceConfig(key: string, config: fieldInterfaceConfig) {
|
||||||
|
this.fieldInterfaceConfigs[key] = config;
|
||||||
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.charts.addGroup('antd', { title: 'Ant Design', charts: antd });
|
this.charts.addGroup('antd', { title: 'Ant Design', charts: antd });
|
||||||
this.charts.addGroup('ant-design-charts', { title: 'Ant Design Charts', charts: g2plot });
|
this.charts.addGroup('ant-design-charts', { title: 'Ant Design Charts', charts: g2plot });
|
||||||
|
@ -106,40 +106,6 @@ export const getSelectedFields = (fields: FieldOption[], query: QueryProps) => {
|
|||||||
return selectedFields;
|
return selectedFields;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const processData = (selectedFields: (FieldOption & { query?: any })[], data: any[], scope: any) => {
|
|
||||||
const parseEnum = (field: FieldOption, value: any) => {
|
|
||||||
const options = field.uiSchema?.enum as { value: string; label: string }[];
|
|
||||||
if (!options || !Array.isArray(options)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.map((v) => parseEnum(field, v));
|
|
||||||
}
|
|
||||||
const option = options.find((option) => option.value === (value?.toString?.() || value));
|
|
||||||
return Schema.compile(option?.label || value, scope);
|
|
||||||
};
|
|
||||||
return data.map((record) => {
|
|
||||||
const processed = {};
|
|
||||||
Object.entries(record).forEach(([key, value]) => {
|
|
||||||
const field = selectedFields.find((field) => field.value === key && !field?.query?.aggregation);
|
|
||||||
if (!field) {
|
|
||||||
processed[key] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (field.interface) {
|
|
||||||
case 'select':
|
|
||||||
case 'radioGroup':
|
|
||||||
case 'multipleSelect':
|
|
||||||
processed[key] = parseEnum(field, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
processed[key] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return processed;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeUnparsableFilter = (filter: any) => {
|
export const removeUnparsableFilter = (filter: any) => {
|
||||||
if (typeof filter === 'object' && filter !== null) {
|
if (typeof filter === 'object' && filter !== null) {
|
||||||
if (Array.isArray(filter)) {
|
if (Array.isArray(filter)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user