Browse Source

指标看板可视化话模式调整/主题设计应用

zhuth 6 years ago
parent
commit
3547290a28

+ 37 - 32
src/components/chartDesigner/charts/aggregateTableView.jsx

@@ -14,16 +14,17 @@ class AggregateTableView extends React.Component {
     getTableBody = () => {
         const { chartOption } = this.props;
         const { group1Name, group2Name } = chartOption;
+        const { themeConfig } = chartOption;
 
         try{
             if(!!group1Name) {
                 if(!!group2Name) {
-                    return this.createTableBodyInTwoGroups(chartOption);
+                    return this.createTableBodyInTwoGroups(chartOption, themeConfig);
                 }else {
-                    return this.createTableBodyInOneGroups(chartOption);
+                    return this.createTableBodyInOneGroups(chartOption, themeConfig);
                 }
             }else {
-                return this.createTableBodyInNoGroups(chartOption);
+                return this.createTableBodyInNoGroups(chartOption, themeConfig);
             }
         }catch(e) {
             console.error(e.message);
@@ -33,69 +34,71 @@ class AggregateTableView extends React.Component {
     }
 
     // 无分组
-    createTableBodyInNoGroups = (chartOption) => {
+    createTableBodyInNoGroups = (chartOption, themeConfig) => {
         const { targetColumn, direction, statistics, data } = chartOption;
+        const { targetStyle, statisticsStyle, cellStyle } = themeConfig;
         if(direction === 'vertical') {
             return <tbody>
                 <tr>
-                    <th className='target-cell' rowSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+                    <th className='target-cell' rowSpan={`${statistics.length + 1}`} style={targetStyle}>{targetColumn.label}</th>
                 </tr>
                 {
                     statistics.map((s, i) => <tr key={i}>
-                        <td>{s.label}</td>
-                        <td>{data[s.name]}</td>
+                        <td style={statisticsStyle}>{s.label}</td>
+                        <td style={cellStyle}>{data[s.name]}</td>
                     </tr>)
                 }
             </tbody>
         }
         return <tbody>
             <tr>
-                <th className='target-cell' colSpan={`${statistics.length}`}>{targetColumn.label}</th>
+                <th className='target-cell' colSpan={`${statistics.length}`} style={targetStyle}>{targetColumn.label}</th>
             </tr>
             <tr>
-                { statistics.map((s, i) => <td key={i}>{s.label}</td>) }
+                { statistics.map((s, i) => <td key={i} style={statisticsStyle}>{s.label}</td>) }
             </tr>
             <tr>
-                { statistics.map((s, i) => <td key={i}>{data[s.name]}</td>) }
+                { statistics.map((s, i) => <td key={i} style={cellStyle}>{data[s.name]}</td>) }
             </tr>
         </tbody>;
     }
 
     // 有一个分组
-    createTableBodyInOneGroups = (chartOption) => {
+    createTableBodyInOneGroups = (chartOption, themeConfig) => {
         const { targetColumn, direction, group1s, statistics, data } = chartOption;
+        const { targetStyle, statisticsStyle, groupByStyle, cellStyle } = themeConfig;
         if(direction === 'vertical') {
             return <tbody>
                 <tr>
                     <td colSpan="2"></td>
-                    { group1s.map((g, i) => <td key={i}>{g}</td>) }
+                    { group1s.map((g, i) => <td key={i} style={groupByStyle}>{g}</td>) }
                 </tr>
                 <tr>
-                    <th className='target-cell' rowSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+                    <th className='target-cell' rowSpan={`${statistics.length + 1}`} style={targetStyle}>{targetColumn.label}</th>
                 </tr>
                 {
                     statistics.map((s, si) => <tr key={si}>
-                        <td>{s.label}</td>
-                        { group1s.map((g1, gi) => <td key={gi}>{data[gi][s.name]}</td>) }
+                        <td style={statisticsStyle}>{s.label}</td>
+                        { group1s.map((g1, gi) => <td key={gi} style={cellStyle}>{data[gi][s.name]}</td>) }
                     </tr>)
                 }
             </tbody>
         }
         return <tbody>
             <tr>
-                <th className='target-cell' colSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+                <th className='target-cell' colSpan={`${statistics.length + 1}`} style={targetStyle}>{targetColumn.label}</th>
             </tr>
             <tr>
                 <td></td>
                 {
-                    statistics.map((s, i) => <td key={i}>{s.label}</td>)
+                    statistics.map((s, i) => <td key={i} style={statisticsStyle}>{s.label}</td>)
                 }
             </tr>
             {
                 group1s.map((g, gi) => <tr key={gi}>
-                    <td>{g}</td>
+                    <td style={groupByStyle}>{g}</td>
                     {
-                        statistics.map((s, si) => <td key={si}>{data[gi][s.name]}</td>)
+                        statistics.map((s, si) => <td key={si} style={cellStyle}>{data[gi][s.name]}</td>)
                     }
                 </tr>)
             }
@@ -103,25 +106,26 @@ class AggregateTableView extends React.Component {
     }
 
     // 有两个分组
-    createTableBodyInTwoGroups = (chartOption) => {
+    createTableBodyInTwoGroups = (chartOption, themeConfig) => {
         const { targetColumn, direction, group1Name, group1s, group2s, statistics, data } = chartOption;
+        const { targetStyle, statisticsStyle, groupByStyle, cellStyle } = themeConfig;
         if(direction === 'vertical') {
             return <tbody>
                 <tr>
                     <td colSpan="3"></td>
-                    {group1s.map((g, i) => <td key={i}>{g}</td>)}
+                    {group1s.map((g, i) => <td key={i} style={groupByStyle}>{g}</td>)}
                 </tr>
                 <tr>
-                    <th className='target-cell' rowSpan={`${group2s.length * statistics.length + 1}`}>{targetColumn.label}</th>
+                    <th className='target-cell' rowSpan={`${group2s.length * statistics.length + 1}`} style={targetStyle}>{targetColumn.label}</th>
                 </tr>
                 {
                     group2s.map((g2, idx2) => {
                         return statistics.map((s, si) => <tr key={si}>
-                            { si === 0 && <td rowSpan={`${statistics.length}`}>{g2}</td>}
-                            <td>{s.label}</td>
+                            { si === 0 && <td rowSpan={`${statistics.length}`} style={groupByStyle}>{g2}</td>}
+                            <td style={statisticsStyle}>{s.label}</td>
                             {group1s.map((g1, idx1) => {
                                 let values = data[idx1].data[idx2];
-                                return <td key={idx1}>{values[s.name]}</td>
+                                return <td key={idx1} style={cellStyle}>{values[s.name]}</td>
                             })}
                         </tr>)
                     })
@@ -130,23 +134,23 @@ class AggregateTableView extends React.Component {
         }
         return <tbody>
             <tr>
-                <th className='target-cell' colSpan={direction === 'vertical' ? group1s.length + 3 : statistics.length * group2s.length + 1}>{targetColumn.label}</th>
+                <th className='target-cell' colSpan={direction === 'vertical' ? group1s.length + 3 : statistics.length * group2s.length + 1} style={targetStyle}>{targetColumn.label}</th>
             </tr>
             <tr>
                 <td rowSpan={2}></td>
-                { group2s.map((g, i) => <td colSpan={statistics.length} key={i}>{g}</td>) }
+                { group2s.map((g, i) => <td colSpan={statistics.length} key={i} style={groupByStyle}>{g}</td>) }
             </tr>
             <tr>
-                { group2s.map((g, gi) => statistics.map((s, si) => <td key={`${gi}-${si}`}>{s.label}</td>)) }
+                { group2s.map((g, gi) => statistics.map((s, si) => <td key={`${gi}-${si}`} style={statisticsStyle}>{s.label}</td>)) }
             </tr>
             {
                 data.map((d1, d1i) => {
                     let data2 = d1.data;
                     return <tr key={d1i}>
-                        <td>{ d1[group1Name] }</td>
+                        <td style={groupByStyle}>{ d1[group1Name] }</td>
                         { data2.map(d2 => {
                                 return statistics.map((s, si) => {
-                                    return <td key={si}>{d2[s.name]}</td>
+                                    return <td key={si} style={cellStyle}>{d2[s.name]}</td>
                                 })
                         }) }
                     </tr>
@@ -157,7 +161,8 @@ class AggregateTableView extends React.Component {
 
     render() {
         const { chartOption } = this.props;
-        const { direction } = chartOption;
+        const { direction, themeConfig } = chartOption;
+        const { backgroundColor } = themeConfig || {};
         if(!chartOption || !chartOption.targetColumn || !chartOption.statistics || chartOption.statistics.length === 0) {
             return <EmptyContent />
         }
@@ -165,7 +170,7 @@ class AggregateTableView extends React.Component {
         if(!body) {
             return <EmptyContent />
         }
-        return <div ref={ node => this.formRef = node } className='aggregate-container'>
+        return <div ref={ node => this.formRef = node } className='aggregate-container' style={{ backgroundColor }}>
             <table className={`aggregate-table ${direction}`} border='1'>
                 { body }
             </table>

+ 42 - 20
src/components/chartDesigner/charts/indicatorView.jsx

@@ -27,35 +27,56 @@ class IndicatorView extends React.Component {
         let cardCount = cards.length;
         let bodyBox = body.getBoundingClientRect();
 
-        let maxColNum = cardCount < 4 ? cardCount : 4; // 
-        let maxRowNum = Math.ceil(cardCount / maxColNum); // 
+        let maxColNum = cardCount < 4 ? cardCount : 4; // 
+        let maxRowNum = Math.ceil(cardCount / maxColNum); // 
         let cardHeight = bodyBox.height / maxRowNum - 8;
-        let cardWidth = (bodyBox.width - 18) / maxColNum - 8;
+        cardHeight = cardHeight < 100 ? 100 : cardHeight;
+        let inScroll = maxRowNum * (8 + cardHeight) > bodyBox.height;
+        let cardWidth = (bodyBox.width - (inScroll ? 18 : 0)) / maxColNum - (inScroll ? 8 : 11);
         while(cardWidth < 180 && maxColNum > 1) {
             maxColNum--;
             maxRowNum = Math.ceil(cardCount / maxColNum);
             cardHeight = bodyBox.height / maxRowNum - 8;
-            cardWidth = (bodyBox.width - 18) / maxColNum - 8;
+            cardHeight = cardHeight < 100 ? 100 : cardHeight;
+            inScroll = maxRowNum * (8 + cardHeight) > bodyBox.height;
+            cardWidth = (bodyBox.width - (inScroll ? 18 : 0)) / maxColNum - (inScroll ? 8 : 11);
         }
-        cardHeight = cardHeight < 100 ? 100 : cardHeight;
 
         if(cardCount === 1) {
             cards[0].style.border = 'none';
         }
+        let fun = function(els, width) {
+            for(let j = 0; j < els.length; j++) {
+                let wrap = els[j].getElementsByClassName('over-wrapper')[0];
+                wrap.style.width = width + 'px';
+            }
+        };
         for(let i = 0; i < cards.length; i++) {
             cards[i].style.width = cardWidth + 'px';
             cards[i].style.height = cardHeight + 'px';
             let cells = cards[i].getElementsByClassName('cell');
-            for(let j = 0; j < cells.length; j++) {
-                let wrap = cells[j].getElementsByClassName('over-wrapper')[0];
-                wrap.style.width = cardWidth - 8 * 2 + 'px';
-            }
+            let w = cardWidth - 8 * 2;
+            fun(cells, w);
         }
     }
 
+    generateExtra = (data, nameLabelColor, valueLabelColor) => {
+        return data.map((d, i) => (
+            <div className='row indicator-extra' key={i}>
+                <div className='cell c-extra'>
+                    <div className='over-wrapper'>
+                        <span className='til' title={d.name} style={{ color: nameLabelColor }}>{d.name}</span>
+                        <span className='val' title={d.value} style={{ color: valueLabelColor }}>{d.value === undefined || d.value === null ? '--' : d.value}</span>
+                    </div>
+                </div>
+            </div>
+        ));
+    }
+
     render() {
         const { chartOption } = this.props;
-        const { data } = chartOption;
+        const { data, themeConfig } = chartOption;
+        const { backgroundColor, boxBackgroundColor, nameLabelColor, keyLabelColor, valueLabelColor, extraNameLabelColor, extraValueLabelColor } = themeConfig || {};
         if(!chartOption || !chartOption.data || !chartOption.data.length === 0) {
             return <div className='indicator-container empty' ref={ node => this.containerRef = node }>
                 <div className='indicator-body'>
@@ -63,26 +84,27 @@ class IndicatorView extends React.Component {
                 </div>
             </div>
         }
-        return <div className='indicator-container' ref={ node => this.containerRef = node }>
+        return <div className='indicator-container' ref={ node => this.containerRef = node } style={{ backgroundColor: backgroundColor }}>
             <div className='indicator-body'>
                 {
                     data.map((d, i) => (
-                        <div className={`indicator-box`} key={i}>
-                            <div className='row indicator-name'>
-                                <div className='cell rc-name'>
-                                    <div className='over-wrapper' title={d.name}>{d.name}</div>
+                        <div className={`indicator-box`} key={i} style={{ backgroundColor: boxBackgroundColor }}>
+                            {d.name && <div className='row indicator-name'>
+                                <div className='cell c-name'>
+                                    <div className='over-wrapper' title={d.name} style={{ color: nameLabelColor }}>{d.name}</div>
                                 </div>
-                            </div>
+                            </div>}
                             <div className='row indicator-key'>
-                                <div className='cell rc-key'>
-                                    <div className='over-wrapper' title={d.key}>{d.key}</div>
+                                <div className='cell c-key'>
+                                    <div className='over-wrapper' title={d.key} style={{ color: keyLabelColor }}>{d.key}</div>
                                 </div>
                             </div>
                             <div className='row indicator-value'>
-                                <div className='cell rc-value'>
-                                    <div className='over-wrapper' title={d.value}>{(d.value === undefined || d.value === null) ? '--' : d.value}</div>
+                                <div className='cell c-value'>
+                                    <div className='over-wrapper' title={d.value} style={{ color: valueLabelColor }}>{(d.value === undefined || d.value === null) ? '--' : d.value}</div>
                                 </div>
                             </div>
+                            {d.others && d.others.length > 0 && this.generateExtra(d.others, extraNameLabelColor, extraValueLabelColor)}
                         </div>
                     ))
                 }

+ 27 - 4
src/components/chartDesigner/charts/indicatorView.less

@@ -17,7 +17,7 @@
         .indicator-box {
             float: left;
             display: table;
-            border: 1px solid #F6F6F7;
+            border: 1px solid #ececec;
             padding: 8px;
             margin: 4px;
             .row {
@@ -32,20 +32,43 @@
                         white-space: nowrap;
                         text-overflow: ellipsis;
                     }
-                    &.rc-name {
+                    &.c-name {
 
                     }
-                    &.rc-key {
+                    &.c-key {
                         font-size: 12px;
                         color: #999;
                     }
-                    &.rc-value {
+                    &.c-value {
                         font-size: 28px;
                         line-height: 28px;
                         font-weight: bold;
                         text-align: center;
                         color: rgb(33, 83, 212);
                     }
+                    &.c-extra {
+                        .over-wrapper {
+                            display: table;
+                            table-layout: fixed;
+                            >span {
+                                display: table-cell;
+                                font-size: 12px;
+                                &.til {
+                                    white-space: nowrap;
+                                    color: #999;
+                                    overflow: hidden;
+                                    text-overflow: ellipsis;
+                                }
+                                &.val {
+                                    text-align: right;
+                                    white-space: nowrap;
+                                    font-size: 14px;
+                                    overflow: hidden;
+                                    text-overflow: ellipsis;
+                                }
+                            }
+                        }
+                    }
                 }
             }
             .indicator-name {

+ 1 - 1
src/components/chartDesigner/sections/aggregateTableConfigForm.jsx

@@ -92,7 +92,7 @@ class AggregateTableConfigForm extends React.Component {
 						onChange={(values) => {
 							const groupBy = [];
 							if(values.length > 2) {
-								message.warning('最多支持选择个分组');
+								message.warning('最多支持选择2个分组');
 								return false;
 							}
 							let statisticsArr = chartDesigner.aggregateTableConfig.statistics;

+ 1 - 1
src/components/chartDesigner/sections/chartType.json

@@ -24,6 +24,6 @@
     "icon": "dot-chart"
 }, {
     "type": "indicator",
-    "label": "指标板",
+    "label": "指标板",
     "myIcon": "bi-chartType-indicator"
 }]

+ 29 - 0
src/components/chartDesigner/sections/gauge.json

@@ -102,5 +102,34 @@
         "value": "min",
         "label": "最小值",
         "columnType": ["scale", "ordinal"]
+    }],
+    "indicator": [{
+        "value": "sum",
+        "label": "累计",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "avg",
+        "label": "平均值",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "median",
+        "label": "中位数",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "count",
+        "label": "计数",
+        "columnType": ["index", "time", "categorical", "scale", "ordinal", "string"]
+    }, {
+        "value": "distinctCount",
+        "label": "不重复计数",
+        "columnType": ["index", "time", "categorical", "scale", "ordinal", "string"]
+    }, {
+        "value": "max",
+        "label": "最大值",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "min",
+        "label": "最小值",
+        "columnType": ["scale", "ordinal"]
     }]
 }

+ 113 - 16
src/components/chartDesigner/sections/indicatorConfigForm.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
-import { Form, Select, InputNumber } from 'antd'
+import { Form, Select, Cascader, Menu, InputNumber, Dropdown, Tag, message } from 'antd'
 import { connect } from 'dva'
 import '../../../models/chartDesigner'
 import { deepAssign } from '../../../utils/baseUtils'
+import GAUGE from './gauge.json'
 const FormItem = Form.Item
 const { Option } = Select
 const formItemLayout = {
@@ -10,11 +11,11 @@ const formItemLayout = {
 	wrapperCol: { span: 16 },
 }
 const IndicatorConfigForm = ({ autoRefresh, chartDesigner, dispatch }) => {
-	const { columns, indicatorConfig, defaultIndicatorThreshold } = chartDesigner;
+	const { columns, baseConfig, indicatorConfig, defaultIndicatorThreshold } = chartDesigner;
 
 	return (
 		<Form hideRequiredMark={true}>
-			<FormItem label='显示字段' {...formItemLayout}>
+			<FormItem label='标题/维度' {...formItemLayout}>
                 <Select
 					labelInValue={false}
 					placeholder='请选择...'
@@ -23,31 +24,127 @@ const IndicatorConfigForm = ({ autoRefresh, chartDesigner, dispatch }) => {
 					allowClear={true}
 					onChange={(value) => {
                         let column = value ? columns.find(c => c.name === value) : null;
-                        column = column || {};
-						dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { ...indicatorConfig, xAxis: { column }, sortColumn: { key: column.name, label: column.label } }, autoRefresh });
+                        column = column ? {
+							value: column.name,
+							label: column.label,
+							type: column.type
+						} : {};
+						dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { ...indicatorConfig, xAxis: { column }, sortColumn: column.value ? { key: column.value, label: column.label } : (indicatorConfig.yAxis.column.value ? {key: indicatorConfig.yAxis.column.value, label:indicatorConfig.yAxis.column.label} : undefined) }, autoRefresh });
 					}}
-					value={indicatorConfig.xAxis.column.name}
+					value={indicatorConfig.xAxis.column.value}
 				>
-					{columns.filter(c => c.name !== indicatorConfig.yAxis.column.name).map((c, i)=>{
+					{columns.filter(c => c.name !== indicatorConfig.yAxis.column.value).map((c, i)=>{
 						return (<Option key={i} value={c.name}>{c.label}</Option>)
 					})}
 				</Select>
 			</FormItem>
-			<FormItem label='值字段' {...formItemLayout}>
+			<FormItem label='指标/度量' {...formItemLayout}>
+				<Cascader
+					className='gauge-item'
+					value={[indicatorConfig.yAxis.column.value, indicatorConfig.yAxis.gauge.value]}
+					allowClear={true}
+					showSearch={{
+						filter: (inputValue, path) => {
+							let p0 = path[0].label.toLowerCase();
+							let v = inputValue.toLowerCase();
+							return p0.indexOf(v) !== -1;
+						},
+						sort: (a, b, inputValue) => {
+							return a[0].label.localeCompare(b[0].label,"zh");
+						},
+						render: (inputValue, path) => {
+							const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+							let v = inputValue.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
+							let label0 = (path[0].label.split(new RegExp(`(${v})`, 'i')).map((fragment, i) => {
+								return (
+									fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === v.toLowerCase() ?
+									<span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+									fragment
+								)
+							}))
+							return <div>{label0}>{path[1].label}</div>
+						}
+					}}
+					options={columns.filter(c => c.name !== indicatorConfig.xAxis.column.value).map((c, i)=>{
+						return {
+							value: c.name,
+							label: c.label,
+							type: c.type,
+							children: GAUGE[baseConfig.viewType].filter(g => g.columnType.indexOf(c.type) !== -1)
+						}
+					})}
+					onChange={(value, items) => {
+						let column = {};
+						let gauge = {};
+						if(value.length > 0) {
+							column = { type: items[0].type, value: items[0].value, label: items[0].label };
+							gauge = { value: items[1].value, label: items[1].label };
+						}
+						dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { ...indicatorConfig, yAxis: { column, gauge }, sortColumn: indicatorConfig.xAxis.column.value ? { key: indicatorConfig.xAxis.column.value, label: indicatorConfig.xAxis.column.label } : (column.value ? { key: column.value, label: column.label } : undefined) }, autoRefresh });
+					}}
+					displayRender={(label, selectedOptions) => {
+						let menu = selectedOptions.length > 0 ? <Menu
+							selectedKeys={[indicatorConfig.yAxis.gauge.value]}
+							selectable={true}
+						>
+							{selectedOptions[0].children.map((c, i) => {
+								return <Menu.Item  data-value={c.value} data-label={c.label} key={c.value} onClick={(e) => {
+									let value = e.domEvent.target.getAttribute('data-value');
+									let label = e.domEvent.target.getAttribute('data-label');
+									dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { 
+										...indicatorConfig,
+										yAxis: {
+											column: indicatorConfig.yAxis.column,
+											gauge: { value, label }
+										}
+									}, autoRefresh });
+									e.domEvent.stopPropagation();
+								}}>{c.label}</Menu.Item>
+							})}
+						</Menu>: [];
+						let tag = selectedOptions.length > 0 ? <Dropdown
+							trigger={['click']}
+							overlay={menu}
+						>
+							<Tag size='small' onClick={(e) => {e.stopPropagation()}}>{label[1]}</Tag>
+						</Dropdown>
+						: null;
+
+						return <div className={`cascader-label ${tag?'full' : 'empty'}-label`}>
+							{tag}
+							<span>{label[0] || '请选择...'}</span>
+						</div>
+					}}
+				>
+				</Cascader>
+			</FormItem>
+			<FormItem label='备注字段' {...formItemLayout}>
                 <Select
 					labelInValue={false}
 					placeholder='请选择...'
+					mode="multiple"
 					showSearch
 					filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 					allowClear={true}
 					onChange={(value) => {
-                        let column = value ? columns.find(c => c.name === value) : null;
-                        column = column || {};
-						dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { ...indicatorConfig, yAxis: { column }, sortColumn: indicatorConfig.xAxis.column.name ? { key: indicatorConfig.xAxis.column.name, label: indicatorConfig.xAxis.column.label } : { key: column.name, label: column.label } }, autoRefresh });
+						if(value.length > 2) {
+							message.warning('最多支持选择2个备注字段');
+							return false;
+						}
+						let otherColumn = value.map(v => {
+							let t = columns.find(c => c.name === v);
+							return t ? {
+								value: t.name,
+								label: t.label,
+								type: t.type
+							} : null
+						}).filter(v => v !== null);
+						dispatch({ type: 'chartDesigner/changeField',
+							name: 'indicatorConfig', value: { ...indicatorConfig, otherColumn }, autoRefresh });
 					}}
-					value={indicatorConfig.yAxis.column.name}
+					value={indicatorConfig.otherColumn.map(c => c.value)}
 				>
-					{columns.filter(c => c.type === 'scale').filter(c => c.name !== indicatorConfig.xAxis.column.name).map((c, i)=>{
+					{columns.filter(c => c.name !== indicatorConfig.xAxis.column.value && c.name !== indicatorConfig.yAxis.column.value).map((c, i)=>{
 						return (<Option key={i} value={c.name}>{c.label}</Option>)
 					})}
 				</Select>
@@ -57,15 +154,15 @@ const IndicatorConfigForm = ({ autoRefresh, chartDesigner, dispatch }) => {
                     allowClear
                     value={indicatorConfig.sortColumn}
                     labelInValue={true}
-                    placeholder={indicatorConfig.xAxis.column.name ? indicatorConfig.xAxis.column.label : (indicatorConfig.yAxis.column.label || '无')}
+                    placeholder={indicatorConfig.xAxis.column.value ? indicatorConfig.xAxis.column.label : (indicatorConfig.yAxis.column.label || '无')}
                     showSearch
                     filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                     onChange={(value) => {
                         dispatch({ type: 'chartDesigner/changeField', name: 'indicatorConfig', value: { ...indicatorConfig, sortColumn: value }, autoRefresh });
                     }}
                 >
-                    {[indicatorConfig.xAxis.column, indicatorConfig.yAxis.column].filter(x => !!x.name).map((c, i)=>{
-                        return <Option key={c.name} value={c.name}>{c.label}</Option>
+                    {[indicatorConfig.xAxis.column, indicatorConfig.yAxis.column].filter(x => !!x.value).map((c, i)=>{
+                        return <Option key={c.value} value={c.value}>{c.label}</Option>
                     })}
                 </Select>
             </FormItem>

+ 5 - 4
src/components/chartDesigner/sections/style/theme.jsx

@@ -17,7 +17,7 @@ class ThemeConfig extends React.Component {
     render() {
         const { chartDesigner, dispatch } = this.props;
         const { formItemLayout } = this.state;
-        const { theme, chartOption } = chartDesigner;
+        const { theme } = chartDesigner;
 
         return <Form>
             <Divider>主题</Divider>
@@ -26,12 +26,13 @@ class ThemeConfig extends React.Component {
                     defaultValue={theme || 'default'}
                     onChange={(value, item) => {
                         dispatch({ type: 'chartDesigner/changeFields', fields: [
-                            { name: 'theme', value: value },
-                            { name: 'chartOption', value: { ...chartOption, themeConfig: themes[value] } }
+                            { name: 'theme', value: value }
                         ] });
                     }}
                 >
-                    <Select.Option value='default'>默认</Select.Option>
+                    {themes.map((theme) => (
+                        <Select.Option value={theme.name} key={theme.name}>{theme.label}</Select.Option>
+                    ))}
                 </Select>
             </Form.Item>
         </Form>

+ 194 - 0
src/components/chartDesigner/sections/style/theme/bluelight.json

@@ -0,0 +1,194 @@
+{
+    "base": {
+        "color": [
+            "#2eb4ff",
+            "#51edda",
+            "#8121fc",
+            "#a0a7e6",
+            "#c4ebad",
+            "#96dee8",
+            "#19a0bd",
+            "#3772eb",
+            "#59c4e6",
+            "#a5e7f0",
+            "#f7e426",
+            "#07a2a4",
+            "#9a7fd1",
+            "#da9bf7"
+        ],
+        "backgroundColor": "#fff",
+        "textStyle": {},
+        "legend": {
+            "textStyle": {
+                "color": "#333"
+            }
+        },
+        "tooltip": {
+            "axisPointer": {
+                "lineStyle": {
+                    "color": "#cccccc",
+                    "width": 1
+                },
+                "crossStyle": {
+                    "color": "#cccccc",
+                    "width": 1
+                }
+            }
+        }
+    },
+    "dataZoom": {
+        "dataZoom": {
+            "backgroundColor": "rgba(47,69,84,0)",
+            "dataBackgroundColor": "rgba(47,69,84,0.3)",
+            "fillerColor": "rgba(167,183,204,0.4)",
+            "handleColor": "#a7b7cc",
+            "handleSize": "100%",
+            "textStyle": {
+                "color": "#333"
+            }
+        }
+    },
+    "line": {
+        "line": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 1
+                }
+            },
+            "lineStyle": {
+                "normal": {
+                    "width": 2
+                }
+            }
+        }
+    },
+    "bar": {
+        "bar": {
+            "itemStyle": {
+                "normal": {
+                    "barBorderWidth": 0,
+                    "barBorderColor": "#ccc"
+                },
+                "emphasis": {
+                    "barBorderWidth": 0,
+                    "barBorderColor": "#ccc"
+                }
+            }
+        }
+    },
+    "pie": {
+        "pie": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                },
+                "emphasis": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                }
+            }
+        },
+        "dataZoom": null,
+        "markPoint": null
+    },
+    "scatter": {
+        "scatter": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                },
+                "emphasis": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                }
+            }
+        }
+    },
+    "xAxis": {
+        "xAxis": {
+            "0": {
+                "axisLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "axisTick": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "axisLabel": {
+                    "show": true,
+                    "textStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "splitLine": {
+                    "show": false,
+                    "lineStyle": {
+                        "color": [
+                            "#F5F6FB"
+                        ]
+                    }
+                },
+                "splitArea": {
+                    "show": false,
+                    "areaStyle": {
+                        "color": [
+                            "rgba(250,250,250,0.3)",
+                            "rgba(200,200,200,0.3)"
+                        ]
+                    }
+                }
+            }
+        }
+    },
+    "yAxis": {
+        "yAxis": {
+            "0": {
+                "axisLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "axisTick": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "axisLabel": {
+                    "show": true,
+                    "textStyle": {
+                        "color": "#aaaaaa"
+                    }
+                },
+                "splitLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": [
+                            "#F5F6FB"
+                        ]
+                    }
+                },
+                "splitArea": {
+                    "show": false,
+                    "areaStyle": {
+                        "color": [
+                            "rgba(250,250,250,0.3)",
+                            "rgba(200,200,200,0.3)"
+                        ]
+                    }
+                }
+            }
+        }
+    },
+    "indicator": {
+        "boxBackgroundColor": "#F0F8FF"
+    }
+}

+ 232 - 0
src/components/chartDesigner/sections/style/theme/dark.json

@@ -0,0 +1,232 @@
+{
+    "base": {
+        "color": [
+            "#0190eb",
+            "#fabd62",
+            "#7971ff",
+            "#3bdbf0",
+            "#59aefc",
+            "#6cdeff",
+            "#9b79ff",
+            "#fae41f",
+            "#1fc1d6",
+            "#325beb",
+            "#8d98b3",
+            "#e5cf0d",
+            "#97b552",
+            "#95706d",
+            "#dc69aa",
+            "#07a2a4",
+            "#9a7fd1",
+            "#588dd5",
+            "#f5994e",
+            "#c05050",
+            "#59678c",
+            "#c9ab00",
+            "#7eb00a",
+            "#6f5553",
+            "#c14089"
+        ],
+        "backgroundColor": "rgba(41,52,65,1)",
+        "textStyle": {},
+        "legend": {
+            "textStyle": {
+                "color": "#999999"
+            },
+            "inactiveColor": "#575A5D",
+            "pageIconColor": "#aaa",
+            "pageIconInactiveColor": "#2f4554",
+            "pageTextStyle": {
+                "color": "#aaa"
+            }
+        },
+        "tooltip": {
+            "axisPointer": {
+                "lineStyle": {
+                    "color": "#cccccc",
+                    "width": 1
+                },
+                "crossStyle": {
+                    "color": "#cccccc",
+                    "width": 1
+                }
+            }
+        }
+    },
+    "dataZoom": {
+        "dataZoom": {
+            "backgroundColor": "rgba(255,255,255,0)",
+            "dataBackgroundColor": "rgba(222,222,222,1)",
+            "fillerColor": "rgba(255,113,94,0.2)",
+            "handleColor": "#cccccc",
+            "handleSize": "100%",
+            "textStyle": {
+                "color": "#999999"
+            }
+        }
+    },
+    "line": {
+        "line": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 2
+                }
+            },
+            "lineStyle": {
+                "normal": {
+                    "width": 3
+                }
+            }
+        }
+    },
+    "bar": {
+        "bar": {
+            "itemStyle": {
+                "normal": {
+                    "barBorderWidth": 0,
+                    "barBorderColor": "#ccc"
+                },
+                "emphasis": {
+                    "barBorderWidth": 0,
+                    "barBorderColor": "#ccc"
+                }
+            }
+        }
+    },
+    "pie": {
+        "pie": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                },
+                "emphasis": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                }
+            }
+        },
+        "dataZoom": null,
+        "markPoint": null
+    },
+    "scatter": {
+        "scatter": {
+            "itemStyle": {
+                "normal": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                },
+                "emphasis": {
+                    "borderWidth": 0,
+                    "borderColor": "#ccc"
+                }
+            }
+        }
+    },
+    "xAxis": {
+        "xAxis": {
+            "0": {
+                "axisLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#666666"
+                    }
+                },
+                "axisTick": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#333"
+                    }
+                },
+                "axisLabel": {
+                    "show": true,
+                    "textStyle": {
+                        "color": "#999"
+                    }
+                },
+                "splitLine": {
+                    "show": false,
+                    "lineStyle": {
+                        "color": [
+                            "#555555"
+                        ]
+                    }
+                },
+                "splitArea": {
+                    "show": false,
+                    "areaStyle": {
+                        "color": [
+                            "rgba(250,250,250,0.05)",
+                            "rgba(200,200,200,0.02)"
+                        ]
+                    }
+                }
+            }
+        }
+    },
+    "yAxis": {
+        "yAxis": {
+            "0": {
+                "axisLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#666"
+                    }
+                },
+                "axisTick": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": "#333"
+                    }
+                },
+                "axisLabel": {
+                    "show": true,
+                    "textStyle": {
+                        "color": "#999"
+                    }
+                },
+                "splitLine": {
+                    "show": true,
+                    "lineStyle": {
+                        "color": [
+                            "#555"
+                        ]
+                    }
+                },
+                "splitArea": {
+                    "show": false,
+                    "areaStyle": {
+                        "color": [
+                            "rgba(250,250,250,0.05)",
+                            "rgba(200,200,200,0.02)"
+                        ]
+                    }
+                }
+            }
+        }
+    },
+    "indicator": {
+        "nameLabelColor": "#fff",
+        "keyLabelColor": "#999",
+        "valueLabelColor": "#2153D4",
+        "extraNameLabelColor": "#999",
+        "extraValueLabelColor": "#fff"
+    },
+    "aggregateTable": {
+        "targetStyle": {
+            "color": "#50595F"
+        },
+        "statisticsStyle": {
+            "color": "#fff"
+        },
+        "groupByStyle": {
+            "color": "#fff"
+        },
+        "cellStyle": {
+            "color": "#fff"
+        }
+    },
+    "dashboard": {
+        
+    }
+}

