Browse Source

总体统计图展示组件优化

zhuth 6 years ago
parent
commit
9e240eaa1c

+ 133 - 23
src/components/chartDesigner/charts/aggregateTableView.jsx

@@ -2,50 +2,157 @@
  * 总体统计图
  * 总体统计图
  */
  */
 import React from 'react'
 import React from 'react'
+import { message } from 'antd'
 import EmptyContent from '../../common/emptyContent/index'
 import EmptyContent from '../../common/emptyContent/index'
 import './aggregateTableView.less'
 import './aggregateTableView.less'
-
 class AggregateTableView extends React.Component {
 class AggregateTableView extends React.Component {
     constructor(props) {
     constructor(props) {
         super(props);
         super(props);
         this.state = {}
         this.state = {}
     }
     }
 
 
-    getTableHeader = () => {
+    getTableBody = () => {
         const { chartOption } = this.props;
         const { chartOption } = this.props;
-        const { statistics, direction } = chartOption;
-        return <thead>
+        const { group1Name, group2Name } = chartOption;
+
+        try{
+            if(!!group1Name) {
+                if(!!group2Name) {
+                    return this.createTableBodyInTwoGroups(chartOption);
+                }else {
+                    return this.createTableBodyInOneGroups(chartOption);
+                }
+            }else {
+                return this.createTableBodyInNoGroups(chartOption);
+            }
+        }catch(e) {
+            console.error(e.message);
+            message.error('总体统计图渲染错误');
+            return null;
+        }
+    }
+
+    // 无分组
+    createTableBodyInNoGroups = (chartOption) => {
+        const { targetColumn, direction, group1Name, group2Name, group1s, group2s, statistics, data } = chartOption;
+        if(direction === 'vertical') {
+            return <tbody>
+                <tr>
+                    <th rowSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+                </tr>
+                {
+                    statistics.map((s, i) => <tr key={i}>
+                        <td>{s.label}</td>
+                        <td>{data[s.name]}</td>
+                    </tr>)
+                }
+            </tbody>
+        }
+        return <tbody>
+            <tr>
+                <th colSpan={`${statistics.length}`}>{targetColumn.label}</th>
+            </tr>
+            <tr>
+                { statistics.map((s, i) => <td key={i}>{s.label}</td>) }
+            </tr>
             <tr>
             <tr>
-                <th colSpan={direction === 'vertical' ? '2' : statistics.length + ''} style={{ textAlign: 'center' }}>{chartOption.targetColumn.label}</th>
+                { statistics.map((s, i) => <td key={i}>{data[s.name]}</td>) }
             </tr>
             </tr>
-        </thead>
+        </tbody>;
     }
     }
-    getTableBody = () => {
-        const { chartOption } = this.props;
-        const { statistics, direction } = chartOption;
+
+    // 有一个分组
+    createTableBodyInOneGroups = (chartOption) => {
+        const { targetColumn, direction, group1Name, group2Name, group1s, group2s, statistics, data } = chartOption;
         if(direction === 'vertical') {
         if(direction === 'vertical') {
             return <tbody>
             return <tbody>
-                {statistics.map((s, i) => (
-                    <tr key={i}>
+                <tr>
+                    <td colSpan="2"></td>
+                    { group1s.map((g, i) => <td key={i}>{g}</td>) }
+                </tr>
+                <tr>
+                    <th rowSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+                </tr>
+                {
+                    statistics.map((s, si) => <tr key={si}>
                         <td>{s.label}</td>
                         <td>{s.label}</td>
-                        <td>{Number(Number(s.value).toFixed(4))}</td>
-                    </tr>
-                ))}
+                        { group1s.map((g1, gi) => <td key={gi}>{data[gi][s.name]}</td>) }
+                    </tr>)
+                }
             </tbody>
             </tbody>
-        }else {
+        }
+        return <tbody>
+            <tr>
+                <th colSpan={`${statistics.length + 1}`}>{targetColumn.label}</th>
+            </tr>
+            <tr>
+                <td></td>
+                {
+                    statistics.map((s, i) => <td key={i}>{s.label}</td>)
+                }
+            </tr>
+            {
+                group1s.map((g, gi) => <tr key={gi}>
+                    <td>{g}</td>
+                    {
+                        statistics.map((s, si) => <td key={si}>{data[gi][s.name]}</td>)
+                    }
+                </tr>)
+            }
+        </tbody>
+    }
+
+    // 有两个分组
+    createTableBodyInTwoGroups = (chartOption) => {
+        const { targetColumn, direction, group1Name, group2Name, group1s, group2s, statistics, data } = chartOption;
+        if(direction === 'vertical') {
             return <tbody>
             return <tbody>
                 <tr>
                 <tr>
-                    {statistics.map((s, i) => (
-                        <td key={i}>{s.label}</td>
-                    ))}
+                    <td colSpan="3"></td>
+                    {group1s.map((g, i) => <td key={i}>{g}</td>)}
                 </tr>
                 </tr>
                 <tr>
                 <tr>
-                    {statistics.map((s, i) => (
-                        <td key={i}>{Number(Number(s.value).toFixed(4))}</td>
-                    ))}
+                    <th rowSpan={`${group2s.length * statistics.length + 1}`}>{targetColumn.label}</th>
                 </tr>
                 </tr>
+                {
+                    group2s.map((g2, idx2) => {
+                        return statistics.map((s, i) => <tr key={i}>
+                            { i === 0 && <td rowSpan={`${statistics.length}`}>{g2}</td>}
+                            <td>{s.label}</td>
+                            {group1s.map((g1, idx1) => {
+                                let values = data[idx1].data[idx2];
+                                return <td key={idx1}>{values[s.name]}</td>
+                            })}
+                        </tr>)
+                    })
+                }
             </tbody>
             </tbody>
         }
         }
+        return <tbody>
+            <tr>
+                <th colSpan={direction === 'vertical' ? group1s.length + 3 : statistics.length * group2s.length + 1}>{targetColumn.label}</th>
+            </tr>
+            <tr>
+                <td rowSpan={2}></td>
+                { group2s.map((g, i) => <td colSpan={statistics.length} key={i}>{g}</td>) }
+            </tr>
+            <tr>
+                { group2s.map((g, i) => statistics.map(s => <td key={i}>{s.label}</td>)) }
+            </tr>
+            {
+                data.map((d1, d1i) => {
+                    let data2 = d1.data;
+                    return <tr key={d1i}>
+                        <td>{ d1[group1Name] }</td>
+                        { data2.map(d2 => {
+                                return statistics.map((s, si) => {
+                                    return <td key={si}>{d2[s.name]}</td>
+                                })
+                        }) }
+                    </tr>
+                })
+            }
+        </tbody>
     }
     }
 
 
     render() {
     render() {
@@ -53,10 +160,13 @@ class AggregateTableView extends React.Component {
         if(!chartOption || !chartOption.targetColumn || !chartOption.statistics || chartOption.statistics.length === 0) {
         if(!chartOption || !chartOption.targetColumn || !chartOption.statistics || chartOption.statistics.length === 0) {
             return <EmptyContent />
             return <EmptyContent />
         }
         }
+        let body = this.getTableBody();
+        if(!body) {
+            return <EmptyContent />
+        }
         return <div ref={ node => this.formRef = node } className='aggregate-container'>
         return <div ref={ node => this.formRef = node } className='aggregate-container'>
             <table className={`aggregate-table`} border='1'>
             <table className={`aggregate-table`} border='1'>
-                { this.getTableHeader() }
-                { this.getTableBody() }
+                { body }
             </table>
             </table>
         </div>
         </div>
     }
     }

+ 26 - 5
src/components/chartDesigner/sections/aggregateTableConfigForm.jsx

@@ -2,7 +2,7 @@
  * 总体统计数据表
  * 总体统计数据表
  */
  */
 import React from 'react';
 import React from 'react';
-import { Form, Select, Checkbox, Row, Col } from 'antd';
+import { Form, Select, Checkbox, Row, Col, Tooltip, message } from 'antd';
 import { connect } from 'dva';
 import { connect } from 'dva';
 import STATISTICS_OPTION from './statisticsOption.json'
 import STATISTICS_OPTION from './statisticsOption.json'
 const FormItem = Form.Item;
 const FormItem = Form.Item;
@@ -62,12 +62,25 @@ class AggregateTableConfigForm extends React.Component {
 						}}
 						}}
 					>
 					>
 						<Row>
 						<Row>
