Browse Source

总体统计图、饼图样式设置

zhuth 6 years ago
parent
commit
9b0fa20262

+ 2 - 1
src/components/chartDesigner/charts/echartsView.jsx

@@ -4,9 +4,10 @@ import { hashcode } from '../../../utils/baseUtils'
 import EmptyContent from '../../common/emptyContent'
 
 const EchartsView = ({ chartOption }) => {
-    if(!chartOption.series) {
+    if(!chartOption || !chartOption.series) {
         return <EmptyContent />
     }else {
+        console.log(chartOption);
         return <Echarts
         key={hashcode(chartOption)}
         option={chartOption}

+ 16 - 22
src/components/chartDesigner/sections/style/aggregateTable.jsx

@@ -38,28 +38,22 @@ class AggregateTable extends React.Component {
         const { styleConfig, chartOption } = chartDesigner;
         const aggregateTableStyle = styleConfig.aggregateTable || {};
 
-        return (
-            <Collapse className='collapse-bar-style' defaultActiveKey={['layout']}>
-                <Collapse.Panel className='chart' header='布局' key='layout'>
-                    <Form>
-                        <FormItem label='排布方向' {...formItemLayout}>
-                            <Radio.Group onChange={e => {
-                                let value = e.target.value;
-                                dispatch({ type: 'chartDesigner/setFields', fields: [
-                                    { name: 'styleConfig', value: { ...styleConfig, aggregateTable: { ...aggregateTableStyle, direction: value } } },
-                                    { name: 'chartOption', value: { ...chartOption, direction: value } },
-                                ] });
-                            }}
-                                defaultValue={aggregateTableStyle.direction || 'vertical'}
-                            >
-                                <Radio value='vertical'>垂直</Radio>
-                                <Radio value='horizontal'>水平</Radio>
-                            </Radio.Group>
-                        </FormItem>
-                    </Form>
-                </Collapse.Panel>
-            </Collapse>
-        )
+        return <Form>
+            <FormItem label='排布方向' {...formItemLayout}>
+                <Radio.Group onChange={e => {
+                    let value = e.target.value;
+                    dispatch({ type: 'chartDesigner/setFields', fields: [
+                        { name: 'styleConfig', value: { ...styleConfig, aggregateTable: { ...aggregateTableStyle, direction: value } } },
+                        { name: 'chartOption', value: { ...chartOption, direction: value } },
+                    ] });
+                }}
+                    defaultValue={aggregateTableStyle.direction || 'vertical'}
+                >
+                    <Radio value='vertical'>垂直</Radio>
+                    <Radio value='horizontal'>水平</Radio>
+                </Radio.Group>
+            </FormItem>
+        </Form>
     }
 }
 

+ 8 - 1
src/components/chartDesigner/sections/style/index.jsx

@@ -1,6 +1,8 @@
 // import TableStyle from './table'
 // import BarStyle from './bar'
+import ThemeForm from './theme'
 import AggregateTableStyle from './aggregateTable'
+import PieStyle from './pie'
 
 export default ({ viewType }) => {
     let styleForm = <div>无可用样式配置</div>;
@@ -15,6 +17,11 @@ export default ({ viewType }) => {
         // styleForm = <TableStyle formItemLayout={formItemLayout}/>
     }else if(viewType === 'bar') {
         // styleForm = <BarStyle />
+    }else if(viewType === 'pie') {
+        styleForm = <PieStyle />
     }
-    return styleForm;
+    return <div className='style-config'>
+        <ThemeForm />
+        { styleForm }
+    </div>
 }

+ 253 - 0
src/components/chartDesigner/sections/style/pie.jsx

@@ -0,0 +1,253 @@
+import React from 'react'
+import { Collapse, Form, Select, Divider, Row, Col, Tooltip, Input, Checkbox } from 'antd'
+import { connect } from 'dva'
+import { deepAssign } from '../../../../utils/baseUtils'
+import themes from './theme/index'
+
+class PieStyleConfig extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            formItemLayout: {
+                labelCol: { span: 8 },
+                wrapperCol: { span: 16 },
+            }
+        }
+    }
+
+    render() {
+        const { chartDesigner, dispatch } = this.props;
+        const { formItemLayout } = this.state;
+        const { styleConfig, chartOption } = chartDesigner;
+        const pieStyle = styleConfig.pie || {};
+
+        return <Form>
+            {/* <Divider>图形属性</Divider> */}
+            <Form.Item label={<Tooltip title='支持设置为数字或百分比,设置成百分比时第一项是相对于容器宽度,第二项是相对于容器高度'>圆心坐标</Tooltip>} {...formItemLayout}>
+                <Row>
+                    <Col span={11}>
+                        <Input
+                            defaultValue={pieStyle.pieCenter ? pieStyle.pieCenter[0] : null}
+                            placeholder='40%'
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { pieCenter: { 0: value } } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { series: [ { center: { 0: value || '40%' } }] }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                    <Col span={2}></Col>
+                    <Col span={11}>
+                        <Input
+                            defaultValue={pieStyle.pieCenter ? pieStyle.pieCenter[1] : null}
+                            placeholder='50%'
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { pieCenter: { 1: value } } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { series: [ { center: { 1: value || '50%' } }] }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </Form.Item>
+            <Form.Item label={<Tooltip title='支持设置为数字或百分比,设置成百分比时其值相对于可视区尺寸大小(容器高宽度较小值)'>半径</Tooltip>} {...formItemLayout}>
+                <Row>
+                    <Col span={11}>
+                        <Input
+                            defaultValue={pieStyle.pieRadius ? pieStyle.pieRadius[0] : null}
+                            placeholder={0}
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { pieRadius: { 0: value } } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { series: [ { radius: { 0: value || 0 } }] }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                    <Col span={2}></Col>
+                    <Col span={11}>
+                        <Input
+                            defaultValue={pieStyle.pieRadius ? pieStyle.pieRadius[1] : null}
+                            placeholder='75%'
+                            onBlur={e => {
+                                let value = e.target.value;
+                                if(value !== (!!pieStyle.pieRadius ? pieStyle.pieRadius : '')) {
+                                    dispatch({ type: 'chartDesigner/setFields', fields: [
+                                        { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { pieRadius: { 1: value} } }) },
+                                        { name: 'chartOption', value: deepAssign(chartOption, { series: [ { radius: { 1: value || '75%'} }] }) },
+                                    ] });
+                                }
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </Form.Item>
+            <Row>
+                <Col span={!!pieStyle.legendHidden ? 24 : 12}>
+                    <Form.Item label='隐藏图例' labelCol={{ span: !!pieStyle.legendHidden ? 8 : 16 }} wrapperCol={{ span: !!pieStyle.legendHidden ? 16 : 8 }}>
+                        <Checkbox
+                            checked={!!pieStyle.legendHidden}
+                            onChange={e => {
+                                let checked = e.target.checked;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendHidden: checked } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { show: !checked } }) },
+                                ] });
+                            }}
+                        />
+                    </Form.Item>
+                </Col>
+                {!pieStyle.legendHidden && <Col span={12}>
+                    <Form.Item label='图例分页' labelCol={{ span: 16 }} wrapperCol={{ span: 8 }}>
+                        <Checkbox
+                            checked={!!pieStyle.legendInPagination}
+                            onChange={e => {
+                                let checked = e.target.checked;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendInPagination: checked } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { type: checked ? 'scroll' : 'plain' } }) },
+                                ] });
+                            }}
+                        />
+                    </Form.Item>
+                </Col>}
+            </Row>
+            {!pieStyle.legendHidden && <Form.Item label='图例排布方向' {...formItemLayout}>
+                <Select
+                    defaultValue={pieStyle.legendOrient || 'vertical'}
+                    placeholder='vertical'
+                    onChange={(value, item) => {
+                        dispatch({ type: 'chartDesigner/setFields', fields: [
+                            { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendOrient: value } }) },
+                            { name: 'chartOption', value: deepAssign(chartOption, { legend: { orient: value } }) },
+                        ] });
+                    }}
+                >
+                    <Select.Option value='vertical'>纵向排布</Select.Option>
+                    <Select.Option value='horizontal'>横向排布</Select.Option>
+                </Select>
+            </Form.Item>}
+            {!pieStyle.legendHidden && <Form.Item label='图例位置' {...formItemLayout}>
+                <Row>
+                    <Col span={8}>上边距:</Col>
+                    <Col span={16}>
+                        <Input
+                            defaultValue={pieStyle.legendPosition ? pieStyle.legendPosition['top'] : null}
+                            placeholder={50}
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendPosition: { top: value} } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { top: value } }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                </Row>
+                <Row>
+                    <Col span={8}>下边距:</Col>
+                    <Col span={16}>
+                        <Input
+                            defaultValue={pieStyle.legendPosition ? pieStyle.legendPosition['bottom'] : null}
+                            placeholder={50}
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendPosition: { bottom: value} } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { bottom: value } }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                </Row>
+                <Row>
+                    <Col span={8}>左边距:</Col>
+                    <Col span={16}>
+                        <Input
+                            defaultValue={pieStyle.legendPosition ? pieStyle.legendPosition['left'] : null}
+                            placeholder='auto'
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendPosition: { left: value} } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { left: value } }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                </Row>
+                <Row>
+                    <Col span={8}>右边距:</Col>
+                    <Col span={16}>
+                        <Input
+                            defaultValue={pieStyle.legendPosition ? pieStyle.legendPosition['right'] : null}
+                            placeholder='10%'
+                            onBlur={e => {
+                                let value = e.target.value;
+                                dispatch({ type: 'chartDesigner/setFields', fields: [
+                                    { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendPosition: { right: value} } }) },
+                                    { name: 'chartOption', value: deepAssign(chartOption, { legend: { right: value } }) },
+                                ] });
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </Form.Item>}
+            {/* <Form.Item label={<Tooltip title='用于格式化图例文本,原值为name'>图例格式化</Tooltip>} {...formItemLayout}>
+                <Input
+                    defaultValue={pieStyle.legendFormatter}
+                    placeholder='{name}'
+                    onBlur={e => {
+                        let value = e.target.value;
+                        dispatch({ type: 'chartDesigner/setFields', fields: [
+                            { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { legendFormatter: value } }) },
+                            { name: 'chartOption', value: deepAssign(chartOption, { legend: { formatter: value } }) }
+                            // { name: 'chartOption', value: deepAssign(chartOption, { legend: { formatter: name => {
+                            //     let result = name;
+                            //     try {
+                            //         let matchRes = value.match(new RegExp('/(?<=\{)[^}]*(?=\})/g'));
+                            //         let matchStr = '$';
+                            //         if(matchRes) {
+                            //             for(let i = 0; i < matchRes.length; i++) {
+                            //                 eval('str' + i + '=' + matchRes[0]);
+                            //                 matchStr += (i + 1) + eval('str' + i) + '$';
+                            //             }
+                            //             matchStr = matchStr.substring(0, matchStr.length - 1);
+                            //             result = value.replace(/([.]*){[^}{]*}([.]*)/g, matchStr);
+                            //             console.log('formatter:' + result);
+                            //         }
+                            //     }catch(e) {
+                            //         console.error(e.message);
+                            //     }
+                            //     return result;
+                            // } } }) },
+                        ] });
+                    }}
+                />
+            </Form.Item> */}
+            <Form.Item label='隐藏标注' {...formItemLayout}>
+                <Checkbox
+                    checked={!!pieStyle.labelHidden}
+                    onChange={e => {
+                        let checked = e.target.checked;
+                        dispatch({ type: 'chartDesigner/setFields', fields: [
+                            { name: 'styleConfig', value: deepAssign(styleConfig, { pie: { labelHidden: checked } }) },
+                            { name: 'chartOption', value: deepAssign(chartOption, { series: { 0: {
+                                label: { normal: { show: !checked } },
+                                labelLine: { normal: { show: !checked } },
+                                avoidLabelOverlap: !checked,
+                            } } }) },
+                        ] });
+                    }}
+                />
+            </Form.Item>
+        </Form>
+    }
+}
+
+export default connect(({ present: { chartDesigner } }) => ({ chartDesigner }))(PieStyleConfig)