+ 7 - 16
src/components/chartDesigner/sections/style/theme/default.json

@@ -28,7 +28,7 @@
             "#6f5553",
             "#c14089"
         ],
-        "backgroundColor": "rgba(0,0,0,0)",
+        "backgroundColor": "#fff",
         "textStyle": {},
         "legend": {
             "textStyle": {
@@ -46,7 +46,9 @@
                     "width": 1
                 }
             }
-        },
+        }
+    },
+    "dataZoom": {
         "dataZoom": {
             "backgroundColor": "rgba(0,0,0,0)",
             "dataBackgroundColor": "rgba(255,255,255,0.3)",
@@ -56,20 +58,6 @@
             "textStyle": {
                 "color": "#333"
             }
-        },
-        "markPoint": {
-            "label": {
-                "normal": {
-                    "textStyle": {
-                        "color": "#eee"
-                    }
-                },
-                "emphasis": {
-                    "textStyle": {
-                        "color": "#eee"
-                    }
-                }
-            }
         }
     },
     "line": {
@@ -132,5 +120,8 @@
                 }
             }
         }
+    },
+    "indicator": {
+        "boxBackgroundColor": "#ececec"
     }
 }

+ 16 - 4
src/components/chartDesigner/sections/style/theme/index.js

@@ -1,5 +1,17 @@
-import defaultTheme from './default.json'
+import defaultTheme from './default.json';
+import bluelight from './bluelight.json';
+import dark from './dark.json';
 
