import { message } from 'antd'
import * as service from '../services/index'
import URLS from '../constants/url'
import CHART_TYPE from './chartType.json'

export default {
    namespace: 'chart',
    state: {
        originData: {
            listLoading: false,
            list: [],
            filterLabel: '',
            typeLabel: 'name',
            groupList: [],
            currentGroup: null,
            filterItems: [ // 可选过滤字段
                { name: 'name', label: '名称', type: 'string' },
                // { name: 'description', label: '备注', type: 'string' },
                { name: 'creatorName', label: '创建人', type: 'string' },
                { name: 'createTime', label: '创建时间', type: 'date' },
                { name: 'dataSourceCode', label: '数据源', type: 'enum', options: () => {
                    return service.fetch({
                        url: URLS.DATASOURCE_LIST,
                        method: 'GET',
                        body: {
                            pageNum: 1,
                            pageSize: 999999999
                        }
                    }).then(res => {
                        if(!res.error && res.data.code > 0) {
                            return res.data.data.list.map(l => ({
                                name: l.dataId,
                                label: l.dataName,
                            }));
                        }else {
                            message.error('获取数据源下拉列表失败: ' + res.data.msg);
                        }
                    }).catch(e => {
                        message.error('获取数据源下拉列表失败: ' + e.message);
                    });
                } },
                { name: 'dataConnectCode', label: '数据链接', type: 'enum', options: () => {
                    return service.fetch({
                        url: URLS.DATACONNECT_LIST,
                        method: 'GET',
                        body: {
                            pageNum: 1,
                            pageSize: 999999999
                        }
                    }).then(res => {
                        if(!res.error && res.data.code > 0) {
                            return res.data.data.list.map(l => ({
                                name: l.id,
                                label: l.name,
                            }));
                        }else {
                            message.error('获取数据链接下拉列表失败: ' + res.data.msg);
                        }
                    }).catch(e => {
                        message.error('获取数据链接下拉列表失败: ' + e.message);
                    });
                } }
            ],
            filterItem: { name: 'name', label: '名称', type: 'string' }, // 已选过滤字段
            listScrollTop: 0, // 记录列表界面滚动条位置
            page: 1, // 分页器当前所在页码
        },
    },
    reducers: {
        setField(state, action) {
            const { name, value } = action;
            let obj = {};
            obj[name] = value;
            return Object.assign({}, state, obj);
        },
        setFields(state, action) {
            const { fields } = action;
            let obj = {};
            fields.map(f => (obj[f.name] = f.value));
            return Object.assign({}, state, obj);
        },
        add(state, action) {
            const { data } = action;
            let list = state.list;
            list.push(data);
            return Object.assign({}, state, {list});
        },
        list(state, action) {
            let list = action.list;
            return Object.assign({}, state, {list: list});
        },
        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});
        },
        groupList(state, action) {
            let data = action.data;
            return Object.assign({}, state, {groupList: data});
        },
        /**
         * 设置数据源过滤用分组
         */
        setCurrentGroup(state, action) {
            const { group } = action;
            return Object.assign({}, state, {currentGroup: group});
        },
        addGroup(state, action) {
            const { group } = action;
            let list = state.groupList;
            list.push(group);
            return Object.assign({}, state, {groupList: list});
        },
        modifyGroup(state, action) {
            const { group } = action;
            let list = state.groupList;
            let dirty = false;
            for(let i = 0; i < list.length; i++) {
                let l = list[i];
                if(l.code === group.code) {
                    for(let k in l) {
                        if(group[k] !== undefined && l[k] !== group[k]) {
                            l[k] = group[k];
                            dirty = true;
                        }
                    }
                    break;
                }
            }
            return Object.assign({}, state, {groupDirty: dirty, groupList: list});
        },
        modifyGroups(state, action) {
            const { groups } = action;
            let list = state.groupList;
            const modifyGroupCodes = groups.map(g => g.code);

            list = list.filter(l => modifyGroupCodes.indexOf(l.code) === -1);
            list = list.concat(groups);

            return Object.assign({}, state, {groupList: list});
        },
        deleteGroup(state, action) {
            const { group } = action;
            let list = state.groupList;
            for(let i = 0; i < list.length; i++) {
                let l = list[i];
                if(l.code === group.code) {
                    list.splice(i, 1);
                    break;
                }
            }
            return Object.assign({}, state, {groupList: list});
        },
        setGroupDirty(state, action) {
            let dirty = action.dirty;
            return Object.assign({}, state, {groupDirty: dirty});
        },
        /** 
         * 设置图表所属分组
         */
        setChartGroup(state, action) {
            const { chartCode, groupCode } = action;
            let list = state.list;
            for(let i = 0; i < list.length; i++) {
                let l = list[i];
                if(l.code === chartCode) {
                    l.groupCode = groupCode;
                    break;
                }
            }
            return Object.assign({}, state, {list: list});
        },
        reset(state, action) {
            let newState = Object.assign({}, state, state.originData);
            return Object.assign({}, newState);
        },
    },
    effects: {
        *fetchList(action, { select, call, put }) {
            const { pageNum, pageSize } = action;
            const body = {
                pageNum: pageNum || 1,
                pageSize: pageSize || 999999999
            }
            try{
                const chart = yield select(state => state.present.chart);
                if(!action.mandatory && chart.list.length > 0) {
                    return chart.list;
                }
                if(!action.silent) {
                    yield put({ type: 'setField', name: 'listLoading', value: true });
                }
                const res = yield call(service.fetch, {
                    url: URLS.CHART_LIST,
                    method: 'GET',
                    body
                });
                if(!res.err && res.data.code > 0) {
                    let list = res.data.data.list.map(d => {
                        let chartOption = d.chartOption ? JSON.parse(d.chartOption) : {};
                        return {
                            code:  d.chartId+'',
                            name: d.chartName,
                            dataSourceCode: d.dataId + '',
                            dataSourceName: d.dataName,
                            dataConnectCode: d.dataConnectionId,
                            dataConnectName: d.dataConnectionName,
                            access: d.authority === '1', // 权限
                            database: d.dbStatus === '0', // 数据源是否还存在
                            type: CHART_TYPE[d.chartType],
                            creatorCode: d.createId + '',
                            creatorName: d.createBy,
                            createTime: d.createDate,
                            description: d.describes || '',
                            groupCode: d.chartsGroup + '',
                            chartOption: chartOption,
                            theme: d.theme,
                            styleConfig: JSON.parse(d.style || '{}'),
                            demo: d.demo,
                        }
                    })
                    yield put({ type: 'list', list: list });
                    return list;
                }else {
                    message.error('请求图表列表失败: ' + (res.err || res.data.msg));
                    return false;
                }
            }catch(e) {
                message.error('请求图表列表失败: ' + e.message);
            }finally {
                yield put({ type: 'setField', name: 'listLoading', value: false });
            }
        },
        *remoteDetail(action, { select, call, put }) {
            const code = action.code;
            if(!code){
                return
            }
            try {
                const res = yield call(service.fetch, {
                    url: URLS.CHART_DETAIL,
                    method: 'GET',
                    body: {
                        id: code
                    }
                });
                if(!res.err && res.data.code > 0) {
                    let resData = res.data.data;
                    if(!resData) {
                        message.error('该图表不存在');
                        return false;
                    }
                    let chartConfig = JSON.parse(resData.chartConfig || '{ "xAxis": { "column": {}, "granularity": {} }, "yAxis": { "column": {}, "gauge": {} } }');
                    let styleConfig = JSON.parse(resData.style || '{}');
                    let otherConfig = JSON.parse(resData.otherConfig || '{}');
                    let viewType = CHART_TYPE[resData.chartType];
                    let filters = JSON.parse(resData.filters || '[]');
                    let chartOption = JSON.parse(resData.chartOption || '{}');

                    let data = {
                        code: resData.chartId + '',
                        creatorCode: resData.createId+'',
                        creatorName: resData.createBy,
                        dataSourceCode: resData.dataId + '',
                        dataSourceName: resData.dataName,
                        dataConnectCode: resData.dataConnectionId,
                        dataConnectName: resData.dataConnectionName,
                        header: {
                            label: resData.chartName
                        },
                        baseConfig: {
                            dataSource: {
                                code: resData.dataId + '',
                                name: resData.dataName,
                            },
                            viewType: viewType
                        },
                        theme: resData.theme,
                        styleConfig: styleConfig,
                        otherConfig: otherConfig,
                        description: resData.describes,
                        group: resData.chartsGroup+'',
                        filters: filters,
                        chartOption: chartOption,
                        demo: resData.demo
                    }

                    if(viewType === 'bar') {
                        data.barConfig = chartConfig;
                    }else if(viewType === 'pie') {
                        data.pieConfig = chartConfig;
                    }else if(viewType === 'line') {
                        data.lineConfig = chartConfig;
                    }else if(viewType === 'scatter') {
                        data.scatterConfig = chartConfig;
                    }else if(viewType === 'aggregateTable') {
                        data.aggregateTableConfig = chartConfig;
                    }else if(viewType === 'dataView') {
                        data.dataViewConfig = chartConfig;
                    }

                    let fields = [];
                    for(let key in data) {
                        fields.push({
                            name: key,
                            value: data[key]
                        })
                    }
                    yield put({ type: 'chartDesigner/silentChangeFields', fields: fields });
                    yield put({ type: 'chartDesigner/silentChangeDataSource', dataSource: data.baseConfig.dataSource });
                    let pageSize = ~~((document.getElementsByClassName('content-body')[0].offsetHeight - 12 * 2 - 40 - 24 - 8 * 2)/38) + 1;
                    yield put({ type: 'chartDesigner/fetchChartData', page: 1, pageSize });
                }else {
                    message.error('解析图表错误: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('解析图表错误: ' + e.message);
            }
        },
        *remoteAdd(action, { select, call, put }) {
            try{
                const chartDesigner = yield select(state => state.present.chartDesigner);
                const { header, baseConfig, pieConfig, lineConfig, aggregateTableConfig, dataViewConfig,
                    barConfig, scatterConfig, otherConfig, description, group, filters, chartOption } = chartDesigner;
                let body = {
                    chartName: header.label,
                    dataId: baseConfig.dataSource.code,
                    describes: description,
                    style: '{}',
                    otherConfig: JSON.stringify(otherConfig),
                    chartsGroup: group ? group : '-1',
                    filters: JSON.stringify(filters),
                    chartOption: JSON.stringify(chartOption),
                }; // 基本属性
                if(baseConfig.viewType === 'bar') {
                    body.chartType = 'Histogram';
                    body.chartConfig = JSON.stringify(barConfig);
                }else if(baseConfig.viewType === 'pie') {
                    body.chartType = 'Pie';
                    body.chartConfig = JSON.stringify(pieConfig);
                }else if(baseConfig.viewType === 'line') {
                    body.chartType = 'Line';
                    body.chartConfig = JSON.stringify(lineConfig);
                }else if(baseConfig.viewType === 'scatter') {
                    body.chartType = 'scatter';
                    body.chartConfig = JSON.stringify(scatterConfig);
                }else if(baseConfig.viewType === 'aggregateTable') {
                    body.chartType = 'population';
                    body.chartConfig = JSON.stringify(aggregateTableConfig);
                }else if(baseConfig.viewType === 'dataView') {
                    body.chartType = 'individual';
                    body.chartConfig = JSON.stringify(dataViewConfig);
                }else {
                    body.chartType = '';
                    body.chartConfig = JSON.stringify({});
                }
                const res = yield call(service.fetch, {
                    url: URLS.CHART_ADD,
                    body: body
                })
                if(!res.err && res.data.code > 0) {
                    message.success('新增成功');
                    yield put({ type: 'fetchList', mandatory: true });
                }else {
                    message.error('新增失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                console.error(e);
                message.error('新增失败: ' + e.message);
            }
        },
        *remoteModify(action, { select, call, put }) {
            try{
                const chartDesigner = yield select(state => state.present.chartDesigner);
                const { filters, code, header, baseConfig, pieConfig, lineConfig, aggregateTableConfig, dataViewConfig,
                    barConfig, scatterConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig } = chartDesigner;
                let body = {
                    chartId: code,
                    filters: JSON.stringify(filters),
                    chartName: header.label,
                    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),
                }; // 基本属性
                if(baseConfig.viewType === 'bar') {
                    body.chartType = 'Histogram';
                    body.chartConfig = JSON.stringify(barConfig);
                }else if(baseConfig.viewType === 'pie') {
                    body.chartType = 'Pie';
                    body.chartConfig = JSON.stringify(pieConfig);
                }else if(baseConfig.viewType === 'line') {
                    body.chartType = 'Line';
                    body.chartConfig = JSON.stringify(lineConfig);
                }else if(baseConfig.viewType === 'scatter') {
                    body.chartType = 'scatter';
                    body.chartConfig = JSON.stringify(scatterConfig);
                }else if(baseConfig.viewType === 'aggregateTable') {
                    body.chartType = 'population';
                    body.chartConfig = JSON.stringify(aggregateTableConfig);
                }else if(baseConfig.viewType === 'dataView') {
                    body.chartType = 'individual';
                    body.chartConfig = JSON.stringify(dataViewConfig);
                }
                const res = yield call(service.fetch, {
                    url: URLS.CHART_UPDATE,
                    body: body
                })
                if(!res.err && res.data.code > 0) {
                    message.success('修改成功');
                    yield put({ type: 'fetchList', mandatory: true });
                }else {
                    message.error('修改失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                console.error(e);
                message.error('修改失败: ' + e.message);
            }
        },
        *remoteDelete(action, { select, call, put, takeEvery, takeLatest }) {
            const chart = yield select(state => state.present.chart);
            const code = action.code;
            let list = chart.list;
            try {
                const res = yield call(service.fetch, {
                    url: URLS.CHART_DELETE,
                    body: code
                });
                if(!res.err && res.data.code > 0) {
                    for(let i = 0; i < list.length; i++) {
                        if(list[i].code === code) {
                            list.splice(i, 1);
                            break;
                        }
                    }
                    yield put({ type: 'list', list: list });
                    message.success('删除成功');
                }else {
                    message.error('删除失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('删除失败: ' + e.message);
            }
        },
        
        *remoteGroupList(action, { select, call, put }) {
            try {
                const chart = yield select(state => state.present.chart);
                if(!action.mandatory && chart.groupList.length > 0) {
                    return;
                }
                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_LIST,
                });
                
                if(!res.err && res.data.code > 0) {
                    const resData = res.data.data;
                    let data = resData.map(d => {
                        return {
                            code: d.id+'',
                            pcode: d.fatherId+'',
                            index: +d.groupIndex,
                            label: d.groupName+'',
                        }
                    });
                    yield put({ type: 'groupList', data });
                }else {
                    message.error('读取图表分组列表错误: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('读取图表分组列表错误: ' + e.message);
            }
        },
        *remoteSetGroups(action, { select, call, put }) {
            const { aGroups, mGroups, dGroups } = action;
            const body = aGroups.concat(mGroups).concat(dGroups).map(g => ({
                id: g.operate === 'add' ? null : g.code,
                fatherId: g.pcode,
                groupIndex: g.index,
                operate: g.operate,
                groupName: g.label
            }))
            try{
                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_BATCH_SET,
                    body
                });
                if(!res.err && res.data.code > 0) {
                    message.success('修改分组信息成功');
                    yield put({ type: 'remoteGroupList', mandatory: true });
                    return true;
                }else {
                    message.error('修改分组信息失败: ' + (res.data.msg));
                    return false;
                }
            }catch(e) {
                message.error('修改分组信息失败: ' + e.message);
            }
        },
        /**
         * 新增分组/子分组，需要传入父节点code
         */
        *remoteAddGroup(action, { select, call, put }) {
            try {
                const chart = yield select((state) => state.present.chart);
                const group = chart.groupList;
                const pgroups = group.filter(g => g.pcode === '-1');
                const cgroups = group.filter(g => g.pcode !== '-1');
                const { pgroup } = action;

                let body = {};
                if(pgroup) {
                    body = {
                        fatherId: pgroup.code,
                        groupName: '新子分组',
                        groupIndex: cgroups.filter(c => c.pcode === pgroup.code).length,
                    }
                }else {
                    body = {
                        fatherId: '-1',
                        groupName: '新分组',
                        groupIndex: pgroups.length,
                    }
                }
                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_ADD,
                    body: body
                });
                if(!res.err && res.data.code > 0) {
                    let group = {
                        code: res.data.data + '',
                        pcode: body.fatherId + '',
                        index: body.groupIndex,
                        label: body.groupName+'',
                    }
                    yield put({ type: 'addGroup', group });
                }else {
                    message.error('添加分组失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('添加分组失败: ' + e.message);
            }
        },
        /**
         * 修改单个分组信息（因为不涉及顺序号的修改，所以一般只用于label的修改）
         */
        *remoteModifyGroup(action, { select, call, put }) {
            try {
                const chart = yield select((state) => state.present.chart);
                const groupDirty = chart.groupDirty;
                const group = action.group;

                if(!groupDirty) { // 如果属性无改动则取消修改请求
                    return;
                }
                let body = {
                    id: group.code,
                    fatherId: group.pcode,
                    groupName: group.label,
                    groupIndex: group.index,
                }
                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_UPDATE,
                    body: body
                });
                if(!res.err && res.data.code > 0) {
                    yield put({ type: 'setGroupDirty', dirty: false });
                }else {
                    message.error('修改分组失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('修改分组失败: ' + e.message);
            }
        },
        /**
         * 批量修改多个分组信息(在移动位置时用)
         */
        *remoteModifyGroups(action, { select, call, put }) {
            try {
                const groups = action.groups;

                let body = groups.map(g => {
                    return {
                        id: g.code,
                        groupName: g.label,
                        groupIndex: g.index,
                        fatherId: g.pcode,
                    }
                });
                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_LIST_UPDATE,
                    body: body
                });
                if(!res.err && res.data.code > 0) {
                    yield put({ type: 'modifyGroups', groups: groups });
                }else {
                    message.error('修改分组失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('修改分组失败: ' + e.message);
            }
        },
        *remoteDeleteGroup(action, { select, call, put }) {
            try {
                const chart = yield select((state) => state.present.chart);
                const groupList = chart.groupList;
                const { group } = action;

                let bgroups = groupList.filter(l => l.pcode === group.pcode);
                bgroups.splice(group.index, 1);
                bgroups = bgroups.map((b, i) => {
                    return { ...b, index: i }
                });

                yield put({ type: 'remoteModifyGroups', groups: bgroups });

                const res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_DELETE,
                    body: group.code
                });
                
                if(!res.err && res.data.code > 0) {
                    yield put({ type: 'deleteGroup', group});
                }else {
                    message.error('删除分组失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('删除分组失败: ' + e.message);
            }
        },
        *remoteMoveGroup(action, { select, call, put }) {
            try {
                const { dragCode, dropCode, dropPosition } = action;
                const chart = yield select((state) => state.present.chart);
                let group = chart.groupList;

                const dragGroup = group.filter(g => g.code === dragCode)[0];
                const dropGroup = group.filter(g => g.code === dropCode)[0];

                let modifyGroups = [];
                if((dragGroup.pcode === '-1' || dropGroup.pcode === '-1') && (dragGroup.pcode !== dropGroup.pcode)) { // 跨级
                    if(dropGroup.pcode !== '-1') { // 从父级到子级
                        return; // 不允许
                    }else { // 从子级到父级
                        if(dragGroup.pcode === dropGroup.code) { // 不跨组
                            if(dropPosition === -1) { // 目标前
                                let dragGroups = group.filter(g => g.pcode === dragGroup.pcode).sort((a, b) => a.index - b.index);
                                let dropGroups = group.filter(g => g.pcode === dropGroup.pcode).sort((a, b) => a.index - b.index);
                                dragGroups.splice(dragGroup.index, 1);
                                dragGroups = dragGroups.map((g, i) => {
                                    return { ...g, index: i }
                                });
                                dropGroups.splice(dropGroup.index, 0, dragGroup);
                                dropGroups = dropGroups.map((g, i) => {
                                    return { ...g, index: i, pcode: dropGroup.pcode }
                                });
                                modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                            }else if(dropPosition === 0) { // 目标内
                                return; // 无变化
                            }else if(dropPosition === 1) { // 目标后
                                let dragGroups = group.filter(g => g.pcode === dragGroup.pcode).sort((a, b) => a.index - b.index);
                                let dropGroups = group.filter(g => g.pcode === dropGroup.pcode).sort((a, b) => a.index - b.index);
                                dragGroups.splice(dragGroup.index, 1);
                                dragGroups = dragGroups.map((g, i) => {
                                    return { ...g, index: i }
                                });
                                dropGroups.splice(dropGroup.index + 1, 0, dragGroup);
                                dropGroups = dropGroups.map((g, i) => {
                                    return { ...g, index: i, pcode: dropGroup.pcode }
                                });
                                modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                            }
                        }else { // 跨组
                            let dragGroups = group.filter(g => g.pcode === dragGroup.pcode).sort((a, b) => a.index - b.index);
                            let dropGroups = group.filter(g => g.pcode === dropGroup.pcode).sort((a, b) => a.index - b.index);
                            let dropChildrenGroups = group.filter(g => g.pcode === dropGroup.code).sort((a, b) => a.index - b.index);
                            
                            dragGroups.splice(dragGroup.index, 1);
                            dragGroups = dragGroups.map((g, i) => {
                                return { ...g, index: i }
                            });
                            if(dropPosition === -1) { // 目标前
                                dropGroups.splice(dropGroup.index, 0, dragGroup);
                                dropGroups = dropGroups.map((g, i) => {
                                    return { ...g, index: i, pcode: dropGroup.pcode }
                                });
                                modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                            }else if(dropPosition === 0) { // 目标内
                                dropChildrenGroups.push({
                                    ...dragGroup,
                                    index: dropChildrenGroups.length,
                                    pcode: dropGroup.code
                                });
                                modifyGroups = modifyGroups.concat(dragGroups, dropChildrenGroups);
                            }else if(dropPosition === 1) { // 目标后
                                dropGroups.splice(dropGroup.index + 1, 0, dragGroup);
                                dropGroups = dropGroups.map((g, i) => {
                                    return { ...g, index: i, pcode: dropGroup.pcode }
                                });
                                modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                            }
                        }
                    }
                }else { // 不跨级
                    if(dragGroup.pcode === dropGroup.pcode) { // 不跨组
                        let dGroups = group.filter(g => g.pcode === dragGroup.pcode).sort((a, b) => a.index - b.index);
                        dGroups.splice(dragGroup.index, 1, {code: 'temp', index: dragGroup.index});
                        if(dropPosition === -1) { // 目标前
                            dGroups.splice(dropGroup.index, 0, dragGroup);
                            dGroups = dGroups.filter(g => g.code !== 'temp').map((g, i) => {
                                return { ...g, index: i }
                            });
                            modifyGroups = modifyGroups.concat(dGroups);
                        }else if(dropPosition === 0) { // 目标内
                            return;
                        }else if(dropPosition === 1) { // 目标后
                            dGroups.splice(dropGroup.index + 1, 0, dragGroup);
                            dGroups = dGroups.filter(g => g.code !== 'temp').map((g, i) => {
                                return { ...g, index: i }
                            });
                            modifyGroups = modifyGroups.concat(dGroups);
                        }
                    }else { // 跨组
                        let dragGroups = group.filter(g => g.pcode === dragGroup.pcode).sort((a, b) => a.index - b.index);
                        let dropGroups = group.filter(g => g.pcode === dropGroup.pcode).sort((a, b) => a.index - b.index);
                        
                        dragGroups.splice(dragGroup.index, 1);
                        dragGroups = dragGroups.map((g, i) => {
                            return { ...g, index: i }
                        });
                        for(let i = 0; i < dropGroups.length; i++) {
                            if(dropGroups[i].code === dropGroup.code) {
                                if(dropPosition === -1) { // 目标前
                                    dropGroups.splice(i, 0, dragGroup);
                                    dropGroups = dropGroups.map((g, i) => {
                                        return { ...g, index: i, pcode: dropGroup.pcode }
                                    });
                                    modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                                }else if(dropPosition === 0) { // 目标内
                                    return; // 不允许
                                }else if(dropPosition === 1) { // 目标后
                                    dropGroups.splice(i + 1, 0, dragGroup);
                                    dropGroups = dropGroups.map((g, i) => {
                                        return { ...g, index: i, pcode: dropGroup.pcode }
                                    });
                                    modifyGroups = modifyGroups.concat(dragGroups, dropGroups);
                                }
                                break;
                            }
                        }
                    }
                }
                yield put({ type: 'remoteModifyGroups', groups: modifyGroups });
            }catch(e) {
                message.error('位置调整失败: ' + e.message);
            }
        },
        /**
         * 设置图表所属分组
         */
        *remoteSetGroup(action, { select, call, put }) {
            const { chart, group } = action;
            const chartCode = chart.code;
            const groupCode = group.code;
            try {
                let body = { 
                    id: chartCode,
                    groupId: groupCode
                }
                let res = yield call(service.fetch, {
                    url: URLS.GROUP_CHART_SET_GROUP,
                    body: body
                });
                if(!res.err && res.data.code > 0) {
                    yield put({ type: 'setChartGroup', chartCode, groupCode });
                }else {
                    message.error('设置分组失败: ' + (res.err || res.data.msg));
                }
            } catch(e) {
                message.error('设置分组失败: ' + e.message);
            }
        },
        *transfer(action, { put, call, select }) {
            const { userCode, chartCode } = action;
            const body = {
                userId: userCode,
                id: chartCode
            };
            try {
                const res = yield call(service.fetch, {
                    url: URLS.CHART_TRANSFER,
                    body
                });
                if(!res.err && res.data.code > 0) {
                    const chart = yield select(state => state.present.chart);
                    const list = chart.list;
                    for(let i = 0; i < list.length; i++) {
                        if(list[i].code === chartCode) {
                            list.splice(i, 1);
                            break;
                        }
                    }
                    yield put({ type: 'list', list });
                    message.success('移交成功');
                }else {
                    message.error('移交失败: ' + (res.err || res.data.msg));
                }
            }catch(e) {
                message.error('移交失败: ' + e.message);
            }
        },
    },
    subscriptions: {
        setup({ dispatch, history }) {
            dispatch({ type: 'reset' });
        }
    }
}