import { message } from 'antd' import * as service from '../services/index' import URLS from '../constants/url' import parseChartOption from './parseChartOption' import moment from 'moment' function getBodyFilters(filters) { let bodyFilters = []; filters.filter(f => f.using).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(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: [], baseConfig: { dataSource: { code: '' }, viewType: '' }, aggregateTableConfig: { targetColumn: {}, statistics: [], groupBy: [] }, dataViewConfig: { viewColumns: [], sortColumn: {key: ''}, sortType: 'asc' }, barConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 }, lineConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 }, pieConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, threshold: 1000 }, scatterConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 }, styleConfig: { visibleIndex: true }, otherConfig:{}, description: '', filters: [], chartOption: {}, dirty: false, fetchConfig: {}, demo: false }, }, reducers: { /** * 更新model字段值 * 1. 进入撤销重做历史 */ setField(state, action) { const { name, value } = action; let obj = {}; obj[name] = value; let newState = Object.assign({}, state, obj); return Object.assign({}, newState, {dirty: true}); }, /** * 批量更新model字段值 * 1. 进入撤销重做历史 */ 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}); }, /** * 更新model字段值 * 1. 不进入撤销重做历史 */ silentSetField(state, action) { const { name, value } = action; let obj = {}; obj[name] = value; let newState = Object.assign({}, state, obj); return newState; }, /** * 批量更新model字段值 * 1. 不进入撤销重做历史 */ 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; let newState = Object.assign({}, state, { dirty }); return newState; } }, effects: { /** * 初始化批量更新model字段值 * 触发数据刷新、不进入撤销重做历史 */ *defaultChangeFields(action, { select, call, put }) { const { fields } = action; yield put({ type: 'silentSetFields', fields }); const { autoRefresh } = action; if(autoRefresh === undefined ? true : autoRefresh) { yield put({ type: 'fetchChartData' }); } }, /** * 更新model字段值 * 可能影响到数据刷新的model字段改变一般用该action */ *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' }); } }, /** * 批量更新model字段值 */ *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' }); } }, *changeDataSource(action, { select, call, put }) { const { dataSource } = action; const chartDesigner = yield select(state => state.present.chartDesigner); const { baseConfig } = chartDesigner; yield put({ type: 'changeField', name: 'baseConfig', value: { ...baseConfig, dataSource } }); yield put({ type: 'remoteDataColumn', code: dataSource.code }); }, *remoteQucikAdd(action, { select, call, put }) { try{ const { dataSource } = action; yield put({ type: 'silentSetFields', fields: [ { name: 'baseConfig', value: { dataSource: dataSource.code, viewType: '' } } ] }); let body = { chartName: dataSource.name + '_未命名', dataId: dataSource.code, describes: '', chartConfig: '{}', chartType: '', }; const res = yield call(service.fetch, { url: URLS.CHART_ADD, body: body }) if(!res.err && res.data.data > 0) { yield put({ type: 'chart/fetchList', mandatory: true }); yield put({ type: 'main/redirect', path: '/chart/' + res.data.data }); // yield put({ type: 'chart/remoteDetail', code: res.data.data }); }else { message.error('新增失败: ' + (res.err || res.data.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.present.chartDesigner); const { filters, baseConfig, pieConfig, lineConfig, aggregateTableConfig, dataViewConfig, barConfig, scatterConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig } = 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), }; // 基本属性 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) { yield put({ type: 'chart/fetchList', mandatory: true }); yield put({ type: 'main/redirect', path: '/chart/' + res.data.data , reload: true}); }else { message.error('创建副本失败: ' + (res.err || res.data.msg)); } }catch(e) { message.error('创建副本失败: ' + e.message); } }, *remoteDataColumn(action, { select, call, put }) { const code = action.code; try { const res = yield call(service.fetch, { url: URLS.DATASOURCE_QUERY_DATACOLUMNS, body: code }); if(!res.err && res.data.code > 0) { let resData = res.data.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 }); }else { message.error('请求列数据失败:' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'columns', value: [] }); } }catch(e) { message.error('请求列数据失败: ' + e.message); yield put({ type: 'silentSetField', name: 'columns', value: [] }); } }, *fetchChartData(action, { select, call, put }) { const chartDesigner = yield select(state => state.present.chartDesigner); const { baseConfig } = chartDesigner; const { viewType } = baseConfig; yield put({ type: 'silentSetField', name: 'fetchConfig', value: {} }); if(viewType === 'bar') { const { barConfig } = chartDesigner; if(barConfig.xAxis.column.value && barConfig.yAxis.column.value) { yield put({ type: 'fetchBarData' }); }else { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } }else if(viewType === 'pie') { const { pieConfig } = chartDesigner; if(pieConfig.xAxis.column.value && pieConfig.yAxis.column.value) { yield put({ type: 'fetchPieData' }); }else { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } }else if(viewType === 'line') { const { lineConfig } = chartDesigner; if(lineConfig.xAxis.column.value && lineConfig.yAxis.column.value) { yield put({ type: 'fetchLineData' }); }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 && dataViewConfig.sortColumn.key && dataViewConfig.sortType) { yield put({ type: 'fetchDataViewData' }); }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 { console.log('no viewType......') } }, *fetchBarData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, barConfig, filters } = chartDesigner; const body = { id: code, groups: barConfig.groupBy && barConfig.groupBy.key ? [barConfig.groupBy.key] : [], xAxis: { columnRename: barConfig.xAxis.column.value, columnType: barConfig.xAxis.column.type, showDataType: barConfig.xAxis.granularity.value }, yAxis: { columnRename: barConfig.yAxis.column.value, showDataType: barConfig.yAxis.gauge.value }, filters: getBodyFilters(filters), maxCount: barConfig.threshold }; let res = yield call(service.fetch, { url: URLS.CHART_BAR_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { let option = parseChartOption('bar', res.data.data, barConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求柱状图数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求柱状图数据失败: ' + e.message); } }, *fetchPieData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, pieConfig, filters } = chartDesigner; 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, columnName: pieConfig.yAxis.column.label, showDataType: pieConfig.yAxis.gauge.value }, filters: getBodyFilters(filters), maxCount: pieConfig.threshold }; let res = yield call(service.fetch, { url: URLS.CHART_PIE_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { let option = parseChartOption('pie', res.data.data, pieConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求饼图数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求饼图数据失败: ' + e.message); } }, *fetchLineData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, lineConfig, filters } = chartDesigner; const body = { id: code, xAxis: { columnRename: lineConfig.xAxis.column.value, columnType: lineConfig.xAxis.column.type }, yAxis: { columnRename: lineConfig.yAxis.column.value, showDataType: lineConfig.yAxis.gauge.value }, groups: lineConfig.groupBy && lineConfig.groupBy.key ? [lineConfig.groupBy.key] : [], filters: getBodyFilters(filters), maxCount: lineConfig.threshold }; let res = yield call(service.fetch, { url: URLS.CHART_LINE_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { let option = parseChartOption('line', res.data.data, lineConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求折线图数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求折线图数据失败: ' + e.message); } }, *fetchScatterData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, scatterConfig, filters } = chartDesigner; const body = { id: code, xAxis: { columnRename: scatterConfig.xAxis.column.value, columnType: scatterConfig.xAxis.column.type }, yAxis: { columnRename: scatterConfig.yAxis.column.value, showDataType: scatterConfig.yAxis.gauge.value }, groups: scatterConfig.groupBy && scatterConfig.groupBy.key ? [scatterConfig.groupBy.key] : [], filters: getBodyFilters(filters), maxCount: scatterConfig.threshold }; let res = yield call(service.fetch, { url: URLS.CHART_SCATTER_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { let option = parseChartOption('scatter', res.data.data, scatterConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求散点图数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求散点图数据失败: ' + e.message); } }, *fetchDataViewData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, dataViewConfig, filters } = chartDesigner; const { page, pageSize } = action; const body = { id: code, columnListName: dataViewConfig.viewColumns.map(c => c.name), sortColumn: dataViewConfig.sortColumn.key, sort: dataViewConfig.sortType, 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.err && res.data.code > 0) { let option = parseChartOption('dataView', res.data.data, dataViewConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求列表数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求列表数据失败: ' + e.message); } }, *fetchAggregateTableData(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, aggregateTableConfig, filters } = chartDesigner; const { targetColumn, statistics } = aggregateTableConfig; const body = { id: code, columnName: targetColumn.name, operatorList: statistics, groupByList: aggregateTableConfig.groupBy && aggregateTableConfig.groupBy.length > 0 ? aggregateTableConfig.groupBy.map(g => g.key) : [], filters: getBodyFilters(filters), testPage: { pageNum: 1, pageSize: 999, } }; let res = yield call(service.fetch, { url: URLS.CHART_AGGREGATETABLE_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { let option = parseChartOption('aggregateTable', res.data.data, aggregateTableConfig); yield put({ type: 'silentSetField', name: 'chartOption', value: option }); }else { message.error('请求统计数据失败: ' + (res.err || res.data.msg)); yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } yield put({ type: 'silentSetField', name: 'fetchConfig', value: body }); }catch(e) { yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); message.error('请求统计数据失败: ' + e.message); } }, /** * 将图表数据以表格的方式作为预览 */ *remoteChartDataList(action, { select, call, put }) { try { const chartDesigner = yield select(state => state.present.chartDesigner); const { code, baseConfig, aggregateTableConfig, lineConfig, barConfig, pieConfig, scatterConfig, dataViewConfig, filters } = chartDesigner; const { viewType } = baseConfig; const { page, pageSize } = action; let columns = []; let columnListName = []; let sortColumn = null; if(viewType === 'aggregateTable') { const { groupBy, targetColumn } = aggregateTableConfig; groupBy.map(g => ({ title: g.label, dataIndex: g.key, // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v })).concat({ title: targetColumn.label, dataIndex: targetColumn.name, render: targetColumn.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }).filter(x => !!x.dataIndex).forEach(x => { if(!columns.find(c => c.dataIndex === x.dataIndex)) { columns.push(x); } });; sortColumn = targetColumn.name; }else if(viewType === 'line') { const { groupBy, xAxis, yAxis } = lineConfig; [{ title: groupBy ? groupBy.label : '', dataIndex: groupBy ?groupBy.key : '', // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: xAxis.column.label, dataIndex: xAxis.column.value, render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: yAxis.column.label, dataIndex: yAxis.column.value, render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }].filter(x => !!x.dataIndex).forEach(x => { if(!columns.find(c => c.dataIndex === x.dataIndex)) { columns.push(x); } });; sortColumn = xAxis.column.value; }else if(viewType === 'bar') { const { groupBy, xAxis, yAxis } = barConfig; [{ title: groupBy ? groupBy.label : '', dataIndex: groupBy ? groupBy.key : '', // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: xAxis.column.label, dataIndex: xAxis.column.value, render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: yAxis.column.label, dataIndex: yAxis.column.value, render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }].filter(x => !!x.dataIndex).forEach(x => { if(!columns.find(c => c.dataIndex === x.dataIndex)) { columns.push(x); } });; sortColumn = xAxis.column.value; }else if(viewType === 'pie') { const { xAxis, yAxis } = pieConfig; [{ title: xAxis.column.label, dataIndex: xAxis.column.value, render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: yAxis.column.label, dataIndex: yAxis.column.value, render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }].filter(x => !!x.dataIndex).forEach(x => { if(!columns.find(c => c.dataIndex === x.dataIndex)) { columns.push(x); } });; sortColumn = xAxis.column.value; }else if(viewType === 'scatter') { const { groupBy, xAxis, yAxis } = scatterConfig; [{ title: groupBy ? groupBy.label : '', dataIndex: groupBy ? groupBy.key : '', // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: xAxis.column.label, dataIndex: xAxis.column.value, render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }, { title: yAxis.column.label, dataIndex: yAxis.column.value, render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v }].filter(x => !!x.dataIndex).forEach(x => { if(!columns.find(c => c.dataIndex === x.dataIndex)) { columns.push(x); } });; sortColumn = xAxis.column.value; }else if(viewType === 'dataView') { columns = dataViewConfig.viewColumns.map(c => ({ title: c.label, dataIndex: c.name, render: c.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v })); sortColumn = dataViewConfig.sortColumn.key; } columnListName = columns.map(c => c.dataIndex); const body = { id: code, columnListName: columnListName, sortColumn: sortColumn, sort: 'asc', filters: getBodyFilters(filters), testPage: { pageNum: page || 1, pageSize: pageSize || 25, } }; yield put({ type: 'dataList/setField', name: 'loading', value: true }); let res = yield call(service.fetch, { url: URLS.CHART_DATAVIEW_OPTION, body: body, timeout: 30000 }); if(!res.err && res.data.code > 0) { const { valueList } = res.data.data; const { list: dataSource, pageSize, total } = valueList; yield put({ type: 'dataList/setFields', fields: [ { name: 'columns', value: columns }, { name: 'dataSource', value: dataSource }, { name: 'pageSize', value: pageSize }, { name: 'total', value: total } ] }); }else { // message.error('请求列表数据失败: ' + (res.err || res.data.msg)); // yield put({ type: 'silentSetField', name: 'chartOption', value: {} }); } }catch(e) { message.error('请求数据列表失败: ' + e.message); }finally { yield put({ type: 'dataList/setField', name: 'loading', value: false }); } }, }, subscriptions: { setup({ dispatch, history }) { dispatch({ type: 'reset' }); }, }, };