diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/__tests__/utils.test.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/__tests__/utils.test.ts deleted file mode 100644 index f05e5d4169..0000000000 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/__tests__/utils.test.ts +++ /dev/null @@ -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 }]); - }); - }); -}); diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts index ae50ff8b42..eb92c1d047 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts @@ -14,6 +14,7 @@ import { DEFAULT_DATA_SOURCE_KEY, useACLRoleContext, useDataSourceManager, + usePlugin, } from '@nocobase/client'; import { useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +22,8 @@ import { ChartConfigContext } from '../configure'; import formatters from '../configure/formatters'; import { useChartsTranslation } from '../locale'; import { ChartRendererContext } from '../renderer'; -import { getField, getSelectedFields, parseField, processData } from '../utils'; +import { getField, getSelectedFields, parseField } from '../utils'; +import PluginDataVisualiztionClient from '..'; export type FieldOption = { value: string; @@ -226,12 +228,36 @@ export const useOrderReaction = (defaultOptions: any[], fields: FieldOption[]) = }; export const useData = (data?: any[], dataSource?: string, collection?: string) => { - const { t } = useChartsTranslation(); const { service, query } = useContext(ChartRendererContext); + const plugin = usePlugin(PluginDataVisualiztionClient); const fields = useFieldsWithAssociation(dataSource, collection); const form = useForm(); - const selectedFields = getSelectedFields(fields, form?.values?.query || query); - return processData(selectedFields, service?.data || data || [], { t }); + const selectedFields = getSelectedFields(fields, form?.values?.query || query) as (FieldOption & { query?: any })[]; + 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 = []) => { diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/index.tsx b/packages/plugins/@nocobase/plugin-data-visualization/src/client/index.tsx index 1793841235..596511b67e 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/index.tsx +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/index.tsx @@ -41,10 +41,42 @@ import { useChartRefreshActionProps } from './initializers/RefreshAction'; import { useChartBlockRefreshActionProps } from './initializers/BlockRefreshAction'; import { ChartRendererToolbar, ChartFilterBlockToolbar, ChartFilterItemToolbar } from './toolbar'; 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 { 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() { this.charts.addGroup('antd', { title: 'Ant Design', charts: antd }); this.charts.addGroup('ant-design-charts', { title: 'Ant Design Charts', charts: g2plot }); diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/utils.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/utils.ts index e1f217a8a2..c2a6c9cdc0 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/utils.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/utils.ts @@ -106,40 +106,6 @@ export const getSelectedFields = (fields: FieldOption[], query: QueryProps) => { 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) => { if (typeof filter === 'object' && filter !== null) { if (Array.isArray(filter)) {