chartModule.js 10 KB

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