parseChartOption.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /**
  2. * 将请求返回的图表展示数据解析为前台展示用的config
  3. */
  4. import moment from 'moment';
  5. import EllipsisTooltip from '../components/common/ellipsisTooltip/index';
  6. import { deepAssign, numberFormat } from '../utils/baseUtils';
  7. import STATISTICS_OPTION from '../components/chartDesigner/sections/statisticsOption.json';
  8. import themes from '../components/chartDesigner/sections/style/theme/index';
  9. import EChartsMedia from './EChartsMedia';
  10. export default function(viewType, data, chartConfig, themeName, styleConfig) {
  11. if(!data) {
  12. return {};
  13. }
  14. try {
  15. let t = themes.find(t => t.name === themeName);
  16. let theme = t ? t.config : themes[0].config;
  17. let o, themeConfig;
  18. switch(viewType) {
  19. case 'bar': {
  20. themeConfig = deepAssign({}, theme.base, theme.bar, theme.xAxis, theme.yAxis, theme.dataZoom);
  21. o = barOption(data, chartConfig, themeConfig, styleConfig);
  22. break;
  23. }
  24. case 'pie': {
  25. themeConfig = deepAssign({}, theme.base, theme.pie);
  26. o = pieOption(data, chartConfig, themeConfig, styleConfig);
  27. break;
  28. }
  29. case 'line': {
  30. themeConfig = deepAssign({}, theme.base, theme.line, theme.xAxis, theme.yAxis, theme.dataZoom);
  31. o = lineOption(data, chartConfig, themeConfig, styleConfig);
  32. break;
  33. }
  34. case 'scatter': {
  35. themeConfig = deepAssign({}, theme.base, theme.scatter, theme.xAxis, theme.yAxis, theme.dataZoom);
  36. o = scatterOption(data, chartConfig, themeConfig, styleConfig);
  37. break;
  38. }
  39. case 'aggregateTable': {
  40. themeConfig = deepAssign({}, theme.base, theme.aggregateTable);
  41. o = aggregateTableOption(data, chartConfig, themeConfig, styleConfig);
  42. break;
  43. }case 'dataView' : {
  44. themeConfig = deepAssign({}, theme.base, theme.dataView);
  45. o = dataViewOption(data, chartConfig, themeConfig, styleConfig);
  46. break;
  47. }
  48. case 'indicator':
  49. themeConfig = deepAssign({}, theme.base, theme.indicator);
  50. o = indicatorOption(data, chartConfig, themeConfig, styleConfig);
  51. break;
  52. default:{
  53. o = {};
  54. break;
  55. }
  56. }
  57. return o;
  58. }catch(e) {
  59. console.error(e);
  60. }
  61. }
  62. function barOption(data, barConfig, themeConfig, styleConfig) {
  63. const { xAxis, yAxis, groupBy } = barConfig;
  64. const { barMaxWidth, barMinHeight, barGap, stack, labelVisible, labelPosition, labelDistance,
  65. labelRotate, xNameLocation, xNameGap, xNameRotate, xLabelHiddenCover, xLabelRotate, dataZoomVisible,
  66. xLabelMargin, yNameLocation, yNameGap, yNameRotate, labelZeroVisible } = styleConfig;
  67. let xTitle = xAxis?`${xAxis.column.label}`:null
  68. let yTitle = yAxis?`${yAxis.column.label}`:null
  69. let hasGroupBy = !!groupBy && !!groupBy.key;
  70. let legendVisible = hasGroupBy;
  71. data.serieses = data.serieses || [];
  72. let option = deepAssign({
  73. originConfig: {
  74. ...barConfig
  75. },
  76. tooltip : {
  77. trigger: "axis",
  78. axisPointer: {
  79. type: "cross"
  80. }
  81. },
  82. legend: {
  83. show: legendVisible
  84. },
  85. xAxis: [{
  86. type: 'category',
  87. nameLocation: (xNameLocation === '' || xNameLocation === null || xNameLocation === undefined) ? 'end' : xNameLocation,
  88. nameGap: (xNameGap === '' || xNameGap === null || xNameGap === undefined) ? 15 : Number(xNameGap),
  89. nameRotate: (xNameRotate === '' || xNameRotate === null || xNameRotate === undefined) ? 0 : Number(xNameRotate),
  90. axisLabel: {
  91. interval: xLabelHiddenCover === undefined ? 'auto' : (!!xLabelHiddenCover ? 'auto' : 0),
  92. rotate: (xLabelRotate === '' || xLabelRotate === null || xLabelRotate === undefined) ? 0 : Number(xLabelRotate),
  93. margin: (xLabelMargin === '' || xLabelMargin === null || xLabelMargin === undefined) ? 8 : Number(xLabelMargin),
  94. },
  95. data: data.xAxis.map(d => {
  96. let gv= xAxis.granularity.value;
  97. if(!d) {
  98. return '空';
  99. }
  100. let xv = d;
  101. if(gv === 'halfYear') {
  102. let arr = d.split('-H');
  103. xv = `${arr[0] || '-'} ${['上半年', '下半年'][arr[1] - 1]}`;
  104. }else if(gv === 'month') {
  105. let arr = d.split('-');
  106. xv = `${arr[0] || '-'}/${arr[1]}`;
  107. }else if(gv === 'quarter') {
  108. let arr = d.split('-');
  109. xv = `${arr[0] || '-'} ${['一', '二', '三', '四'][arr[1] - 1] + '季度'}`;
  110. }else if(gv === 'week') {
  111. let arr = d.split('-');
  112. xv = (arr[0] || '-') + ' ' + arr[1] + '周'
  113. }
  114. return xv;
  115. }),
  116. name: xTitle || '横轴',
  117. }],
  118. yAxis: [{
  119. name: yTitle || '纵轴',
  120. type: 'value',
  121. nameLocation: (yNameLocation === '' || yNameLocation === null || yNameLocation === undefined) ? 'end' : yNameLocation,
  122. nameGap: (yNameGap === '' || yNameGap === null || yNameGap === undefined) ? 15 : Number(yNameGap),
  123. nameRotate: (yNameRotate === '' || yNameRotate === null || yNameRotate === undefined) ? 0 : Number(yNameRotate),
  124. }],
  125. series: data.serieses.map(s => {
  126. return {
  127. name: !!groupBy && !!groupBy.key ? s.name : yAxis.column.label,
  128. type: 'bar',
  129. data: s.value.map(v => numberFormat(v)),
  130. barMaxWidth: barMaxWidth || 60,
  131. barMinHeight: barMinHeight || 0,
  132. barGap: barGap || '30%',
  133. stack: !!groupBy && !!groupBy.key && !!stack,
  134. label: {
  135. normal: {
  136. show: !!labelVisible,
  137. position: labelPosition || 'inside',
  138. distance: (labelDistance === '' || labelDistance === null || labelDistance === undefined) ? 5 : Number(labelDistance),
  139. rotate: (labelRotate === '' || labelRotate === null || labelRotate === undefined) ? 0 : Number(labelRotate),
  140. formatter: !labelZeroVisible ? (params) => {
  141. const { value } = params;
  142. return Number(value) === 0 ? '' : value;
  143. } : '{c}'
  144. }
  145. }
  146. }
  147. }),
  148. dataZoom: {
  149. show: !!dataZoomVisible
  150. }
  151. }, themeConfig);
  152. let mediaOption = {
  153. baseOption: option,
  154. media: EChartsMedia('bar', legendVisible, dataZoomVisible, {
  155. legend: option.legend
  156. })
  157. }
  158. return mediaOption;
  159. }
  160. function lineOption(data, lineConfig, themeConfig, styleConfig) {
  161. const { labelSymbol, xNameLocation, xNameGap, xNameRotate, xLabelRotate, xLabelMargin, xLabelHiddenCover,
  162. yNameLocation, yNameGap, yNameRotate, stack, labelVisible, labelPosition, labelDistance, labelRotate,
  163. lineSmooth, labelSymbolSize, dataZoomVisible } = styleConfig;
  164. const { xAxis, yAxis, groupBy } = lineConfig;
  165. let xTitle = xAxis?`${xAxis.column.label}`:null
  166. let yTitle = yAxis?`${yAxis.column.label}`:null
  167. let hasGroupBy = !!groupBy && !!groupBy.key;
  168. let legendVisible = hasGroupBy;
  169. data.serieses = data.serieses || [];
  170. let option = deepAssign({
  171. originConfig: {
  172. ...lineConfig
  173. },
  174. tooltip : {
  175. trigger: "axis",
  176. axisPointer: {
  177. type: "cross"
  178. }
  179. },
  180. legend: {
  181. show: legendVisible
  182. },
  183. xAxis: [{
  184. name: xTitle || '横轴',
  185. type: 'category',
  186. nameLocation: (xNameLocation === '' || xNameLocation === null || xNameLocation === undefined) ? 'end' : xNameLocation,
  187. nameGap: (xNameGap === '' || xNameGap === null || xNameGap === undefined) ? 15 : Number(xNameGap),
  188. nameRotate: (xNameRotate === '' || xNameRotate === null || xNameRotate === undefined) ? 0 : Number(xNameRotate),
  189. axisLabel: {
  190. interval: xLabelHiddenCover === undefined ? 'auto' : (!!xLabelHiddenCover ? 'auto' : 0),
  191. rotate: (xLabelRotate === '' || xLabelRotate === null || xLabelRotate === undefined) ? 0 : Number(xLabelRotate),
  192. margin: (xLabelMargin === '' || xLabelMargin === null || xLabelMargin === undefined) ? 8 : Number(xLabelMargin),
  193. },
  194. data: data.xAxis.map(d => {
  195. let gv= xAxis.granularity.value;
  196. if(!d) {
  197. return '空';
  198. }
  199. let xv = d;
  200. if(gv === 'halfYear') {
  201. let arr = d.split('-H');
  202. xv = `${arr[0] || '-'} ${['上半年', '下半年'][arr[1] - 1]}`;
  203. }else if(gv === 'month') {
  204. let arr = d.split('-');
  205. xv = `${arr[0] || '-'}/${arr[1]}`;
  206. }else if(gv === 'quarter') {
  207. let arr = d.split('-');
  208. xv = `${arr[0] || '-'} ${['一', '二', '三', '四'][arr[1] - 1] + '季度'}`;
  209. }else if(gv === 'week') {
  210. let arr = d.split('-');
  211. xv = (arr[0] || '-') + ' ' + arr[1] + '周'
  212. }
  213. return xv;
  214. }),
  215. }],
  216. yAxis: [{
  217. name: yTitle || '纵轴',
  218. type: 'value',
  219. nameLocation: (yNameLocation === '' || yNameLocation === null || yNameLocation === undefined) ? 'end' : yNameLocation,
  220. nameGap: (yNameGap === '' || yNameGap === null || yNameGap === undefined) ? 15 : Number(yNameGap),
  221. nameRotate: (yNameRotate === '' || yNameRotate === null || yNameRotate === undefined) ? 0 : Number(yNameRotate),
  222. }],
  223. series: data.serieses.map(s => {
  224. return {
  225. name: !!groupBy && !!groupBy.key ? s.name : yAxis.column.label,
  226. type: 'line',
  227. data: s.value.map(v => numberFormat(v)),
  228. stack: !!groupBy && !!groupBy.key && !!stack,
  229. label: {
  230. normal: {
  231. show: !!labelVisible,
  232. position: labelPosition || 'inside',
  233. distance: (labelDistance === '' || labelDistance === null || labelDistance === undefined) ? 5 : Number(labelDistance),
  234. rotate: (labelRotate === '' || labelRotate === null || labelRotate === undefined) ? 0 : Number(labelRotate),
  235. formatter: '{c}'
  236. }
  237. },
  238. symbol: !labelSymbol ? 'emptyCircle' : labelSymbol,
  239. symbolSize: (labelSymbolSize === '' || labelSymbolSize === null || labelSymbolSize === undefined) ? 4 : Number(labelSymbolSize),
  240. smooth: !!lineSmooth,
  241. }
  242. }),
  243. dataZoom: {
  244. show: !!dataZoomVisible
  245. }
  246. }, themeConfig);
  247. let mediaOption = {
  248. baseOption: option,
  249. media: EChartsMedia('line', legendVisible, dataZoomVisible, {
  250. legend: option.legend
  251. })
  252. }
  253. return mediaOption;
  254. }
  255. function pieOption(data, pieConfig, themeConfig, styleConfig) {
  256. let { labelHidden } = styleConfig;
  257. let { xAxis, yAxis } = pieConfig;
  258. let columnName = xAxis.column.label;
  259. let dataList = (data.serieses || [{ value: [] }])[0].value;
  260. let option = deepAssign({
  261. originConfig: {
  262. ...pieConfig
  263. },
  264. tooltip : {
  265. trigger: 'item',
  266. formatter: function(params) {
  267. let { seriesName, name, value, percent } = params;
  268. return `${seriesName}:&nbsp;&nbsp;&nbsp;&nbsp;${name}<br/>${yAxis.column.label}:&nbsp;&nbsp;${value}&nbsp;(${percent}%)`;
  269. }
  270. },
  271. grid: {
  272. left: 50,
  273. right: 50,
  274. top: 50,
  275. bottom: 50,
  276. containLabel: true
  277. },
  278. series : [{
  279. name: columnName,
  280. type: 'pie',
  281. radius: [0, '65%'],
  282. center: ['50%', '50%'],
  283. label: {
  284. normal: {
  285. show: labelHidden === undefined ? true : !labelHidden,
  286. },
  287. emphasis: {
  288. show: true,
  289. }
  290. },
  291. labelLine: {
  292. normal: {
  293. show: labelHidden === undefined ? true : !labelHidden,
  294. },
  295. emphasis: {
  296. show: true,
  297. }
  298. },
  299. avoidLabelOverlap: labelHidden === undefined ? true : !labelHidden,
  300. data: dataList.map(v => {
  301. let obj = { ...v };
  302. obj.value = numberFormat(obj.value)
  303. if(!v.name) {
  304. obj.name = '空'
  305. }else {
  306. let gv= pieConfig.xAxis.granularity.value;
  307. if(gv === 'halfYear') {
  308. let arr = v.name.split('-H');
  309. obj.name = `${arr[0] || '-'} ${['上半年', '下半年'][arr[1] - 1]}`;
  310. }else if(gv === 'month') {
  311. let arr = v.name.split('-');
  312. obj.name = `${arr[0] || '-'}/${arr[1]}`;
  313. }else if(gv === 'quarter') {
  314. let arr = v.name.split('-');
  315. obj.name = `${arr[0] || '-'} ${['一', '二', '三', '四'][arr[1] - 1] + '季度'}`;
  316. }else if(gv === 'week') {
  317. let arr = v.name.split('-');
  318. obj.name = (arr[0] || '-') + ' ' + arr[1] + '周'
  319. }
  320. }
  321. return obj;
  322. })
  323. }]
  324. }, themeConfig);
  325. let mediaOption = {
  326. baseOption: option,
  327. media: EChartsMedia('pie', true, false, {
  328. legend: option.legend
  329. })
  330. }
  331. return mediaOption;
  332. }
  333. function scatterOption(data, scatterConfig, themeConfig, styleConfig) {
  334. const { labelSymbol, xNameLocation, xNameGap, xNameRotate, yNameLocation, yNameGap, yNameRotate, labelSymbolSize, dataZoomVisible } = styleConfig;
  335. const { xAxis, yAxis, groupBy } = scatterConfig;
  336. let xTitle = xAxis?`${xAxis.column.label}`:null
  337. let yTitle = yAxis?`${yAxis.column.label}`:null;
  338. let hasGroupBy = !!groupBy && !!groupBy.key;
  339. let legendVisible = hasGroupBy;
  340. let option = deepAssign({
  341. originConfig: {
  342. ...scatterConfig
  343. },
  344. tooltip: {
  345. trigger: 'axis',
  346. axisPointer: {
  347. type: 'cross'
  348. }
  349. },
  350. legend: {
  351. show: legendVisible
  352. },
  353. xAxis : [{
  354. type : 'value',
  355. name: xTitle || '横轴',
  356. nameLocation: (xNameLocation === '' || xNameLocation === null || xNameLocation === undefined) ? 'end' : xNameLocation,
  357. nameGap: (xNameGap === '' || xNameGap === null || xNameGap === undefined) ? 15 : Number(xNameGap),
  358. nameRotate: (xNameRotate === '' || xNameRotate === null || xNameRotate === undefined) ? 0 : Number(xNameRotate),
  359. scale:true,
  360. splitLine: {
  361. show: true
  362. }
  363. }],
  364. yAxis : [{
  365. type : 'value',
  366. name: yTitle || '纵轴',
  367. nameLocation: (yNameLocation === '' || yNameLocation === null || yNameLocation === undefined) ? 'end' : yNameLocation,
  368. nameGap: (yNameGap === '' || yNameGap === null || yNameGap === undefined) ? 15 : Number(yNameGap),
  369. nameRotate: (yNameRotate === '' || yNameRotate === null || yNameRotate === undefined) ? 0 : Number(yNameRotate),
  370. scale:true,
  371. splitLine: {
  372. show: true
  373. },
  374. }],
  375. series : (data.serieses || []).map(s => {
  376. return {
  377. name: s.name,
  378. type: 'scatter',
  379. symbol: !labelSymbol ? 'circle' : labelSymbol,
  380. symbolSize: (labelSymbolSize === '' || labelSymbolSize === null || labelSymbolSize === undefined) ? 10 : Number(labelSymbolSize),
  381. data: s.mdata.map(m => {
  382. return [m.date, numberFormat(m.value)]
  383. })
  384. }
  385. }),
  386. dataZoom: {
  387. show: !!dataZoomVisible
  388. }
  389. }, themeConfig);
  390. let mediaOption = {
  391. baseOption: option,
  392. media: EChartsMedia('scatter', legendVisible, dataZoomVisible, {
  393. legend: option.legend
  394. })
  395. }
  396. return mediaOption;
  397. }
  398. function aggregateTableOption( data, aggregateTableConfig, themeConfig, styleConfig) {
  399. const { targetColumn, groupBy, statistics: statisticsNames } = aggregateTableConfig;
  400. const { direction } = styleConfig;
  401. let statistics = statisticsNames.map(s => {
  402. if(!s.name) {
  403. let f = STATISTICS_OPTION.find(o => o.value === s);
  404. return !!f ? {
  405. name: f.value,
  406. label: f.label
  407. } : null;
  408. }
  409. return s
  410. }).filter(s => !!s),
  411. group1Name = groupBy.length > 0 ? groupBy[0].key : null,
  412. group2Name = groupBy.length > 1 ? groupBy[1].key : null,
  413. group1s = [],
  414. group2s = [],
  415. tableData;
  416. const resData = data.valueList;
  417. if(!group1Name) { // 无分组
  418. let o = {};
  419. statistics.forEach(s => {
  420. o[s.name] = numberFormat(resData[s.name])
  421. });
  422. tableData = o;
  423. }else {
  424. if(!group2Name) { // 只有一个分组
  425. resData.forEach(d => {
  426. let v = d[group1Name];
  427. if(group1s.indexOf(v) === -1) {
  428. group1s.push(v);
  429. }
  430. });
  431. tableData = resData.map(d => {
  432. let o = {};
  433. statistics.forEach(s => {
  434. o[s.name] = numberFormat(d[s.name])
  435. });
  436. return o;
  437. });
  438. }else { // 有两个分组
  439. resData.forEach(d => {
  440. let v1 = d[group1Name];
  441. let v2 = d[group2Name];
  442. if(group1s.indexOf(v1) === -1) {
  443. group1s.push(v1);
  444. }
  445. if(group2s.indexOf(v2) === -1) {
  446. group2s.push(v2);
  447. }
  448. });
  449. tableData = group1s.map(g => {
  450. let obj = {},
  451. list = resData.filter(d => d[group1Name] === g)
  452. // .slice(0, 5);
  453. obj[group1Name] = g;
  454. obj['data'] = group2s.map(g => {
  455. let o = {};
  456. o[group2Name] = g;
  457. statistics.forEach(s => {
  458. let v = list.find(l => l[group2Name] === g);
  459. o[s.name] = v ? (typeof v[s.name] === 'number' ? numberFormat(v[s.name]) : '') : '';
  460. });
  461. return o;
  462. });
  463. return obj;
  464. });
  465. }
  466. }
  467. let option = {
  468. originConfig: {
  469. ...aggregateTableConfig
  470. },
  471. themeConfig,
  472. targetColumn,
  473. direction: direction || 'horizontal',
  474. group1Name: groupBy.length > 0 ? groupBy[0].key : null,
  475. group2Name: groupBy.length > 1 ? groupBy[1].key : null,
  476. group1s,
  477. group2s,
  478. statistics,
  479. data: tableData
  480. };
  481. console.log(option);
  482. return option;
  483. }
  484. function dataViewOption(data, dataViewConfig, themeConfig, styleConfig) {
  485. const { list, pageNum, pageSize, pages, total } = data.valueList;
  486. let { viewColumns } = dataViewConfig;
  487. let columns = viewColumns || [];
  488. let dataSource = list || [];
  489. let option = {
  490. originConfig: {
  491. ...dataViewConfig
  492. },
  493. themeConfig,
  494. columns: columns.map(c => {
  495. let obj = {
  496. title: c.label,
  497. dataIndex: c.name,
  498. }
  499. if(c.type === 'time') {
  500. obj.render = (v, r, i) => {
  501. let text = v === null ? '空' : moment(v).isValid() ? moment(v).format('YYYY-MM-DD') : v
  502. return <EllipsisTooltip title={text}>{text}</EllipsisTooltip>
  503. }
  504. }else {
  505. obj.render = v => {
  506. let text = v === null ? '空' : v
  507. return <EllipsisTooltip title={text}>{text}</EllipsisTooltip>
  508. }
  509. }
  510. return obj;
  511. }),
  512. dataSource: dataSource.map((d, i) => {
  513. return { ...d, key: i}
  514. }),
  515. page: pageNum,
  516. pageSize,
  517. pages,
  518. total,
  519. };
  520. return option;
  521. }
  522. function indicatorOption(data, indicatorConfig, themeConfig, styleConfig) {
  523. const { xAxis, yAxis, otherColumn } = indicatorConfig;
  524. let option = {
  525. originConfig: {
  526. ...indicatorConfig
  527. },
  528. themeConfig,
  529. data: (data.serieses || []).map(d => ({
  530. name: xAxis.column.value ? (xAxis.column.type === 'time' ? (moment(d.name).isValid() ? moment(d.name).format('YYYY-MM-DD') : d.name) : d.name) : undefined,
  531. key: `${yAxis.column.label}(${yAxis.gauge.label})`,
  532. value: d.value,
  533. others: otherColumn.map(c => ({
  534. name: c.label,
  535. value: c.type === 'time' ? (moment(d[c.value]).isValid() ? moment(d[c.value]).format('YYYY-MM-DD') : d[c.value]) : d[c.value]
  536. }))
  537. }))
  538. };
  539. return option;
  540. }