chartModule.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. //y轴显示数据
  3. Data:{
  4. type:number, 0(实线) 1虚线(预测) 2(填充)
  5. name:string,
  6. yIndex:number, //yName的下标索引(第几个y坐标轴)
  7. data:string[],
  8. }
  9. props:{
  10. closeTime:boolen //是否超过7天展示时间
  11. yName:string || string[], //y轴名称 一个或多个
  12. xData:string[], //x轴时间数据
  13. dataList:Data[], //折线的数据列表
  14. currentType:string, //当前选择那个tabs 需要外部更新当前选中type时传入
  15. typeList:string[], //图表下的选择类型列表
  16. onChange:(id:number)=>{}
  17. }
  18. */
  19. import { Tabs } from 'antd';
  20. import dayjs from 'dayjs';
  21. import * as echarts from 'echarts';
  22. import { useEffect, useRef, useState } from 'react';
  23. import styles from './index.less';
  24. const { TabPane } = Tabs;
  25. export const handleExportClick = () => {
  26. const canvas = document.getElementsByTagName('canvas');
  27. if (canvas && canvas.length > 0) {
  28. return canvas[0].toDataURL('image/png');
  29. }
  30. return null;
  31. };
  32. // 图表模块
  33. const ChartModule = (props) => {
  34. const chartDomRef = useRef();
  35. const chartRef = useRef();
  36. const {
  37. yName,
  38. closeTime = false,
  39. xData,
  40. dataList,
  41. typeList = [],
  42. onChange,
  43. currentType,
  44. chartType = 'line',
  45. legend = {},
  46. } = props;
  47. const [curType, setCurType] = useState(currentType);
  48. useEffect(() => {
  49. chartRef.current = echarts.init(chartDomRef.current);
  50. window.addEventListener('resize', resetChart);
  51. return () => window.removeEventListener('resize', resetChart);
  52. }, []);
  53. useEffect(() => {
  54. if (!chartRef.current || !dataList || !chartType === 'gauge') {
  55. return;
  56. }
  57. const option = { ...defaultOption };
  58. if (legend) {
  59. option.legend.textStyle = {
  60. ...option.legend.textStyle,
  61. ...legend.textStyle,
  62. };
  63. }
  64. let series = [];
  65. switch (chartType) {
  66. case 'gauge':
  67. series = option.series[dataList.type];
  68. series.data = [dataList];
  69. for (const key in option) {
  70. delete option[key];
  71. }
  72. option.series = [series];
  73. break;
  74. default:
  75. // 超过7天x轴显示年月日,不到7天显示时分
  76. if (!closeTime && xData && xData.length > 1) {
  77. const timeFormat =
  78. Math.abs(
  79. dayjs(xData[xData?.length - 1]).diff(dayjs(xData[0]), 'days'),
  80. ) + 1;
  81. option.xAxis.data = xData.map((item) => {
  82. if (timeFormat > 150 || chartType === 'bar') {
  83. return `${dayjs(item).format('MM')}月 `;
  84. }
  85. if (timeFormat >= 7) {
  86. return dayjs(item).format('YYYY-MM-DD');
  87. }
  88. return dayjs(item).format('HH:mm');
  89. });
  90. } else {
  91. option.xAxis.data = xData || [];
  92. }
  93. if (Array.isArray(yName)) {
  94. if (yName.length > 2) {
  95. option.grid.right = 120;
  96. }
  97. option.yAxis = yName.map((item, index) => {
  98. return { ...option.yAxis[index], name: item };
  99. });
  100. option.series = dataList.map((item) => {
  101. return {
  102. ...option.series[item.type],
  103. name: item.name,
  104. data: item.data,
  105. yAxisIndex: item.yIndex || 0,
  106. };
  107. });
  108. } else {
  109. option.grid.right = 30;
  110. option.yAxis = { ...option.yAxis[0], name: yName };
  111. option.series = dataList.map((item) => {
  112. return {
  113. ...option.series[item.type],
  114. name: item.name,
  115. data: item.data,
  116. barWidth: dataList.length >= 4 ? 8 : 20,
  117. };
  118. });
  119. }
  120. // 柱状图需要开启boundaryGap避免和Y轴重合
  121. if (chartType === 'bar') {
  122. option.xAxis.boundaryGap = true;
  123. delete option.legend.itemHeight;
  124. } else {
  125. option.xAxis.boundaryGap = false;
  126. option.legend.itemHeight = 0;
  127. }
  128. option.legend.data = dataList.map((item) => item.name);
  129. break;
  130. }
  131. // console.log(props, JSON.stringify(option));
  132. chartRef.current.clear();
  133. chartRef.current.setOption(option);
  134. chartRef.current.resize();
  135. // if (typeList?.length > 0) setCurType(typeList[0]);
  136. }, [yName, xData, dataList, typeList, chartType]);
  137. useEffect(() => {
  138. setCurType(currentType);
  139. }, [currentType]);
  140. const resetChart = () => {
  141. if (chartRef.current) chartRef.current.resize();
  142. };
  143. return (
  144. <div className={styles.content}>
  145. <div
  146. style={{
  147. width: chartType === 'gauge' ? '60%' : '100%',
  148. marginLeft: chartType === 'gauge' ? '20%' : '0',
  149. height: typeList?.length <= 0 ? '100%' : 'calc(100% - 0.57rem)',
  150. }}
  151. ref={chartDomRef}
  152. />
  153. {typeList?.length > 0 && (
  154. <Tabs
  155. activeKey={curType || typeList[0]}
  156. onChange={(type) => {
  157. setCurType(type);
  158. onChange(type);
  159. }}
  160. >
  161. {typeList.map((item) => (
  162. <TabPane tab={item} key={item} />
  163. ))}
  164. </Tabs>
  165. )}
  166. </div>
  167. );
  168. };
  169. export default ChartModule;
  170. const colors = [
  171. '#F5A623',
  172. '#4B9FEC',
  173. '#91cc75',
  174. '#5470c6',
  175. '#fac858',
  176. '#ee6666',
  177. '#73c0de',
  178. '#3ba272',
  179. '#fc8452',
  180. '#9a60b4',
  181. '#ea7ccc',
  182. ];
  183. const defaultOption = {
  184. color: colors,
  185. tooltip: {
  186. trigger: 'axis',
  187. textStyle: {
  188. fontSize: 24,
  189. },
  190. },
  191. grid: {
  192. bottom: 30,
  193. left: 70,
  194. right: 30,
  195. },
  196. xAxis: {
  197. type: 'category',
  198. boundaryGap: false,
  199. axisTick: { show: false },
  200. nameTextStyle: {
  201. fontSize: 24,
  202. },
  203. axisLabel: {
  204. fontSize: 24,
  205. },
  206. data: [
  207. '00:00',
  208. '01:15',
  209. '02:30',
  210. '03:45',
  211. '05:00',
  212. '06:15',
  213. '07:30',
  214. '08:45',
  215. ],
  216. },
  217. yAxis: [
  218. {
  219. type: 'value',
  220. name: '000',
  221. top: 20,
  222. nameTextStyle: {
  223. fontSize: 24,
  224. // align: 'left',
  225. padding: [0, 0, 20, 0],
  226. },
  227. axisLabel: {
  228. fontSize: 24,
  229. },
  230. axisLine: {
  231. show: false,
  232. // lineStyle: {
  233. // color: colors[0],
  234. // },
  235. },
  236. splitLine: {
  237. lineStyle: {
  238. type: 'dashed',
  239. },
  240. },
  241. },
  242. {
  243. type: 'value',
  244. name: '111',
  245. top: 20,
  246. position: 'right',
  247. nameTextStyle: {
  248. fontSize: 24,
  249. // align: 'left',
  250. padding: [0, 0, 20, 0],
  251. },
  252. axisLine: {
  253. show: true,
  254. lineStyle: {
  255. color: colors[7],
  256. },
  257. },
  258. splitLine: {
  259. lineStyle: {
  260. type: 'dashed',
  261. },
  262. },
  263. },
  264. {
  265. type: 'value',
  266. name: '222',
  267. top: 20,
  268. position: 'right',
  269. offset: 80,
  270. nameTextStyle: {
  271. fontSize: 24,
  272. // align: 'left',
  273. padding: [0, 0, 20, 0],
  274. },
  275. axisLine: {
  276. show: true,
  277. lineStyle: {
  278. color: colors[8],
  279. },
  280. },
  281. splitLine: {
  282. lineStyle: {
  283. type: 'dashed',
  284. },
  285. },
  286. },
  287. ],
  288. series: [
  289. {
  290. data: [820, 932, 901, 934, 1290, 1330, 1320],
  291. type: 'line',
  292. name: '进水水量',
  293. yAxisIndex: 0,
  294. smooth: 'true',
  295. // itemStyle:{
  296. // color:'#be7bbe',
  297. // },
  298. showSymbol: false, // 不展示拐点圆圈
  299. },
  300. {
  301. data: [130, 125, 828, 743, 1100],
  302. name: '预测出水量',
  303. yAxisIndex: 0,
  304. type: 'line',
  305. smooth: 'true',
  306. lineStyle: {
  307. normal: {
  308. width: 4,
  309. type: 'dashed',
  310. },
  311. },
  312. itemStyle: {
  313. color: '#be7bbe',
  314. },
  315. showSymbol: false, // 不展示拐点圆圈
  316. },
  317. {
  318. data: [820, 772, 901, 934, 1290, 1120, 1320],
  319. name: '实际出水量',
  320. yAxisIndex: 0,
  321. type: 'line',
  322. smooth: 'true',
  323. showSymbol: false, // 不展示拐点圆圈
  324. itemStyle: {
  325. color: '#50A3C8',
  326. },
  327. areaStyle: {
  328. origin: 'number',
  329. color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
  330. {
  331. offset: 0,
  332. color: 'rgba(31,114,150,1)',
  333. },
  334. {
  335. offset: 1,
  336. color: 'rgba(31,114,150,0.3)',
  337. },
  338. ]),
  339. },
  340. },
  341. {
  342. data: [120, 200, 150, 80, 70, 110, 130],
  343. name: '实际出水量',
  344. type: 'bar',
  345. barGap: 0.2,
  346. barWidth: 30,
  347. },
  348. {
  349. data: [
  350. {
  351. value: 19,
  352. name: '负荷率',
  353. },
  354. ],
  355. name: '负荷率',
  356. type: 'gauge',
  357. startAngle: 180,
  358. endAngle: 0,
  359. radius: '90%',
  360. min: 0,
  361. max: 100,
  362. splitNumber: 10,
  363. center: ['50%', '65%'],
  364. axisLine: {
  365. lineStyle: {
  366. width: 40,
  367. color: [
  368. [0.33, '#a4f428'],
  369. [0.66, '#f6a842'],
  370. [1, '#e02b42'],
  371. ],
  372. },
  373. },
  374. axisPointer: {
  375. show: true,
  376. },
  377. pointer: {
  378. icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z',
  379. length: '50%',
  380. width: 8,
  381. offsetCenter: [0, 0],
  382. itemStyle: {
  383. color: 'auto',
  384. },
  385. },
  386. axisTick: {
  387. show: false,
  388. },
  389. splitLine: {
  390. show: false,
  391. },
  392. axisLabel: {
  393. show: false,
  394. },
  395. title: {
  396. offsetCenter: [0, '25%'],
  397. fontSize: 24,
  398. },
  399. detail: {
  400. show: false,
  401. },
  402. },
  403. ],
  404. legend: {
  405. // 图例配置
  406. // icon:'arrow',
  407. // width:'2',
  408. itemHeight: 0, // 远点宽度为0不显示原点
  409. // right: '10%',
  410. data: ['进水水量', '预测出水量', '实际出水量'],
  411. lineStyle: {},
  412. textStyle: {
  413. fontSize: 24,
  414. },
  415. },
  416. // toolbox: {
  417. // show: true,
  418. // feature: {
  419. // // dataZoom: {
  420. // // yAxisIndex: 'none'
  421. // // },
  422. // // dataView: { readOnly: false },
  423. // // magicType: { type: ['line', 'bar'] },
  424. // restore: {},
  425. // saveAsImage: {},
  426. // },
  427. // },
  428. };