/* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */ import { call, all, put, fork, select, takeLatest, takeEvery } from 'redux-saga/effects' import omit from 'lodash/omit' import { ActionTypes } from './constants' import { DashboardActions, DashboardActionType } from './actions' import { makeSelectCurrentDashboard, makeSelectWidgets, makeSelectItemRelatedWidget, makeSelectItemInfo, makeSelectCurrentItems } from './selectors' import { makeSelectGlobalControlPanelFormValues, makeSelectLocalControlPanelFormValues } from 'containers/ControlPanel/selectors' import { makeSelectFormedViews } from '../View/selectors' import { getRequestParams, getRequestBody, getCurrentControlValues, getUpdatedPagination, getInitialPagination } from './util' import { IDashboardDetailRaw, IDashboardConfig, IDashboardItemInfo, IDashboard, IQueryConditions, IDataDownloadStatistic, IDashboardItem } from './types' import { IGlobalControlConditionsByItem, IGlobalControlConditions, ILocalControlConditions } from 'app/components/Control/types' import { IWidgetFormed } from '../Widget/types' import { IFormedViews } from '../View/types' import { DownloadTypes } from '../App/constants' import { dashboardConfigMigrationRecorder } from 'app/utils/migrationRecorders' import { ControlPanelTypes } from 'app/components/Control/constants' import { RenderType } from '../Widget/components/Widget' import { CancelTokenSource } from 'axios' import request from 'utils/request' import { errorHandler, getErrorMessage } from 'utils/util' import { message } from 'antd' import api from 'utils/api' import { operationWidgetProps } from 'components/DataDrill/abstract/widgetOperating' export function* getDashboardDetail(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_DASHBOARD_DETAIL) { return } const { dashboardDetailLoaded, loadDashboardDetailFail } = DashboardActions const { portalId, dashboardId } = action.payload try { const result = yield call( request, `${api.portal}/${portalId}/dashboards/${dashboardId}` ) const { relations: items, views, config, ...rest } = result.payload as IDashboardDetailRaw const parsedConfig: IDashboardConfig = JSON.parse(config || '{}') const dashboard = { ...rest, config: dashboardConfigMigrationRecorder(parsedConfig) } const widgets: IWidgetFormed[] = yield select(makeSelectWidgets()) operationWidgetProps.widgetIntoPool(widgets) const formedViews: IFormedViews = views.reduce((obj, view) => { obj[view.id] = { ...view, model: JSON.parse(view.model || '{}'), variable: JSON.parse(view.variable || '[]') } return obj }, {}) yield put(dashboardDetailLoaded(dashboard, items, widgets, formedViews)) } catch (err) { yield put(loadDashboardDetailFail()) errorHandler(err) } } export function* addDashboardItems(action: DashboardActionType) { if (action.type !== ActionTypes.ADD_DASHBOARD_ITEMS) { return } const { dashboardItemsAdded, addDashboardItemsFail } = DashboardActions const { portalId, items, resolve } = action.payload try { const result = yield call(request, { method: 'post', url: `${api.portal}/${portalId}/dashboards/${items[0].dashboardId}/widgets`, data: items }) const widgets: IWidgetFormed[] = yield select(makeSelectWidgets()) const formedViews: IFormedViews = yield select(makeSelectFormedViews()) yield put(dashboardItemsAdded(result.payload, widgets, formedViews)) resolve(result.payload) } catch (err) { yield put(addDashboardItemsFail()) errorHandler(err) } } export function* editDashboardItem(action: DashboardActionType) { if (action.type !== ActionTypes.EDIT_DASHBOARD_ITEM) { return } const { dashboardItemEdited, editDashboardItemFail } = DashboardActions const { portalId, item, resolve } = action.payload try { yield call(request, { method: 'put', url: `${api.portal}/${portalId}/dashboards/widgets`, data: [item] }) yield put(dashboardItemEdited(item)) resolve() } catch (err) { yield put(editDashboardItemFail()) errorHandler(err) } } export function* editDashboardItems(action: DashboardActionType) { if (action.type !== ActionTypes.EDIT_DASHBOARD_ITEMS) { return } const { dashboardItemsEdited, editDashboardItemsFail } = DashboardActions const { portalId, items } = action.payload try { yield call(request, { method: 'put', url: `${api.portal}/${portalId}/dashboards/widgets`, data: items }) yield put(dashboardItemsEdited(items)) } catch (err) { yield put(editDashboardItemsFail()) errorHandler(err) } } export function* deleteDashboardItem(action: DashboardActionType) { if (action.type !== ActionTypes.DELETE_DASHBOARD_ITEM) { return } const { dashboardItemDeleted, deleteDashboardItemFail } = DashboardActions const { id, resolve } = action.payload try { yield call(request, { method: 'delete', url: `${api.portal}/dashboards/widgets/${id}` }) yield put(dashboardItemDeleted(id)) if (resolve) { resolve() } } catch (err) { yield put(deleteDashboardItemFail()) errorHandler(err) } } function* getData( renderType: RenderType, itemId: number, queryConditions: Partial, cancelTokenSource: CancelTokenSource, resetPagination?: boolean ) { const { dashboardItemDataLoaded, loadDashboardItemDataFail } = DashboardActions const itemInfo: IDashboardItemInfo = yield select((state) => makeSelectItemInfo()(state, itemId) ) const relatedWidget: IWidgetFormed = yield select((state) => makeSelectItemRelatedWidget()(state, itemId) ) const requestParams = getRequestParams( relatedWidget, itemInfo.queryConditions, renderType === 'flush', queryConditions ) if (resetPagination) { const initialPagination = getInitialPagination(relatedWidget) if (initialPagination) { const { pageNo, pageSize } = initialPagination requestParams.pagination = { ...requestParams.pagination, pageNo, pageSize } } } try { const result = yield call(request, { method: 'post', url: `${api.view}/${relatedWidget.viewId}/getdata`, data: getRequestBody(requestParams), cancelToken: cancelTokenSource.token }) result.payload = result.payload || {} const { payload } = result payload.resultList = payload.resultList || [] requestParams.pagination = getUpdatedPagination( requestParams.pagination, result.payload ) yield put( dashboardItemDataLoaded( renderType, itemId, requestParams, result.payload, { ...requestParams, widget: relatedWidget } ) ) } catch (err) { yield put(loadDashboardItemDataFail(itemId, getErrorMessage(err))) } } export function* getDashboardItemData(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_DASHBOARD_ITEM_DATA) { return } const { renderType, itemId, queryConditions, cancelTokenSource } = action.payload yield getData(renderType, itemId, queryConditions, cancelTokenSource) } export function* getBatchDataWithControlValues(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_BATCH_DATA_WITH_CONTROL_VALUES) { return } const { type, itemId, formValues, cancelTokenSource } = action.payload const formedViews: IFormedViews = yield select(makeSelectFormedViews()) const currentItems: IDashboardItem[] = yield select(makeSelectCurrentItems()) if (type === ControlPanelTypes.Global) { const currentDashboard: IDashboard = yield select( makeSelectCurrentDashboard() ) const globalControlFormValues = yield select( makeSelectGlobalControlPanelFormValues() ) const globalControlConditionsByItem = getCurrentControlValues( type, currentDashboard.config.filters, formedViews, globalControlFormValues, formValues, currentItems ) const globalControlConditionsByItemEntries: Array<[ string, IGlobalControlConditions ]> = Object.entries( globalControlConditionsByItem as IGlobalControlConditionsByItem ) while (globalControlConditionsByItemEntries.length) { const [itemId, queryConditions] = globalControlConditionsByItemEntries[0] yield fork( getData, 'clear', Number(itemId), queryConditions, cancelTokenSource, true ) globalControlConditionsByItemEntries.shift() } } else { const relatedWidget: IWidgetFormed = yield select((state) => makeSelectItemRelatedWidget()(state, itemId) ) const localControlFormValues = yield select((state) => makeSelectLocalControlPanelFormValues()(state, itemId) ) const localControlConditions = getCurrentControlValues( type, relatedWidget.config.controls, formedViews, localControlFormValues, formValues ) yield getData( 'clear', itemId, localControlConditions as ILocalControlConditions, cancelTokenSource, true ) } } function getDownloadInfo( type: DownloadTypes, itemId: number, itemInfo: IDashboardItemInfo, relatedWidget: IWidgetFormed, formedViews: IFormedViews, localControlFormValues: object, globalControlConditions: IGlobalControlConditions ): IDataDownloadStatistic { const localControlConditions = getCurrentControlValues( ControlPanelTypes.Local, relatedWidget.config.controls, formedViews, localControlFormValues ) const requestParams = getRequestParams( relatedWidget, itemInfo.queryConditions, false, { ...globalControlConditions, ...localControlConditions } ) const id = type === DownloadTypes.Dashboard ? itemId : relatedWidget.id return { id, param: { ...getRequestBody(requestParams), flush: true, pageNo: 0, pageSize: 0 }, itemId, widget: relatedWidget } } export function* initiateDownloadTask(action: DashboardActionType) { if (action.type !== ActionTypes.INITIATE_DOWNLOAD_TASK) { return } const { DownloadTaskInitiated, initiateDownloadTaskFail } = DashboardActions const { type, itemId } = action.payload const currentDashboard: IDashboard = yield select( makeSelectCurrentDashboard() ) const formedViews: IFormedViews = yield select(makeSelectFormedViews()) const globalControlFormValues = yield select( makeSelectGlobalControlPanelFormValues() ) const currentItems = yield select(makeSelectCurrentItems()) const globalControlConditionsByItem: IGlobalControlConditionsByItem = getCurrentControlValues( ControlPanelTypes.Global, currentDashboard.config.filters, formedViews, globalControlFormValues, null, currentItems ) let id = action.payload.id const downloadInfo: IDataDownloadStatistic[] = [] if (type === DownloadTypes.Dashboard) { const itemIds = currentItems.map((item) => item.id) while (itemIds.length) { const itemId = itemIds[0] const globalControlConditions = globalControlConditionsByItem[itemId] const itemInfo: IDashboardItemInfo = yield select((state) => makeSelectItemInfo()(state, itemId) ) const relatedWidget: IWidgetFormed = yield select((state) => makeSelectItemRelatedWidget()(state, itemId) ) const localControlFormValues = yield select((state) => makeSelectLocalControlPanelFormValues()(state, itemId) ) downloadInfo.push( getDownloadInfo( type, itemId, itemInfo, relatedWidget, formedViews, localControlFormValues, globalControlConditions ) ) itemIds.shift() } } else { const itemInfo: IDashboardItemInfo = yield select((state) => makeSelectItemInfo()(state, itemId) ) const relatedWidget: IWidgetFormed = yield select((state) => makeSelectItemRelatedWidget()(state, itemId) ) const localControlFormValues = yield select((state) => makeSelectLocalControlPanelFormValues()(state, itemId) ) id = relatedWidget.id downloadInfo.push( getDownloadInfo( type, itemId, itemInfo, relatedWidget, formedViews, localControlFormValues, globalControlConditionsByItem[itemId] ) ) } try { yield call(request, { method: 'POST', url: `${api.download}/submit/${type}/${id}`, data: downloadInfo.map((d) => omit(d, 'widget', 'itemId')) }) message.success('下载任务创建成功!') yield put(DownloadTaskInitiated(type, downloadInfo, itemId)) } catch (err) { yield put(initiateDownloadTaskFail(err, type, itemId)) errorHandler(err) } } export function* getDashboardShareLink(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_DASHBOARD_SHARE_LINK) { return } const { dashboardAuthorizedShareLinkLoaded, dashboardShareLinkLoaded, dashboardPasswordShareLinkLoaded, loadDashboardShareLinkFail } = DashboardActions const { id, mode, permission, expired, roles, viewers } = action.payload.params let requestData = null switch (mode) { case 'AUTH': requestData = { mode, expired, permission, roles, viewers } break case 'PASSWORD': case 'NORMAL': requestData = { mode, expired } break default: break } try { const result = yield call(request, { method: 'post', url: `${api.portal}/dashboards/${id}/share`, data: requestData }) const { token, password } = result.payload switch (mode) { case 'AUTH': yield put(dashboardAuthorizedShareLinkLoaded(token)) break case 'PASSWORD': yield put(dashboardPasswordShareLinkLoaded(token, password)) break case 'NORMAL': yield put(dashboardShareLinkLoaded(token)) break default: break } } catch (err) { yield put(loadDashboardShareLinkFail()) errorHandler(err) } } export function* getWidgetShareLink(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_WIDGET_SHARE_LINK) { return } const { widgetAuthorizedShareLinkLoaded, widgetPasswordShareLinkLoaded, widgetShareLinkLoaded, loadWidgetShareLinkFail } = DashboardActions const { id, itemId, mode, expired, permission, roles, viewers } = action.payload.params let requestData = null switch (mode) { case 'AUTH': requestData = { mode, expired, permission, roles, viewers } break case 'PASSWORD': case 'NORMAL': requestData = { mode, expired } break default: break } try { const result = yield call(request, { method: 'post', url: `${api.widget}/${id}/share`, data: requestData }) const { token, password } = result.payload switch (mode) { case 'AUTH': yield put(widgetAuthorizedShareLinkLoaded(token, itemId)) break case 'PASSWORD': yield put(widgetPasswordShareLinkLoaded(token, password, itemId)) break case 'NORMAL': yield put(widgetShareLinkLoaded(token, itemId)) break default: break } } catch (err) { yield put(loadWidgetShareLinkFail(itemId)) errorHandler(err) } } export function* getWidgetCsv(action: DashboardActionType) { if (action.type !== ActionTypes.LOAD_WIDGET_CSV) { return } const { widgetCsvLoaded, loadWidgetCsvFail } = DashboardActions const { itemId, widgetId, requestParams } = action.payload // @TODO combine widget static filters with local filters const { filters, tempFilters, linkageFilters, globalFilters, variables, linkageVariables, globalVariables, ...rest } = requestParams try { const path = yield call(request, { method: 'post', url: `${api.widget}/${widgetId}/excel`, data: { ...rest, filters: filters .concat(tempFilters) .concat(linkageFilters) .concat(globalFilters), params: variables.concat(linkageVariables).concat(globalVariables) } }) yield put(widgetCsvLoaded(itemId)) location.href = path.payload // location.href = `data:application/octet-stream,${encodeURIComponent(asyncData)}` } catch (err) { yield put(loadWidgetCsvFail(itemId)) errorHandler(err) } } export default function* rootDashboardSaga() { yield all([ takeLatest(ActionTypes.LOAD_DASHBOARD_DETAIL, getDashboardDetail), takeEvery(ActionTypes.ADD_DASHBOARD_ITEMS, addDashboardItems), takeEvery(ActionTypes.EDIT_DASHBOARD_ITEM, editDashboardItem), takeEvery(ActionTypes.EDIT_DASHBOARD_ITEMS, editDashboardItems), takeEvery(ActionTypes.DELETE_DASHBOARD_ITEM, deleteDashboardItem), takeEvery(ActionTypes.LOAD_DASHBOARD_ITEM_DATA, getDashboardItemData), takeEvery( ActionTypes.LOAD_BATCH_DATA_WITH_CONTROL_VALUES, getBatchDataWithControlValues ), takeEvery(ActionTypes.INITIATE_DOWNLOAD_TASK, initiateDownloadTask), takeLatest(ActionTypes.LOAD_DASHBOARD_SHARE_LINK, getDashboardShareLink), takeLatest(ActionTypes.LOAD_WIDGET_SHARE_LINK, getWidgetShareLink), takeLatest(ActionTypes.LOAD_WIDGET_CSV, getWidgetCsv) ]) }