Browse Source

钻取代码提交

hy 6 years ago
parent
commit
15fa546633

+ 59 - 0
src/components/chartDesigner/charts/drillDownBar.jsx

@@ -0,0 +1,59 @@
+import React, { Component } from 'react';
+import { connect } from 'dva'
+import { Breadcrumb } from 'antd';
+import './drillDownBar.less';
+
+class DrillDownBar extends Component {
+    render() { 
+        const { drillDown } = this.props
+        return ( 
+            <Breadcrumb className="drillDownBar"
+                separator=">"
+            > 
+            {drillDown.map((g,index) => (
+                <Breadcrumb.Item 
+                    key={g.value+index} 
+                    onClick={( drillDown.length!==index+1 )?this.itemClick:null }
+                    className={( drillDown.length!==index+1 )?'linkItem':'lastItem'}
+                >
+                    <span data-index={index}>{( index===0 && drillDown.length!==index+1 )?g.field:g.value}</span>
+                </Breadcrumb.Item>
+            ))}
+            </Breadcrumb> 
+        );
+    }
+    itemClick = (e) => {
+        //绑定的drillDown的index
+        let index = e.target.getAttribute('data-index');
+        const { dispatch, inDashBoard, item } = this.props;
+        let { drillDown } = this.props;
+        //从第一层开始到点击处的层级的数组
+        drillDown = drillDown.slice(0,Number(index)+1)
+        let page = null;
+        let pageSize = null;
+
+        if(inDashBoard){
+            //调用对应的图表类型的接口
+            let { chartType } = item
+            let type = `${chartType.charAt(0).toUpperCase()}${chartType.slice(1,chartType.length)}`
+            dispatch({ 
+                type: 'dashboardDesigner/setItemFetching', code: item.code, fetching: true 
+            })
+            dispatch({ 
+                type: `chartDesigner/fetch${type}Data`, page, pageSize, drillDown, inDashBoard, item 
+            }).then(option => {
+                dispatch({ 
+                    type: 'dashboardDesigner/setItemFetching', code: item.code, fetching: false 
+                })
+                dispatch({ type: 'dashboardDesigner/setItemFields', code: item.code, fields: [
+                    { name: 'chartOption', value: option }
+                ]})
+            })
+            
+        }else{
+            dispatch({ type: 'chartDesigner/fetchChartData', page, pageSize, drillDown });
+        }
+    }
+}
+ 
+export default connect()(DrillDownBar);

+ 10 - 0
src/components/chartDesigner/charts/drillDownBar.less

@@ -0,0 +1,10 @@
+.drillDownBar{
+    margin:0px 0px 0px 15px;
+}
+.linkItem {
+    color: #4fb5cedd;
+    cursor: pointer;
+}
+.lastItem {
+    cursor: context-menu;
+}

+ 285 - 11
src/components/chartDesigner/charts/echartsView.jsx

@@ -1,17 +1,291 @@
-import React from 'react'
+import React, { Component,Fragment } from 'react';
 import Echarts from '../../common/echarts/index'
+import DrillDownBar from '../charts/drillDownBar'
 import { hashcode } from '../../../utils/baseUtils'
 import EmptyContent from '../../common/emptyContent'
