dashboardDesigner.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. import html2canvas from 'html2canvas'
  2. import { message } from 'antd'
  3. import * as service from '../services/index'
  4. import parseChartOption from './parseChartOption'
  5. import moment from 'moment'
  6. import URLS from '../constants/url'
  7. import CHART_TYPE from './chartType.json'
  8. import { ArrayEquals } from '../utils/baseUtils.js'
  9. /**
  10. * 获得报表中图表的真实过滤规则
  11. */
  12. function getTrueFilters(item, filters) {
  13. let trueFilters = [];
  14. filters.forEach(f => {
  15. const { type, operator, value1, value2 } = f;
  16. if(((type === 'index' || type === 'string') && !!value1) || // 因为数字类型会生成数字字符串,所以为0也是可以正常传入条件的
  17. ((type === 'scale' || type === 'time' || type === 'ordinal') && (operator === 'between' ? (!!value1 && !!value2) : (!!value1))) ||
  18. (type === 'categorical' &&
  19. (operator === 'contain' || operator === 'notContain' ?
  20. (value1 && value1.length > 0) : (!!value1)
  21. )
  22. )) {
  23. if(f.operator === 'betweent' ? ( !!f.value1 && (f.value1.length ? f.value1.length > 0 : true) && !!f.value2 && (f.value2.length ? f.value2.length > 0 : true)) : (!!f.value1 && (f.value1.length ? f.value1.length > 0 : true))) {
  24. if(f.combined) {
  25. f.dataSource && f.dataSource.forEach(d => {
  26. if(d.dataSource.code === item.dataSourceCode) {
  27. trueFilters.push({
  28. dataSourceCode: d.dataSource.code,
  29. name: d.column.name,
  30. operator: f.operator,
  31. type: f.type,
  32. value1: f.value1,
  33. value2: f.value2,
  34. using: f.using
  35. });
  36. }
  37. });
  38. }else {
  39. if(f.dataSource.code === item.dataSourceCode) {
  40. trueFilters.push({
  41. dataSourceCode: f.dataSource.code,
  42. name: f.name,
  43. operator: f.operator,
  44. type: f.type,
  45. value1: f.value1,
  46. value2: f.value2,
  47. using: f.using
  48. });
  49. }
  50. }
  51. }
  52. }
  53. });
  54. return trueFilters;
  55. }
  56. function getBodyFilters(filters) {
  57. return filters.filter(f => f.using).map(f => {
  58. let { dataSourceCode, name, operator, type, value1, value2 } = f;
  59. let bodyFilter = {
  60. dataSourceCode,
  61. columnName: name,
  62. columnType: type,
  63. symbol: operator,
  64. value: value1
  65. };
  66. if(type === 'scale' && operator === 'between') {
  67. bodyFilter['value'] = value1 + ',' + value2;
  68. }else if(type === 'time') {
  69. let v1 = value1.dynamic ? value1.name : moment(value1).format('YYYY-MM-DD');
  70. let v2 = value2.dynamic ? value2.name : moment(value2).format('YYYY-MM-DD');
  71. if(operator === 'between') {
  72. bodyFilter['value'] = v1 + ',' + v2;
  73. }else {
  74. bodyFilter['value'] = v1;
  75. }
  76. }else if(type === 'categorical' && (operator === 'contain' || operator === 'notContain')) {
  77. bodyFilter['value'] = JSON.stringify(value1);
  78. }
  79. return bodyFilter;
  80. });
  81. }
  82. export default {
  83. namespace: 'dashboardDesigner',
  84. state: {
  85. originData: {
  86. code: null,
  87. name: '无标题',
  88. minLayoutHeight: 40,
  89. defaultLayout: { x: 0, y: 50, w: 12, h: 6, minW: 2, maxW: 12, minH: 1 },
  90. items: [],
  91. chartCodes: [], // 报表包含的所有图表
  92. description: '',
  93. thumbnail: '',
  94. dirty: false,
  95. editMode: true,
  96. filterColumns: [],
  97. filters: [],
  98. dataSources: [], // 图表关联的所有数据源
  99. relationColumns: [], // 自定义的列
  100. columnFetching: false,
  101. loading: false,
  102. shareCode: '', // 分享码
  103. demo: false,
  104. filterItems: [ // 可选过滤字段(选择图表时用)
  105. { name: 'name', label: '图表名称', type: 'string' },
  106. { name: 'description', label: '说明', type: 'string' },
  107. { name: 'creatorName', label: '创建人', type: 'string' },
  108. { name: 'createTime', label: '创建时间', type: 'date' },
  109. ],
  110. filterItem: { name: 'name', label: '图表名称', type: 'string' }, // 已选过滤字段(选择图表时用)
  111. styleConfig: {
  112. aggregateTable: { direction: ['vertical', 'horizontal'][0] },
  113. },
  114. },
  115. },
  116. reducers: {
  117. silentSetField(state, action) {
  118. const { name, value } = action;
  119. let obj = {};
  120. obj[name] = value;
  121. return Object.assign({}, state, obj);
  122. },
  123. setField(state, action) {
  124. const { name, value } = action;
  125. let obj = {};
  126. obj[name] = value;
  127. let newState = Object.assign({}, state, obj);
  128. return Object.assign({}, newState, {dirty: true});
  129. },
  130. silentSetFields(state, action) {
  131. const { fields } = action;
  132. let obj = {};
  133. fields.map(f => (obj[f.name] = f.value));
  134. let newState = Object.assign({}, state, obj);
  135. return newState;
  136. },
  137. setFields(state, action) {
  138. const { fields } = action;
  139. let obj = {};
  140. fields.map(f => (obj[f.name] = f.value));
  141. let newState = Object.assign({}, state, obj);
  142. return Object.assign({}, newState, {dirty: true});
  143. },
  144. setFilterItem(state, action) {
  145. const { item } = action;
  146. return Object.assign({}, state, {filterItem: item, filterLabel: ''});
  147. },
  148. setFilterLabel(state, action) {
  149. const { label } = action;
  150. return Object.assign({}, state, {filterLabel: label});
  151. },
  152. addChart(state, action) {
  153. let { items, dataSources, chartCodes, defaultLayout } = state;
  154. const { chart } = action;
  155. items = items.concat([{
  156. code: chart.code,
  157. chartCode: chart.code,
  158. name: chart.name,
  159. creatorCode: chart.creatorCode,
  160. creatorName: chart.creatorName,
  161. dataSourceCode: chart.dataSourceCode+'',
  162. dataSourceName: chart.dataSourceName,
  163. dataConnectCode: chart.dataConnectCode,
  164. dataConnectName: chart.dataConnectName,
  165. viewType: 'chart',
  166. chartType: chart.type,
  167. filters: chart.filters,
  168. layout: { ...defaultLayout },
  169. theme: chart.theme,
  170. styleConfig: chart.styleConfig,
  171. chartOption: chart.type === 'dataView' ? {
  172. total: 0,
  173. page: 1,
  174. pageSize: 0, // 设为0以保证在tableView组件渲染时getTableLayout方法中总能触发取数
  175. columns: [],
  176. dataSource: []
  177. } : null
  178. }]);
  179. chartCodes.push(chart.code);
  180. dataSources.findIndex(d => d.code === chart.dataSourceCode+'') === -1 && dataSources.push({
  181. code: chart.dataSourceCode+'',
  182. name: chart.dataSourceName
  183. });
  184. return Object.assign({}, state, {items, chartCodes, dataSources, dirty: true});
  185. },
  186. deleteItem(state, action) {
  187. let { items, chartCodes, dataSources, relationColumns, dirty } = state;
  188. const { item } = action;
  189. let count = 0;
  190. let targetIndex = -1;
  191. for(let i = 0; i < items.length; i++) {
  192. let tempItem = items[i];
  193. if(item.viewType === 'chart' && tempItem.dataSourceCode === item.dataSourceCode) {
  194. count++;
  195. }
  196. if(tempItem.code === item.code) {
  197. targetIndex = i;
  198. }
  199. }
  200. if(count === 1) {
  201. let idx = dataSources.findIndex(d => d.code === item.dataSourceCode);
  202. let idx2 = chartCodes.findIndex(c => c === item.chartCode);
  203. dataSources.splice(idx, 1);
  204. chartCodes.splice(idx2, 1);
  205. // 同时删除已定义的关联字段
  206. relationColumns.forEach(rc => {
  207. rc.relations.forEach((r, x) => {
  208. if(r.dataSourceCode === item.dataSourceCode) {
  209. rc.relations.splice(x, 1);
  210. }
  211. })
  212. });
  213. }
  214. if(targetIndex !== -1) {
  215. items.splice(targetIndex, 1);
  216. }
  217. return { ...state, dirty: dirty, items, dataSources };
  218. },
  219. addRichText(state, action) {
  220. let { items, defaultLayout } = state;
  221. items.push({
  222. code: Math.random() + '',
  223. viewType: 'richText',
  224. name: '',
  225. layout: defaultLayout
  226. });
  227. return Object.assign({}, state, {items, dirty: true});
  228. },
  229. changeLayout(state, action) {
  230. const { layout } = action;
  231. let { items, dirty, minLayoutHeight } = state;
  232. const ly = ['x', 'y', 'w', 'h'];
  233. for(let i = 0; i < items.length; i++) {
  234. if(layout[i]) { // 非删除引起
  235. for(let j = 0; j < ly.length; j ++) {
  236. if(items[i].layout[ly[j]] !== layout[i][ly[j]]) {
  237. dirty = true;
  238. items[i].layout[ly[j]] = layout[i][ly[j]];
  239. }
  240. }
  241. if(items[i].chartOption) {
  242. items[i].chartOption = { ...items[i].chartOption,
  243. page: 1,
  244. pageSize: (~~((layout[i][ly[3]] * minLayoutHeight + (layout[i][ly[3]] - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
  245. }
  246. }
  247. }else { // 删除引起
  248. dirty = true;
  249. }
  250. }
  251. return Object.assign({}, state, {items, dirty});
  252. },
  253. modifyItem(state, action) {
  254. let { item } = action;
  255. let { items } = state;
  256. let dirty = false;
  257. let idx = items.findIndex(i => i.code === item.code);
  258. if(idx > -1) {
  259. dirty = true;
  260. items[idx] = { ...items[idx], ...item };
  261. }
  262. return Object.assign({}, state, {items, dirty});
  263. },
  264. reset(state, action) {
  265. let newState = Object.assign({}, state, state.originData);
  266. return Object.assign({}, newState);
  267. },
  268. setEditMode(state, action) {
  269. const { checked } = action;
  270. return { ...state, editMode: checked };
  271. },
  272. addRelationColumn(state, action) {
  273. const { relationColumns } = state;
  274. relationColumns.push({
  275. code: Math.random()+'',
  276. name: '新字段',
  277. relations: []
  278. });
  279. return { ...state, relationColumns, dirty: true };
  280. },
  281. deleteRelationColumn(state, action) {
  282. const { code } = action;
  283. const { relationColumns } = state;
  284. let index = relationColumns.findIndex(r => r.code === code);
  285. relationColumns.splice(index, 1);
  286. return { ...state, relationColumns, dirty: true };
  287. },
  288. setRelationColumn(state, action) {
  289. const { code, relationColumn } = action
  290. const { relationColumns } = state;
  291. let index = relationColumns.findIndex(r => r.code === code);
  292. relationColumns[index] = relationColumn;
  293. return { ...state, relationColumns, dirty: true };
  294. },
  295. setItemFetching(state, action) {
  296. const { code, fetching } = action
  297. const { items } = state;
  298. let index = items.findIndex(item => item.code === code);
  299. items[index] = {
  300. ...items[index],
  301. fetching
  302. };
  303. return { ...state, items };
  304. },
  305. setItemField(state, action) {
  306. const { code, name, value } = action;
  307. const { items } = state;
  308. let index = items.findIndex(item => item.code === code);
  309. const targetItem = items[index];
  310. targetItem[name] = value;
  311. items[index] = {
  312. ...targetItem
  313. };
  314. return { ...state, items };
  315. },
  316. setItemFields(state, action) {
  317. const { code, fields } = action;
  318. const { items } = state;
  319. let index = items.findIndex(item => item.code === code);
  320. if(index !== -1) {
  321. const targetItem = items[index];
  322. fields.forEach(field => {
  323. targetItem[field.name] = field.value;
  324. });
  325. items[index] = {
  326. ...targetItem
  327. };
  328. }
  329. return { ...state, items };
  330. },
  331. },
  332. effects: {
  333. *addCharts(action, { call, put, select }) {
  334. const { charts } = action;
  335. try{
  336. for(let i = 0; i < charts.length; i++) {
  337. yield put({ type: 'addChart', chart: charts[i] });
  338. }
  339. }catch(e) {
  340. message.error('添加图表错误: ' + e.message);
  341. }
  342. },
  343. /**
  344. * 刷新报表
  345. */
  346. *refresh(action, { call, put, select }) {
  347. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  348. const { items, minLayoutHeight } = dashboardDesigner;
  349. for(let i = 0; i < items.length; i++) {
  350. let item = items[i];
  351. if(item.viewType === 'chart') {
  352. let page = item.chartOption ? item.chartOption.page : 1;
  353. let pageSize = item.chartOption ? item.chartOption.pageSize : (~~((item.layout.h * minLayoutHeight + (item.layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
  354. yield put({ type:'fetchChartData', item: items[i], mandatory: true, page, pageSize });
  355. }
  356. }
  357. },
  358. *remoteGetColumns(action, { call, put, select }) {
  359. const { dataSourceCode, mandatory } = action;
  360. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  361. const { dataSources } = dashboardDesigner;
  362. const body = dataSourceCode;
  363. let idx = dataSources.findIndex(d => d.code === dataSourceCode);
  364. if(!mandatory && dataSources[idx].columns && dataSources[idx].columns.length > 0) {
  365. return;
  366. }
  367. try {
  368. const res = yield call(service.fetch, {
  369. url: URLS.DATASOURCE_QUERY_DATACOLUMNS,
  370. body: body
  371. });
  372. if(!res.err && res.data.code > 0) {
  373. let resData = res.data.data;
  374. let columns = resData.map((c, i) => {
  375. return {
  376. key: i,
  377. name: c.columnName,
  378. label: c.columnRaname,
  379. type: c.columnType,
  380. groupable: c.isGroup==='1'?true:false,
  381. filterable: c.isFilter==='1'?true:false,
  382. bucketizable: c.isSubsection==='1'?true:false,
  383. selection: []
  384. }
  385. })
  386. dataSources[idx] = { ...dataSources[idx], columns }
  387. yield put({ type: 'silentSetField', name: 'dataSources', value: dataSources });
  388. }else {
  389. message.error('请求列数据失败:' + (res.err || res.data.msg));
  390. yield put({ type: 'silentSetField', name: 'dataSources', value: [] });
  391. }
  392. }catch(e) {
  393. message.error('请求列数据失败: ' + e.message);
  394. }
  395. },
  396. /**
  397. * 同时更改多个filter
  398. */
  399. *changeFilters(action, { put, call, select }) {
  400. try {
  401. const { filters } = action;
  402. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  403. const { items, minLayoutHeight } = dashboardDesigner;
  404. yield put({ type: 'silentSetField', name: 'filters', value: filters });
  405. for(let i = 0; i < items.length; i++) {
  406. let page = items[i].chartOption ? items[i].chartOption.page : 1;
  407. let pageSize = items[i].chartOption ? items[i].chartOption.pageSize : (~~((items[i].layout.h * minLayoutHeight + (items[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
  408. yield put({ type:'fetchChartData', item: items[i], mandatory: true, page, pageSize });
  409. }
  410. }catch(e) {
  411. message.error('更改过滤条件失败: ' + e.message);
  412. }
  413. },
  414. /**
  415. * 只更改一个filter
  416. */
  417. *changeFilter(action, { put, call, select }) {
  418. const { filter } = action;
  419. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  420. let { filters, items, minLayoutHeight } = dashboardDesigner;
  421. let targetDataSourceCodes = [];
  422. filters = filters.map(f => {
  423. if(f.key === filter.key) {
  424. if(f.combined) {
  425. targetDataSourceCodes = targetDataSourceCodes.concat(f.dataSource.map(d => d.dataSource.code));
  426. }else {
  427. if(targetDataSourceCodes.indexOf(f.dataSource.code) === -1) {
  428. targetDataSourceCodes.push(f.dataSource.code);
  429. }
  430. }
  431. return Object.assign({}, f, filter);
  432. }else {
  433. return f;
  434. }
  435. });
  436. // 找到filters有影响的item
  437. let targetItems = items.filter(item => targetDataSourceCodes.indexOf(item.dataSourceCode) !== -1);
  438. yield put({ type: 'silentSetField', name: 'filters', value: filters });
  439. for(let i = 0; i < targetItems.length; i++) {
  440. let page = targetItems[i].chartOption ? targetItems[i].chartOption.page : 1;
  441. let pageSize = targetItems[i].chartOption ? targetItems[i].chartOption.pageSize : (~~((targetItems[i].layout.h * minLayoutHeight + (targetItems[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
  442. yield put({ type:'fetchChartData', item: targetItems[i], mandatory: true, page, pageSize });
  443. }
  444. },
  445. *fetchChartData(action, { put, call, select }) {
  446. const { item, mandatory, page, pageSize } = action;
  447. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  448. const { creatorCode, filters } = dashboardDesigner;
  449. const { chartCode, theme } = item;
  450. if(!mandatory && !!item.chartOption) {
  451. return false;
  452. }
  453. try {
  454. yield put({ type: 'setItemFetching', code: chartCode, fetching: true });
  455. const body = {
  456. dashboardCreatorId: creatorCode,
  457. chartId: chartCode,
  458. filters: getBodyFilters(getTrueFilters(item, filters)),
  459. testPage: {
  460. pageNum: page|| 1,
  461. pageSize: pageSize || 99,
  462. }
  463. };
  464. const res = yield call(service.fetch, {
  465. url: URLS.CHART_OPTION,
  466. allow: true,
  467. body,
  468. timeout: 30000
  469. });
  470. if(!res.err && res.data.code > 0) {
  471. let resData = res.data.data;
  472. if(!resData) {
  473. yield put({ type: 'setItemFields', code: chartCode, fields: [
  474. { name: 'chartType', value: '' },
  475. { name: 'chartOption', value: {} }
  476. ] });
  477. return false;
  478. }
  479. let styleConfig = resData.styleConfig ? JSON.parse(resData.styleConfig) || {} : {};
  480. const { chartType : ctype, chartConfig: cfg } = resData.chartsColumnConfig;
  481. const chartType = CHART_TYPE[ctype];
  482. const chartConfig = JSON.parse(cfg);
  483. let chartOption = parseChartOption(chartType, resData, chartConfig, theme, styleConfig[chartType] || {});
  484. yield put({ type: 'setItemFields', code: chartCode, fields: [
  485. { name: 'chartType', value: chartType },
  486. { name: 'chartOption', value: chartOption }
  487. ] });
  488. return { chartType, chartOption };
  489. }else {
  490. yield put({ type: 'setItemFields', code: chartCode, fields: [
  491. { name: 'chartType', value: '' },
  492. { name: 'chartOption', value: {} }
  493. ] });
  494. message.error('请求图表展示数据失败: ' + (res.err || res.data.msg));
  495. return false;
  496. }
  497. }catch(e) {
  498. yield put({ type: 'setItemFields', code: chartCode, fields: [
  499. { name: 'chartType', value: '' },
  500. { name: 'chartOption', value: {} }
  501. ] });
  502. message.error('请求图表展示数据失败: ' + e.message);
  503. return false;
  504. }finally {
  505. yield put({ type: 'setItemFetching', code: chartCode, fetching: false });
  506. }
  507. },
  508. *saveWithThumbnail(action, {put, call}) {
  509. try {
  510. let thumbnail;
  511. yield put({ type: 'silentSetField', name: 'loading', value: true });
  512. let canvas = yield html2canvas(document.getElementsByClassName('viewlayout')[0]);
  513. thumbnail = canvas.toDataURL('image/png', 1.0);
  514. yield put({ type: 'setField', name: 'thumbnail', value: thumbnail });
  515. yield put({ type: 'dashboard/remoteModify' });
  516. yield put({ type: 'silentSetField', name: 'loading', value: false });
  517. }catch(e) {
  518. message.error('生成缩略图失败: ' + e.message);
  519. }
  520. },
  521. *encryptCode(action, { put, call, select }) {
  522. const { shareCode } = action;
  523. const res = yield call(service.fetch, {
  524. url: URLS.DASHBOARD_ENCRYPT_CODE,
  525. method: 'GET',
  526. body: {
  527. code: shareCode
  528. },
  529. });
  530. if(!res.err && res.data.code > 0) {
  531. let resData = res.data.data;
  532. return resData;
  533. }else {
  534. return false;
  535. }
  536. },
  537. *setRelationColumns(action, { put, call, select }) {
  538. const { relationColumns } = action;
  539. let targetDataSourceCodes = []; // 记录有改动的数据源code
  540. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  541. let { filters, items, minLayoutHeight } = dashboardDesigner;
  542. for(let i = filters.length - 1; i >= 0; i--) {
  543. let f = filters[i];
  544. let idx = relationColumns.findIndex(rc => rc.code === f.name);
  545. if(idx > -1) {
  546. // 如果改变的自定义条件已经添加到了筛选条件区域
  547. let nrc = relationColumns[idx];
  548. let willRemove = false;
  549. if(f.type !== nrc.relations[0].column.type) { // 如果改变了所选的列
  550. willRemove = true;
  551. }
  552. let oldDataSourceCodes = f.dataSource.map(d => d.dataSource.code);
  553. let oldColumns = f.dataSource.map(d => d.column.name);
  554. let newDataSourceCodes = nrc.relations.map(r => r.dataSource.code);
  555. let newColumns = nrc.relations.map(r => r.column.name);
  556. if(!ArrayEquals(oldDataSourceCodes, newDataSourceCodes) || !ArrayEquals(oldColumns, newColumns)) { // 如果改变了数据源或者数据列
  557. willRemove = true;
  558. }
  559. if(willRemove) {
  560. f.dataSource.forEach(d => {
  561. targetDataSourceCodes.push(d.dataSource.code);
  562. });
  563. filters.splice(i, 1); // 将该过滤字段移除
  564. }else { // 只剩下label会改变了
  565. filters[i] = { ...filters[i], label: nrc.name }
  566. }
  567. }else if(f.combined) { // 自定义字段已被删除
  568. f.dataSource.forEach(d => {
  569. targetDataSourceCodes.push(d.dataSource.code);
  570. });
  571. filters.splice(i, 1)
  572. }
  573. }
  574. yield put({ type: 'setFields', fields: [
  575. { name: 'relationColumns', value: relationColumns },
  576. { name: 'filters', value: filters },
  577. { name: 'dirty', value: true },
  578. ] });
  579. // 找到filters有影响的item
  580. let targetItems = items.filter(item => targetDataSourceCodes.indexOf(item.dataSourceCode) !== -1);
  581. yield put({ type: 'silentSetField', name: 'filters', value: filters });
  582. for(let i = 0; i < targetItems.length; i++) {
  583. let page = 1;
  584. let pageSize = targetItems[i].chartOption ? targetItems[i].chartOption.pageSize : (~~((targetItems[i].layout.h * minLayoutHeight + (targetItems[i].layout.h - 1) * 12 - 20 - 40 - 24 - 8 * 2)/38) + 1)
  585. yield put({ type:'fetchChartData', item: targetItems[i], mandatory: true, page, pageSize });
  586. }
  587. },
  588. *modifyItem(action, { select, call, put }) {
  589. try{
  590. const { item } = action;
  591. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  592. const { items } = dashboardDesigner;
  593. let idx = items.findIndex(i => i.code === item.code);
  594. if(idx > -1) {
  595. items[idx] = { ...items[idx], ...item };
  596. yield put({ type: 'setField', name: 'items', value: items });
  597. }
  598. }catch(e) {
  599. message.error('修改失败: ' + e.message);
  600. }
  601. },
  602. /**
  603. * dataView预览窗口取数
  604. */
  605. *fetchDataList(action, { select, call, put }) {
  606. const { item, page, pageSize } = action;
  607. const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
  608. const { creatorCode, filters } = dashboardDesigner;
  609. const { chartCode } = item;
  610. try {
  611. yield put({ type: 'dataList/setField', name: 'loading', value: true });
  612. const body = {
  613. dashboardCreatorId: creatorCode,
  614. chartId: chartCode,
  615. filters: getBodyFilters(getTrueFilters(item, filters)),
  616. testPage: {
  617. pageNum: page || 1,
  618. pageSize: pageSize || 25,
  619. }
  620. };
  621. const res = yield call(service.fetch, {
  622. url: URLS.CHART_OPTION,
  623. allow: true,
  624. body,
  625. timeout: 30000
  626. });
  627. if(!res.err && res.data.code > 0) {
  628. const { chartsColumnConfig, valueList } = res.data.data;
  629. const { list, pageSize, total } = valueList;
  630. const chartConfig = JSON.parse(chartsColumnConfig.chartConfig);
  631. const columns = chartConfig.viewColumns;
  632. yield put({ type: 'dataList/setFields', fields: [
  633. { name: 'columns', value: columns },
  634. { name: 'dataSource', value: list },
  635. { name: 'pageSize', value: pageSize },
  636. { name: 'total', value: total }
  637. ] });
  638. // 主动触发一次window的resize事件
  639. yield window.setTimeout(() => {
  640. var e = document.createEvent("Event");
  641. e.initEvent("resize", true, true);
  642. window.dispatchEvent(e);
  643. }, 20);
  644. }else {
  645. message.error('请求图表展示数据失败: ' + (res.err || res.data.msg));
  646. return false;
  647. }
  648. }catch(e) {
  649. message.error('请求图表展示数据错误: ' + e.message);
  650. return false;
  651. }finally {
  652. yield put({ type: 'dataList/setField', name: 'loading', value: false });
  653. }
  654. }
  655. },
  656. subscriptions: {
  657. setup({ dispatch, history}) {
  658. dispatch({ type: 'reset' });
  659. }
  660. }
  661. };