import { message } from 'antd';
import * as service from '../services/index';
import URLS from '../constants/url';
import parseChartOption from './parseChartOption';
import { deepAssign } from '../utils/baseUtils';
import STATISTICS_OPTION from '../components/chartDesigner/sections/statisticsOption.json';
import moment from 'moment';

function getBodyFilters(filters,drillDown) {
    let bodyFilters = [];
    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))) ||
        (type === 'categorical' &&
            (operator === 'contain' || operator === 'notContain' ?
                (value1 && value1.length > 0) : (!!value1)
            )
        )) {
            let bodyFilter = {
                columnName: name,
                columnType: type,
                symbol: operator,
                value: value1
            };
            if(type === 'scale' && operator === 'between') {
                bodyFilter['value'] = value1 + ',' + value2;
            }else if(type === 'time') {
                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 {
                    bodyFilter['value'] = v1;
                }
            }else if(type === 'categorical' && (operator === 'contain' || operator === 'notContain')) {
                bodyFilter['value'] = JSON.stringify(value1);
            }
            bodyFilters.push(bodyFilter);
        }
    });
    return bodyFilters;
}

export default {
    namespace: 'chartDesigner',
    state: {
        originData: {
            code: null,
            creatorCode: null,
            creatorName: null,
            header: { label: '无标题' },
            columns: [],
            defaultBarThreshold: 20,
            defaultLineThreshold: 200,
            defaultPieThreshold: 20,
            defaultScatterThreshold: 1000,
            defaultIndicatorThreshold: 6,
            baseConfig: { dataSource: { code: '' }, viewType: '' },
            aggregateTableConfig: { targetColumn: {}, statistics: [], groupBy: [] },
            dataViewConfig: { viewColumns: [], sortColumn: {key: ''}, sortType: 'asc' },
            barConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, sortTarget: 'x', sortType: 'ASC', threshold: 20, drillable: false, drillList: [{ column: {}, granularity:{} }] },
            lineConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 200, drillable: false, drillList: [{ column: {}, granularity:{} }] },
            pieConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, sortTarget: 'x', sortType: 'ASC', threshold: 20, drillable: false, drillList: [{ column: {}, granularity:{} }] },
            scatterConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 },
            indicatorConfig: { xAxis: { column: {} }, yAxis: { column: {}, gauge: {} }, otherColumn: [], threshold: 6 },
            theme: 'default',
            styleConfig: {},
            otherConfig:{},
            description: '',
            filters: [],
            chartOption: {},
            dirty: false,
            fetchConfig: {},
            demo: false,
            thumbnail: null,
        },
    },
    reducers: {
        /**
         * 更新model字段值
         * 1. 改变dirty
         */
        setField(state, action) {
            const { name, value } = action;
            let obj = {};
            obj[name] = value;
            return Object.assign({}, state, obj, { dirty: true });
        },
        /**
         * 批量更新model字段值
         * 1. 改变dirty
         */
        setFields(state, action) {
            const { fields } = action;
            let obj = {};
            fields.map(f => (obj[f.name] = f.value));
            return Object.assign({}, state, obj, { dirty: true });
        },
        /**
         * 更新model字段值
         * 1. 不改变dirty
         */
        silentSetField(state, action) {
            const { name, value } = action;
            let obj = {};
            obj[name] = value;
            let newState = Object.assign({}, state, obj);
            return newState;
        },
        /**
         * 批量更新model字段值
         * 1. 不改变dirty
         */
        silentSetFields(state, action) {
            const { fields } = action;
            let obj = {};
            fields.map(f => (obj[f.name] = f.value));
            let newState = Object.assign({}, state, obj);
            return newState;
        },
        reset(state, action) {
            let newState = Object.assign({}, state, state.originData);
            return Object.assign({}, newState);
        },
        setDirty(state, action) {
            const { dirty } = action;
            return Object.assign({}, state, { dirty });
        }
    },
    effects: {
        *changeField(action, { select, call, put }) {
            const { name, value } = action;
            yield put({ type: 'setField', name, value });

            const { autoRefresh } = action;
            if(autoRefresh === undefined ? true : autoRefresh) {
                yield put({ type: 'fetchChartData' });
            }
        },
        *silentChangeField(action, { select, call, put }) {
            const { name, value } = action;
            yield put({ type: 'silentSetField', name, value });
        },
        *changeFields(action, { select, call, put }) {
            const { fields } = action;
            yield put({ type: 'setFields', fields });

            const { autoRefresh } = action;
            if(autoRefresh === undefined ? true : autoRefresh) {
                yield put({ type: 'fetchChartData' });
            }
        },
        *silentChangeFields(action, { select, call, put }) {
            const { fields } = action;
            yield put({ type: 'silentSetFields', fields });
        },
        *changeDataSource(action, { select, call, put }) {
            const { dataSource } = action;
            const chartDesigner = yield select(state => state.chartDesigner);
            const { baseConfig } = chartDesigner;
            yield put({ type: 'changeField', name: 'baseConfig', value: { ...baseConfig, dataSource } });
            yield put({ type: 'remoteDataColumn', code: dataSource.code });
        },
        *initChangeDataSource(action, { select, call, put, take }) {
            const { dataSource } = action;
            const chartDesigner = yield select(state => state.chartDesigner);
            const { baseConfig } = chartDesigner;
            yield put({ type: 'silentChangeField', name: 'baseConfig', value: { ...baseConfig, dataSource } });
            yield put({ type: 'remoteDataColumn', code: dataSource.code });
            yield take('remoteDataColumn/@@end');
            let pageSize = ~~((document.getElementsByClassName('content-body')[0].offsetHeight - 12 * 2 - 40 - 24 - 8 * 2)/38) + 1;
            yield put({ type: 'fetchChartData', page: 1, pageSize });
        },
        *remoteQucikAdd(action, { select, call, put }) {
            try{
                const { dataSource, group } = action;

                yield put({ type: 'silentSetFields', fields: [
                    { name: 'baseConfig', value: { dataSource: dataSource.code, viewType: '' } }
                ] });
                let body = {
                    chartName: dataSource.name + '_未命名',
                    dataId: dataSource.code,
                    describes: '',
                    chartConfig: '{}',
                    chartType: '',
                    chartsGroup: group ? group.code : '-1',
                };
                yield put({ type: 'chart/setField', name: 'listLoading', value: true });
                const res = yield call(service.fetch, {
                    url: URLS.CHART_ADD,
                    body: body
                })
                yield put({ type: 'chart/setField', name: 'listLoading', value: false });

                if(res.data > 0) {
                    yield put({ type: 'chart/fetchList', mandatory: true });
                    yield put({ type: 'main/redirect', path: '/chart/' + res.data });
                    // yield put({ type: 'chart/remoteDetail', code: res.data });
                }else {
                    message.error('新增失败: ' + res.msg);
                }
            }catch(e) {
                message.error('新增失败: ' + e.message);
            }
        },
        /**
         * 复制新增
         */
        *remoteCopyAdd(action, { select, call, put }) {
            try{
                yield put({ type: 'chart/remoteModify' });
                const { newHeaderLabel } = action;
                const chartDesigner = yield select(state => state.chartDesigner);
                const { filters, baseConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig, theme } = chartDesigner;
                let body = {
                    filters: JSON.stringify(filters),
                    chartName: newHeaderLabel,
                    dataId: baseConfig.dataSource.code,
                    describes: description || '',
                    style: JSON.stringify(styleConfig),
                    otherConfig: JSON.stringify(otherConfig),
                    chartsGroup: group+'' ? group : '-1',
                    chartOption: JSON.stringify(chartOption),
                    fetchConfig: JSON.stringify(fetchConfig),
                    theme: theme
                }; // 基本属性
                if(!!baseConfig.viewType) {
                    let _CHART_TYPE = {
                        "bar": "Histogram",
                        "line": "Line",
                        "pie": "Pie",
                        "scatter": "scatter",
                        "aggregateTable": "population",
                        "dataView": "individual",
                        "indicator": "indicator"
                    };
                    body.chartType = _CHART_TYPE[baseConfig.viewType];
                    body.chartConfig = JSON.stringify(chartDesigner[`${baseConfig.viewType}Config`]);
                }

                const res = yield call(service.fetch, {
                    url: URLS.CHART_ADD,
                    body: body
                })
                if(res.code > 0) {
                    yield put({ type: 'chart/fetchList', mandatory: true });
                    yield put({ type: 'main/redirect', path: '/chart/' + res.data , reload: true});
                }else {
                    message.error('创建副本失败: ' + res.msg);
                }
            }catch(e) {
                message.error('创建副本失败: ' + e.message);
            }
        },
        *remoteDataColumn(action, { select, call, put, take }) {
            const code = action.code;
            try {
                const res = yield call(service.fetch, {
                    url: URLS.DATASOURCE_QUERY_DATACOLUMNS,
                    body: code
                });
                if(res.code > 0) {
                    let resData = res.data;

                    let columns = resData.map((c, i) => {
                        return {
                            key: i,
                            name: c.columnName,
                            label: c.columnRaname,
                            type: c.columnType,
                            groupable: c.isGroup==='1'?true:false,
                            filterable: c.isFilter==='1'?true:false,
                            bucketizable: c.isSubsection==='1'?true:false,
                            selection: []
                        }
                    })
                    yield put({ type: 'silentSetField', name: 'columns', value: columns });
                    yield put({ type: 'updateColumns' });
                    yield take('updateColumns/@@end');
                }else {
                    message.error('请求列数据失败:' + res.msg);
                    yield put({ type: 'silentSetField', name: 'columns', value: [] });
                }
            }catch(e) {
                message.error('请求列数据失败: ' + e.message);
                yield put({ type: 'silentSetField', name: 'columns', value: [] });
            }
        },
        // 因为数据源列字段可能发生重命名，需要替换到最新的列
        *updateColumns(action, { select, call, put }) {
            const chartDesigner = yield select(state => state.chartDesigner);
            const { columns, baseConfig } = chartDesigner;
            const { viewType } = baseConfig;
            try {
                if(viewType === 'bar') {
                    const { barConfig } = chartDesigner;
                    const { xAxis, yAxis, groupBy } = barConfig;
                    const newXAxisColumn = columns.find(c => c.name === xAxis.column.value);
                    const newYAxisColumn = columns.find(c => c.name === yAxis.column.value);
                    const newGroupByColumn = groupBy ? columns.find(c => c.name === groupBy.key) : null;
                    let fields = {};
                    fields.xAxis = newXAxisColumn ? { column: { value: newXAxisColumn.name, label: newXAxisColumn.label, type: newXAxisColumn.type }, granularity: xAxis.granularity } : {column:{}, granularity:{}};
                    fields.yAxis = newYAxisColumn ? { column: { value: newYAxisColumn.name, label: newYAxisColumn.label, type: newYAxisColumn.type }, gauge: yAxis.gauge } : {column:{}, gauge:{}};
                    fields.groupBy = newGroupByColumn ? { key: newGroupByColumn.name, label: newGroupByColumn.label, type: newGroupByColumn.type } : {key:''};
                    yield put({ type: 'silentSetField', name: 'barConfig', value: { ...barConfig, ...fields } });
                }else if(viewType === 'line') {
                    const { lineConfig } = chartDesigner;
                    const { xAxis, yAxis, groupBy } = lineConfig;
                    const newXAxisColumn = columns.find(c => c.name === xAxis.column.value);
                    const newYAxisColumn = columns.find(c => c.name === yAxis.column.value);
                    const newGroupByColumn = groupBy ? columns.find(c => c.name === groupBy.key) : null;
                    let fields = {};
                    fields.xAxis = newXAxisColumn ? { column: { value: newXAxisColumn.name, label: newXAxisColumn.label, type: newXAxisColumn.type }, granularity: xAxis.granularity } : {column:{}, granularity:{}};
                    fields.yAxis = newYAxisColumn ? { column: { value: newYAxisColumn.name, label: newYAxisColumn.label, type: newYAxisColumn.type }, gauge: yAxis.gauge } : {column:{}, gauge:{}};
                    fields.groupBy = newGroupByColumn ? { key: newGroupByColumn.name, label: newGroupByColumn.label, type: newGroupByColumn.type } : {key:''};
                    yield put({ type: 'silentSetField', name: 'lineConfig', value: { ...lineConfig, ...fields }});
                }else if(viewType === 'pie') {
                    const { pieConfig } = chartDesigner;
                    const { xAxis, yAxis } = pieConfig;
                    const newXAxisColumn = columns.find(c => c.name === xAxis.column.value);
                    const newYAxisColumn = columns.find(c => c.name === yAxis.column.value);
                    let fields = {};
                    fields.xAxis = newXAxisColumn ? { column: { value: newXAxisColumn.name, label: newXAxisColumn.label, type: newXAxisColumn.type }, granularity: xAxis.granularity } : {column: {}, granularity:{}};
                    fields.yAxis = newYAxisColumn ? { column: { value: newYAxisColumn.name, label: newYAxisColumn.label, type: newYAxisColumn.type }, gauge: yAxis.gauge } : {column:{}, gauge:{}};
                    yield put({ type: 'silentSetField', name: 'pieConfig', value: { ...pieConfig, ...fields } });
                }else if(viewType === 'scatter') {
                    const { scatterConfig } = chartDesigner;
                    const { xAxis, yAxis, groupBy } = scatterConfig;
                    const newXAxisColumn = columns.find(c => c.name === xAxis.column.value);
                    const newYAxisColumn = columns.find(c => c.name === yAxis.column.value);
                    const newGroupByColumn = groupBy ? columns.find(c => c.name === groupBy.key) : null;
                    let fields = {};
                    fields.xAxis = newXAxisColumn ? { column: { value: newXAxisColumn.name, label: newXAxisColumn.label, type: newXAxisColumn.type }, granularity: xAxis.granularity } : {column:{},granularity:{}};
                    fields.yAxis = newYAxisColumn ? { column: { value: newYAxisColumn.name, label: newYAxisColumn.label, type: newYAxisColumn.type }, gauge: yAxis.gauge } : {column:{}, gauge:{}};
                    fields.groupBy = newGroupByColumn ? { key: newGroupByColumn.name, label: newGroupByColumn.label, type: newGroupByColumn.type } : {key:''};
                    yield put({ type: 'silentSetField', name: 'scatterConfig', value: { ...scatterConfig, ...fields } });
                }else if(viewType === 'dataView') {
                    const { dataViewConfig } = chartDesigner;
                    const { sortColumn, viewColumns } = dataViewConfig;
                    let fields = {};
                    const newSortColumn = columns.find(c => c.name === sortColumn.key);
                    let arr = viewColumns.map(v => {
                        const newViewColumn = columns.find(c => c.name === v.name);
                        return newViewColumn ? newViewColumn : null;
                    }).filter(c => !!c);
                    fields.sortColumn = newSortColumn ? { label: newSortColumn.label, key: newSortColumn.name } : {key:''};
                    fields.viewColumns = arr;
                    yield put({ type: 'silentSetField', name: 'dataViewConfig', value: { ...dataViewConfig, ...fields } });
                }else if(viewType === 'aggregateTable') {
                    const { aggregateTableConfig } = chartDesigner;
                    const { groupBy, targetColumn } = aggregateTableConfig;
                    let fields = {};
                    const newTargetColumn = columns.find(c => c.name === targetColumn.name);
                    let arr = groupBy ? groupBy.map(g => {
                        const newGroupByColumn = columns.find(c => c.name === g.key);
                        return newGroupByColumn ? { key: newGroupByColumn.name, label: newGroupByColumn.label, type: newGroupByColumn.type } : null;
                    }).filter(c => !!c) : [];
                    fields.targetColumn = newTargetColumn ? { name: newTargetColumn.name, label: newTargetColumn.label, type: newTargetColumn.type } : {name:''};
                    fields.groupBy = arr;
                    yield put({ type: 'silentSetField', name: 'aggregateTableConfig', value: { ...aggregateTableConfig, ...fields } });
                }else if(viewType === 'indicator') {
                    const { indicatorConfig } = chartDesigner;
                    const { xAxis, yAxis, otherColumn, sortColumn } = indicatorConfig;
                    const newXAxisColumn = columns.find(c => c.name === xAxis.column.value);
                    const newYAxisColumn = columns.find(c => c.name === yAxis.column.value);
                    const newOtherColumns = otherColumn.filter(oc => !!columns.find(c => c.name === oc.value)).map(oc => {
                        let t = columns.find(c => c.name === oc.value);
                        return {
                            value: t.name,
                            label: t.label,
                            type: t.type
                        }
                    });
                    const newSortColumn = columns.find(c => c.name === sortColumn.key);
                    let fields = {};
                    fields.xAxis = newXAxisColumn ? { column: { value: newXAxisColumn.name, label: newXAxisColumn.label, type: newXAxisColumn.type } } : { column: {} };
                    fields.yAxis = newYAxisColumn ? { column: { value: newYAxisColumn.name, label: newYAxisColumn.label, type: newYAxisColumn.type }, gauge: yAxis.gauge } : {column:{}, gauge:{}};
                    fields.otherColumn = newOtherColumns;
                    fields.sortColumn = newSortColumn ? { key: newSortColumn.name, label: newSortColumn.label }: {key:''}
                    yield put({ type: 'silentSetField', name: 'indicatorConfig', value: { ...indicatorConfig, ...fields } });
                }
                const { filters } = chartDesigner;
                let newFilters = filters.map(f => {
                    const newFilterColumn = columns.find(c => c.name === f.name);
                    return newFilterColumn ? deepAssign(f, { label: newFilterColumn.label, type: newFilterColumn.type }) : null;
                }).filter(c => !!c);
                yield put({ type: 'silentSetField', name: 'filters', value: newFilters });
            }catch(e) {
                console.error(e.message);
            }
        },
        *fetchChartData(action, { select, call, put }) {
            const chartDesigner = yield select(state => state.chartDesigner);
            const { baseConfig } = chartDesigner;
            const { viewType } = baseConfig;
            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 &&
                        (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 &&
                        (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 &&
                        (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: {} });
                    }
                }else if(viewType === 'scatter') {
                    const { scatterConfig } = chartDesigner;
                    if(scatterConfig.xAxis.column.value && scatterConfig.yAxis.column.value) {
                        yield put({ type: 'fetchScatterData' });
                    }else {
                        yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                    }
                }else if(viewType === 'dataView') {
                    const { dataViewConfig } = chartDesigner;
                    if(dataViewConfig.viewColumns.length > 0) {
                        yield put({ type: 'fetchDataViewData', page, pageSize });
                    }else {
                        yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                    }
                }else if(viewType === 'aggregateTable') {
                    const { aggregateTableConfig } = chartDesigner;
                    if(aggregateTableConfig.targetColumn.name && aggregateTableConfig.statistics.length > 0) {
                        yield put({ type: 'fetchAggregateTableData' });
                    }else {
                        yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                    }
                }else if(viewType === 'indicator') {
                    const { indicatorConfig } = chartDesigner;
                    if(indicatorConfig.yAxis.column.value) {
                        yield put({ type: 'fetchIndicatorData' });
                    }else {
                        yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                    }
                }else {
                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                }
            }catch(e) {
                message.error('加载数据错误: ' + e.message);
            }
        },
        *fetchBarData(action, { select, call, put }) {
            try {
                const { inDashBoard, item } = action;
                let chartDesigner = yield select(state => state.chartDesigner);
                let dashboardDesigner = yield select(state => state.dashboardDesigner);
                let { code, barConfig, filters, theme, styleConfig, defaultBarThreshold } = chartDesigner;
                if(inDashBoard){
                    code = item.code;
                    filters = item.filters || [];
                    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] : [],
                    xAxis: {
                        columnRename: xAxis.column.value,
                        columnType: xAxis.column.type,
                        showDataType: xAxis.granularity.value
                    },
                    yAxis: {
                        columnRename: yAxis.column.value,
                        columnType: yAxis.column.type,
                        showDataType: yAxis.gauge.value
                    },
                    filters: getBodyFilters(filters),
                    sort: groupBy && groupBy.key ? undefined : (sortTarget ? (sortTarget === 'y' ? yAxis.column.value : xAxis.column.value) : xAxis.column.value),
                    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,
                    allow: true,
                    body: body,
                    timeout: 30000
                });
                let option;
                if(res.code > 0) {
                    if( inDashBoard ){
                        styleConfig = JSON.parse(res.data.chartsColumnConfig.chartStyle || '{}');
                    }
                    option = parseChartOption('bar', res.data, barConfig, theme, styleConfig.bar || {}, drillDown );
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                    if(!drillDown || drillDown.length===1 ){
                        yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
                    }
                }else {
                    option = {}
                    message.error('请求柱状图数据失败: ' + res.msg);
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: null },
                        { name: 'chartOption', value: {} },
                    ] });
                    yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                }
                return option
            }catch(e) {
                yield put({ type: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                message.error('请求柱状图数据失败: ' + e.message);
                return {}
            }
        },
        *fetchPieData(action, { select, call, put }) {
            try {
                const { inDashBoard, item, drillDown } = action;
                let chartDesigner = yield select(state => state.chartDesigner);
                let dashboardDesigner = yield select(state => state.dashboardDesigner);
                let { code, pieConfig, filters, theme, styleConfig, defaultPieThreshold } = chartDesigner;
                if(inDashBoard){
                    code = item.code;
                    filters = item.filters || [];
                    theme = dashboardDesigner.theme;
                    pieConfig = item.chartOption.baseOption.originConfig
                }

                const body = {
                    id: code,
                    legendData: {
                        columnRename: pieConfig.xAxis.column.value,
                        columnType: pieConfig.xAxis.column.type,
                        showDataType: pieConfig.xAxis.granularity.value
                    },
                    series: {
                        columnRename: pieConfig.yAxis.column.value,
                        columnType: pieConfig.yAxis.column.type,
                        columnName: pieConfig.yAxis.column.label,
                        showDataType: pieConfig.yAxis.gauge.value
                    },
                    filters: getBodyFilters(filters),
                    maxCount: pieConfig.threshold || defaultPieThreshold,
                    sort: pieConfig.sortTarget ? (pieConfig.sortTarget === 'y' ? pieConfig.yAxis.column.value : pieConfig.xAxis.column.value) : pieConfig.xAxis.column.value,
                    rule: pieConfig.sortType || 'ASC',
                };

                //钻取逻辑
                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,
                    allow: true,
                    timeout: 30000
                });
                let option;
                if(res.code > 0) {
                    if( inDashBoard ){
                        styleConfig = JSON.parse(res.data.chartsColumnConfig.chartStyle || '{}');
                    }
                    option = parseChartOption('pie', res.data, pieConfig, theme, styleConfig.pie || {}, drillDown );
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                    if(!drillDown || drillDown.length===1 ){
                        yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
                    }
                }else {
                    option = {}
                    message.error('请求饼图数据失败: ' + res.msg);
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: null },
                        { name: 'chartOption', value: {} },
                    ] });
                    yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                }
                return option
            }catch(e) {
                yield put({ type: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                message.error('请求饼图数据失败: ' + e.message);
                return {}
            }
        },
        *fetchLineData(action, { select, call, put }) {
            try {
                const chartDesigner = yield select(state => state.chartDesigner);
                let { code, lineConfig, filters, theme, styleConfig, defaultLineThreshold } = chartDesigner;
                const { inDashBoard, item } = action;
                let dashboardDesigner = yield select(state => state.dashboardDesigner);
                if(inDashBoard){
                    code = item.code;
                    filters = item.filters || [];
                    theme = dashboardDesigner.theme;
                    lineConfig = item.chartOption.baseOption.originConfig
                }
                const { drillDown } = action;
                
                const body = {
                    id: code,
                    xAxis: {
                        columnRename: lineConfig.xAxis.column.value,
                        columnType: lineConfig.xAxis.column.type,
                        showDataType: lineConfig.xAxis.granularity.value
                    },
                    yAxis: {
                        columnRename: lineConfig.yAxis.column.value,
                        columnType: lineConfig.yAxis.column.type,
                        showDataType: lineConfig.yAxis.gauge.value
                    },
                    groups: lineConfig.groupBy && lineConfig.groupBy.key ? [lineConfig.groupBy.key] : [],
                    filters: getBodyFilters(filters),
                    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,
                    allow: true,
                    timeout: 30000
                });
                let option;
                if(res.code > 0) {
                    if( inDashBoard ){
                        styleConfig = JSON.parse(res.data.chartsColumnConfig.chartStyle || '{}');
                    }
                    option = parseChartOption('line', res.data, lineConfig, theme, styleConfig.line || {}, drillDown );
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                    if(!drillDown || drillDown.length===1 ){
                        yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
                    }
                }else {
                    option = {}
                    message.error('请求折线图数据失败: ' + res.msg);
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: null },
                        { name: 'chartOption', value: {} },
                    ] });
                    yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                }
                return option
            }catch(e) {
                yield put({ type: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} });
                message.error('请求折线图数据失败: ' + e.message);
                return {}
            }
        },
        *fetchScatterData(action, { select, call, put }) {
            try {
                const chartDesigner = yield select(state => state.chartDesigner);
                const { code, scatterConfig, filters, theme, styleConfig, defaultScatterThreshold } = chartDesigner;
                const body = {
                    id: code,
                    xAxis: {
                        columnRename: scatterConfig.xAxis.column.value,
                        columnType: scatterConfig.xAxis.column.type
                    },
                    yAxis: {
                        columnRename: scatterConfig.yAxis.column.value,
                        columnType: scatterConfig.yAxis.column.type,
                        showDataType: scatterConfig.yAxis.gauge.value
                    },
                    groups: scatterConfig.groupBy && scatterConfig.groupBy.key ? [scatterConfig.groupBy.key] : [],
                    filters: getBodyFilters(filters),
                    maxCount: scatterConfig.threshold || defaultScatterThreshold
                };

                let res = yield call(service.fetch, {
                    url: URLS.CHART_SCATTER_OPTION,
                    body: body,
                    timeout: 30000
                });
                if(res.code > 0) {
                    let option = parseChartOption('scatter', res.data, scatterConfig, theme, styleConfig.scatter || {});
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                }else {
                    message.error('请求散点图数据失败: ' + res.msg);
                    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: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                message.error('请求散点图数据失败: ' + e.message);
            }
        },
        *fetchDataViewData(action, { select, call, put }) {
            try {
                const chartDesigner = yield select(state => state.chartDesigner);
                const { code, dataViewConfig, filters, theme, styleConfig } = chartDesigner;
                const { page, pageSize } = action;
                const body = {
                    id: code,
                    columnListName: dataViewConfig.viewColumns.map(c => c.name),
                    sortColumn: dataViewConfig.sortColumn ? dataViewConfig.sortColumn.key : dataViewConfig.viewColumns[0].name,
                    sort: dataViewConfig.sortType || 'asc',
                    filters: getBodyFilters(filters),
                    testPage: {
                        pageNum: page || 1,
                        pageSize: pageSize || 25,
                    }
                };

                let res = yield call(service.fetch, {
                    url: URLS.CHART_DATAVIEW_OPTION,
                    body: body,
                    timeout: 30000
                });
                if(res.code > 0) {
                    let option = parseChartOption('dataView', res.data, dataViewConfig, theme, styleConfig.dataView);
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                }else {
                    message.error('请求列表数据失败: ' + res.msg);
                    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: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                message.error('请求列表数据失败: ' + e.message);
            }
        },
        *fetchAggregateTableData(action, { select, call, put }) {
            try {
                const chartDesigner = yield select(state => state.chartDesigner);
                const { code, aggregateTableConfig, filters, theme, styleConfig } = chartDesigner;
                const { targetColumn, statistics: statisticsNames } = aggregateTableConfig;

                let statistics = statisticsNames.map(s => {
                    if(!s.name) {
                        let f = STATISTICS_OPTION.find(o => o.value === s);
                        return !!f ? {
                            name: f.value,
                            label: f.label
                        } : null;
                    }
                    return s
                }).filter(s => !!s);

                const body = {
                    id: code,
                    columnName: targetColumn.name,
                    operatorList: statistics.map(s => s.name || s),
                    groupByList: aggregateTableConfig.groupBy && aggregateTableConfig.groupBy.length > 0 ? aggregateTableConfig.groupBy.map(g => g.key) : [],
                    filters: getBodyFilters(filters),
                    testPage: {
                        pageNum: 1,
                        pageSize: 99,
                    }
                };

                let res = yield call(service.fetch, {
                    url: URLS.CHART_AGGREGATETABLE_OPTION,
                    body: body,
                    timeout: 30000
                });
                if(res.code > 0) {
                    let option = parseChartOption('aggregateTable', res.data, aggregateTableConfig, theme, styleConfig.aggregateTable || {});
                    yield put({ type: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                }else {
                    message.error('请求统计数据失败: ' + res.msg);
                    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: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                message.error('请求统计数据失败: ' + e.message);
            }
        },
        *fetchIndicatorData(action, { select, call, put }) {
            try {
                const chartDesigner = yield select(state => state.chartDesigner);
                const { code, indicatorConfig, filters, theme, styleConfig, defaultIndicatorThreshold } = chartDesigner;
                const { xAxis, yAxis, otherColumn, sortColumn, sortType, threshold } = indicatorConfig;
                const body = {
                    id: code,
                    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),
                    threshold: threshold || defaultIndicatorThreshold
                };

                let res = yield call(service.fetch, {
                    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: 'silentSetFields', fields: [
                        { name: 'resData', value: res.data },
                        { name: 'chartOption', value: option },
                    ] });
                }else {
                    message.error('请求指标数据失败: ' + res.msg);
                    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: 'silentSetFields', fields: [
                    { name: 'resData', value: null },
                    { name: 'chartOption', value: {} },
                ] });
                message.error('请求指标数据失败: ' + e.message);
            }
        },
        *getChartTableData(action, { select, call, put }) {
            try {
                const { chartCode, chartType, chartOption, filters } = action;
                let columns = [];
                let dataSource = [];
                const TimeType = {
                    year: '年',
                    halfYear: '半年',
                    quarter: '季度',
                    month: '月',
                    week: '周',
                    day: '日',
                };
                switch(chartType) {
                    case 'bar':
                    case 'line': {
                        const { baseOption, drillDown } = chartOption;
                        const { originConfig, xAxis, series } = baseOption;
                        const { xAxis: oxAxis, yAxis: oyAxis, groupBy: ogroupBy } = originConfig;
                        columns = [{
                            title: drillDown && drillDown.length > 0 ? `${drillDown[drillDown.length - 1].label}${drillDown[drillDown.length - 1].type === 'time' && drillDown[drillDown.length - 1].showDataType !== 'day' ? `(${TimeType[drillDown[drillDown.length - 1].showDataType]})` : ''}` : `${oxAxis.column.label}${oxAxis.granularity.value && oxAxis.granularity.value !== 'day' ? `(${oxAxis.granularity.label})` : ''}`,
                            dataIndex: 'xAxis',
                            type: 'string'
                        }, {
                            title: `${oyAxis.column.label}${oyAxis.gauge.value ? `(${oyAxis.gauge.label})` : ''}`,
                            dataIndex: 'yAxis',
                            type: 'scale'
                        }];
                        if(ogroupBy && ogroupBy.key) {
                            columns.push({
                                title: ogroupBy.label,
                                dataIndex: 'groupBy',
                                type: 'string'
                            });
                        }
                        xAxis[0].data.forEach((d, i) => {
                            if(ogroupBy && ogroupBy.key) {
                                series.forEach(xs => {
                                    dataSource.push({
                                        xAxis: d,
                                        yAxis: +xs.data[i],
                                        groupBy: xs.name
                                    });
                                })
                            }else {
                                dataSource.push({
                                    xAxis: d,
                                    yAxis: +series[0].data[i]
                                })
                            }
                        })
                        break;
                    }
                    case 'pie': {
                        const { baseOption, drillDown } = chartOption;
                        const { originConfig, series } = baseOption;
                        const { xAxis: oxAxis, yAxis: oyAxis } = originConfig;
                        columns = [{
                            // title: `${oxAxis.column.label}${oxAxis.granularity.value ? `(${oxAxis.granularity.label})` : ''}`,
                            title: drillDown && drillDown.length > 0 ? `${drillDown[drillDown.length - 1].label}${drillDown[drillDown.length - 1].type === 'time' && drillDown[drillDown.length - 1].showDataType !== 'day' ? `(${TimeType[drillDown[drillDown.length - 1].showDataType]})` : ''}` : `${oxAxis.column.label}${oxAxis.granularity.value && oxAxis.granularity.value !== 'day' ? `(${oxAxis.granularity.label})` : ''}`,
                            dataIndex: 'xAxis',
                            type: 'string'
                        }, {
                            title: `${oyAxis.column.label}${oyAxis.gauge.value ? `(${oyAxis.gauge.label})` : ''}`,
                            dataIndex: 'yAxis',
                            type: 'scale'
                        }];
                        dataSource = series[0].data.map(d => ({
                            xAxis: d.name,
                            yAxis: +d.value
                        }))
                        break;
                    }
                    case 'scatter': {
                        const { baseOption } = chartOption;
                        const { originConfig, series } = baseOption;
                        const { xAxis: oxAxis, yAxis: oyAxis, groupBy: ogroupBy } = originConfig;
                        columns = [{
                            title: oxAxis.column.label,
                            dataIndex: 'xAxis',
                            type: 'scale'
                        }, {
                            title: `${oyAxis.column.label}${oyAxis.gauge.value ? `(${oyAxis.gauge.label})` : ''}`,
                            dataIndex: 'yAxis',
                            type: 'scale'
                        }];
                        if(ogroupBy && ogroupBy.key) {
                            columns.push({
                                title: ogroupBy.label,
                                dataIndex: 'groupBy',
                                type: 'string'
                            });
                        }
                        series.forEach(xs => {
                            xs.data.forEach(xd => {
                                if(ogroupBy && ogroupBy.key) {
                                    dataSource.push({
                                        xAxis: xd[0],
                                        yAxis: +xd[1],
                                        groupBy: xs.name
                                    });
                                }else {
                                    dataSource.push({
                                        xAxis: xd[0],
                                        yAxis: +xd[1]
                                    });
                                }
                            });
                        })
                        break;
                    }
                    case 'dataView': {
                        const { originConfig } = chartOption;
                        const { viewColumns } = originConfig;
                        columns = viewColumns.map(c => ({
                            title: c.label,
                            dataIndex: c.name,
                            type: 'string'
                        }));
                        const body = {
                            id: chartCode,
                            columnListName: viewColumns.map(c => c.name),
                            sortColumn: originConfig.sortColumn ? originConfig.sortColumn.key : originConfig.viewColumns[0].name,
                            sort: originConfig.sortType || 'asc',
                            filters: getBodyFilters(filters),
                            testPage: {
                                pageNum: 1,
                                pageSize: 9999999,
                            }
                        };
        
                        let res = yield call(service.fetch, {
                            url: URLS.CHART_DATAVIEW_OPTION,
                            body: body,
                            timeout: 30000
                        });
                        if(res.code > 0) {
                            res.data.valueList.list.forEach(l => {
                                let r = {};
                                viewColumns.forEach(c => {
                                    let v = l[c.name];
                                    if(c.type === 'time') {
                                        v = moment(v).isValid() ? moment(v).format('YYYY-MM-DD'): v;
                                    }else if(c.type === 'scale') {
                                        v = +v;
                                    }
                                    r[c.name] = v;
                                });
                                dataSource.push(r);
                            })
                        }else {
                            console.error(res.msg);
                        }
                        break;
                    }
                    case 'aggregateTable': {
                        const { originConfig, data, group1Name, group2Name, group1s, group2s } = chartOption;
                        const { groupBy, statistics: statisticsNames } = originConfig;

                        let statistics = statisticsNames.map(s => {
                            if(!s.name) {
                                let f = STATISTICS_OPTION.find(o => o.value === s);
                                return !!f ? {
                                    name: f.value,
                                    label: f.label
                                } : null;
                            }
                            return s
                        }).filter(s => !!s);

                        columns = groupBy.map((g, i) => ({ title: g.label, dataIndex: `groupBy${i+1}`, type: 'string' })).concat(statistics.map(s => ({ title: s.label, dataIndex: s.name, type: 'scale' })));

                        if(!group1Name) { // 无分组
                            let obj = {};
                            statistics.forEach(s => {
                                obj[s.name] = +data[s.name];
                            });
                            dataSource.push(obj);
                        }else {
                            if(!group2Name) { // 只有一个分组
                                dataSource = group1s.map((g, i) => {
                                    let obj = {};
                                    obj['groupBy1'] = g;
                                    statistics.forEach(s => {
                                        obj[s.name] = +data[i][s.name];
                                    });
                                    return obj;
                                });
                            }else { // 有两个分组
                                group1s.forEach((g1, i1) => {
                                    let d = data[i1];
                                    group2s.forEach((g2, i2) => {
                                        let obj = {};
                                        obj['groupBy1'] = g1;
                                        obj['groupBy2'] = g2;
                                        statistics.forEach(s => {
                                            obj[s.name] = +d.data[i2][s.name];
                                        })
                                        dataSource.push(obj);
                                    })
                                });
                            }
                        }
                        break;
                    }
                    case 'indicator': {
                        let { originConfig, data } = chartOption;
                        let { xAxis, yAxis, otherColumn } = (originConfig || {});
                        columns = [{
                            title: xAxis.column.label,
                            dataIndex: 'xAxis',
                            type: xAxis.column.type
                        }, {
                            title: `${yAxis.column.label}${yAxis.gauge.value ? `(${yAxis.gauge.label})` : ''}`,
                            dataIndex: 'yAxis',
                            type: yAxis.column.type
                        }].concat(otherColumn.map((c, i) => ({
                            title: c.label,
                            dataIndex: `other${i}`,
                            type: c.type
                        })));
                        dataSource = data.map(d => {
                            let obj = {};
                            obj['xAxis'] = d.name;
                            obj['yAxis'] = +d.value;
                            d.others.forEach((o, i) => {
                                obj[`other${i}`] = o.value;
                            })
                            return obj;
                        });
                        break;
                    }
                    default:
                        break;
                }
                return { columns, dataSource};
            }catch(e) {
                message.error('解析数据失败: ' + e.message);
                return { columns: [], dataSource: []}
            }
        },
    },
    subscriptions: {
        setup({ dispatch, history }) {
            dispatch({ type: 'reset' });  
        },
    },
};