-export default {
-    default: defaultTheme,
-}
+export default [{
+    name: 'default',
+    label: '默认',
+    config: defaultTheme
+}, {
+    name: 'light',
+    label: '主题一',
+    config: bluelight
+}, {
+    name: 'dark',
+    label: '主题二',
+    config: dark
+}]

+ 17 - 1
src/components/dashboardDesigner/configSider.jsx

@@ -1,9 +1,10 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Form, Input, Cascader, Icon } from 'antd'
+import { Form, Input, Cascader, Select, Icon } from 'antd'
 import ChooseChartBox from './chooseChartBox'
 import CusFilterBox from './cusFilterBox'
 import copy from 'copy-to-clipboard'
+import themes from '../chartDesigner/sections/style/theme/index'
 import './configSider.less'
 const FormItem = Form.Item
 
@@ -113,6 +114,7 @@ class ConfigSider extends React.Component {
     render() {
         const { dashboard, dashboardDesigner, dispatch } = this.props;
         const { description, visibleCusFilterBox, copyDisabled, copyText, validInfo } = this.state;
+        const { theme } = dashboardDesigner;
         const { menuTree } = dashboard;
 
         return <Form className='form-config' layout={'vertical'}>
@@ -208,6 +210,20 @@ class ConfigSider extends React.Component {
                     }}>{copyText}</span>}
                 />
             </FormItem>