-							{STATISTICS_OPTION.map((s)=>{return <Col span={12} key={s.value}><Checkbox disabled={s.columnType.indexOf(aggregateTableConfig.targetColumn.type) === -1} value={s.value}>{s.label}</Checkbox></Col>})}
+							{
+								STATISTICS_OPTION.map((s)=>{
+									let inGroupDisabled = s.value === 'dictinctCount' && aggregateTableConfig.groupBy.length > 0;
+									let disabled = s.columnType.indexOf(aggregateTableConfig.targetColumn.type) === -1 ||
+									(inGroupDisabled)
+									return <Tooltip title={inGroupDisabled ? '存在分组时[条数]即为[去重条数]' : ''} key={s.value}><Col span={12} key={s.value}>
+										<Checkbox disabled={disabled}
+											value={s.value}
+										>
+											{s.label}
+										</Checkbox>
+									</Col></Tooltip>
+								})
+							}
 						</Row>
 						</Row>
 						
 						
 					</CheckboxGroup>
 					</CheckboxGroup>
 				</FormItem>
 				</FormItem>
-				{/* <FormItem label='分组' {...formItemLayout}>
+				<FormItem label='分组' {...formItemLayout}>
 					<Select
 					<Select
 						mode='multiple'
 						mode='multiple'
 						labelInValue={true}
 						labelInValue={true}
@@ -78,13 +91,21 @@ class AggregateTableConfigForm extends React.Component {
 						filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 						filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 						onChange={(values) => {
 						onChange={(values) => {
 							const groupBy = [];
 							const groupBy = [];
+							if(values.length > 2) {
+								message.warning('最多支持选择两个分组');
+								return false;
+							}
+							let statisticsArr = chartDesigner.aggregateTableConfig.statistics;
+							if(values.length > 0) {
+								statisticsArr = statisticsArr.filter(s => s !== 'dictinctCount');
+							}
 							values.forEach(value => {
 							values.forEach(value => {
 								const column = value.key ? columns.find(c => c.name === value.key) : null;
 								const column = value.key ? columns.find(c => c.name === value.key) : null;
 								const g = column ? { ...value, type: column.type } : undefined;
 								const g = column ? { ...value, type: column.type } : undefined;
 								groupBy.push(g);
 								groupBy.push(g);
 							});
 							});
 							
 							
-							dispatch({ type: 'chartDesigner/changeField', name: 'aggregateTableConfig', value: { ...props.chartDesigner.aggregateTableConfig, groupBy }, autoRefresh });
+							dispatch({ type: 'chartDesigner/changeField', name: 'aggregateTableConfig', value: { ...props.chartDesigner.aggregateTableConfig, groupBy, statistics: statisticsArr }, autoRefresh });
 						}}
 						}}
 						value={chartDesigner.aggregateTableConfig.groupBy}
 						value={chartDesigner.aggregateTableConfig.groupBy}
 					>
 					>
@@ -92,7 +113,7 @@ class AggregateTableConfigForm extends React.Component {
 							return (<Option key={i} value={c.name}>{c.label}</Option>)
 							return (<Option key={i} value={c.name}>{c.label}</Option>)
 						})}
 						})}
 					</Select>
 					</Select>
-				</FormItem> */}
+				</FormItem>
 			</Form>
 			</Form>
 		);
 		);
 	}
 	}

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

