import { message } from 'antd'
import * as service from '../services/index'
import parseChartOption from './parseChartOption'
import moment from 'moment'
import URLS from '../constants/url'
import CHART_TYPE from './chartType.json'
import { ArrayEquals } from '../utils/baseUtils.js'
import Exportor from '../utils/exportor'

/**
 * 获得报表中图表的真实过滤规则
 */
function getTrueFilters(item, filters) {
    let trueFilters = [];
    filters.forEach(f => {
        const { type, operator, 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)
            )
        )) {
            if(f.operator === 'betweent' ? ( !!f.value1 && (f.value1.length ? f.value1.length > 0 : true) && !!f.value2 && (f.value2.length ? f.value2.length > 0 : true)) : (!!f.value1 && (f.value1.length ? f.value1.length > 0 : true))) {
                if(f.combined) {
                    f.dataSource && f.dataSource.forEach(d => {
                        if(d.dataSource.code === item.dataSourceCode) {
                            trueFilters.push({
                                dataSourceCode: d.dataSource.code,
                                name: d.column.name,
                                operator: f.operator,
                                type: f.type,
                                value1: f.value1,
                                value2: f.value2,
                                using: f.using
                            });
                        }
                    });
                }else {
                    if(f.dataSource.code === item.dataSourceCode) {
                        trueFilters.push({
                            dataSourceCode: f.dataSource.code,
                            name: f.name,
                            operator: f.operator,
                            type: f.type,
                            value1: f.value1,
                            value2: f.value2,
                            using: f.using
                        });
                    }
                }
            }
        }
    });
    return trueFilters;
}

function getBodyFilters(filters) {
    return filters.filter(f => f.using).map(f => {
        let { dataSourceCode, name, operator, type, value1, value2 } = f;
        let bodyFilter = {
            dataSourceCode,
            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(operator === 'between') {
                bodyFilter['value'] = v1 + ',' + v2;
            }else {
                bodyFilter['value'] = v1;
            }
        }else if(type === 'categorical' && (operator === 'contain' || operator === 'notContain')) {
            bodyFilter['value'] = JSON.stringify(value1);
        }
        return bodyFilter;
    }); 
}

const _maxLayoutW = 12;