+            <div className='divider'>看板主题</div>
+            <FormItem>
+                <Select
+                    disabled
+                    defaultValue={theme || 'default'}
+                    onChange={(value, item) => {
+                        dispatch({ type: 'dashboardDesigner/setField', name: 'theme', value: value });
+                    }}
+                >
+                    {themes.map((theme) => (
+                        <Select.Option value={theme.name} key={theme.name}>{theme.label}</Select.Option>
+                    ))}
+                </Select>
+            </FormItem>
         </Form>
     }
 }

+ 1 - 0
src/components/dashboardDesigner/viewLayout.less

@@ -142,6 +142,7 @@
     .chartview-content {
       height: calc(~'100% - 20px');
       width: 100%;
+      padding: 8px;
       .richtexteditor {
         .w-e-text-container {
           pointer-events: all;

+ 4 - 2
src/constants/url.js

@@ -1,6 +1,6 @@
 // const BASE_URL = 'http://10.1.1.168:8081/BI/';
-// const BASE_URL = 'http://10.1.81.2:24000/BI_DEV/';
-// const BASE_URL = 'http://10.1.80.78:8011/BI/';
+// const BASE_URL = 'http://10.10.100.39:8011/BI/';
+// const BASE_URL = 'http://10.1.80.116:8011/BI/';
 const BASE_URL = ''
 
 /**后台接口地址 */
@@ -114,6 +114,8 @@ const URLS = {
 
     CHART_AGGREGATETABLE_OPTION: BASE_URL + 'showPopulation', // 请求总体统计数据
 
+    CHART_INDICATOR_OPTION: BASE_URL + 'showIndicator', // 请求指标看板数据
+
     CHART_QUERY_DATACOLUMNS: BASE_URL + 'DataBase/getColumnByChartId', // 通过图表id获得列数据
 
     CHART_POLICY_LIST: BASE_URL + 'getChartStrategys', // 获得图表的策略

+ 26 - 25
src/models/EChartsMedia.js

@@ -1,3 +1,4 @@
+import { deepAssign } from '../utils/baseUtils'
 /**
  * 图表自适应调整,宽高阈值分为两档,500和1000
  * 
@@ -9,70 +10,70 @@
  *    0 +----+----+---
  *      0   500  1000
  */
-export default function(chartType, legendVisible, dataZoomVisible) {
+export default function(chartType, legendVisible, dataZoomVisible, assignConfig) {
     if(chartType === 'bar' || chartType === 'line' || chartType === 'scatter') {
         return legendVisible ? [{
             query: { minWidth: 1000, minHeight: 1000 }, // I
-            option: { legend: { type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '20%'} }
+            option: deepAssign({ legend: { type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '20%'} }, assignConfig)
         }, {
             query: { maxWidth: 1000, minHeight: 1000 }, // H
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, } :
-                { legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, }, assignConfig)
         }, {
             query: { maxWidth: 500, minHeight: 1000 }, // G
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, } :
-                { legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, }, assignConfig)
         }, {
             query: { minWidth: 1000, maxHeight: 1000 }, // F
-            option: { legend: { type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '16%' } }
+            option: deepAssign({ legend: { type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '16%' } }, assignConfig)
         }, {
             query: { maxWidth: 1000, maxHeight: 1000 }, // E
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, } :
-                { legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, grid: { top: 100 } }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 50, orient: 'horizontal', width: '90%' }, grid: { top: 100 } }, assignConfig)
         }, {
             query: { maxWidth: 500, maxHeight: 1000 }, // D
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, } :
-                { legend: { type: 'scroll', right: 'center', top: 0, orient: 'horizontal', width: '90%' }, }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 0, orient: 'horizontal', width: '90%' }, }, assignConfig)
         }, {
             query: { minWidth: 1000, maxHeight: 500 }, // C
-            option: { legend: {  type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '20%'} }
+            option: deepAssign({ legend: {  type: 'scroll', top: 50, right: 50, orient: 'vertical', height: '80%', width: '35%' }, grid: { right: '20%'} }, assignConfig)
         }, {
             query: { maxWidth: 1000, maxHeight: 500 }, // B
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, grid: { top: 50, left: 80, right: 80 } } :
-                { legend: { type: 'scroll', right: 'center', top: 20, orient: 'horizontal', width: '90%' }, grid: { top: 80, left: 80, right: 80 } }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, grid: { top: 50, left: 80, right: 80 } }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 20, orient: 'horizontal', width: '90%' }, grid: { top: 80, left: 80, right: 80 } }, assignConfig)
         }, {
             query: { maxWidth: 500, maxHeight: 500 }, // A
-            option: !dataZoomVisible ? { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, grid: { top: 50, left: 80, right: 80 } } :
-                { legend: { type: 'scroll', right: 'center', top: 20, orient: 'horizontal', width: '90%' }, grid: { top: 80, left: 80, right: 80 } }
+            option: !dataZoomVisible ? deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, grid: { top: 50, left: 80, right: 80 } }, assignConfig) :
+                deepAssign({ legend: { type: 'scroll', right: 'center', top: 20, orient: 'horizontal', width: '90%' }, grid: { top: 80, left: 80, right: 80 } }, assignConfig)
         }] : []
     }else if(chartType === 'pie') {
         return [{
             query: { minWidth: 1000, minHeight: 1000 }, // I
-            option: { legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 1000, minHeight: 1000 }, // H
-            option: { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 500, minHeight: 1000 }, // G
-            option: { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '55%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '55%'] }] }, assignConfig)
         }, {
             query: { minWidth: 1000, maxHeight: 1000 }, // F
-            option: { legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 1000, maxHeight: 1000 }, // E
-            option: { legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 50, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 500, maxHeight: 1000 }, // D
-            option: { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '55%'], center: ['50%', '40%'] }] }, assignConfig)
         }, {
             query: { minWidth: 1000, maxHeight: 500 }, // C
-            option: { legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', top: 'center', right: 50, orient: 'vertical', height: '80%', width: '35%' }, series: [{ radius: [0, '65%'], center: ['35%', '50%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 1000, maxHeight: 500 }, // B
-            option: { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '45%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '45%'] }] }, assignConfig)
         }, {
             query: { maxWidth: 500, maxHeight: 500 }, // A
-            option: { legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '45%'] }] }
+            option: deepAssign({ legend: { type: 'scroll', right: 'center', bottom: 0, orient: 'horizontal', width: '90%' }, series: [{ radius: [0, '65%'], center: ['50%', '45%'] }] }, assignConfig)
         }]
     } 
 }

