echartLine.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <template>
  2. <!-- 折线图 -->
  3. <div style="width: 100%; height: 100%">
  4. <div class="echartitem" style="width: 100%; height: 100%">
  5. <!-- <h3 class="selcal">{{ props.selval }}</h3> -->
  6. <div
  7. id="line2"
  8. ref="chartContainer"
  9. style="width: 100%; height: 100%"
  10. ></div>
  11. </div>
  12. </div>
  13. </template>
  14. <script setup>
  15. import { ref, onMounted, reactive, markRaw, inject, watch, nextTick } from "vue"
  16. import { RouterView, RouterLink } from "vue-router"
  17. import { request, uploadFile } from "@/utils/request"
  18. import { ElMessage, ElButton, ElDialog, ElSelect } from "element-plus"
  19. import * as echarts from "echarts"
  20. let vars = ref([])
  21. let vals = ref([])
  22. let series = ref([])
  23. let xdata = ref([])
  24. let chartContainer = ref(null)
  25. let myChart
  26. let chartDom = ref()
  27. let option = ref(null)
  28. let convergeDate = ref([])
  29. let curvedata = ref("")
  30. let state = reactive({
  31. instance: null
  32. })
  33. const props = defineProps({
  34. echartdata: {
  35. type: String
  36. },
  37. curvedata: {
  38. type: String
  39. },
  40. echartLineshow: {
  41. type: Boolean,
  42. default: false
  43. }
  44. })
  45. onMounted(() => {
  46. myChart = echarts.init(chartContainer.value)
  47. linechart()
  48. myChart.resize();
  49. })
  50. // 监听 scattershow 变化
  51. watch(
  52. () => props.echartLineshow,
  53. (newValue) => {
  54. if (newValue) {
  55. nextTick(() => {
  56. const chartDom = chartContainer.value
  57. if (chartDom) {
  58. myChart = echarts.init(chartDom)
  59. myChart.resize() // 手动调整图表大小
  60. linechart()// 设置图表配置
  61. }
  62. })
  63. }
  64. },
  65. { immediate: true }
  66. )
  67. //初始化
  68. const echatinit = () => {
  69. convergeDate.value = []
  70. series.value = []
  71. xdata.value = []
  72. }
  73. // 从websocket取数据
  74. // const getsockechart=(data)=>{
  75. // curvedata.value=data;
  76. // series.value=[];
  77. // let rows=curvedata.value;
  78. // for(let i=0;i<rows.length;i++){
  79. // vars.value=(rows[i].vars).split(",");
  80. // vals.value=(rows[i].vals).split(" ");
  81. // let data= vals.value.map((str) => Number(str));
  82. // convergeDate.value.push(data);
  83. // xdata.value.push(rows[i].step);
  84. // }
  85. // for(let j=0;j<vars.value.length;j++){
  86. // let firstColumn=(convergeDate.value.map((row) => row[j]));
  87. // let item= {
  88. // name:vars.value[j] ,
  89. // type: 'line',
  90. // stack: 'Total',
  91. // data: firstColumn
  92. // }
  93. // series.value.push(item)
  94. // }
  95. // myChart.setOption({
  96. // legend: {
  97. // data:vars.value
  98. // },
  99. // xAxis:{
  100. // data: xdata.value,
  101. // },
  102. // series: series.value
  103. // });
  104. // }
  105. const getsockechart = (data) => {
  106. curvedata.value = data;
  107. convergeDate.value = [];
  108. series.value = [];
  109. xdata.value = [];
  110. let rows = JSON.parse(curvedata.value);
  111. if (!rows.length) return; // 处理空数据
  112. let varsList = rows[0].vars.split(","); // 提取变量名
  113. vars.value = varsList;
  114. // 解析数据
  115. rows.forEach((row, index) => {
  116. xdata.value.push(index + 1); // 直接填充 X 轴数据
  117. convergeDate.value.push(row.vals.split(" ").map(Number)); // 解析数值数组
  118. });
  119. // 生成 series 数据
  120. series.value = varsList.map((varName, j) => ({
  121. name: varName,
  122. type: "line",
  123. stack: "Total",
  124. symbol: "none",
  125. data: convergeDate.value.map(row => row[j]) // 获取每列数据
  126. }));
  127. // 更新图表
  128. myChart.setOption({
  129. legend: {
  130. data: vars.value,
  131. },
  132. xAxis: {
  133. type: "category",
  134. data: xdata.value,
  135. },
  136. yAxis: {
  137. type: "value",
  138. },
  139. series: series.value,
  140. });
  141. };
  142. // 绘制所有数据的曲线
  143. const getshuju = (dataheader, data) => {
  144. console.log("散点图曲线数据1:", data);
  145. curvedata.value = data;
  146. series.value = [];
  147. xdata.value = [];
  148. vars.value = dataheader; // dataheader 是包含 label 和 prop 的数组
  149. vals.value = data;
  150. // 生成 x 轴数据
  151. for (let i = 0; i < vals.value.length; i++) {
  152. xdata.value.push(i); // x 轴数据为行索引
  153. }
  154. // 为每个字段生成一条曲线
  155. for (let colIndex = 0; colIndex < vars.value.length; colIndex++) {
  156. const prop = vars.value[colIndex].prop; // 获取当前列的字段名
  157. const label = vars.value[colIndex].label; // 获取当前列的显示名称
  158. let seriesData = [];
  159. // 遍历每一行,提取该列的数据
  160. for (let rowIndex = 0; rowIndex < vals.value.length; rowIndex++) {
  161. let value = vals.value[rowIndex][prop]; // 获取当前列的值
  162. seriesData.push([xdata.value[rowIndex], value]); // 添加到 seriesData
  163. }
  164. // 为每列数据生成一条曲线,使用 label 作为曲线的名字
  165. series.value.push({
  166. name: label, // 使用 label 作为曲线名
  167. type: "line", // 散点图类型
  168. data: seriesData,
  169. });
  170. }
  171. // 处理 ECharts 图表配置
  172. const myChart = echarts.init(chartContainer.value);
  173. myChart.setOption({
  174. legend: {
  175. data: vars.value.map(item => item.label), // 图例数据是列名
  176. },
  177. xAxis: {
  178. type: "category", // 设置为 "category" 表示 x 轴为分类数据
  179. data: xdata.value, // x 轴数据为索引
  180. },
  181. yAxis: {
  182. type: "value", // y 轴是数值型
  183. },
  184. series: series.value, // 使用生成的 series 数据
  185. });
  186. console.log("初始化后的 series:", series.value);
  187. };
  188. const selectshuju = (listcbval,listcbval2,dataheader, data) => {
  189. console.log("图曲线数据1:", data);
  190. curvedata.value = data;
  191. series.value = [];
  192. xdata.value = [];
  193. const ydata = ref([]);
  194. vars.value = dataheader; // dataheader 是包含 label 和 prop 的数组
  195. vals.value = data; // vals 是表格数据
  196. console.log('listcbval',listcbval)
  197. console.log('listcbval2',listcbval2)
  198. // 生成 X 轴数据和 Y 轴数据
  199. const xColumns = listcbval; // 获取 listcbval 中选择的列,作为 X 轴
  200. const yColumns = listcbval2; // 获取 listcbval2 中选择的列,作为 Y 轴
  201. console.log('xColumns',xColumns)
  202. console.log('yColumns',yColumns)
  203. if (xColumns.length === 0 || yColumns.length === 0) {
  204. console.error('请先选择 X 轴和 Y 轴的变量');
  205. return;
  206. }
  207. // 遍历每一行数据,为每对 X 和 Y 轴选择的列生成数据
  208. for (let i = 0; i < vals.value.length; i++) {
  209. let xValue = [];
  210. let yValue = [];
  211. // 对于每个 X 轴选择的列,提取该列数据
  212. for (let xCol of xColumns) {
  213. let xColumnData = vals.value[i][xCol]; // 获取当前行的 X 列数据
  214. xValue.push(xColumnData);
  215. }
  216. // 对于每个 Y 轴选择的列,提取该列数据
  217. for (let yCol of yColumns) {
  218. let yColumnData = vals.value[i][yCol]; // 获取当前行的 Y 列数据
  219. yValue.push(yColumnData);
  220. }
  221. xdata.value.push(xValue);
  222. ydata.value.push(yValue);
  223. }
  224. // 为每个 Y 轴数据列生成一条曲线
  225. for (let yColIndex = 0; yColIndex < yColumns.length; yColIndex++) {
  226. const label = yColumns[yColIndex]; // 使用 Y 轴列名作为曲线的标签
  227. let seriesData = [];
  228. for (let rowIndex = 0; rowIndex < vals.value.length; rowIndex++) {
  229. let xValues = xdata.value[rowIndex]; // 获取当前行的 X 轴数据
  230. let yValue = ydata.value[rowIndex][yColIndex]; // 获取当前行的 Y 轴数据
  231. // 将 X 和 Y 的数据组成成对的数据,传给 seriesData
  232. for (let xVal of xValues) {
  233. seriesData.push([xVal, yValue]); // 每对 (X, Y) 数据
  234. }
  235. }
  236. // 生成曲线的数据
  237. series.value.push({
  238. name: label, // 使用列名作为曲线名
  239. type: "line", // 散点图类型
  240. data: seriesData,
  241. });
  242. }
  243. // 处理 ECharts 图表配置
  244. const myChart = echarts.init(chartContainer.value);
  245. const xAxisLabel = dataheader.find(item => item.prop === xColumns[0])?.label || xColumns[0];
  246. const yAxisLabel = getFormattedYLabels(yColumns, dataheader);
  247. const yAxisLabel1 = getFormattedYLabels1(yColumns, dataheader);
  248. const chartTitle = `${xAxisLabel} - ${yAxisLabel1}`;
  249. myChart.setOption({
  250. title: {
  251. text: chartTitle,
  252. },
  253. legend: {
  254. data: yColumns, // 图例数据使用 Y 轴的列名
  255. },
  256. xAxis: {
  257. type: "value",
  258. data: xdata.value, // x 轴数据为 X 轴数据
  259. name: xAxisLabel,
  260. scale: true,
  261. },
  262. yAxis: {
  263. type: "value", // y 轴是数值型
  264. name: yAxisLabel,
  265. scale: true,
  266. },
  267. series: series.value, // 使用生成的 series 数据
  268. });
  269. console.log("初始化后的 series:", series.value);
  270. };
  271. const getFormattedYLabels = (columns, header, maxLength = 20) => {
  272. const labels = columns.map(col => {
  273. return header.find(item => item.prop === col)?.label || col;
  274. });
  275. const fullLabel = labels.join(',\n');
  276. // 若总字符数超出 maxLength,则截断
  277. if (fullLabel.length > maxLength) {
  278. return fullLabel.slice(0, maxLength) + '...';
  279. }
  280. return fullLabel;
  281. };
  282. // 不换行
  283. const getFormattedYLabels1 = (columns, header, maxLength = 20) => {
  284. const labels = columns.map(col => {
  285. return header.find(item => item.prop === col)?.label || col;
  286. });
  287. const fullLabel = labels.join(',');
  288. // 若总字符数超出 maxLength,则截断
  289. if (fullLabel.length > maxLength) {
  290. return fullLabel.slice(0, maxLength) + '...';
  291. }
  292. return fullLabel;
  293. };
  294. const linechart = () => {
  295. option.value = {
  296. title: {
  297. text: " ",
  298. left: "center",
  299. top: "5%",
  300. textStyle: {
  301. color: "#333333",
  302. fontSize: 16,
  303. fontWeight: "normal",
  304. fontFamily:'Microsoft YaHei'
  305. }
  306. },
  307. tooltip: {
  308. trigger: "axis", //item
  309. backgroundColor: "rgba(0,0,0,.6)",
  310. borderColor: "rgba(147, 235, 248, .8)",
  311. textStyle: {
  312. color: "#FFF"
  313. }
  314. },
  315. legend: {
  316. data: vars.value,
  317. orient: "vertical", // 图例方向 (horizontal水平, vertical垂直)
  318. right: "0%",
  319. top: "2%",
  320. itemWidth: 30,
  321. pageButtonItemGap: 10,
  322. pageButtonPosition: 'end',
  323. type: 'scroll',
  324. },
  325. grid: {
  326. show: false,
  327. left: "5%",
  328. right: "14%",
  329. bottom: "10%",
  330. containLabel: true
  331. },
  332. // toolbox: {
  333. // feature: {
  334. // saveAsImage: {}
  335. // }
  336. // },
  337. xAxis: {
  338. type: "value",
  339. boundaryGap: false,
  340. name: "",
  341. nameGap:30,
  342. nameTextStyle:{
  343. fontSize: 16,
  344. color:'#333333',
  345. fontFamily:'Microsoft YaHei'
  346. },
  347. nameLocation: 'middle',
  348. data: xdata.value,
  349. axisLine: {
  350. //X轴线
  351. show: true,
  352. lineStyle: {
  353. color: "#333333", // 线的颜色
  354. width: 1, // 线宽
  355. type: "solid" // 线的类型,默认为实线,可选:'solid', 'dashed', 'dotted'
  356. }
  357. },
  358. splitLine: {
  359. show: true // 如果不需要网格线,可以设置为 false
  360. }
  361. },
  362. yAxis: {
  363. type: "value",
  364. name:"",
  365. nameGap:50,
  366. nameTextStyle:{
  367. fontsize:16,
  368. color:'#333333',
  369. fontFamily:'Microsoft YaHei'
  370. },
  371. nameLocation:"middle",
  372. axisTick: {
  373. show: true
  374. },
  375. splitLine: {
  376. show:true
  377. },
  378. axisLine: {
  379. //Y轴线
  380. show: true,
  381. lineStyle: {
  382. color: "#333333", // 线的颜色
  383. width: 1, // 线宽
  384. type: "solid" // 线的类型,默认为实线,可选:'solid', 'dashed', 'dotted'
  385. }
  386. }
  387. },
  388. dataZoom: [
  389. {
  390. type:"inside",
  391. xAxisIndex: [0],
  392. },
  393. {
  394. type:'slider',
  395. xAxisIndex: [0],
  396. show: false,
  397. },
  398. ],
  399. series: series.value
  400. }
  401. option.value && myChart.setOption(option.value)
  402. window.onresize = function () {
  403. // 让图表自适应大小
  404. myChart.resize()
  405. }
  406. }
  407. // 监听数据变化,重绘图表
  408. // watch( myChart.value, () => {
  409. // console.log(11111)
  410. // window.onresize = function() {
  411. // // 让图表自适应大小
  412. // myChart.value.resize();
  413. // };
  414. // });
  415. //window.addEventListener('resize', handleResize);
  416. // watch(series, () => {
  417. // linechart();
  418. // }, { deep: true });
  419. // watch(() =>props.echartdata, (newValue, oldValue) => {
  420. // // 这里处理 echartData 变化的逻辑
  421. // console.log(11111)
  422. // console.log('echartData changed:', newValue);
  423. // // console.log(optmonitor1.value);
  424. // }, {deep: true ,immediate: true });
  425. defineExpose({ linechart, getsockechart, echatinit ,getshuju,selectshuju})
  426. </script>
  427. <style lang="scss" scoped>
  428. .selcal {
  429. position: absolute;
  430. top: 37%;
  431. width: 26px;
  432. color: #000;
  433. font-size: 14px;
  434. font-weight: bold;
  435. left: 5px;
  436. }
  437. .diedai {
  438. color: #000;
  439. font-size: 14px;
  440. padding-bottom: 20px;
  441. font-weight: bold;
  442. }
  443. </style>