+import { connect } from 'dva'
+import { Icon,message } from 'antd';
+import moment from 'moment';
+moment.locale('zh-cn');
+class EchartsView extends Component {
+    render() { 
+        const { chartOption, inDashBoard, item } = this.props;
+        if(!chartOption || ((!chartOption.series || chartOption.series.length === 0) && (!chartOption.baseOption || !chartOption.baseOption.series || chartOption.baseOption.series.length === 0))) {
+            return <EmptyContent />
+        }else {
+            const { baseOption } = chartOption;
+            //drillDown 钻取层级数组
+            let { drillDown } = chartOption;
+            const { originConfig } = baseOption;
+            //drillable 是否开启钻取 boolean
+            //drillList 钻取配置数组
+            const { drillList, drillable } = originConfig;
 
-const EchartsView = ({ chartOption }) => {
-    if(!chartOption || ((!chartOption.series || chartOption.series.length === 0) && (!chartOption.baseOption || !chartOption.baseOption.series || chartOption.baseOption.series.length === 0))) {
-        return <EmptyContent />
-    }else {
-        return <Echarts
-            key={hashcode(chartOption)}
-            option={chartOption}
-        />
+            if(!drillDown && drillable && drillList && drillList.length>0 ){
+                //初始化drillDown
+                let baseDrill = drillList[0].column;
+                let granularity = drillList[0].granularity;
+                drillDown = chartOption.drillDown = [{
+                    label : baseDrill.label,
+                    field : baseDrill.value,
+                    type : baseDrill.type,
+                    filter : {},
+                    showDataType : baseDrill.type==='time'?granularity.value:'',
+                    value : null 
+                }]
+            }
+
+            return (
+                <Fragment>
+                    {drillable?
+                        (
+                            <div style={{display:'flex'}}>
+                                <Icon type="gold" style={{margin:'0 0 0 15px',fontSize:'20px'}} theme="twoTone" />
+                                {
+                                    /* 
+                                     *  在钻取开启后 drillDown会生成钻取数组 默认为第一层
+                                     *  DrillDownBar组件为钻取层级工具栏
+                                     */
+                                    <DrillDownBar inDashBoard={inDashBoard} item={item} key={hashcode(drillDown)} drillDown={drillDown}></DrillDownBar>
+                                }
+                            </div>
+                        )
+                    :null}
+                    <Echarts
+                        key={hashcode(chartOption)}
+                        option={chartOption}
+                        onEvents={drillable?this.onClickByModelEvents:null}
+                    />
+                </Fragment>
+            ) 
+        }
     }
-}
+    onByModelClick(e){
+        // 钻取(下钻)
+        const { chartOption, dispatch, inDashBoard, item } = this.props;
+        let { drillDown } = chartOption;
+        const { baseOption } = chartOption;
+        const { originConfig } = baseOption;
+        const { drillList } = originConfig;
+        // 若下钻层级已到达钻取配置最后一层 提示信息
+        if( drillList.length === drillDown.length ){
+            message.warning('已经钻取到最后一层了!');
+            return false;
+        }
+        //拿到上一层级的钻取配置
+        let preDrill = drillList.slice(drillDown.length-1,drillDown.length)[0];
+        //拿到当前层级的钻取配置
+        let curDrill = drillList.slice(drillDown.length,drillDown.length+1)[0];
+        //将本次钻取的数据加入drillDown中
+        switch(e.componentSubType){
+            case 'bar':{
+                drillDown.push({
+                    label : curDrill.column.label,
+                    field : curDrill.column.value,
+                    type : curDrill.column.type,
+                    filter : this.getFilter(preDrill,e),
+                    value : e.name,
+                    showDataType : curDrill.column.type==='time'?curDrill.granularity.value:''
+                });
+                break;
+            }
+            case 'line':{
+                drillDown.push({
+                    label : curDrill.column.label,
+                    field : curDrill.column.value,
+                    type : curDrill.column.type,
+                    filter : this.getFilter(preDrill,e),
+                    value : e.name,
+                    showDataType : curDrill.column.type==='time'?curDrill.granularity.value:''
+                });
+                break;
+            }
+            case 'pie':{
+                //饼图如果是其它 并且tooMany(后台判断有 其他 部分) 为 true 时 不能下钻
+                if( e.name==='其它' && chartOption.baseOption.originConfig.tooMany ){
+                    message.warning('其它类型无法钻取');
+                    return false;
+                }
+                drillDown.push({
+                    label : curDrill.column.label,
+                    field : curDrill.column.value,
+                    type : curDrill.column.type,
+                    filter : this.getFilter(preDrill,e),
+                    value : e.name,
+                    showDataType : curDrill.column.type==='time'?curDrill.granularity.value:''
+                });
+                break;
+            }
+            default:{
+                return false;
+            }
+        }
+        
+        let page = null;
+        let pageSize = null;
+
+        if(inDashBoard){
+            //调用对应的图表类型的接口
+            let { chartType } = item
+            let type = `${chartType.charAt(0).toUpperCase()}${chartType.slice(1,chartType.length)}`
+            dispatch({ 
+                type: 'dashboardDesigner/setItemFetching', code: item.code, fetching: true 
+            })
+            dispatch({ 
+                type: `chartDesigner/fetch${type}Data`, page, pageSize, drillDown, inDashBoard, item 
+            }).then(option => {
+                dispatch({ 
+                    type: 'dashboardDesigner/setItemFetching', code: item.code, fetching: false 
+                })
+                dispatch({ type: 'dashboardDesigner/setItemFields', code: item.code, fields: [
+                    { name: 'chartOption', value: option }
+                ]})
+            })
+            
+        }else{
+            dispatch({ type: 'chartDesigner/fetchChartData', page, pageSize, drillDown });
+        }
+    }
+
+    onClickByModelEvents = {
+        'click': this.onByModelClick.bind(this)
+    }
+
+    getFilter(curDrill,e){
+        const { column } = curDrill;
+        if( e.name === '空'){
+            return {
+                name: column.value,
+                label: column.label,
+                type: column.type,
+                operator: 'isNull',
+                operatorLabel: '为空',
+                value1: e.name,
+                value2: e.name,
+            }
+        }
+        switch(column.type){
+            case 'categorical':{
+                return {
+                    name: column.value,
+                    label: column.label,
+                    type: column.type,
+                    operator: '=',
+                    operatorLabel: '等于',
+                    value1: e.name,
+                    value2: e.name,
+                }
+            }
+            case 'time':{
+                //解析时间
+                const { granularity } = curDrill;
+                let values = this.getTimeByType(granularity,e.name);
+                return {
+                    name: column.value,
+                    label: column.label,
+                    type: column.type,
+                    operator: values.operator,
+                    operatorLabel: values.operatorLabel,
+                    value1: values.value1,
+                    value2: values.value2,
+                    drillDownType: granularity.value
+                }
+            }
+            default:{
+                return {};
+            }
+        }
+    }
+
+    getTimeByType(granularity,value){
+        //解析时间
+        switch(granularity.value){
+            case 'year':{
+                return {
+                    value1: `${value}`,
+                    value2: '',
+                    operator: '=',
+                    operatorLabel: '等于'
+                }
+            }
+            case 'halfYear':{
+                //解析value
+                //可能有 (- 上半年) 的情况
+                let year = value.split(' ')[0];
+                let value1,value2;
+                if(value.indexOf('上半年')>-1){
+                    value1 = `${year}-01-01`
+                    value2 = `${year}-06-30`
+                }else{
+                    value1 = `${year}-07-01`
+                    value2 = `${year}-12-31`
+                }
+                return {
+                    value1: value1,
+                    value2: value2,
+                    operator: 'between',
+                    operatorLabel: '介于'
+                }
+            }
+            case 'quarter':{
+                //解析value
+                let to_number = {
+                    '一':1,
+                    '二':2,
+                    '三':3,
+                    '四':4
+                }
+                let quarter = to_number[value.split(' ')[1].charAt(0)];
+                let year = value.split(' ')[0];
+
+                return {
+                    value1: `${year}-${quarter}`,
+                    value2: '',
+                    operator: '=',
+                    operatorLabel: '等于'
+                }
+            }
+            case 'month':{
+                //解析value
+                let month = value.split('/')[1];
+                let year = value.split('/')[0];
+
+                /* 对月数进行格式化 */
+                if(month < 10){
+                    month = '0' + month
+                } 
 
-export default EchartsView;
+                return {
+                    value1: moment(`${year}-${month}-01`).format('YYYY-MM'),
+                    value2: '',
+                    operator: '=',
+                    operatorLabel: '等于'
+                }
+            }
+            case 'week':{
+                //解析value
+                let week = value.replace(' ','-').split('周')[0];
+
+                return {
+                    value1: week,
+                    value2: '',
+                    operator: '=',
+                    operatorLabel: '等于'
+                }
+            }
+            case 'day':{
+                return {
+                    value1: moment(`${value}`).format('YYYY-MM-DD'),
+                    value2: '',
+                    operator: '=',
+                    operatorLabel: '等于'
+                }
+            }
+            default:{
+                return {};
+            }
+        }
+    }
+}
+ 
+export default connect()(EchartsView);

+ 3 - 3
src/components/chartDesigner/sections/granularity.json

@@ -7,12 +7,12 @@
     }, {
         "value": "halfYear",
         "label": "半年"
-    }, {
-        "value": "month",
-        "label": "月"
     }, {
         "value": "quarter",
         "label": "季度"
+    }, {
+        "value": "month",
+        "label": "月"
     }, {
         "value": "week",
         "label": "周"

+ 2 - 1
src/components/common/echarts/index.jsx

@@ -10,9 +10,10 @@ import 'echarts/lib/component/legend';
 import 'echarts/lib/component/legendScroll';
 import 'echarts/lib/component/dataZoom';
 
-const Echarts = ({ option }) => <ReactEchartsCore
+const Echarts = ({ option,onEvents }) => <ReactEchartsCore
     echarts={echarts}
     option={option}
+    onEvents={onEvents}
     className='rc-echarts'
     style={{height: '100%'}}
 />

+ 1 - 1
src/components/dashboardDesigner/chartView.jsx

@@ -69,7 +69,7 @@ class ChartView extends React.Component {
                         }}
                     />);
                 }else if(['line', 'bar', 'pie', 'scatter'].indexOf(chartType) > -1) {
-                    children = (<EchartsView key={`${chartCode}-${layout.w}-${layout.h}`} chartOption={chartOption}/>);
+                    children = (<EchartsView item={item} inDashBoard={true} key={`${chartCode}-${layout.w}-${layout.h}`} chartOption={chartOption}/>);
                 }else if(chartType === 'indicator') {
                     children = (<IndicatorView key={`${chartCode}-${hashcode(chartOption)}-${layout.w}-${layout.h}`} chartOption={chartOption}/>);
                 }