+ 3 - 2
src/models/chart.js

@@ -316,7 +316,7 @@ export default {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { filters, code, header, baseConfig, otherConfig, description, group, chartOption,
-                    fetchConfig, styleConfig, thumbnail } = chartDesigner;
+                    fetchConfig, styleConfig, thumbnail, theme } = chartDesigner;
                 let body = {
                     chartId: code,
                     filters: JSON.stringify(filters),
@@ -328,7 +328,8 @@ export default {
                     chartsGroup: group+'' ? group : '-1',
                     chartOption: JSON.stringify(chartOption),
                     fetchConfig: JSON.stringify(fetchConfig),
-                    thumbnail: thumbnail
+                    thumbnail: thumbnail,
+                    theme: theme
                 }; // 基本属性
                 let styleObj = {};
                 if(!!baseConfig.viewType) {

+ 103 - 33
src/models/chartDesigner.js

@@ -63,7 +63,7 @@ export default {
             lineConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 200 },
             pieConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, threshold: 20 },
             scatterConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 },
-            indicatorConfig: { xAxis: { column: {} }, yAxis: { column: {} }, threshold: 6 },
+            indicatorConfig: { xAxis: { column: {} }, yAxis: { column: {}, gauge: {} }, otherColumn: [], threshold: 6 },
             theme: 'default',
             styleConfig: {},
             otherConfig:{},
