@nocobase/plugin-data-visualization 0.11.0-alpha.1 → 0.11.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/lib/client/block/ChartBlock.js +16 -15
  2. package/lib/client/block/ChartBlockInitializer.js +4 -4
  3. package/lib/client/block/ChartConfigure.d.ts +4 -5
  4. package/lib/client/block/ChartConfigure.js +109 -94
  5. package/lib/client/block/schemas/configure.js +7 -7
  6. package/lib/client/index.js +1 -1
  7. package/lib/client/locale/index.d.ts +2 -2
  8. package/lib/client/locale/index.js +3 -5
  9. package/lib/client/locale/zh-CN.d.ts +1 -0
  10. package/lib/client/locale/zh-CN.js +2 -1
  11. package/lib/client/renderer/ChartLibrary.d.ts +1 -0
  12. package/lib/client/renderer/ChartLibrary.js +8 -2
  13. package/lib/client/renderer/ChartRenderer.d.ts +1 -4
  14. package/lib/client/renderer/ChartRenderer.js +28 -103
  15. package/lib/client/renderer/ChartRendererProvider.d.ts +11 -1
  16. package/lib/client/renderer/ChartRendererProvider.js +62 -11
  17. package/lib/client/renderer/library/G2PlotLibrary.js +24 -25
  18. package/lib/server/actions/query.js +6 -1
  19. package/package.json +9 -9
  20. package/src/client/block/ChartBlock.tsx +5 -3
  21. package/src/client/block/ChartBlockInitializer.tsx +2 -3
  22. package/src/client/block/ChartConfigure.tsx +106 -85
  23. package/src/client/block/schemas/configure.ts +7 -7
  24. package/src/client/index.tsx +1 -1
  25. package/src/client/locale/index.ts +2 -3
  26. package/src/client/locale/zh-CN.ts +1 -0
  27. package/src/client/renderer/ChartLibrary.tsx +6 -1
  28. package/src/client/renderer/ChartRenderer.tsx +22 -90
  29. package/src/client/renderer/ChartRendererProvider.tsx +60 -7
  30. package/src/client/renderer/library/G2PlotLibrary.tsx +24 -25
  31. package/src/server/__tests__/api.test.ts +47 -50
  32. package/src/server/actions/query.ts +6 -1
@@ -17,10 +17,9 @@ import {
17
17
  useCollectionFilterOptions,
18
18
  useDesignable,
19
19
  } from '@nocobase/client';
20
- import { Alert, Button, Card, Col, Modal, Row, Space, Table, Tabs, Typography } from 'antd';
20
+ import { Alert, App, Button, Card, Col, Modal, Row, Space, Table, Tabs, Typography } from 'antd';
21
21
  import { cloneDeep, isEqual } from 'lodash';
22
22
  import React, { createContext, useContext, useMemo, useRef } from 'react';
