chartModule.js 9.6 KB

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