@@ -14,21 +14,6 @@ class AggregateTable extends React.Component {
                 labelCol: { span: 8 },
                 labelCol: { span: 8 },
                 wrapperCol: { span: 16 },
                 wrapperCol: { span: 16 },
             },
             },
-            backgroundColor: '#ffffff', // 背景
-            markTextColor: '#eeeeee', // 标签文字
-            colors: [
-                "#c23531",
-                "#2f4554",
-                "#61a0a8",
-                "#d48265",
-                "#91c7ae",
-                "#749f83",
-                "#ca8622",
-                "#bda29a",
-                "#6e7074",
-                "#546570",
-                "#c4ccd3"
-            ], // 主题
         }
         }
     }
     }
 
 
@@ -48,7 +33,7 @@ class AggregateTable extends React.Component {
                         { name: 'chartOption', value: { ...chartOption, direction: value } },
                         { name: 'chartOption', value: { ...chartOption, direction: value } },
                     ] });
                     ] });
                 }}
                 }}
-                    defaultValue={aggregateTableStyle.direction || 'vertical'}
+                    defaultValue={aggregateTableStyle.direction || 'horizontal'}
                 >
                 >
                     <Radio value='vertical'>垂直</Radio>
                     <Radio value='vertical'>垂直</Radio>
                     <Radio value='horizontal'>水平</Radio>
                     <Radio value='horizontal'>水平</Radio>