@@ -210,7 +210,7 @@ export default {
                 yield put({ type: 'chart/remoteModify' });
                 const { newHeaderLabel } = action;
                 const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { filters, baseConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig } = chartDesigner;
+                const { filters, baseConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig, theme } = chartDesigner;
                 let body = {
                     filters: JSON.stringify(filters),
                     chartName: newHeaderLabel,
@@ -221,6 +221,7 @@ export default {
                     chartsGroup: group+'' ? group : '-1',
                     chartOption: JSON.stringify(chartOption),
                     fetchConfig: JSON.stringify(fetchConfig),
+                    theme: theme
                 }; // 基本属性
                 if(!!baseConfig.viewType) {
                     let _CHART_TYPE = {
@@ -419,7 +420,7 @@ export default {
                     }
                 }else if(viewType === 'indicator') {
                     const { indicatorConfig } = chartDesigner;
-                    if(indicatorConfig.xAxis.column.name && indicatorConfig.yAxis.column.name) {
+                    if(indicatorConfig.yAxis.column.value) {
                         yield put({ type: 'fetchIndicatorData' });
                     }else {
                         yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
@@ -462,14 +463,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('bar', res.data, barConfig, theme, styleConfig.bar || {});
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求柱状图数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求柱状图数据失败: ' + e.message);
             }
         },
@@ -501,14 +511,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('pie', res.data, pieConfig, theme, styleConfig.pie || {});
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求饼图数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求饼图数据失败: ' + e.message);
             }
         },
@@ -540,14 +559,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('line', res.data, lineConfig, theme, styleConfig.line || {});
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求折线图数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求折线图数据失败: ' + e.message);
             }
         },