+ 141 - 18
src/models/chartDesigner.js

@@ -6,9 +6,9 @@ import { deepAssign } from '../utils/baseUtils';
 import STATISTICS_OPTION from '../components/chartDesigner/sections/statisticsOption.json';
 import moment from 'moment';
 
-function getBodyFilters(filters) {
+function getBodyFilters(filters,drillDown) {
     let bodyFilters = [];
-    filters.filter(f => f.using).forEach(f => {
+    filters.forEach(f => {
         let { name, operator, type, value1, value2 } = f;
         if(((type === 'index' || type === 'string') && !!value1) || // 因为数字类型会生成数字字符串,所以为0也是可以正常传入条件的
         ((type === 'scale' || type === 'time' || type === 'ordinal') && (operator === 'between' ? (!!value1 && !!value2) : (!!value1))) ||
@@ -29,6 +29,12 @@ function getBodyFilters(filters) {
                 let v1 = value1.dynamic ? value1.name : moment(value1).format('YYYY-MM-DD');
                 let v2 = value2.dynamic ? value2.name : moment(value2).format('YYYY-MM-DD');
     
+                if(drillDown){
+                    v1 = value1;
+                    v2 = value2;
+                    bodyFilter['type'] = f.drillDownType;
+                }
+
                 if(operator === 'between') {
                     bodyFilter['value'] = v1 + ',' + v2;
                 }else {
@@ -396,28 +402,40 @@ export default {
             const chartDesigner = yield select(state => state.present.chartDesigner);
             const { baseConfig } = chartDesigner;
             const { viewType } = baseConfig;
-            const { page, pageSize } = action;
+            const { page, pageSize, drillDown } = action;
 
             try{
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                 if(viewType === 'bar') {
                     const { barConfig } = chartDesigner;
-                    if(barConfig.xAxis.column.value && barConfig.yAxis.column.value) {
-                        yield put({ type: 'fetchBarData' });
+                    if(barConfig.xAxis.column.value && barConfig.yAxis.column.value &&
+                        barConfig.drillable?barConfig.drillList.filter(item=>{
+                            return Object.keys(item.column).length===0
+                        }).length===0:true) {
+                        //开启了钻取 并且钻取配置不能有空的配置 否则不显示图表    
+                        yield put({ type: 'fetchBarData', drillDown });
                     }else {
                         yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                     }
                 }else if(viewType === 'pie') {
                     const { pieConfig } = chartDesigner;
-                    if(pieConfig.xAxis.column.value && pieConfig.yAxis.column.value) {
-                        yield put({ type: 'fetchPieData' });
+                    if(pieConfig.xAxis.column.value && pieConfig.yAxis.column.value &&
+                        pieConfig.drillable?pieConfig.drillList.filter(item=>{
+                            return Object.keys(item.column).length===0
+                        }).length===0:true) {
+                        //开启了钻取 并且钻取配置不能有空的配置 否则不显示图表   
+                        yield put({ type: 'fetchPieData', drillDown });
                     }else {
                         yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                     }
                 }else if(viewType === 'line') {
                     const { lineConfig } = chartDesigner;
-                    if(lineConfig.xAxis.column.value && lineConfig.yAxis.column.value) {
-                        yield put({ type: 'fetchLineData' });
+                    if(lineConfig.xAxis.column.value && lineConfig.yAxis.column.value &&
+                        lineConfig.drillable?lineConfig.drillList.filter(item=>{
+                            return Object.keys(item.column).length===0
+                        }).length===0:true) {
+                        //开启了钻取 并且钻取配置不能有空的配置 否则不显示图表 
+                        yield put({ type: 'fetchLineData', drillDown });
                     }else {
                         yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                     }
@@ -450,7 +468,7 @@ export default {
                         yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                     }
                 }else {
-                    console.log('no viewType......')
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 message.error('加载数据错误: ' + e.message);
@@ -458,9 +476,19 @@ export default {
         },
         *fetchBarData(action, { select, call, put }) {
             try {
-                const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { code, barConfig, filters, theme, styleConfig, defaultBarThreshold } = chartDesigner;
+                const { inDashBoard, item } = action;
+                let chartDesigner = yield select(state => state.present.chartDesigner);
+                let dashboardDesigner = yield select(state => state.present.dashboardDesigner);
+                let { code, barConfig, filters, theme, styleConfig, defaultBarThreshold } = chartDesigner;
+                if(inDashBoard){
+                    code = item.code;
+                    filters = item.filters || [];
+                    styleConfig = item.styleConfig;
+                    theme = dashboardDesigner.theme;
+                    barConfig = item.chartOption.baseOption.originConfig
+                }
                 const { groupBy, xAxis, yAxis, sortTarget, sortType, threshold } = barConfig;
+                const { drillDown } = action;
                 const body = {
                     id: code,
                     groups: groupBy && groupBy.key ? [groupBy.key] : [],
@@ -479,19 +507,42 @@ export default {
                     rule: groupBy && groupBy.key ? undefined : (sortType || 'ASC'),
                     maxCount: threshold || defaultBarThreshold
                 };
+
+                //钻取逻辑
+                if(drillDown){
+                    let curDrillDown = drillDown.slice(-1)[0];
+                    body.sort = groupBy && groupBy.key ? undefined : (sortTarget ? (sortTarget === 'y' ? yAxis.column.value : curDrillDown.field) : curDrillDown.field);
+                    body.xAxis.columnRename = curDrillDown.field;
+                    body.xAxis.columnType = curDrillDown.type;
+                    //如果是时间类型 需要替换showDataType
+                    if(curDrillDown.type==='time'){
+                        body.xAxis.showDataType = curDrillDown.showDataType
+                    }
+                    //加上所有层级过滤条件
+                    let drillDownFilters = [];
+                    drillDown.forEach(f => {
+                        //空对象的filter过滤掉
+                        if( f.filter && Object.keys(f.filter).length !== 0 ){
+                            drillDownFilters.push(f.filter)
+                        }
+                    });
+                    body.filters.push(...getBodyFilters( drillDownFilters, true ));
+                }
                 
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_BAR_OPTION,
                     body: body,
                     timeout: 30000
                 });
+                let option;
                 if(res.code > 0) {
-                    let option = parseChartOption('bar', res.data, barConfig, theme, styleConfig.bar || {});
+                    option = parseChartOption('bar', res.data, barConfig, theme, styleConfig.bar || {}, drillDown );
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: res.data },
                         { name: 'chartOption', value: option },
                     ] });
                 }else {
+                    option = {}
                     message.error('请求柱状图数据失败: ' + res.msg);
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: null },
@@ -499,18 +550,31 @@ export default {
                     ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
+                return option
             }catch(e) {
                 yield put({ type: 'silentSetFields', fields: [
                     { name: 'resData', value: null },
                     { name: 'chartOption', value: {} },
                 ] });
                 message.error('请求柱状图数据失败: ' + e.message);
+                return {}
             }
         },
         *fetchPieData(action, { select, call, put }) {
             try {
-                const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { code, pieConfig, filters, theme, styleConfig, defaultPieThreshold } = chartDesigner;
+                const { inDashBoard, item } = action;
+                let chartDesigner = yield select(state => state.present.chartDesigner);
+                let dashboardDesigner = yield select(state => state.present.dashboardDesigner);
+                let { code, pieConfig, filters, theme, styleConfig, defaultPieThreshold } = chartDesigner;
+                if(inDashBoard){
+                    code = item.code;
+                    filters = item.filters || [];
+                    styleConfig = item.styleConfig;
+                    theme = dashboardDesigner.theme;
+                    pieConfig = item.chartOption.baseOption.originConfig
+                }
+                const { drillDown } = action;
+
                 const body = {
                     id: code,
                     legendData: {
@@ -528,18 +592,40 @@ export default {
                     maxCount: pieConfig.threshold || defaultPieThreshold
                 };
 
+                //钻取逻辑
+                if(drillDown){
+                    let curDrillDown = drillDown.slice(-1)[0];
+                    body.legendData.columnRename = curDrillDown.field;
+                    body.legendData.columnType = curDrillDown.type;
+                    //如果是时间类型 需要替换showDataType
+                    if(curDrillDown.type==='time'){
+                        body.legendData.showDataType = curDrillDown.showDataType
+                    }
+                    //加上所有层级过滤条件
+                    let drillDownFilters = [];
+                    drillDown.forEach(f => {
+                        //空对象的filter过滤掉
+                        if( f.filter && Object.keys(f.filter).length !== 0 ){
+                            drillDownFilters.push(f.filter)
+                        }
+                    });
+                    body.filters.push(...getBodyFilters( drillDownFilters, true ));
+                }
+
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_PIE_OPTION,
                     body: body,
                     timeout: 30000
                 });
+                let option;
                 if(res.code > 0) {
-                    let option = parseChartOption('pie', res.data, pieConfig, theme, styleConfig.pie || {});
+                    option = parseChartOption('pie', res.data, pieConfig, theme, styleConfig.pie || {}, drillDown );
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: res.data },
                         { name: 'chartOption', value: option },
                     ] });
                 }else {
+                    option = {}
                     message.error('请求饼图数据失败: ' + res.msg);
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: null },
@@ -547,18 +633,31 @@ export default {
                     ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
+                return option
             }catch(e) {
                 yield put({ type: 'silentSetFields', fields: [
                     { name: 'resData', value: null },
                     { name: 'chartOption', value: {} },
                 ] });
                 message.error('请求饼图数据失败: ' + e.message);
+                return {}
             }
         },
         *fetchLineData(action, { select, call, put }) {
             try {
                 const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { code, lineConfig, filters, theme, styleConfig, defaultLineThreshold } = chartDesigner;
+                let { code, lineConfig, filters, theme, styleConfig, defaultLineThreshold } = chartDesigner;
+                const { inDashBoard, item } = action;
+                let dashboardDesigner = yield select(state => state.present.dashboardDesigner);
+                if(inDashBoard){
+                    code = item.code;
+                    filters = item.filters || [];
+                    styleConfig = item.styleConfig;
+                    theme = dashboardDesigner.theme;
+                    lineConfig = item.chartOption.baseOption.originConfig
+                }
+                const { drillDown } = action;
+                
                 const body = {
                     id: code,
                     xAxis: {
@@ -576,18 +675,40 @@ export default {
                     maxCount: lineConfig.threshold || defaultLineThreshold
                 };
 
+                //钻取逻辑
+                if(drillDown){
+                    let curDrillDown = drillDown.slice(-1)[0];
+                    body.xAxis.columnRename = curDrillDown.field;
+                    body.xAxis.columnType = curDrillDown.type;
+                    //如果是时间类型 需要替换showDataType
+                    if(curDrillDown.type==='time'){
+                        body.xAxis.showDataType = curDrillDown.showDataType
+                    }
+                    //加上所有层级过滤条件
+                    let drillDownFilters = [];
+                    drillDown.forEach(f => {
+                        //空对象的filter过滤掉
+                        if( f.filter && Object.keys(f.filter).length !== 0 ){
+                            drillDownFilters.push(f.filter)
+                        }
+                    });
+                    body.filters.push(...getBodyFilters( drillDownFilters, true ));
+                }
+
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_LINE_OPTION,
                     body: body,
                     timeout: 30000
                 });
+                let option;
                 if(res.code > 0) {
-                    let option = parseChartOption('line', res.data, lineConfig, theme, styleConfig.line || {});
+                    option = parseChartOption('line', res.data, lineConfig, theme, styleConfig.line || {}, drillDown );
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: res.data },
                         { name: 'chartOption', value: option },
                     ] });
                 }else {
+                    option = {}
                     message.error('请求折线图数据失败: ' + res.msg);
                     yield put({ type: 'silentSetFields', fields: [
                         { name: 'resData', value: null },
@@ -595,12 +716,14 @@ export default {
                     ] });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
+                return option
             }catch(e) {
                 yield put({ type: 'silentSetFields', fields: [
                     { name: 'resData', value: null },
                     { name: 'chartOption', value: {} },
                 ] });
                 message.error('请求折线图数据失败: ' + e.message);
+                return {}
             }
         },
         *fetchScatterData(action, { select, call, put }) {

+ 3 - 2
src/models/dashboardDesigner.js

@@ -504,10 +504,11 @@ export default {
 
             try {
                 yield put({ type: 'setItemFetching', code: chartCode, fetching: true });
+                const itemFilters = getTrueFilters(item, filters);
                 const body = {
                     dashboardCreatorId: creatorCode,
                     chartId: chartCode,
-                    filters: getBodyFilters(getTrueFilters(item, filters)),
+                    filters: getBodyFilters(itemFilters),
                     testPage: {
                         pageNum: page|| 1,
                         pageSize: chartType === 'indicator' ? 99 : pageSize || 99, 
@@ -533,8 +534,8 @@ export default {
                     const chartConfig = JSON.parse(chartConfigStr);
                     const styleConfig = JSON.parse(styleConfigStr);
                     let chartOption = parseChartOption(chartType, resData, chartConfig, theme, styleConfig[chartType] || {});
-                    
                     yield put({ type: 'setItemFields', code: chartCode, fields: [
+                        { name: 'filters', value: itemFilters },
                         { name: 'chartType', value: chartType },
                         { name: 'chartOption', value: chartOption }
                     ] });

+ 47 - 12
src/models/parseChartOption.js

@@ -8,7 +8,7 @@ import STATISTICS_OPTION from '../components/chartDesigner/sections/statisticsOp
 import themes from '../components/chartDesigner/sections/style/theme/index';
 import EChartsMedia from './EChartsMedia';
 
-export default function(viewType, data, chartConfig, themeName, styleConfig) {
+export default function(viewType, data, chartConfig, themeName, styleConfig, drillDown) {
     if(!data) {
         return {};
     }
@@ -19,17 +19,17 @@ export default function(viewType, data, chartConfig, themeName, styleConfig) {
         switch(viewType) {
             case 'bar': {
                 themeConfig = deepAssign({}, theme.base, theme.bar, theme.xAxis, theme.yAxis, theme.dataZoom);
-                o = barOption(data, chartConfig, themeConfig, styleConfig);
+                o = barOption(data, chartConfig, themeConfig, styleConfig, drillDown);
                 break;
             }
             case 'pie': {
                 themeConfig = deepAssign({}, theme.base, theme.pie);
-                o = pieOption(data, chartConfig, themeConfig, styleConfig);
+                o = pieOption(data, chartConfig, themeConfig, styleConfig, drillDown);
                 break;
             }
             case 'line': {
                 themeConfig = deepAssign({}, theme.base, theme.line, theme.xAxis, theme.yAxis, theme.dataZoom);
-                o = lineOption(data, chartConfig, themeConfig, styleConfig);
+                o = lineOption(data, chartConfig, themeConfig, styleConfig, drillDown);
                 break;
             }
             case 'scatter': {
@@ -61,17 +61,27 @@ export default function(viewType, data, chartConfig, themeName, styleConfig) {
     }
 }
 
-function barOption(data, barConfig, themeConfig, styleConfig) {
+function barOption(data, barConfig, themeConfig, styleConfig, drillDown) {
     const { xAxis, yAxis, groupBy } = barConfig;
     const { barMaxWidth, barMinHeight, barGap, stack, labelVisible, labelPosition, labelDistance,
         labelRotate, xNameLocation, xNameGap, xNameRotate, xLabelHiddenCover, xLabelRotate, dataZoomVisible,
         xLabelMargin, yNameLocation, yNameGap, yNameRotate, labelZeroVisible } = styleConfig;
     let xTitle = xAxis?`${xAxis.column.label}`:null
     let yTitle = yAxis?`${yAxis.column.label}`:null
+    let xGranularityV = xAxis?`${xAxis.granularity.value}`:null
     let hasGroupBy = !!groupBy && !!groupBy.key;
     let legendVisible = hasGroupBy;
     data.serieses = data.serieses || [];
 
+    //钻取逻辑
+    if(drillDown){
+        let curDrillDown = drillDown.slice(-1)[0];
+        xTitle = xAxis?`${curDrillDown.label}`:null
+        if(curDrillDown.type==='time'){
+            xGranularityV = curDrillDown.showDataType
+        }
+    }
+
     let option = deepAssign({
         originConfig: {
             ...barConfig
@@ -96,8 +106,8 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
                 margin: (xLabelMargin === '' || xLabelMargin === null || xLabelMargin === undefined) ? 8 : Number(xLabelMargin),
             },
             data: data.xAxis.map(d => {
-                let gv= xAxis.granularity.value;
-                if(!d) {
+                let gv= xGranularityV;
+                if( !d && d === null ) {
                     return '空';
                 }
                 let xv = d;
@@ -154,6 +164,7 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
     }, themeConfig);
 
     let mediaOption = {
+        drillDown: drillDown,
         baseOption: option,
         media: EChartsMedia('bar', legendVisible, dataZoomVisible, {
             legend: option.legend
@@ -163,7 +174,7 @@ function barOption(data, barConfig, themeConfig, styleConfig) {
     return mediaOption;
 }
 
-function lineOption(data, lineConfig, themeConfig, styleConfig) {
+function lineOption(data, lineConfig, themeConfig, styleConfig, drillDown) {
     const { labelSymbol, xNameLocation, xNameGap, xNameRotate, xLabelRotate, xLabelMargin, xLabelHiddenCover,
         yNameLocation, yNameGap, yNameRotate, stack, labelVisible, labelPosition, labelDistance, labelRotate,
         lineSmooth, labelSymbolSize, dataZoomVisible } = styleConfig;
@@ -172,8 +183,18 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
     let yTitle = yAxis?`${yAxis.column.label}`:null
     let hasGroupBy = !!groupBy && !!groupBy.key;
     let legendVisible = hasGroupBy;
+    let xGranularityV = xAxis.granularity.value;
     data.serieses = data.serieses || [];
 
+    //钻取逻辑
+    if(drillDown){
+        let curDrillDown = drillDown.slice(-1)[0];
+        xTitle = xAxis?`${curDrillDown.label}`:null
+        if(curDrillDown.type==='time'){
+            xGranularityV = curDrillDown.showDataType
+        }
+    }
+
     let option = deepAssign({
         originConfig: {
             ...lineConfig
@@ -199,7 +220,7 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
                 margin: (xLabelMargin === '' || xLabelMargin === null || xLabelMargin === undefined) ? 8 : Number(xLabelMargin),
             },
             data: data.xAxis.map(d => {
-                let gv= xAxis.granularity.value;
+                let gv= xGranularityV;
                 if(!d) {
                     return '空';
                 }
@@ -253,6 +274,7 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
     }, themeConfig);
 
     let mediaOption = {
+        drillDown: drillDown,
         baseOption: option,
         media: EChartsMedia('line', legendVisible, dataZoomVisible, {
             legend: option.legend
@@ -262,14 +284,26 @@ function lineOption(data, lineConfig, themeConfig, styleConfig) {
     return mediaOption;
 }
 
-function pieOption(data, pieConfig, themeConfig, styleConfig) {
+function pieOption(data, pieConfig, themeConfig, styleConfig, drillDown) {
     let { labelHidden } = styleConfig;
     let { xAxis, yAxis } = pieConfig;
+    let { tooMany } = data;
     let columnName = xAxis.column.label;
     let dataList = (data.serieses || [{ value: [] }])[0].value;
+    let xGranularityV = pieConfig.xAxis.granularity.value;
+
+    //钻取逻辑
+    if(drillDown){
+        let curDrillDown = drillDown.slice(-1)[0];
+        if(curDrillDown.type==='time'){
+            xGranularityV = curDrillDown.showDataType
+        }
+    }
+
     let option = deepAssign({
         originConfig: {
-            ...pieConfig
+            ...pieConfig,
+            tooMany
         },
         tooltip : {
             trigger: 'item',
@@ -313,7 +347,7 @@ function pieOption(data, pieConfig, themeConfig, styleConfig) {
                 if(!v.name) {
                     obj.name = '空'
                 }else {
-                    let gv= pieConfig.xAxis.granularity.value;
+                    let gv= xGranularityV;
                     if(gv === 'halfYear') {
                         let arr = v.name.split('-H');
                         obj.name = `${arr[0] || '-'} ${['上半年', '下半年'][arr[1] - 1]}`;
@@ -334,6 +368,7 @@ function pieOption(data, pieConfig, themeConfig, styleConfig) {
     }, themeConfig);
 
     let mediaOption = {
+        drillDown: drillDown,
         baseOption: option,
         media: EChartsMedia('pie', true, false, {
             legend: option.legend