+ 40 - 0
src/components/chartDesigner/sections/style/theme.jsx

@@ -0,0 +1,40 @@
+import React from 'react'
+import { Collapse, Form, Select } from 'antd'
+import { connect } from 'dva'
+import themes from './theme/index'
+
+class ThemeConfig extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            formItemLayout: {
+                labelCol: { span: 8 },
+                wrapperCol: { span: 16 },
+            }
+        }
+    }
+
+    render() {
+        const { chartDesigner, dispatch } = this.props;
+        const { formItemLayout } = this.state;
+        const { theme, chartOption } = chartDesigner;
+
+        return <Form>
+            <Form.Item label='主题' {...formItemLayout}>
+                <Select
+                    defaultValue={theme || 'default'}
+                    onChange={(value, item) => {
+                        dispatch({ type: 'chartDesigner/changeFields', fields: [
+                            { name: 'theme', value: value },
+                            { name: 'chartOption', value: { ...chartOption, themeConfig: themes[value] } }
+                        ] });
+                    }}
+                >
+                    <Select.Option value='default'>默认</Select.Option>
+                </Select>
+            </Form.Item>
+        </Form>
+    }
+}
+
+export default connect(({ present: { chartDesigner } }) => ({ chartDesigner }))(ThemeConfig)

+ 0 - 0
src/models/theme/default.json → src/components/chartDesigner/sections/style/theme/default.json


