Echarts 配置功能

* 添加 datamapping 功能

* echarts dataMapping 不处理function

* 配置改成异步加载

* 添加 打包
This commit is contained in:
liaoxuezhi 2020-12-17 18:16:29 +08:00 committed by GitHub
parent d08b2d69d2
commit 84f5a08c52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 198 additions and 117 deletions

View File

@ -4,14 +4,82 @@
import {createHierarchy} from './EChartsEditor/Common';
import example from './EChartsEditor/Example';
// import title from './EChartsEditor/Title';
// import legend from './EChartsEditor/Legend';
// import Global from './EChartsEditor/Global';
// import Axis from './EChartsEditor/Axis';
// import polar from './EChartsEditor/Polar';
// import tooltip from './EChartsEditor/Tooltip';
// import toolbox from './EChartsEditor/Toolbox';
// import series from './EChartsEditor/Series';
import {lazyData} from './LazyData';
import React from 'react';
import {Spinner} from '../../src';
const LazyComponent = lazyData(
async () =>
(
await Promise.all([
import('./EChartsEditor/Title'),
import('./EChartsEditor/Legend'),
import('./EChartsEditor/Global'),
import('./EChartsEditor/Axis'),
import('./EChartsEditor/Polar'),
import('./EChartsEditor/Tooltip'),
import('./EChartsEditor/Toolbox'),
import('./EChartsEditor/Series')
])
).map(item => item.default),
([title, legend, Global, Axis, polar, tooltip, toolbox, series]) => {
return ({renderFormItems}: any) => {
return renderFormItems({
controls: [
createHierarchy('config', [
{
type: 'tabs',
mountOnEnter: true,
// unmountOnExit: true, // 加了更慢的样子
mode: 'vertical',
className: 'echarts-editor',
tabs: [
{
title: '图表',
controls: [series]
},
{
title: '标题',
controls: [title]
},
{
title: '图例',
controls: [legend]
},
{
title: 'X 轴',
controls: Axis('x')
},
{
title: 'Y 轴',
controls: Axis('y')
},
{
title: '极标',
controls: [polar]
},
{
title: '提示框',
controls: [tooltip]
},
{
title: '工具栏',
controls: [toolbox]
},
{
title: '全局',
controls: [Global]
}
// TODO: grid, radar, dataZoom, visualMap, axisPointer, brush, geo, parallel, parallelAxis, singleAxis, timeline, graphic, calendar, dataset, aria,
]
}
])
]
});
};
}
);
export default {
title: 'ECharts 编辑器',
@ -24,74 +92,22 @@ export default {
title: '',
controls: [
{
type: 'grid',
columns: [
{
sm: 12,
md: 5,
controls: [
{
type: 'chart',
source: '${config}',
unMountOnHidden: false
type: 'chart',
source: '${config}',
replaceChartOption: true
},
{
children: (props: any) => {
return (
<React.Suspense
fallback={
<Spinner overlay spinnerClassName="m-t-lg" size="lg" />
}
]
},
{
sm: 12,
md: 7,
controls: [
createHierarchy('config', [
{
type: 'tabs',
mountOnEnter: true,
// unmountOnExit: true, // 加了更慢的样子
mode: 'vertical',
className: 'echarts-editor',
tabs: [
// {
// title: '图表',
// controls: [series]
// },
// {
// title: '标题',
// controls: [title]
// },
// {
// title: '图例',
// controls: [legend]
// },
// {
// title: 'X 轴',
// controls: Axis('x')
// },
// {
// title: 'Y 轴',
// controls: Axis('y')
// },
// {
// title: '极标',
// controls: [polar]
// },
// {
// title: '提示框',
// controls: [tooltip]
// },
// {
// title: '工具栏',
// controls: [toolbox]
// },
// {
// title: '全局',
// controls: [Global]
// }
// TODO: grid, radar, dataZoom, visualMap, axisPointer, brush, geo, parallel, parallelAxis, singleAxis, timeline, graphic, calendar, dataset, aria,
]
}
])
]
}
]
>
<LazyComponent {...props} />
</React.Suspense>
);
}
},
{
type: 'editor',

View File

@ -2,8 +2,6 @@
* @file
*/
import {debug} from 'console';
/**
* name title name title.name
* @param name
@ -160,6 +158,7 @@ export const fieldSet = (
title: label,
collapsable: true,
collapsed: collapsed,
mountOnEnter: true,
controls: controls
};
};
@ -347,6 +346,7 @@ export const viewport = (scope: string, label: string) => {
title: '离容器边距',
collapsable: true,
collapsed: true,
mountOnEnter: true,
controls: [
...viewportControl(
`${scope}left`,
@ -666,6 +666,7 @@ export const shadowControls = (scope: string) => {
title: '阴影',
collapsable: true,
collapsed: true,
mountOnEnter: true,
controls: [
color(`${scope}shadowColor`, '阴影颜色'),
number(`${scope}shadowBlur`, '阴影模糊大小'),

View File

@ -16,32 +16,25 @@ const buildSerieOptions = (type: string, options: any) => {
};
export default {
type: 'tabs',
tabs: [
type: 'combo',
name: 'series',
tabsMode: true,
tabsLabelTpl: '系列${index|plus}',
lazyLoad: true,
label: '',
multiLine: true,
multiple: true,
addButtonText: '新增系列',
controls: [
select('type', '图表类型', ['line', 'bar']),
buildSerieOptions('line', lineOptions),
{
title: '系列',
controls: [
{
type: 'combo',
name: 'series',
label: '',
multiLine: true,
multiple: true,
addButtonText: '新增系列',
controls: [
select('type', '图表类型', ['line', 'bar']),
buildSerieOptions('line', lineOptions),
{
type: 'array',
name: 'data', //TODO: 目前只支持一维
label: '数据',
items: {
type: 'number'
}
}
]
}
]
type: 'array',
name: 'data', //TODO: 目前只支持一维
label: '数据',
items: {
type: 'number'
}
}
]
};

View File

@ -0,0 +1,17 @@
import React from 'react';
export function lazyData<T, U>(
getData: () => Promise<U>,
getComponent: (
data: U
) => React.ComponentType<T> | Promise<React.ComponentType<T>>
) {
return React.lazy(async () => {
const data = await getData();
let component = await getComponent(data);
return {
default: component as React.ComponentType<T>
};
});
}

View File

@ -948,6 +948,7 @@ body.dark {
// echarts 编辑器的 tabs 更紧凑
.echarts-editor > .a-Tabs-links {
width: 4rem;
flex-shrink: 0;
> .a-Tabs-link > a {
padding: 0.5rem 0.5rem;
}

View File

@ -684,6 +684,12 @@ if (fis.project.currentMedia() === 'publish') {
'/examples/components/App.tsx:deps'
],
'pkg/echarts-editor.js': [
'/examples/components/EChartsEditor/*.tsx',
'!/examples/components/EChartsEditor/Example.tsx',
'!/examples/components/EChartsEditor/Common.tsx'
],
'pkg/rest.js': [
'**.{js,jsx,ts,tsx}',
'!static/mod.js',
@ -760,7 +766,7 @@ if (fis.project.currentMedia() === 'publish') {
ghPages.match('*.{css,less,scss}', {
optimizer: [
function(contents) {
function (contents) {
if (typeof contents === 'string') {
contents = contents.replace(/\/\*\!markdown[\s\S]*?\*\//g, '');
}

View File

@ -354,7 +354,7 @@ export class Chart extends React.Component<ChartProps> {
if (config) {
try {
if (!this.props.disableDataMapping) {
config = dataMapping(config, this.props.data);
config = dataMapping(config, this.props.data, true);
}
recoverFunctionType(config!);

View File

@ -62,6 +62,16 @@ export interface CollapseSchema extends BaseSchema {
*
*/
size?: 'xs' | 'sm' | 'md' | 'lg' | 'base';
/**
*
*/
mountOnEnter?: boolean;
/**
*
*/
unmountOnExit?: boolean;
}
export interface CollapseProps
@ -88,7 +98,9 @@ export default class Collapse extends React.Component<
'bodyClassName',
'collapsed',
'headingClassName',
'title'
'title',
'mountOnEnter',
'unmountOnExit'
];
static defaultProps: Partial<CollapseProps> = {
@ -146,7 +158,9 @@ export default class Collapse extends React.Component<
bodyClassName,
render,
collapsable,
translate: __
translate: __,
mountOnEnter,
unmountOnExit
} = this.props;
// 默认给个 title不然没法点
const finalTitle = this.state.collapsed ? title : collapseTitle || title;
@ -168,6 +182,8 @@ export default class Collapse extends React.Component<
classnames={cx}
classPrefix={ns}
key="body"
mountOnEnter={mountOnEnter}
unmountOnExit={unmountOnExit}
>
<div className={cx(`Collapse-body`, bodyClassName)}>
{children

View File

@ -838,9 +838,8 @@ export default class ComboControl extends React.Component<ComboProps> {
handleComboTypeChange(index: number, selection: any) {
const {multiple, onChange, value, flat, submitOnChange} = this.props;
const conditions: Array<ComboCondition> = this.props.conditions as Array<
ComboCondition
>;
const conditions: Array<ComboCondition> = this.props
.conditions as Array<ComboCondition>;
const condition = find(conditions, item => item.label === selection.label);
if (!condition) {
@ -1027,6 +1026,10 @@ export default class ComboControl extends React.Component<ComboProps> {
]
: controls;
const hasUnique =
Array.isArray(finnalControls) &&
finnalControls.some((item: any) => item.unique);
return (
<Tab
title={filter(
@ -1038,7 +1041,7 @@ export default class ComboControl extends React.Component<ComboProps> {
toolbar={toolbar}
eventKey={index}
// 不能按需渲染,因为 unique 会失效。
mountOnEnter={false}
mountOnEnter={!hasUnique}
unmountOnExit={false}
>
{condition && typeSwitchable !== false ? (

View File

@ -27,6 +27,16 @@ export interface FieldSetControlSchema
*/
controls?: Array<FormControlSchema>;
/**
*
*/
collapsable?: boolean;
/**
*
*/
collapsed?: boolean;
/**
*
*/
@ -41,6 +51,16 @@ export interface FieldSetControlSchema
*
*/
collapseTitle?: SchemaTpl;
/**
*
*/
mountOnEnter?: boolean;
/**
*
*/
unmountOnExit?: boolean;
}
export interface FieldSetProps

View File

@ -590,11 +590,15 @@ function resolveMapping(
: value;
}
export function dataMapping(to: any, from: PlainObject): any {
export function dataMapping(
to: any,
from: PlainObject,
ignoreFunction = false
): any {
let ret = {};
if (Array.isArray(to)) {
return to.map(item => dataMapping(item, from));
return to.map(item => dataMapping(item, from, ignoreFunction));
} else if (!to) {
return ret;
}
@ -616,7 +620,11 @@ export function dataMapping(to: any, from: PlainObject): any {
from[keys[0].substring(1)] &&
Array.isArray(from[keys[0].substring(1)])
? from[keys[0].substring(1)].map((raw: object) =>
dataMapping(value[keys[0]], createObject(from, raw))
dataMapping(
value[keys[0]],
createObject(from, raw),
ignoreFunction
)
)
: resolveMapping(value, from);
@ -663,19 +671,19 @@ export function dataMapping(to: any, from: PlainObject): any {
const mapping = value[keys[0]];
(ret as PlainObject)[key] = arr.map((raw: object) =>
dataMapping(mapping, createObject(from, raw))
dataMapping(mapping, createObject(from, raw), ignoreFunction)
);
} else if (isPlainObject(value)) {
(ret as PlainObject)[key] = dataMapping(value, from);
(ret as PlainObject)[key] = dataMapping(value, from, ignoreFunction);
} else if (Array.isArray(value)) {
(ret as PlainObject)[key] = value.map((value: any) =>
isPlainObject(value)
? dataMapping(value, from)
? dataMapping(value, from, ignoreFunction)
: resolveMapping(value, from)
);
} else if (typeof value == 'string' && ~value.indexOf('$')) {
(ret as PlainObject)[key] = resolveMapping(value, from);
} else if (typeof value === 'function') {
} else if (typeof value === 'function' && !ignoreFunction) {
(ret as PlainObject)[key] = value(from);
} else {
(ret as PlainObject)[key] = value;