@@ -578,14 +606,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('scatter', res.data, scatterConfig, theme, styleConfig.scatter || {});
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求散点图数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求散点图数据失败: ' + e.message);
             }
         },
@@ -613,14 +650,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('dataView', res.data, dataViewConfig, theme, styleConfig.dataView);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求列表数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求列表数据失败: ' + e.message);
             }
         },
@@ -649,14 +695,23 @@ export default {
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('aggregateTable', res.data, aggregateTableConfig, theme, styleConfig.aggregateTable || {});
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求统计数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求统计数据失败: ' + e.message);
             }
         },
@@ -664,34 +719,49 @@ export default {
             try {
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { code, indicatorConfig, filters, theme, styleConfig, defaultIndicatorThreshold } = chartDesigner;
-                const { xAxis, yAxis, sortColumn, sortType, threshold } = indicatorConfig;
+                const { xAxis, yAxis, otherColumn, sortColumn, sortType, threshold } = indicatorConfig;
                 const body = {
                     id: code,
-                    columnListName: [xAxis.column.name, yAxis.column.name],
-                    sortColumn: sortColumn ? sortColumn.key : xAxis.column.name,
-                    sort: sortType || 'asc',
+                    xField: {
+                        columnRename: xAxis.column.value,
+                        columnType: xAxis.column.type
+                    },
+                    yField: {
+                        columnRename: yAxis.column.value,
+                        columnType: yAxis.column.type,
+                        showDataType: yAxis.gauge.value
+                    },
+                    otherColumn: otherColumn.map(c => c.value),
+                    sort: sortColumn ? sortColumn.key : (xAxis.column.value || yAxis.column.value),
+                    rule: sortType || 'asc',
                     filters: getBodyFilters(filters),
-                    testPage: {
-                        pageNum: 1,
-                        pageSize: threshold || defaultIndicatorThreshold,
-                    }
+                    threshold: threshold || defaultIndicatorThreshold
                 };
 
                 let res = yield call(service.fetch, {
-                    url: URLS.CHART_DATAVIEW_OPTION,
+                    url: URLS.CHART_INDICATOR_OPTION,
                     body: body,
                     timeout: 30000
                 });
                 if(res.code > 0) {
                     let option = parseChartOption('indicator', res.data, indicatorConfig, theme, styleConfig.indicator);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: res.data },
+                        { name: 'chartOption', value: option },
+                    ] });
                 }else {
                     message.error('请求指标数据失败: ' + res.msg);
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    yield put({ type: 'silentSetFields', fields: [
+                        { name: 'resData', value: null },
+                        { name: 'chartOption', value: {} },
+                    ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'resData', value: null },
+                    { name: 'chartOption', value: {} },
+                ] });
                 message.error('请求指标数据失败: ' + e.message);
             }
         },

+ 5 - 3
src/models/dashboard.js

@@ -192,7 +192,8 @@ export default {
                         filters: JSON.parse((resData.filters|| "[]")),
                         shareCode: resData.bdCode,
                         chartCodes: chartCodes,
-                        demo: resData.demo
+                        demo: resData.demo,
+                        theme: resData.theme
                     }
 
                     let fields = [];