+ 0 - 0
src/models/theme/index.js → src/components/chartDesigner/sections/style/theme/index.js


+ 3 - 2
src/models/chartDesigner.js

@@ -59,7 +59,8 @@ export default {
             scatterConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 },
             theme: 'default',
             styleConfig: {
-                aggregateTable: { direction: ['vertical', 'horizontal'][0] },
+                aggregateTable: {},
+                pie: {},
             },
             otherConfig:{},
             description: '',
@@ -403,7 +404,7 @@ export default {
                     timeout: 30000
                 });
                 if(!res.err && res.data.code > 0) {
-                    let option = parseChartOption('pie', res.data.data, pieConfig, theme, styleConfig.pie);
+                    let option = parseChartOption('pie', res.data.data, pieConfig, theme, styleConfig.pie || {});
                     yield put({ type: 'silentSetField', name: 'chartOption', value: option });
                 }else {
                     message.error('请求饼图数据失败: ' + (res.err || res.data.msg));

+ 37 - 14
src/models/parseChartOption.js

@@ -5,7 +5,7 @@ import moment from 'moment'
 import EllipsisTooltip from '../components/common/ellipsisTooltip/index'
 import { deepAssign } from '../utils/baseUtils'
 import STATISTICS_OPTION from '../components/chartDesigner/sections/statisticsOption.json'
-import themes from './theme/index'
+import themes from '../components/chartDesigner/sections/style/theme/index'
 
 export default function(viewType, data, chartConfig, themeName, styleConfig) {
     if(!data) {
@@ -123,20 +123,29 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
 
 function pieOption(data, pieConfig, themeConfig, styleConfig) {
     let columnName = pieConfig.xAxis.column.label + (pieConfig.xAxis.granularity.value ? '('+pieConfig.xAxis.granularity.label+')' : '');
-      
+    const { pieCenter, pieRadius, legendHidden, legendInPagination, legendOrient, legendPosition, legendFormatter,
+        labelHidden } = styleConfig;
+
     let option = deepAssign({
         tooltip : {
             trigger: 'item',
             formatter: "{a} <br/>{b} : {c} ({d}%)"
         },
         legend: {
-            show: true,
-            orient: 'vertical',
-            right: '30%',
-            top: '100',
+            show: legendHidden === undefined ? true : !legendHidden,
+            type: legendInPagination === undefined ? 'plain' : (!!legendInPagination ? 'scroll' : 'plain'),
+            orient: legendOrient || 'vertical',
+            top: legendPosition ? legendPosition.top || 50 : 50,
+            left: legendPosition ? legendPosition.left || 'auto' : 'auto',
+            right: legendPosition ? legendPosition.right || '10%' : '10%',
+            bottom: legendPosition ? legendPosition.bottom || 50 : 50,
+            formatter: legendFormatter || '{name}',
             data: data.xAxis.map(d => {
                 let gv= pieConfig.xAxis.granularity.value;
-                let xv = d || '空';
+                let xv;
+                if(!d) {
+                    return '空';
+                }
                 if(gv === 'halfYear') {
                     let arr = d.split('-H');
                     xv = arr[0] + ['上半年', '下半年'][arr[1] - 1]
@@ -163,12 +172,25 @@ function pieOption(data, pieConfig, themeConfig, styleConfig) {
             {
                 name: columnName,
                 type: 'pie',
-                center: ['30%', '50%'],
-                radius: [0, '65%'],
-                // radius : '55%',
-                // center: ['50%', '60%'],
-                // data: (data.serieses || [{ value: [] }])[0].value,
-                // data: (data.serieses || [{ value: [] }])[0].value.map(v => ({ ...v, name: v.name || '空' })),
+                center: pieCenter ? [pieCenter[0] || '40%', pieCenter[1] || '50%'] : ['40%', '50%'],
+                radius: pieRadius ? [pieRadius[0] || 0, pieRadius[1] || '75%'] : [0, '75%'],
+                label: {
+                    normal: {
+                        show: labelHidden === undefined ? true : !labelHidden,
+                    },
+                    emphasis: {
+                        show: true,
+                    }
+                },
+                labelLine: {
+                    normal: {
+                        show: labelHidden === undefined ? true : !labelHidden,
+                    },
+                    emphasis: {
+                        show: true,
+                    }
+                },
+                avoidLabelOverlap: labelHidden === undefined ? true : !labelHidden,
                 data: (data.serieses || [{ value: [] }])[0].value.map(v => {
                     let obj = { ...v };
                     if(!v.name) {
@@ -199,7 +221,7 @@ function pieOption(data, pieConfig, themeConfig, styleConfig) {
                 }
             }
         ]
-    }, themeConfig, styleConfig);
+    }, themeConfig);
 
     return option;
 }
@@ -310,6 +332,7 @@ function aggregateTableOption( data, aggregateTableConfig, themeConfig, styleCon
     let statistics = STATISTICS_OPTION.filter(o => statisticsNames.indexOf(o.value) !== -1);
 
     let option = {
+        themeConfig,
         targetColumn: targetColumn,
         statistics: statistics.map(s => ({
             name: s.value,

+ 1 - 1
src/themes/default/chartdesigner.less

@@ -43,7 +43,7 @@
                                     background: @tab-bg-color-active;
                                 }
                             }
-                            .ant-form-item-label > label {
+                            .ant-form-item, .ant-form-item-label > label {
                                 color: @text-color;
                             }
                             .checkbox-group-statistics {