+ 5 - 0
src/components/common/filterBox/filterBox.less

@@ -1,3 +1,8 @@
+.filter-box {
+  .ant-modal-footer {
+    border: none;
+  }
+}
 .ant-form-item {
 .ant-form-item {
   margin-bottom: 0;
   margin-bottom: 0;
 }
 }

+ 0 - 1
src/models/chart.js

@@ -299,7 +299,6 @@ export default {
                         data.scatterConfig = chartConfig;
                         data.scatterConfig = chartConfig;
                     }else if(viewType === 'aggregateTable') {
                     }else if(viewType === 'aggregateTable') {
                         data.aggregateTableConfig = chartConfig;
                         data.aggregateTableConfig = chartConfig;
-                        data.aggregateTableConfig.groupBy = [];
                     }else if(viewType === 'dataView') {
                     }else if(viewType === 'dataView') {
                         data.dataViewConfig = chartConfig;
                         data.dataViewConfig = chartConfig;
                     }
                     }

+ 66 - 11
src/models/parseChartOption.js

@@ -632,24 +632,79 @@ function scatterOption(data, scatterConfig, themeConfig, styleConfig) {
 }
 }
 
 
 function aggregateTableOption( data, aggregateTableConfig, themeConfig, styleConfig) {
 function aggregateTableOption( data, aggregateTableConfig, themeConfig, styleConfig) {
-    const resData = data.valueList;
-    const { targetColumn, statistics: statisticsNames } = aggregateTableConfig;
+    const { targetColumn, groupBy, statistics: statisticsNames } = aggregateTableConfig;
     const { direction } = styleConfig;
     const { direction } = styleConfig;
 
 
-    
-    let statistics = STATISTICS_OPTION.filter(o => statisticsNames.indexOf(o.value) !== -1);
+    let statistics = STATISTICS_OPTION.filter(o => statisticsNames.indexOf(o.value) !== -1).map(s => ({
+            name: s.value,
+            label: s.label
+        })),
+        group1Name = groupBy.length > 0 ? groupBy[0].key : null,
+        group2Name = groupBy.length > 1 ? groupBy[1].key : null,
+        group1s = [],
+        group2s = [],
+        tableData;
+    const resData = data.valueList;
+
+
+
+    if(!group1Name) { // 无分组
+        tableData = resData;
+    }else {
+        if(!group2Name) { // 只有一个分组
+            resData.forEach(d => {
+                let v = d[group1Name];
+                if(group1s.indexOf(v) === -1) {
+                    group1s.push(v);
+                }
+            });
+            tableData = resData;
+        }else { // 有两个分组
+            resData.forEach(d => {
+                let v1 = d[group1Name];
+                let v2 = d[group2Name];
+                if(group1s.indexOf(v1) === -1) {
+                    group1s.push(v1);
+                }
+                if(group2s.indexOf(v2) === -1) {
+                    group2s.push(v2);
+                }
+            });
+            tableData = group1s.map(g => {
+                let obj = {},
+                    list = resData.filter(d => d[group1Name] === g)
+                    // .slice(0, 5);
+                
+                
+                obj[group1Name] = g;
+                obj['data'] = group2s.map(g => {
+                    let o = {};
+                    o[group2Name] = g;
+                    statistics.forEach(s => {
+                        let v = list.find(l => l[group2Name] === g);
+                        o[s.name] = v ? (typeof v[s.name] === 'number' ? v[s.name] : '') : '';
+                    });
+                    return o;
+                });
+                return obj;
+            });
+
+        }
+    }
 
 
     let option = {
     let option = {
         themeConfig,
         themeConfig,
-        targetColumn: targetColumn,
-        statistics: statistics.map(s => ({
-            name: s.value,
-            label: s.label,
-            value: resData[s.value]
-        })),
-        direction: direction || 'vertical'
+        targetColumn,
+        direction,
+        group1Name: groupBy.length > 0 ? groupBy[0].key : null,
+        group2Name: groupBy.length > 1 ? groupBy[1].key : null,
+        group1s,
+        group2s,
+        statistics,
+        data: tableData
     };
     };
 
 
+    console.log(option);
     return option;
     return option;
 }
 }