23
- import { useTranslation } from 'react-i18next';
24
23
  import {
25
24
  useChartFields,
26
25
  useCollectionOptions,
@@ -32,8 +31,8 @@ import {
32
31
  useTransformers,
33
32
  } from '../hooks';
34
33
  import { useChartsTranslation } from '../locale';
35
- import { ChartRenderer, ChartRendererProvider, useChartTypes, useCharts } from '../renderer';
36
- import { createRendererSchema, getField, getSelectedFields } from '../utils';
34
+ import { ChartRenderer, ChartRendererContext, useChartTypes, useCharts, useDefaultChartType } from '../renderer';
35
+ import { createRendererSchema, getField, getSelectedFields, processData } from '../utils';
37
36
  import { getConfigSchema, querySchema, transformSchema } from './schemas/configure';
38
37
  const { Paragraph, Text } = Typography;
39
38
 
@@ -41,6 +40,9 @@ export type ChartConfigCurrent = {
41
40
  schema: ISchema;
42
41
  field: any;
43
42
  collection: string;
43
+ service: any;
44
+ initialValues?: any;
45
+ data: any[];
44
46
  };
45
47
 
46
48
  export type SelectedField = {
@@ -53,8 +55,6 @@ export const ChartConfigContext = createContext<{
53
55
  setVisible?: (visible: boolean) => void;
54
56
  current?: ChartConfigCurrent;
55
57
  setCurrent?: (current: ChartConfigCurrent) => void;
56
- data?: any[] | string;
57
- setData?: (data: any[] | string) => void;
58
58
  }>({
59
59
  visible: true,
60
60
  });
@@ -68,18 +68,18 @@ export const ChartConfigure: React.FC<{
68
68
  },
69
69
  ) => void;
70
70
  }> & {
71
- Renderer: React.FC<{
72
- runQuery?: any;
73
- }>;
71
+ Renderer: React.FC;
74
72
  Config: React.FC;
75
73
  Query: React.FC;
76
74
  Transform: React.FC;
77
75
  Data: React.FC;
78
76
  } = (props) => {
79
77
  const { t } = useChartsTranslation();
78
+ const { service } = useContext(ChartRendererContext);
80
79
  const { visible, setVisible, current } = useContext(ChartConfigContext);
81
- const { schema, field, collection } = current || {};
80
+ const { schema, field, collection, initialValues } = current || {};
82
81
  const { dn } = useDesignable();
82
+ const { modal } = App.useApp();
83
83
  const { insert } = props;
84
84
 
85
85
  const charts = useCharts();
@@ -124,53 +124,48 @@ export const ChartConfigure: React.FC<{
124
124
  setMeasures(cloneDeep(currentMeasures));
125
125
  setDimensions(cloneDeep(currentDimensions));
126
126
  };
127
- const chartTypes = useChartTypes();
127
+ const chartType = useDefaultChartType();
128
128
  const form = useMemo(
129
129
  () => {
130
- const chartType = chartTypes[0]?.children?.[0]?.value;
131
130
  return createForm({
132
- values: { config: { chartType }, ...schema?.['x-decorator-props'], collection, data: '' },
131
+ values: { config: { chartType }, ...(initialValues || field?.decoratorProps), collection },
133
132
  effects: (form) => {
134
133
  onFieldChange('config.chartType', () => initChart(true));
135
- onFormInit(() => queryReact(form));
134
+ onFormInit(() => {
135
+ queryReact(form);
136
+ });
136
137
  },
137
138
  });
138
139
  },
139
140
  // visible, collection added here to re-initialize form when visible, collection change
140
141
  // eslint-disable-next-line react-hooks/exhaustive-deps
141
- [schema, visible, collection],
142
+ [field, visible, collection],
142
143
  );
143
144
 
144
- const runQuery = useRef(null);
145
- const RunButton: React.FC = () => {
146
- const [loading, setLoading] = React.useState(false);
147
- return (
148
- <Button
149
- type="link"
150
- loading={loading}
151
- icon={<RightSquareOutlined />}
152
- onClick={async () => {
153
- const queryField = form.query('query').take() as ObjectField;
154
- try {
155
- await queryField?.validate();
156
- } catch (e) {
157
- return;
158
- }
145
+ const RunButton: React.FC = () => (
146
+ <Button
147
+ type="link"
148
+ loading={service?.loading}
149
+ icon={<RightSquareOutlined />}
150
+ onClick={async () => {
151
+ const queryField = form.query('query').take() as ObjectField;
152
+ try {
153
+ await queryField?.validate();
154
+ } catch (e) {
155
+ return;
156
+ }
159
157
 
160
- setLoading(true);
161
- try {
162
- await runQuery?.current(form.values.query);
163
- } catch (e) {
164
- console.log(e);
165
- }
166
- queryReact(form, initChart);
167
- setLoading(false);
168
- }}
169
- >
170
- {t('Run query')}
171
- </Button>
172
- );
173
- };
158
+ try {
159
+ await service.runAsync(collection, form.values.query);
160
+ } catch (e) {
161
+ console.log(e);
162
+ }
163
+ queryReact(form, initChart);
164
+ }}
165
+ >
166
+ {t('Run query')}
167
+ </Button>
168
+ );
174
169
 
175
170
  const queryRef = useRef(null);
176
171
  const configRef = useRef(null);
@@ -180,6 +175,13 @@ export const ChartConfigure: React.FC<{
180
175
  open={visible}
181
176
  onOk={() => {
182
177
  const { query, config, transform, mode } = form.values;
178
+ const afterSave = () => {
179
+ setVisible(false);
180
+ current.service?.run(collection, query);
181
+ queryRef.current.scrollTop = 0;
182
+ configRef.current.scrollTop = 0;
183
+ service.mutate(undefined);
184
+ };
183
185
  const rendererProps = {
184
186
  query,
185
187
  config,
@@ -194,18 +196,16 @@ export const ChartConfigure: React.FC<{
194
196
  dn.emit('patch', {
195
197
  schema,
196
198
  });
197
- setVisible(false);
198
- queryRef.current.scrollTop = 0;
199
- configRef.current.scrollTop = 0;
199
+ afterSave();
200
200
  return;
201
201
  }
202
202
  insert(createRendererSchema(rendererProps), {
203
- onSuccess: () => setVisible(false),
203
+ onSuccess: afterSave,
204
204
  wrap: gridRowColWrap,
205
205
  });
206
206
  }}
207
207
  onCancel={() => {
208
- Modal.confirm({
208
+ modal.confirm({
209
209
  title: t('Are you sure to cancel?'),
210
210
  content: t('You changes are not saved. If you click OK, your changes will be lost.'),
211
211
  okButtonProps: {
@@ -215,6 +215,7 @@ export const ChartConfigure: React.FC<{
215
215
  setVisible(false);
216
216
  queryRef.current.scrollTop = 0;
217
217
  configRef.current.scrollTop = 0;
218
+ service.mutate(undefined);
218
219
  },
219
220
  });
220
221
  }}
@@ -229,7 +230,8 @@ export const ChartConfigure: React.FC<{
229
230
  <Card
230
231
  style={{
231
232
  height: 'calc(100vh - 300px)',
232
- overflow: 'scroll',
233
+ overflow: 'auto',
234
+ margin: '12px 0 12px 12px',
233
235
  }}
234
236
  ref={queryRef}
235
237
  >
@@ -254,7 +256,8 @@ export const ChartConfigure: React.FC<{
254
256
  <Card
255
257
  style={{
256
258
  height: 'calc(100vh - 300px)',
257
- overflow: 'scroll',
259
+ overflow: 'auto',
260
+ margin: '12px 3px 12px 3px',
258
261
  }}
259
262
  ref={configRef}
260
263
  >
@@ -275,8 +278,12 @@ export const ChartConfigure: React.FC<{
275
278
  </Card>
276
279
  </Col>
277
280
  <Col span={11}>
278
- <Card>
279
- <ChartConfigure.Renderer runQuery={runQuery} />
281
+ <Card
282
+ style={{
283
+ margin: '12px 12px 12px 0',
284
+ }}
285
+ >
286
+ <ChartConfigure.Renderer />
280
287
  </Card>
281
288
  </Col>
282
289
  </Row>
@@ -287,7 +294,8 @@ export const ChartConfigure: React.FC<{
287
294
 
288
295
  ChartConfigure.Renderer = function Renderer(props) {
289
296
  const { current } = useContext(ChartConfigContext);
290
- const { collection } = current || {};
297
+ const { collection, data } = current || {};
298
+ const { service } = useContext(ChartRendererContext);
291
299
  return (
292
300
  <FormConsumer>
293
301
  {(form) => {
@@ -296,15 +304,9 @@ ChartConfigure.Renderer = function Renderer(props) {
296
304
  const config = cloneDeep(form.values.config);
297
305
  const transform = cloneDeep(form.values.transform);
298
306
  return (
299
- <ChartRendererProvider
300
- collection={collection}
301
- query={form.values.query}
302
- config={config}
303
- transform={transform}
304
- mode={form.values.mode}
305
- >
306
- <ChartRenderer configuring={true} {...props} />
307
- </ChartRendererProvider>
307
+ <ChartRendererContext.Provider value={{ collection, config, transform, service, data }}>
308
+ <ChartRenderer {...props} />
309
+ </ChartRendererContext.Provider>
308
310
  );
309
311
  }}
310
312
  </FormConsumer>
@@ -313,27 +315,30 @@ ChartConfigure.Renderer = function Renderer(props) {
313
315
 
314
316
  ChartConfigure.Query = function Query() {
315
317
  const { t } = useChartsTranslation();
316
- const { t: lang } = useTranslation();
317
318
  const fields = useFieldsWithAssociation();
318
319
  const useFormatterOptions = useFormatters(fields);
319
320
  const collectionOptions = useCollectionOptions();
320
321
  const { current, setCurrent } = useContext(ChartConfigContext);
321
322
  const { collection } = current || {};
322
323
  const fieldOptions = useCollectionFieldsOptions(collection, 1);
323
- const compiledFieldOptions = Schema.compile(fieldOptions, { t: lang });
324
+ const compiledFieldOptions = Schema.compile(fieldOptions, { t });
324
325
  const filterOptions = useCollectionFilterOptions(collection);
325
- const formCollapse = FormCollapse.createFormCollapse(['measures', 'dimensions', 'filter', 'sort']);
326
+
327
+ const { service } = useContext(ChartRendererContext);
326
328
  const onCollectionChange = (value: string) => {
327
329
  const { schema, field } = current;
328
- const newSchema = { ...schema };
329
- newSchema['x-decorator-props'] = { collection: value };
330
- newSchema['x-acl-action'] = `${value}:list`;
331
330
  setCurrent({
332
- schema: newSchema,
331
+ schema,
333
332
  field,
334
333
  collection: value,
334
+ service: current.service,
335
+ initialValues: {},
336
+ data: undefined,
335
337
  });
338
+ service.mutate(undefined);
336
339
  };
340
+
341
+ const formCollapse = FormCollapse.createFormCollapse(['measures', 'dimensions', 'filter', 'sort']);
337
342
  const FromSql = () => (
338
343
  <Text code>
339
344
  From <span style={{ color: '#1890ff' }}>{current?.collection}</span>
@@ -429,22 +434,38 @@ ChartConfigure.Transform = function Transform() {
429
434
  };
430
435
 
431
436
  ChartConfigure.Data = function Data() {
432
- const { data } = useContext(ChartConfigContext);
437
+ const { t } = useChartsTranslation();
438
+ const { current } = useContext(ChartConfigContext);
439
+ const { service } = useContext(ChartRendererContext);
433
440
  const fields = useFieldsWithAssociation();
434
- return Array.isArray(data) ? (
435
- <Table
436
- dataSource={data}
437
- columns={Object.keys(data[0] || {}).map((col) => {
438
- const field = getField(fields, col.split('.'));
439
- return {
440
- title: field?.label || col,
441
- dataIndex: col,
442
- key: col,
443
- };
444
- })}
445
- size="small"
446
- />
441
+ const data = processData(fields, service?.data || current?.data || [], { t });
442
+ const error = service?.error;
443
+ return !error ? (
444
+ <div
445
+ style={{
446
+ overflowX: 'auto',
447
+ overflowY: 'hidden',
448
+ }}
449
+ >
450
+ <Table
451
+ dataSource={data}
452
+ columns={Object.keys(data[0] || {}).map((col) => {
453
+ const field = getField(fields, col.split('.'));
454
+ return {
455
+ title: field?.label || col,
456
+ dataIndex: col,
457
+ key: col,
458
+ };
459
+ })}
460
+ size="small"
461
+ />
462
+ </div>
447
463
  ) : (
448
- <Alert message="Error" type="error" description={data} showIcon />
464
+ <Alert
465
+ message="Error"
466
+ type="error"
467
+ description={error?.response?.data?.errors?.map?.((error: any) => error.message).join('\n') || error.message}
468
+ showIcon
469
+ />
449
470
  );
450
471
  };
@@ -154,7 +154,7 @@ export const querySchema: ISchema = {
154
154
  'x-component-props': {
155
155
  options: '{{ collectionOptions }}',
156
156
  onChange: '{{ onCollectionChange }}',
157
- placeholder: lang('Collection'),
157
+ placeholder: '{{t("Collection")}}',
158
158
  },
159
159
  },
160
160
  },
@@ -206,11 +206,11 @@ export const querySchema: ISchema = {
206
206
  placeholder: '{{t("Aggregation")}}',
207
207
  },
208
208
  enum: [
209
- { label: lang('Sum'), value: 'sum' },
210
- { label: lang('Count'), value: 'count' },
211
- { label: lang('Avg'), value: 'avg' },
212
- { label: lang('Max'), value: 'max' },
213
- { label: lang('Min'), value: 'min' },
209
+ { label: '{{t("Sum")}}', value: 'sum' },
210
+ { label: '{{t("Count")}}', value: 'count' },
211
+ { label: '{{t("Avg")}}', value: 'avg' },
212
+ { label: '{{t("Max")}}', value: 'max' },
213
+ { label: '{{t("Min")}}', value: 'min' },
214
214
  ],
215
215
  },
216
216
  alias: {
@@ -287,7 +287,7 @@ export const querySchema: ISchema = {
287
287
  'x-decorator': 'FormItem',
288
288
  'x-decorator-props': {
289
289
  style: {
290
- overflow: 'scroll',
290
+ overflow: 'auto',
291
291
  },
292
292
  },
293
293
  'x-component': 'Filter',
@@ -14,7 +14,7 @@ const Chart: React.FC = (props) => {
14
14
  children.push({
15
15
  key: 'chart-v2',
16
16
  type: 'item',
17
- title: t('Chart'),
17
+ title: t('Charts'),
18
18
  component: 'ChartV2BlockInitializer',
19
19
  });
20
20
  }
@@ -1,10 +1,9 @@
1
1
  import { i18n } from '@nocobase/client';
2
2
  import { useTranslation } from 'react-i18next';
3
- import zhCN from './zh-CN';
4
3
 
5
- export const NAMESPACE = 'charts-v2';
4
+ export const NAMESPACE = 'data-visualization';
6
5
 
7
- i18n.addResources('zh-CN', NAMESPACE, zhCN);
6
+ // i18n.addResources('zh-CN', NAMESPACE, zhCN);
8
7
  // i18n.addResources('en-US', NAMESPACE, enUS);
9
8
  // i18n.addResources('ja-JP', NAMESPACE, jaJP);
10
9
  // i18n.addResources('ru-RU', NAMESPACE, ruRU);
@@ -68,4 +68,5 @@ export default {
68
68
  Min: '最小值',
69
69
  Max: '最大值',
70
70
  'Please select a chart type.': '请选择图表类型',
71
+ Collection: '数据表',
71
72
  };
@@ -77,7 +77,7 @@ export const useChartTypes = (): {
77
77
  const children = Object.entries(l.charts).map(([type, chart]) => ({
78
78
  ...chart,
79
79
  key: type,
80
- label: chart.name,
80
+ label: lang(chart.name),
81
81
  value: type,
82
82
  }));
83
83
  return [
@@ -90,6 +90,11 @@ export const useChartTypes = (): {
90
90
  }, []);
91
91
  };
92
92
 
93
+ export const useDefaultChartType = () => {
94
+ const chartTypes = useChartTypes();
95
+ return chartTypes[0]?.children?.[0]?.value;
96
+ };
97
+
93
98
  export const useToggleChartLibrary = () => {
94
99
  const ctx = useContext(ChartLibraryContext);
95
100
  return {
@@ -1,107 +1,34 @@
1
1
  import { useField, useFieldSchema } from '@formily/react';
2
2
  import {
3
3
  GeneralSchemaDesigner,
4
- gridRowColWrap,
5
4
  SchemaSettings,
5
+ gridRowColWrap,
6
6
  useAPIClient,
7
7
  useCollection,
8
8
  useDesignable,
9
- useRequest,
10
9
  } from '@nocobase/client';
11
- import { Empty, Result, Typography } from 'antd';
12
- import React, { useContext, useEffect, useState } from 'react';
10
+ import { Empty, Result, Spin, Typography } from 'antd';
11
+ import React, { useContext } from 'react';
13
12
  import { ErrorBoundary } from 'react-error-boundary';
14
13
  import { ChartConfigContext } from '../block';
15
- import { useFieldsWithAssociation, useFieldTransformer } from '../hooks';
14
+ import { useFieldTransformer, useFieldsWithAssociation } from '../hooks';
16
15
  import { useChartsTranslation } from '../locale';
17
- import { createRendererSchema, getField, parseField, processData } from '../utils';
16
+ import { createRendererSchema, getField, processData } from '../utils';
18
17
  import { useCharts } from './ChartLibrary';
19
- import { ChartRendererContext, DimensionProps, MeasureProps, QueryProps } from './ChartRendererProvider';
18
+ import { ChartRendererContext } from './ChartRendererProvider';
20
19
  const { Paragraph, Text } = Typography;
21
20
 
22
- export const ChartRenderer: React.FC<{
23
- configuring?: boolean;
24
- runQuery?: any;
25
- }> & {
21
+ export const ChartRenderer: React.FC & {
26
22
  Designer: React.FC;
27
23
  } = (props) => {
28
24
  const { t } = useChartsTranslation();
29
- const { setData: setQueryData, current } = useContext(ChartConfigContext);
30
- const { query, config, collection, transform } = useContext(ChartRendererContext);
31
- const { configuring, runQuery } = props;
25
+ const ctx = useContext(ChartRendererContext);
26
+ const { config, transform, collection, service, data: _data } = ctx;
27
+ const fields = useFieldsWithAssociation(collection);
28
+ const data = processData(fields, service?.data || _data || [], { t });
32
29
  const general = config?.general || {};
33
30
  const advanced = config?.advanced || {};
34
- const schema = useFieldSchema();
35
- const currentSchema = schema || current?.schema;
36
- const fields = useFieldsWithAssociation(collection);
37
31
  const api = useAPIClient();
38
- const [data, setData] = useState<any[]>([]);
39
- const { runAsync } = useRequest(
40
- (query) =>
41
- api
42
- .request({
43
- url: 'charts:query',
44
- method: 'POST',
45
- data: {
46
- uid: currentSchema?.['x-uid'],
47
- collection,
48
- ...query,
49
- dimensions: (query?.dimensions || []).map((item: DimensionProps) => {
50
- const dimension = { ...item };
51
- if (item.format && !item.alias) {
52
- const { alias } = parseField(item.field);
53
- dimension.alias = alias;
54
- }
55
- return dimension;
56
- }),
57
- measures: (query?.measures || []).map((item: MeasureProps) => {
58
- const measure = { ...item };
59
- if (item.aggregation && !item.alias) {
60
- const { alias } = parseField(item.field);
61
- measure.alias = alias;
62
- }
63
- return measure;
64
- }),
65
- },
66
- })
67
- .then((res) => {
68
- const data = res?.data?.data || [];
69
- return processData(fields, data, { t });
70
- }),
71
- {
72
- manual: true,
73
- onSuccess: (data) => {
74
- setData(data);
75
- },
76
- onFinally(params, data, error: any) {
77
- if (!configuring) {
78
- return;
79
- }
80
- if (error) {
81
- const message = error?.response?.data?.errors?.map?.((error: any) => error.message).join('\n');
82
- setQueryData(message || error.message);
83
- return;
84
- }
85
- setQueryData(data);
86
- },
87
- },
88
- );
89
-
90
- useEffect(() => {
91
- setData([]);
92
- const run = async (query: QueryProps) => {
93
- if (
94
- query?.measures?.length
95
- // || (query?.sql?.fields && query?.sql?.clauses)
96
- ) {
97
- await runAsync(query);
98
- }
99
- };
100
- if (runQuery) {
101
- runQuery.current = run;
102
- }
103
- run(query);
104
- }, [query, runAsync, runQuery]);
105
32
 
106
33
  const charts = useCharts();
107
34
  const chart = charts[config?.chartType];
@@ -137,16 +64,21 @@ export const ChartRenderer: React.FC<{
137
64
  <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('Please configure chart')} />
138
65
  );
139
66
 
140
- return data && data.length ? (
141
- <C />
142
- ) : (
143
- <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('Please configure and run query')} />
144
- );
67
+ if (service.loading) {
68
+ return <Spin />;
69
+ }
70
+
71
+ if (!(data && data.length)) {
72
+ return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('Please configure and run query')} />;
73
+ }
74
+
75
+ return <C />;
145
76
  };
146
77
 
147
78
  ChartRenderer.Designer = function Designer() {
148
79
  const { t } = useChartsTranslation();
149
80
  const { setVisible, setCurrent } = useContext(ChartConfigContext);
81
+ const { service } = useContext(ChartRendererContext);
150
82
  const field = useField();
151
83
  const schema = useFieldSchema();
152
84
  const { insertAdjacent } = useDesignable();
@@ -156,7 +88,7 @@ ChartRenderer.Designer = function Designer() {
156
88
  <SchemaSettings.Item
157
89
  key="configure"
158
90
  onClick={() => {
159
- setCurrent({ schema, field, collection: name });
91
+ setCurrent({ schema, field, collection: name, service, data: service?.data });
160
92
  setVisible(true);
161
93
  }}
162
94
  >
@@ -1,6 +1,8 @@
1
1
  import { css } from '@emotion/css';
2
- import { MaybeCollectionProvider } from '@nocobase/client';
2
+ import { useFieldSchema } from '@formily/react';
3
+ import { MaybeCollectionProvider, useAPIClient, useRequest } from '@nocobase/client';
3
4
  import React, { createContext } from 'react';
5
+ import { parseField } from '../utils';
4
6
 
5
7
  export type MeasureProps = {
6
8
  field: string | string[];
@@ -47,10 +49,62 @@ export type ChartRendererProps = {
47
49
  mode?: 'builder' | 'sql';
48
50
  };
49
51
 
50
- export const ChartRendererContext = createContext<ChartRendererProps>({} as any);
52
+ export const ChartRendererContext = createContext<{
53
+ collection: string;
54
+ config?: {
55
+ chartType: string;
56
+ general: any;
57
+ advanced: any;
58
+ };
59
+ transform?: TransformProps[];
60
+ service: any;
61
+ data?: any[];
62
+ }>({} as any);
51
63
 
52
64
  export const ChartRendererProvider: React.FC<ChartRendererProps> = (props) => {
53
- const { collection } = props;
65
+ const { query, config, collection, transform } = props;
66
+ const schema = useFieldSchema();
67
+ const api = useAPIClient();
68
+ const service = useRequest(
69
+ (collection, query) =>
70
+ new Promise((resolve, reject) => {
71
+ if (!(collection && query?.measures?.length)) return resolve(undefined);
72
+ api
73
+ .request({
74
+ url: 'charts:query',
75
+ method: 'POST',
76
+ data: {
77
+ uid: schema?.['x-uid'],
78
+ collection,
79
+ ...query,
80
+ dimensions: (query?.dimensions || []).map((item: DimensionProps) => {
81
+ const dimension = { ...item };
82
+ if (item.format && !item.alias) {
83
+ const { alias } = parseField(item.field);
84
+ dimension.alias = alias;
85
+ }
86
+ return dimension;
87
+ }),
88
+ measures: (query?.measures || []).map((item: MeasureProps) => {
89
+ const measure = { ...item };
90
+ if (item.aggregation && !item.alias) {
91
+ const { alias } = parseField(item.field);
92
+ measure.alias = alias;
93
+ }
94
+ return measure;
95
+ }),
96
+ },
97
+ })
98
+ .then((res) => {
99
+ resolve(res?.data?.data);
100
+ })
101
+ .catch(reject);
102
+ }),
103
+ {
104
+ defaultParams: [collection, query],
105
+ },
106
+ );
107
+
54
108
  return (
55
109
  <MaybeCollectionProvider collection={collection}>
56
110
  <div
@@ -58,12 +112,11 @@ export const ChartRendererProvider: React.FC<ChartRendererProps> = (props) => {
58
112
  .ant-card {
59
113
  box-shadow: none;
60
114
  }
61
- .ant-card-body {
62
- padding: 0;
63
- }
64
115
  `}
65
116
  >
66
- <ChartRendererContext.Provider value={{ ...props }}>{props.children}</ChartRendererContext.Provider>
117
+ <ChartRendererContext.Provider value={{ collection, config, transform, service }}>
118
+ {props.children}
119
+ </ChartRendererContext.Provider>
67
120
  </div>
68
121
  </MaybeCollectionProvider>
69
122
  );