export default {
    namespace: 'dashboardDesigner',
    state: {
        originData: {
            code: null,
            name: '无标题',
            theme: 'default',
            minLayoutHeight: 40, // 元素最小高度
            maxLayoutW: _maxLayoutW,
            layoutMargin: [8, 8], // 元素margin
            defaultLayout: { x: 0, y: 50, w: _maxLayoutW, h: 6, minW: 2, maxW: _maxLayoutW, minH: 1 },
            items: [],
            chartCodes: [], // 报表包含的所有图表
            description: '',
            thumbnail: '',
            dirty: false,
            editMode: false,
            filterColumns: [],
            filters: [],
            dataSources: [], // 图表关联的所有数据源
            relationColumns: [], // 自定义的列
            columnFetching: false,
            loading: false,
            shareCode: '', // 分享码
            demo: false,
            filterItems: [ // 可选过滤字段（选择图表时用）
                { name: 'name', label: '图表名称', type: 'string' },
                { name: 'description', label: '说明', type: 'string' },
                { name: 'creatorName', label: '创建人', type: 'string' },
                { name: 'createTime', label: '创建时间', type: 'date' },
            ],
            filterItem: { name: 'name', label: '图表名称', type: 'string' }, // 已选过滤字段（选择图表时用）
            styleConfig: {
                aggregateTable: { direction: ['vertical', 'horizontal'][0] },
            },
        },
    },
    
    reducers: {
        silentSetField(state, action) {
            const { name, value } = action;
            let obj = {};
            obj[name] = value;
            return Object.assign({}, state, obj);
        },
        setField(state, action) {
            const { name, value } = action;
            let obj = {};
            obj[name] = value;
            let newState = Object.assign({}, state, obj);
            return Object.assign({}, newState, {dirty: true});
        },
        silentSetFields(state, action) {
            const { fields } = action;
            let obj = {};
            fields.map(f => (obj[f.name] = f.value));
            let newState = Object.assign({}, state, obj);
            return newState;
        },
        setFields(state, action) {
            const { fields } = action;
            let obj = {};
            fields.map(f => (obj[f.name] = f.value));
            let newState = Object.assign({}, state, obj);
            return Object.assign({}, newState, {dirty: true});
        },
        setFilterItem(state, action) {
            const { item } = action;
            return Object.assign({}, state, {filterItem: item, filterLabel: ''});
        },
        setFilterLabel(state, action) {
            const { label } = action;
            return Object.assign({}, state, {filterLabel: label});
        },
        addChart(state, action) {
            let { items, dataSources, chartCodes, defaultLayout } = state;
            const { chart } = action;

            items = items.concat([{
                code: chart.code,
                chartCode: chart.code,
                name: chart.name,
                creatorCode: chart.creatorCode,
                creatorName: chart.creatorName,
                dataSourceCode: chart.dataSourceCode+'',
                dataSourceName: chart.dataSourceName,
                dataConnectCode: chart.dataConnectCode,
                dataConnectName: chart.dataConnectName,
                viewType: 'chart',
                chartType: chart.type,
                filters: chart.filters,
                layout: { ...defaultLayout },
                theme: chart.theme,
                styleConfig: chart.styleConfig,
                chartOption: chart.type === 'dataView' ? {
                    total: 0,
                    page: 1,
                    pageSize: 0, // 设为0以保证在tableView组件渲染时getTableLayout方法中总能触发取数
                    columns: [],
                    dataSource: []
                } : null
            }]);
            chartCodes.push(chart.code);
            dataSources.findIndex(d => d.code === chart.dataSourceCode+'') === -1 && dataSources.push({
                code: chart.dataSourceCode+'',
                name: chart.dataSourceName
            });
            return Object.assign({}, state, {items, chartCodes, dataSources, dirty: true});
        },
        deleteItem(state, action) {
            let { items, chartCodes, dataSources, relationColumns, dirty, filters } = state;
            const { item } = action;

            let targetDataSourceCode = item.dataSourceCode;
            let count = 0;
            let targetIndex = -1;
            for(let i = 0; i < items.length; i++) {
                let tempItem = items[i];
                if(tempItem.code === item.code) {
                    targetIndex = i; 
                    break;
                }
            }

            for(let i = 0; i < items.length; i++) {
                if(targetDataSourceCode && items[i].dataSourceCode === targetDataSourceCode) {
                    count++;
                }
            }

            // 如果删除图表的数据源是同类数据源的最后一个
            if(count === 1) {
                let idx = dataSources.findIndex(d => d.code === item.dataSourceCode);
                let idx2 = chartCodes.findIndex(c => c === item.chartCode);
                dataSources.splice(idx, 1);
                chartCodes.splice(idx2, 1);
                // 删除已定义的关联字段
                relationColumns.forEach(rc => {
                    rc.relations.forEach((r, x) => {
                        if(r.dataSourceCode === item.dataSourceCode) {
                            rc.relations.splice(x, 1);
                        }
                    })
                });
                for(let i = relationColumns.length - 1; i >= 0; i--) {
                    let r = relationColumns[i];
                    let l = r.relations;
                    // 自定义关联条件关联了一个以上数据源，则只删除与该数据源相等的关联数据源列
                    for(let j = l.length - 1; j >= 0; j--) {
                        if(l[j].dataSource.code === targetDataSourceCode) {
                            l.splice(j, 1);
                        }
                    }
                    if(l.length === 0) {
                        // 自定义关联条件只关联了这一个数据源则直接删除该自定义关联条件
                        relationColumns.splice(i, 1);
                    }
                }
                // 删除过滤条件
                for(let i = filters.length - 1; i >= 0; i--) {
                    let f = filters[i];
                    if((f.combined && (f.dataSource.length === 1 && f.dataSource[0].dataSource.code === targetDataSourceCode || f.dataSource.length === 0)) ||
                    (!f.combined && f.dataSource.code === targetDataSourceCode)) {
                        filters.splice(i, 1);
                    }
                }
            }
            if(targetIndex !== -1) {
                items.splice(targetIndex, 1);
            }
            return { ...state, dirty: dirty, items, dataSources, relationColumns, filters };
        },
        addRichText(state, action) {
            let { items, defaultLayout } = state;
            items.push({
                code: Math.random() + '',
                viewType: 'richText',
                name: '',
                layout: JSON.parse(JSON.stringify(defaultLayout)) // 深拷贝
            });
            return Object.assign({}, state, {items, dirty: true});
        },
        changeLayout(state, action) {
            const { layout } = action;
            let { items, dirty, minLayoutHeight } = state;
            const ly = ['x', 'y', 'w', 'h'];
            for(let i = 0; i < items.length; i++) {
                if(layout[i]) { // 非删除引起
                    for(let j = 0; j < ly.length; j ++) {
                        if(items[i].layout[ly[j]] !== layout[i][ly[j]]) {
                            dirty = true;
                            items[i].layout[ly[j]] = layout[i][ly[j]];
                        }
                    }
                    if(items[i].chartOption) {
                        items[i].chartOption = { ...items[i].chartOption,
                            page: 1,
                            pageSize: Math.max((~~((layout[i][ly[3]] * minLayoutHeight + (layout[i][ly[3]] - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1), 10)
                        }
                    }
                }else { // 删除引起
                    dirty = true;
                }
            }
            return Object.assign({}, state, {items, dirty});
        },
        modifyItem(state, action) {
            let { item } = action;
            let { items } = state;
            let dirty = false;
            let idx = items.findIndex(i => i.code === item.code);
            if(idx > -1) {
                dirty = true;
                items[idx] = { ...items[idx], ...item };
            }
            return Object.assign({}, state, {items, dirty});
        },
        reset(state, action) {
            let newState = Object.assign({}, state, state.originData);
            return Object.assign({}, newState);
        },
        setEditMode(state, action) {
            const { checked } = action;
            return { ...state, editMode: checked };
        },
        addRelationColumn(state, action) {
            const { relationColumns } = state;
            relationColumns.push({
                code: Math.random()+'',
                name: '新字段',
                relations: []
            });
            return { ...state, relationColumns, dirty: true };
        },
        deleteRelationColumn(state, action) {
            const { code } = action;
            const { relationColumns } = state;
            let index = relationColumns.findIndex(r => r.code === code);
            relationColumns.splice(index, 1);
            return { ...state, relationColumns, dirty: true };
        },
        setRelationColumn(state, action) {
            const { code, relationColumn } = action
            const { relationColumns } = state;
            let index = relationColumns.findIndex(r => r.code === code);
            relationColumns[index] = relationColumn;
            return { ...state, relationColumns, dirty: true };
        },
        setItemFetching(state, action) {
            const { code, fetching } = action
            const { items } = state;
            let index = items.findIndex(item => item.code === code);
            items[index] = {
                ...items[index],
                fetching
            };
            return { ...state, items };
        },
        setItemField(state, action) {
            const { code, name, value } = action;
            const { items } = state;
            let index = items.findIndex(item => item.code === code);
            const targetItem = items[index];
            targetItem[name] = value;

            items[index] = {
                ...targetItem
            };
            return { ...state, items };
        },
        setItemFields(state, action) {
            const { code, fields } = action;
            const { items } = state;
            let index = items.findIndex(item => item.code === code);
            if(index !== -1) {
                const targetItem = items[index];
                fields.forEach(field => {
                    targetItem[field.name] = field.value;
                });
                items[index] = {
                    ...targetItem
                };
            }
            return { ...state, items };
        },
    },

    effects: {
        *addCharts(action, { call, put, select }) {
            const { charts } = action;

            try{
                for(let i = 0; i < charts.length; i++) {
                    yield put({ type: 'addChart', chart: charts[i] });
                }
            }catch(e) {
                message.error('添加图表错误: ' + e.message);
            }
        },
        /**
         * 刷新报表
         */
        *refresh(action, { call, put, select }) {
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            const { items, minLayoutHeight } = dashboardDesigner;

            for(let i = 0; i < items.length; i++) {
                let item = items[i];
                if(item.viewType === 'chart') {
                    let page = item.chartOption ? item.chartOption.page : 1;
                    let pageSize = item.chartOption ? item.chartOption.pageSize : (~~((item.layout.h * minLayoutHeight + (item.layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
                    yield put({ type:'fetchChartData', item: items[i], mandatory: true, page, pageSize: Math.max(pageSize, 10) });
                }
            }
        },
        *remoteGetColumns(action, { call, put, select }) {
            const { dataSourceCode, mandatory } = action;
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            const { dataSources } = dashboardDesigner;
            const body = dataSourceCode;
            let idx = dataSources.findIndex(d => d.code === dataSourceCode);
            if(!mandatory && dataSources[idx].columns && dataSources[idx].columns.length > 0) {
                return;
            }
            
            try {
                const res = yield call(service.fetch, {
                    url: URLS.DATASOURCE_QUERY_DATACOLUMNS,
                    body: body
                });
                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: []
                        }
                    }).filter(c => c.filterable);
                    
                    dataSources[idx] = { ...dataSources[idx], columns }
                    yield put({ type: 'silentSetField', name: 'dataSources', value: dataSources });
                }else {
                    message.error('请求列数据失败:' + res.msg);
                    yield put({ type: 'silentSetField', name: 'dataSources', value: [] });
                }
            }catch(e) {
                message.error('请求列数据失败: ' + e.message);
            }
        },
        /**
         * 同时更改多个filter
         */
        *changeFilters(action, { put, call, select }) {
            try {
                const { filters } = action;
                const dashboardDesigner = yield select(state => state.dashboardDesigner);
                const { items, minLayoutHeight } = dashboardDesigner;
    
                yield put({ type: 'silentSetField', name: 'filters', value: filters });
                
                for(let i = 0; i < items.length; i++) {
                    let page = items[i].chartOption ? items[i].chartOption.page : 1;
                    let pageSize = items[i].chartOption ? items[i].chartOption.pageSize : (~~((items[i].layout.h * minLayoutHeight + (items[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
                    yield put({ type:'fetchChartData', item: items[i], mandatory: true, page, pageSize: Math.max(pageSize, 10) });
                }
            }catch(e) {
                message.error('更改过滤条件失败: ' + e.message);
            }
        },
        /**
         * 只更改一个filter
         */
        *changeFilter(action, { put, call, select }) {
            const { filter } = action;
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            let { filters, items, minLayoutHeight } = dashboardDesigner;
            let targetDataSourceCodes = [];

            filters = filters.map(f => {
                if(f.key === filter.key) {
                    if(f.combined) {
                        targetDataSourceCodes = targetDataSourceCodes.concat(f.dataSource.map(d => d.dataSource.code));
                    }else {
                        if(targetDataSourceCodes.indexOf(f.dataSource.code) === -1) {
                            targetDataSourceCodes.push(f.dataSource.code);
                        }
                    }
                    return Object.assign({}, f, filter);
                }else {
                    return f;
                }
            });
            // 找到filters有影响的item
            let targetItems = items.filter(item => targetDataSourceCodes.indexOf(item.dataSourceCode) !== -1);

            yield put({ type: 'silentSetField', name: 'filters', value: filters });

            for(let i = 0; i < targetItems.length; i++) {
                let page = targetItems[i].chartOption ? targetItems[i].chartOption.page : 1;
                let pageSize = targetItems[i].chartOption ? targetItems[i].chartOption.pageSize : (~~((targetItems[i].layout.h * minLayoutHeight + (targetItems[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
                yield put({ type:'fetchChartData', item: targetItems[i], mandatory: true, page, pageSize: Math.max(pageSize, 10) });
            }
        },
        *fetchChartData(action, { put, call, select }) {
            const { item, mandatory, page, pageSize } = action;
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            const { creatorCode, filters, theme } = dashboardDesigner;
            const { chartCode, chartType } = item;
            if(!mandatory && !!item.chartOption) {
                return false;
            }

            try {
                yield put({ type: 'setItemFetching', code: chartCode, fetching: true });
                const itemFilters = getTrueFilters(item, filters);
                const body = {
                    dashboardCreatorId: creatorCode,
                    chartId: chartCode,
                    filters: getBodyFilters(itemFilters),
                    testPage: {
                        pageNum: page|| 1,
                        pageSize: chartType === 'indicator' ? 99 : pageSize || 99, 
                    }
                };
                const res = yield call(service.fetch, {
                    url: URLS.CHART_OPTION,
                    allow: true,
                    body,
                    timeout: 30000
                });
                if(res.code > 0) {
                    let resData = res.data;
                    if(!resData) {
                        yield put({ type: 'setItemFields', code: chartCode, fields: [
                            { name: 'chartType', value: '' },
                            { name: 'chartOption', value: {} }
                        ] });
                        return false;
                    }
                    const { chartType : ctype, chartConfig: chartConfigStr, chartStyle: styleConfigStr  } = resData.chartsColumnConfig;
                    const chartType = CHART_TYPE[ctype];
                    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 }
                    ] });
                    return { chartType, chartOption };
                }else {
                    yield put({ type: 'setItemFields', code: chartCode, fields: [
                        { name: 'chartType', value: '' },
                        { name: 'chartOption', value: {} }
                    ] });
                    message.error('请求图表展示数据失败: ' + res.msg);
                    return false;
                }
            }catch(e) {
                yield put({ type: 'setItemFields', code: chartCode, fields: [
                    { name: 'chartType', value: '' },
                    { name: 'chartOption', value: {} }
                ] });
                message.error('请求图表展示数据失败: ' + e.message);
                return false;
            }finally {
                yield put({ type: 'setItemFetching', code: chartCode, fetching: false });
            }
        },
        *encryptCode(action, { put, call, select }) {
            const { shareCode } = action;
            const res = yield call(service.fetch, {
                url: URLS.DASHBOARD_ENCRYPT_CODE,
                method: 'GET',
                body: {
                    code: shareCode
                },
            });
            if(res.code > 0) {
                let resData = res.data;
                return resData;
            }else {
                return false;
            }
        },
        *setRelationColumns(action, { put, call, select }) {
            const { relationColumns } = action;
            let targetDataSourceCodes = []; // 记录有改动的数据源code
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            let { filters, items, minLayoutHeight } = dashboardDesigner;
            for(let i = filters.length - 1; i >= 0; i--) {
                let f = filters[i];
                let idx = relationColumns.findIndex(rc => rc.code === f.name);
                if(idx > -1) {
                    // 如果改变的自定义条件已经添加到了筛选条件区域
                    let nrc = relationColumns[idx];
                    let willRemove = false;
                    if(f.type !== nrc.relations[0].column.type) { // 如果改变了所选的列
                        willRemove = true;
                    }
                    let oldDataSourceCodes = f.dataSource.map(d => d.dataSource.code);
                    let oldColumns = f.dataSource.map(d => d.column.name);
                    let newDataSourceCodes = nrc.relations.map(r => r.dataSource.code);
                    let newColumns = nrc.relations.map(r => r.column.name);

                    if(!ArrayEquals(oldDataSourceCodes, newDataSourceCodes) || !ArrayEquals(oldColumns, newColumns)) { // 如果改变了数据源或者数据列
                        willRemove = true;
                    }
                   
                    if(willRemove) {
                        f.dataSource.forEach(d => {
                            targetDataSourceCodes.push(d.dataSource.code);
                        });
                        filters.splice(i, 1); // 将该过滤字段移除
                    }else { // 只剩下label会改变了
                        filters[i] = { ...filters[i], label: nrc.name }
                    }
                }else if(f.combined) { // 自定义字段已被删除
                    f.dataSource.forEach(d => {
                        targetDataSourceCodes.push(d.dataSource.code);
                    });
                    
                    filters.splice(i, 1)
                }
            }
            yield put({ type: 'setFields', fields: [
                { name: 'relationColumns', value: relationColumns },
                { name: 'filters', value: filters },
                { name: 'dirty', value: true },
            ] });
            // 找到filters有影响的item
            let targetItems = items.filter(item => targetDataSourceCodes.indexOf(item.dataSourceCode) !== -1);

            yield put({ type: 'silentSetField', name: 'filters', value: filters });

            for(let i = 0; i < targetItems.length; i++) {
                let page = 1;
                let pageSize = targetItems[i].chartOption ? targetItems[i].chartOption.pageSize : (~~((targetItems[i].layout.h * minLayoutHeight + (targetItems[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
                yield put({ type:'fetchChartData', item: targetItems[i], mandatory: true, page, pageSize: Math.max(pageSize, 10) });
            }
        },
        *modifyItem(action, { select, call, put }) {
            try{
                const { item } = action;
                const dashboardDesigner = yield select(state => state.dashboardDesigner);
                const { items } = dashboardDesigner;
                let idx = items.findIndex(i => i.code === item.code);
                if(idx > -1) {
                    items[idx] = { ...items[idx], ...item };
                    yield put({ type: 'setField', name: 'items', value: items });
                }
            }catch(e) {
                message.error('修改失败: ' + e.message);
            }
        },
        /**
         * dataView预览窗口取数
         */
        *fetchDataList(action, { select, call, put }) {
            const { item, page, pageSize } = action;
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            const { creatorCode, filters } = dashboardDesigner;
            const { chartCode } = item;
            
            try {
                yield put({ type: 'dataList/setField', name: 'loading', value: true });
                const body = {
                    dashboardCreatorId: creatorCode,
                    chartId: chartCode,
                    filters: getBodyFilters(getTrueFilters(item, filters)),
                    testPage: {
                        pageNum: page || 1,
                        pageSize: pageSize || 25, 
                    }
                };
                const res = yield call(service.fetch, {
                    url: URLS.CHART_OPTION,
                    allow: true,
                    body,
                    timeout: 30000
                });
                if(res.code > 0) {
                    const { chartsColumnConfig, valueList } = res.data;
                    const { list, pageSize, total } = valueList;
                    const chartConfig = JSON.parse(chartsColumnConfig.chartConfig);
                    const columns = chartConfig.viewColumns;

                    yield put({ type: 'dataList/setFields', fields: [
                        { name: 'columns', value: columns },
                        { name: 'dataSource', value: list },
                        { name: 'pageSize', value: pageSize },
                        { name: 'total', value: total }
                    ] });
                    // 主动触发一次window的resize事件
                    yield window.setTimeout(() => {
                        var e = document.createEvent("Event");
                        e.initEvent("resize", true, true);
                        window.dispatchEvent(e);
                    }, 20);
                }else {
                    message.error('请求图表展示数据失败: ' + res.msg);
                    return false;
                }
            }catch(e) {
                message.error('请求图表展示数据错误: ' + e.message);
                return false;
            }finally {
                yield put({ type: 'dataList/setField', name: 'loading', value: false });
            }
        },
        *exportToExcel(action, { select, take, put }) {
            const dashboardDesigner = yield select(state => state.dashboardDesigner);
            const { name, filters, items } = dashboardDesigner;
            try {
                yield put({ type: 'setField', name: 'loading', value: true });

                let sheets = [];
                for(let i = 0; i < items.length; i++) {
                    let item = items[i];
                    let { chartCode, name: itemName, chartOption, viewType, chartType, content } = item;
                    let header;
                    let columns = [];
                    let rows = [];
                    const TYPES = {
                        time: 'DateTime',
                        categorical: 'String',
                        scale: 'Number',
                        string: 'String'
                    };
                    if(viewType === 'chart') {
                        let sync = yield put({ type: 'chartDesigner/getChartTableData',
                            chartCode, chartType, chartOption, filters: getTrueFilters(item, filters) });
                        yield take('chartDesigner/getChartTableData/@@end');
                        yield sync.then(tableData => {
                            let { columns: tcolumns, dataSource: tdatasource } = tableData;
                            columns = tcolumns.map(c => ({
                                name: c.title,
                                width: c.width || 25,
                                type: TYPES[c.type]
                            }));
                            rows = tdatasource.map(d => {
                                return tcolumns.map(c => d[c.dataIndex])
                            })
                            if(chartType === 'aggregateTable') {
                                header = `分析目标：${chartOption.originConfig.targetColumn.label}`
                            }
                        });
                    }else if(viewType === 'richText') {
                        content = content || '';
                        columns = [{
                            name: content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/\'/g, '&apos;'),
                            width: 25,
                            type: 'String'
                        }];
                        rows = [];
                    }
                    // 处理重名情况
                    let name = itemName || `未命名`;
                    let new_name = name;
                    let fidx = sheets.findIndex(s => s.name === new_name);
                    let idx = 1;
                    while(fidx > -1){
                        new_name = `${name}(${idx})`;
                        fidx = -1;
                        for(let x = 0; x < sheets.length; x++) {
                            if(sheets[x].name === new_name) {
                                fidx = x;
                            }
                        }
                        idx++;
                    }

                    name = new_name;
                    
                    sheets.push({
                        name,
                        header,
                        columns,
                        rows
                    });
                }
                let e = yield new Exportor();
                e.init({ sheets }, name).then(() => {
                    e.export()
                });
            }catch(e) {
                message.error('报表导出错误: ' + e);
            }finally {
                yield put({ type: 'setField', name: 'loading', value: false });
            }
        }
    },
    subscriptions: {
        setup({ dispatch, history}) {
            dispatch({ type: 'reset' });
        }
    }
};