@@ -253,7 +254,7 @@ export default {
             try {
                 const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
                 const dashboard = yield select(state => state.present.dashboard);
-                const { code, name, items, description, relationColumns, filters, chartCodes, shareCode } = dashboardDesigner;
+                const { code, name, items, description, relationColumns, filters, chartCodes, shareCode, theme } = dashboardDesigner;
                 let body = {
                     id: code,
                     bdName: name,
@@ -277,7 +278,8 @@ export default {
                     relationColumns: JSON.stringify(relationColumns),
                     filters: JSON.stringify(filters) || "",
                     chartIds: chartCodes.join(','),
-                    bdCode: shareCode
+                    bdCode: shareCode,
+                    theme: theme
                 }
                 const res = yield call(service.fetch, {
                     url: URLS.DASHBOARD_UPDATE,

+ 3 - 2
src/models/dashboardDesigner.js

@@ -90,6 +90,7 @@ export default {
         originData: {
             code: null,
             name: '无标题',
+            theme: 'default',
             minLayoutHeight: 40, // 元素最小高度
             layoutMargin: [8, 8], // 元素margin
             defaultLayout: { x: 0, y: 50, w: 12, h: 6, minW: 2, maxW: 12, minH: 1 },
@@ -495,8 +496,8 @@ export default {
         *fetchChartData(action, { put, call, select }) {
             const { item, mandatory, page, pageSize } = action;
             const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
-            const { creatorCode, filters } = dashboardDesigner;
-            const { chartCode, theme, chartType } = item;
+            const { creatorCode, filters, theme } = dashboardDesigner;
+            const { chartCode, chartType } = item;
             if(!mandatory && !!item.chartOption) {
                 return false;
             }

+ 40 - 22
src/models/parseChartOption.js

@@ -13,40 +13,41 @@ export default function(viewType, data, chartConfig, themeName, styleConfig, cha
         return {};
     }
     try {
-        let theme = themes[themeName] || themes['default']
+        let t = themes.find(t => t.name === themeName);
+        let theme = t ? t.config : themes[0].config;
         let o, themeConfig;
         switch(viewType) {
             case 'bar': {
-                themeConfig = Object.assign({}, theme.base, theme.bar);
+                themeConfig = deepAssign({}, theme.base, theme.bar, theme.xAxis, theme.yAxis, theme.dataZoom);
                 o = barOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }
             case 'pie': {
-                themeConfig = Object.assign({}, theme.base, theme.pie);
+                themeConfig = deepAssign({}, theme.base, theme.pie);
                 o = pieOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }
             case 'line': {
-                themeConfig = Object.assign({}, theme.base, theme.line);
+                themeConfig = deepAssign({}, theme.base, theme.line, theme.xAxis, theme.yAxis, theme.dataZoom);
                 o = lineOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }
             case 'scatter': {
-                themeConfig = Object.assign({}, theme.base, theme.scatter);
+                themeConfig = deepAssign({}, theme.base, theme.scatter, theme.xAxis, theme.yAxis, theme.dataZoom);
                 o = scatterOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }
             case 'aggregateTable': {
-                themeConfig = theme.aggregateTable;
+                themeConfig = deepAssign({}, theme.base, theme.aggregateTable);
                 o = aggregateTableOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }case 'dataView' : {
-                themeConfig = theme.dataView;
+                themeConfig = deepAssign({}, theme.base, theme.dataView);
                 o = dataViewOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             }
             case 'indicator':
-                themeConfig = theme.indicator;
+                themeConfig = deepAssign({}, theme.base, theme.indicator);
                 o = indicatorOption(data, chartConfig, themeConfig, styleConfig);
                 break;
             default:{
@@ -98,7 +99,10 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
             },
             data: data.xAxis.map(d => {
                 let gv= xAxis.granularity.value;
-                let xv = d || '空';
+                if(!d) {
+                    return '空';
+                }
+                let xv = d;
                 if(gv === 'halfYear') {
                     let arr = d.split('-H');
                     xv = arr[0] + ['上半年', '下半年'][arr[1] - 1]
@@ -152,7 +156,9 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
 
     let mediaOption = {
         baseOption: option,
-        media: EChartsMedia('bar', legendVisible, dataZoomVisible)
+        media: EChartsMedia('bar', legendVisible, dataZoomVisible, {
+            legend: option.legend
+        })
     }
     
     return mediaOption;
@@ -197,7 +203,10 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
             },
             data: data.xAxis.map(d => {
                 let gv= xAxis.granularity.value;
-                let xv = d || '空';
+                if(!d) {
+                    return '空';
+                }
+                let xv = d;
                 if(gv === 'halfYear') {
                     let arr = d.split('-H');
                     xv = arr[0] + ['上半年', '下半年'][arr[1] - 1]
@@ -247,7 +256,9 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
 
     let mediaOption = {
         baseOption: option,
-        media: EChartsMedia('line', legendVisible, dataZoomVisible)
+        media: EChartsMedia('line', legendVisible, dataZoomVisible, {
+            legend: option.legend
+        })
     }
     
     return mediaOption;
@@ -322,7 +333,9 @@ function pieOption(data, pieConfig, themeConfig, styleConfig) {
 
     let mediaOption = {
         baseOption: option,
-        media: EChartsMedia('pie')
+        media: EChartsMedia('pie', true, false, {
+            legend: option.legend
+        })
     }
 
     return mediaOption;
@@ -391,7 +404,9 @@ function scatterOption(data, scatterConfig, themeConfig, styleConfig) {
 
     let mediaOption = {
         baseOption: option,
-        media: EChartsMedia('scatter', legendVisible, dataZoomVisible)
+        media: EChartsMedia('scatter', legendVisible, dataZoomVisible, {
+            legend: option.legend
+        })
     }
     
     return mediaOption;
@@ -528,21 +543,24 @@ function dataViewOption(data, dataViewConfig, themeConfig, styleConfig) {
 }
 
 function indicatorOption(data, indicatorConfig, themeConfig, styleConfig) {
-    const { list } = data.valueList;
-    let { xAxis, yAxis, threshold } = indicatorConfig;
+    const { xAxis, yAxis, otherColumn, threshold } = indicatorConfig;
     let option = {
         originConfig: {
             xAxis,
             yAxis,
+            otherColumn,
             threshold
         },
-        page: 1,
-        pageSize: threshold,
-        data: list.map(l => ({
-            name: xAxis.column.type === 'time' ? (moment(l[xAxis.column.name]).isValid() ? moment(l[xAxis.column.name]).format('YYYY-MM-DD') : l[xAxis.column.name]) : l[xAxis.column.name],
+        themeConfig,
+        data: data.map(d => ({
+            name: xAxis.column.type === 'time' ? (moment(d.name).isValid() ? moment(d.name).format('YYYY-MM-DD') : d.name) : d.name,
             key: yAxis.column.label,
-            value: yAxis.column.type === 'time' ? (moment(l[yAxis.column.name]).isValid() ? moment(l[yAxis.column.name]).format('YYYY-MM-DD') : l[yAxis.column.name]) : l[yAxis.column.name]
-        })).slice(0, threshold)
+            value: yAxis.column.type === 'time' ? (moment(d.value).isValid() ? moment(d.value).format('YYYY-MM-DD') : d.value) : d.value,
+            others: otherColumn.map(c => ({
+                name: c.label,
+                value: d[c.value]
+            }))
+        }))
     };
 
     return option;