sagas.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import {
  21. call,
  22. put,
  23. all,
  24. fork,
  25. select,
  26. takeLatest,
  27. takeEvery
  28. } from 'redux-saga/effects'
  29. import { IWidgetFormed } from 'app/containers/Widget/types'
  30. import { ActionTypes } from './constants'
  31. import { DashboardActions, DashboardActionType } from './actions'
  32. import {
  33. makeSelectDashboard,
  34. makeSelectItems,
  35. makeSelectItemRelatedWidget,
  36. makeSelectItemInfo,
  37. makeSelectFormedViews,
  38. makeSelectWidgets
  39. } from './selectors'
  40. import { makeSelectShareType } from 'share/containers/App/selectors'
  41. import {
  42. makeSelectGlobalControlPanelFormValues,
  43. makeSelectLocalControlPanelFormValues
  44. } from 'app/containers/ControlPanel/selectors'
  45. import { ControlPanelTypes } from 'app/components/Control/constants'
  46. import { ActionTypes as AppActions } from 'share/containers/App/constants'
  47. import {
  48. dashboardConfigMigrationRecorder,
  49. widgetConfigMigrationRecorder
  50. } from 'app/utils/migrationRecorders'
  51. import {
  52. getRequestParams,
  53. getRequestBody,
  54. getUpdatedPagination,
  55. getCurrentControlValues,
  56. getInitialPagination
  57. } from 'app/containers/Dashboard/util'
  58. import {
  59. IShareDashboardDetailRaw,
  60. IShareWidgetDetailRaw,
  61. IShareDashboardItemInfo
  62. } from './types'
  63. import {
  64. IDashboardConfig,
  65. IDashboard,
  66. IQueryConditions,
  67. IDashboardItem
  68. } from 'app/containers/Dashboard/types'
  69. import { IShareFormedViews } from 'app/containers/View/types'
  70. import {
  71. IGlobalControlConditions,
  72. IGlobalControlConditionsByItem,
  73. ILocalControlConditions
  74. } from 'app/components/Control/types'
  75. import {
  76. IWidgetConfig,
  77. RenderType
  78. } from 'app/containers/Widget/components/Widget'
  79. import request, { IDavinciResponse } from 'utils/request'
  80. import { errorHandler, getErrorMessage } from 'utils/util'
  81. import api from 'utils/api'
  82. import { message } from 'antd'
  83. import { DownloadTypes } from 'app/containers/App/constants'
  84. import { localStorageCRUD, getPasswordUrl } from '../../util'
  85. import { operationWidgetProps } from 'app/components/DataDrill/abstract/widgetOperating'
  86. export function* getDashboard(action: DashboardActionType) {
  87. if (action.type !== ActionTypes.LOAD_SHARE_DASHBOARD) {
  88. return
  89. }
  90. const { dashboardGetted, loadDashboardFail } = DashboardActions
  91. const { token, reject } = action.payload
  92. const shareType = yield select(makeSelectShareType())
  93. const baseUrl = `${api.share}/dashboard/${token}`
  94. const requestUrl = getPasswordUrl(shareType, token, baseUrl)
  95. try {
  96. const result = yield call(request, requestUrl)
  97. const {
  98. widgets,
  99. views,
  100. relations,
  101. config,
  102. ...rest
  103. } = result.payload as IShareDashboardDetailRaw
  104. const parsedConfig: IDashboardConfig = JSON.parse(config || '{}')
  105. const dashboard = {
  106. ...rest,
  107. config: dashboardConfigMigrationRecorder(parsedConfig)
  108. }
  109. const formedWidgets = widgets.map((widget) => {
  110. const { config, ...rest } = widget
  111. const parsedConfig: IWidgetConfig = JSON.parse(config)
  112. return {
  113. ...rest,
  114. config: widgetConfigMigrationRecorder(parsedConfig, {
  115. viewId: widget.viewId
  116. })
  117. }
  118. })
  119. const formedViews = views.reduce(
  120. (obj, { id, model, variable, ...rest }) => ({
  121. ...obj,
  122. [id]: {
  123. ...rest,
  124. model: JSON.parse(model),
  125. variable: JSON.parse(variable)
  126. }
  127. }),
  128. {}
  129. )
  130. yield put(dashboardGetted(dashboard, relations, formedWidgets, formedViews))
  131. const getWidgets: IWidgetFormed = yield select(makeSelectWidgets())
  132. operationWidgetProps.widgetIntoPool(getWidgets)
  133. } catch (err) {
  134. yield put(loadDashboardFail())
  135. errorHandler(err)
  136. reject(err)
  137. }
  138. }
  139. export function* getWidget(action: DashboardActionType) {
  140. if (action.type !== ActionTypes.LOAD_SHARE_WIDGET) {
  141. return
  142. }
  143. const { widgetGetted } = DashboardActions
  144. const { token, resolve, reject } = action.payload
  145. const shareType = yield select(makeSelectShareType())
  146. const baseUrl = `${api.share}/widget/${token}`
  147. const requestUrl = getPasswordUrl(shareType, token, baseUrl)
  148. try {
  149. const result = yield call(request, requestUrl)
  150. const { widget, views } = result.payload as IShareWidgetDetailRaw
  151. const { config, ...rest } = widget
  152. const parsedConfig: IWidgetConfig = JSON.parse(config)
  153. const formedWidget = {
  154. ...rest,
  155. config: widgetConfigMigrationRecorder(parsedConfig, {
  156. viewId: widget.viewId
  157. })
  158. }
  159. const formedViews = views.reduce(
  160. (obj, { id, model, variable, ...rest }) => ({
  161. ...obj,
  162. [id]: {
  163. ...rest,
  164. model: JSON.parse(model),
  165. variable: JSON.parse(variable)
  166. }
  167. }),
  168. {}
  169. )
  170. yield put(widgetGetted(formedWidget, formedViews))
  171. const getWidgets: IWidgetFormed = yield select(makeSelectWidgets())
  172. operationWidgetProps.widgetIntoPool(getWidgets)
  173. if (resolve) {
  174. resolve(formedWidget, formedViews)
  175. }
  176. } catch (err) {
  177. errorHandler(err)
  178. reject(err)
  179. }
  180. }
  181. function* getData(
  182. renderType: RenderType,
  183. itemId: number,
  184. queryConditions: Partial<IQueryConditions>,
  185. resetPagination?: boolean
  186. ) {
  187. const { resultsetGetted, getResultsetFail } = DashboardActions
  188. const itemInfo: IShareDashboardItemInfo = yield select((state) =>
  189. makeSelectItemInfo()(state, itemId)
  190. )
  191. const relatedWidget: IWidgetFormed = yield select((state) =>
  192. makeSelectItemRelatedWidget()(state, itemId)
  193. )
  194. const requestParams = getRequestParams(
  195. relatedWidget,
  196. itemInfo.queryConditions,
  197. renderType === 'flush',
  198. queryConditions
  199. )
  200. if (resetPagination) {
  201. const initialPagination = getInitialPagination(relatedWidget)
  202. if (initialPagination) {
  203. const { pageNo, pageSize } = initialPagination
  204. requestParams.pagination = {
  205. ...requestParams.pagination,
  206. pageNo,
  207. pageSize
  208. }
  209. }
  210. }
  211. try {
  212. const result = yield call(request, {
  213. method: 'post',
  214. url: `${api.share}/data/${relatedWidget.dataToken}`,
  215. data: getRequestBody(requestParams)
  216. })
  217. result.payload.resultList = result.payload.resultList || []
  218. requestParams.pagination = getUpdatedPagination(
  219. requestParams.pagination,
  220. result.payload
  221. )
  222. yield put(
  223. resultsetGetted(renderType, itemId, requestParams, result.payload)
  224. )
  225. } catch (err) {
  226. yield put(getResultsetFail(itemId, getErrorMessage(err)))
  227. }
  228. }
  229. export function* getResultset(action: DashboardActionType) {
  230. if (action.type !== ActionTypes.LOAD_SHARE_RESULTSET) {
  231. return
  232. }
  233. const { renderType, itemId, queryConditions } = action.payload
  234. yield getData(renderType, itemId, queryConditions)
  235. }
  236. export function* getBatchDataWithControlValues(action: DashboardActionType) {
  237. if (action.type !== ActionTypes.LOAD_BATCH_DATA_WITH_CONTROL_VALUES) {
  238. return
  239. }
  240. const { type, itemId, formValues } = action.payload
  241. const formedViews: IShareFormedViews = yield select(makeSelectFormedViews())
  242. const currentItems: IDashboardItem[] = yield select(makeSelectItems())
  243. if (type === ControlPanelTypes.Global) {
  244. const currentDashboard: IDashboard = yield select(makeSelectDashboard())
  245. const globalControlFormValues = yield select(
  246. makeSelectGlobalControlPanelFormValues()
  247. )
  248. const globalControlConditionsByItem = getCurrentControlValues(
  249. type,
  250. currentDashboard.config.filters,
  251. formedViews,
  252. globalControlFormValues,
  253. formValues,
  254. currentItems
  255. )
  256. const globalControlConditionsByItemEntries: Array<[
  257. string,
  258. IGlobalControlConditions
  259. ]> = Object.entries(
  260. globalControlConditionsByItem as IGlobalControlConditionsByItem
  261. )
  262. while (globalControlConditionsByItemEntries.length) {
  263. const [itemId, queryConditions] = globalControlConditionsByItemEntries[0]
  264. yield fork(getData, 'clear', Number(itemId), queryConditions, true)
  265. globalControlConditionsByItemEntries.shift()
  266. }
  267. } else {
  268. const relatedWidget: IWidgetFormed = yield select((state) =>
  269. makeSelectItemRelatedWidget()(state, itemId)
  270. )
  271. const localControlFormValues = yield select((state) =>
  272. makeSelectLocalControlPanelFormValues()(state, itemId)
  273. )
  274. const localControlConditions = getCurrentControlValues(
  275. type,
  276. relatedWidget.config.controls,
  277. formedViews,
  278. localControlFormValues,
  279. formValues
  280. )
  281. yield getData(
  282. 'clear',
  283. itemId,
  284. localControlConditions as ILocalControlConditions,
  285. true
  286. )
  287. }
  288. }
  289. export function* getWidgetCsv(action: DashboardActionType) {
  290. if (action.type !== ActionTypes.LOAD_WIDGET_CSV) {
  291. return
  292. }
  293. const { widgetCsvLoaded, loadWidgetCsvFail } = DashboardActions
  294. const { itemId, requestParams, token } = action.payload
  295. const {
  296. filters,
  297. tempFilters,
  298. linkageFilters,
  299. globalFilters,
  300. variables,
  301. linkageVariables,
  302. globalVariables,
  303. ...rest
  304. } = requestParams
  305. try {
  306. const path = yield call(request, {
  307. method: 'post',
  308. url: `${api.share}/csv/${token}`,
  309. data: {
  310. ...rest,
  311. filters: filters
  312. .concat(tempFilters)
  313. .concat(linkageFilters)
  314. .concat(globalFilters),
  315. params: variables.concat(linkageVariables).concat(globalVariables)
  316. }
  317. })
  318. yield put(widgetCsvLoaded(itemId))
  319. location.href = path.payload
  320. // location.href = `data:application/octet-stream,${encodeURIComponent(asyncData)}`
  321. } catch (err) {
  322. yield put(loadWidgetCsvFail(itemId))
  323. errorHandler(err)
  324. }
  325. }
  326. export function* getSelectOptions(action: DashboardActionType) {
  327. if (action.type !== ActionTypes.LOAD_SELECT_OPTIONS) {
  328. return
  329. }
  330. const { selectOptionsLoaded, loadSelectOptionsFail } = DashboardActions
  331. try {
  332. const { controlKey, requestParams, itemId } = action.payload
  333. const formedViews: IShareFormedViews = yield select(makeSelectFormedViews())
  334. const requests = Object.entries(requestParams).map(([viewId, params]) => {
  335. const { columns, filters, variables, cache, expired } = params
  336. const { dataToken } = formedViews[viewId]
  337. return call(request, {
  338. method: 'post',
  339. url: `${api.share}/data/${dataToken}/distinctvalue`,
  340. data: {
  341. columns: Object.values(columns).filter((c) => !!c),
  342. filters,
  343. params: variables,
  344. cache,
  345. expired
  346. }
  347. })
  348. })
  349. const results: Array<IDavinciResponse<object[]>> = yield all(requests)
  350. yield put(
  351. selectOptionsLoaded(
  352. controlKey,
  353. results.reduce((arr, result) => arr.concat(result.payload), []),
  354. itemId
  355. )
  356. )
  357. } catch (err) {
  358. yield put(loadSelectOptionsFail(err))
  359. }
  360. }
  361. export function* getDownloadList(action: DashboardActionType) {
  362. if (action.type !== ActionTypes.LOAD_DOWNLOAD_LIST) {
  363. return
  364. }
  365. const { downloadListLoaded, loadDownloadListFail } = DashboardActions
  366. const { shareClinetId, token } = action.payload
  367. const shareType = yield select(makeSelectShareType())
  368. const baseUrl = `${api.download}/share/page/${shareClinetId}/${token}`
  369. const requestUrl = getPasswordUrl(shareType, token, baseUrl)
  370. try {
  371. const result = yield call(request, requestUrl)
  372. yield put(downloadListLoaded(result.payload))
  373. } catch (err) {
  374. yield put(loadDownloadListFail(err))
  375. errorHandler(err)
  376. }
  377. }
  378. export function* downloadFile(action: DashboardActionType) {
  379. if (action.type !== ActionTypes.DOWNLOAD_FILE) {
  380. return
  381. }
  382. const { fileDownloaded, downloadFileFail } = DashboardActions
  383. const { id, shareClinetId, token } = action.payload
  384. try {
  385. location.href = `${api.download}/share/record/file/${id}/${shareClinetId}/${token}`
  386. yield put(fileDownloaded(id))
  387. } catch (err) {
  388. yield put(downloadFileFail(err))
  389. errorHandler(err)
  390. }
  391. }
  392. export function* initiateDownloadTask(action: DashboardActionType) {
  393. if (action.type !== ActionTypes.INITIATE_DOWNLOAD_TASK) {
  394. return
  395. }
  396. const { DownloadTaskInitiated, initiateDownloadTaskFail } = DashboardActions
  397. const { shareClientId, itemId } = action.payload
  398. const currentDashboard: IDashboard = yield select(makeSelectDashboard())
  399. const currentItems: IDashboardItem[] = yield select(makeSelectItems())
  400. const currentDashboardFilters = currentDashboard?.config.filters || []
  401. const formedViews: IShareFormedViews = yield select(makeSelectFormedViews())
  402. const globalControlFormValues = yield select(
  403. makeSelectGlobalControlPanelFormValues()
  404. )
  405. const globalControlConditionsByItem: IGlobalControlConditionsByItem = getCurrentControlValues(
  406. ControlPanelTypes.Global,
  407. currentDashboardFilters,
  408. formedViews,
  409. globalControlFormValues,
  410. null,
  411. currentItems
  412. )
  413. const itemInfo: IShareDashboardItemInfo = yield select((state) =>
  414. makeSelectItemInfo()(state, itemId)
  415. )
  416. const relatedWidget: IWidgetFormed = yield select((state) =>
  417. makeSelectItemRelatedWidget()(state, itemId)
  418. )
  419. const localControlFormValues = yield select((state) =>
  420. makeSelectLocalControlPanelFormValues()(state, itemId)
  421. )
  422. const localControlConditions = getCurrentControlValues(
  423. ControlPanelTypes.Local,
  424. relatedWidget.config.controls,
  425. formedViews,
  426. localControlFormValues
  427. )
  428. const requestParams = getRequestParams(
  429. relatedWidget,
  430. itemInfo.queryConditions,
  431. false,
  432. {
  433. ...globalControlConditionsByItem[itemId],
  434. ...localControlConditions
  435. }
  436. )
  437. const { dataToken } = relatedWidget
  438. try {
  439. yield call(request, {
  440. method: 'POST',
  441. url: `${api.download}/share/submit/${DownloadTypes.Widget}/${shareClientId}/${dataToken}`,
  442. data: [
  443. {
  444. id: relatedWidget.id,
  445. param: {
  446. ...getRequestBody(requestParams),
  447. flush: true,
  448. pageNo: 0,
  449. pageSize: 0
  450. }
  451. }
  452. ]
  453. })
  454. message.success('下载任务创建成功!')
  455. yield put(DownloadTaskInitiated(itemId))
  456. } catch (err) {
  457. yield put(initiateDownloadTaskFail(err, itemId))
  458. errorHandler(err)
  459. }
  460. }
  461. export default function* rootDashboardSaga() {
  462. yield all([
  463. takeLatest(ActionTypes.LOAD_SHARE_DASHBOARD, getDashboard),
  464. takeEvery(ActionTypes.LOAD_SHARE_WIDGET, getWidget),
  465. takeEvery(ActionTypes.LOAD_SHARE_RESULTSET, getResultset),
  466. takeEvery(
  467. ActionTypes.LOAD_BATCH_DATA_WITH_CONTROL_VALUES,
  468. getBatchDataWithControlValues
  469. ),
  470. takeLatest(ActionTypes.LOAD_WIDGET_CSV, getWidgetCsv),
  471. takeEvery(ActionTypes.LOAD_SELECT_OPTIONS, getSelectOptions),
  472. takeLatest(ActionTypes.LOAD_DOWNLOAD_LIST, getDownloadList),
  473. takeLatest(ActionTypes.DOWNLOAD_FILE, downloadFile),
  474. takeEvery(ActionTypes.INITIATE_DOWNLOAD_TASK, initiateDownloadTask)
  475. ])
  476. }