123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- <!-- <template>
- <div :class="className" :style="{height:height,width:width}" />
- </template>
- <script>
- import echarts from 'echarts'
- require('echarts/theme/macarons') // echarts theme
- import resize from './mixins/resize'
- export default {
- mixins: [resize],
- props: {
- className: {
- type: String,
- default: 'chart'
- },
- width: {
- type: String,
- default: '100%'
- },
- height: {
- type: String,
- default: '350px'
- },
- autoResize: {
- type: Boolean,
- default: true
- },
- chartData: {
- type: Object,
- required: true
- }
- },
- data() {
- return {
- chart: null
- }
- },
- watch: {
- chartData: {
- deep: true,
- handler(val) {
- this.setOptions(val)
- }
- }
- },
- mounted() {
- this.$nextTick(() => {
- this.initChart()
- })
- },
- beforeDestroy() {
- if (!this.chart) {
- return
- }
- this.chart.dispose()
- this.chart = null
- },
- methods: {
- initChart() {
- this.chart = echarts.init(this.$el, 'macarons')
- this.setOptions(this.chartData)
- },
- setOptions({ expectedData, actualData } = {}) {
- this.chart.setOption({
- xAxis: {
- data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
- boundaryGap: false,
- axisTick: {
- show: false
- }
- },
- grid: {
- left: 10,
- right: 10,
- bottom: 20,
- top: 30,
- containLabel: true
- },
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'cross'
- },
- padding: [5, 10]
- },
- yAxis: {
- axisTick: {
- show: false
- }
- },
- legend: {
- data: ['expected', 'actual']
- },
- series: [{
- name: 'expected', itemStyle: {
- normal: {
- color: '#FF005A',
- lineStyle: {
- color: '#FF005A',
- width: 2
- }
- }
- },
- smooth: true,
- type: 'line',
- data: expectedData,
- animationDuration: 2800,
- animationEasing: 'cubicInOut'
- },
- {
- name: 'actual',
- smooth: true,
- type: 'line',
- itemStyle: {
- normal: {
- color: '#3888fa',
- lineStyle: {
- color: '#3888fa',
- width: 2
- },
- areaStyle: {
- color: '#f3f8ff'
- }
- }
- },
- data: actualData,
- animationDuration: 2800,
- animationEasing: 'quadraticOut'
- }]
- })
- }
- }
- }
- </script>
- -->
- <template>
- <div ref="chartDiv" style="width: 100%; height: 350px"></div>
- </template>
- <script>
- import { gpu_info } from "@/api/knowledge";
- import * as am5 from "@amcharts/amcharts5";
- import * as am5xy from "@amcharts/amcharts5/xy";
- import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
- export default {
- name: "AmAreaChart",
- props: {
- className: {
- type: String,
- default: "chart",
- },
- width: {
- type: String,
- default: "100%",
- },
- height: {
- type: String,
- default: "350px",
- },
- autoResize: {
- type: Boolean,
- default: true,
- },
- data: {
- type: Object,
- default: {},
- },
- },
- data() {
- return {
- chart: null,
- root: null,
- series1: null,
- series2: null,
- xAxis: null,
- data1: [],
- data2: [],
- timer: null,
- isDestroyed: false,
- isOnDashboard: false,
- };
- },
- watch: {
- $route: {
- immediate: true,
- handler(newRoute) {
- this.isOnDashboard = newRoute.path === "/dashboard";
- if (this.isOnDashboard) {
- // 清理旧的资源
- this.cleanupResources();
- // 重新初始化
- this.$nextTick(() => {
- this.initChart();
- /* this.startLiveData(); */
- });
- } else {
- this.stopLiveData();
- }
- },
- },
- data: {
- immediate: true,
- handler(newValue) {
- if (!newValue || !this.series1 || !this.series2) return;
- const currentTime = Date.now();
- // 更新数据数组
- this.data1.push({
- date: currentTime,
- value: newValue.data.system.cpu.total_usage || 0, // 假设传入的data中有cpu字段
- });
- this.data2.push({
- date: currentTime,
- value:newValue.data.system.gpu.devices[0]?.power || 0/* newValue.data.system.gpu.devices|| */ , // 假设传入的data中有gpu字段
- });
- // 限制数据点数量
- if (this.data1.length > 15) {
- this.data1 = this.data1.slice(-15);
- this.data2 = this.data2.slice(-15);
- }
- // 更新图表
- this.series1.data.setAll(this.data1);
- this.series2.data.setAll(this.data2);
- // 更新X轴范围
- if (this.data1.length > 1) {
- this.xAxis.zoomToDates(
- new Date(this.data1[0].date),
- new Date(this.data1[this.data1.length - 1].date)
- );
- }
- },
- },
- },
- mounted() {
- /* this.initChart(); */
- /* if (this.isOnDashboard) {
- this.startLiveData();
- } */
- },
- beforeDestroy() {
- this.cleanupResources();
- },
- methods: {
- /* async cleanupResources() {
- this.isDestroyed = true;
- this.stopLiveData();
- if (this.timer) {
- clearTimeout(this.timer);
- this.timer = null;
- }
-
- }, */
- initChart() {
- // 确保DOM元素存在
- if (!this.$refs.chartDiv) {
- return;
- }
- try {
- this.isDestroyed = false; // 重置销毁标志
- const root = (this.root = am5.Root.new(this.$refs.chartDiv));
- root.setThemes([am5themes_Animated.new(root)]);
- // 创建图表
- this.chart = root.container.children.push(
- am5xy.XYChart.new(root, {
- panX: true,
- panY: true,
- wheelX: "panX",
- wheelY: "zoomX",
- pinchZoomX: true,
- })
- );
- // 创建Y轴
- let yAxis = this.chart.yAxes.push(
- am5xy.ValueAxis.new(root, {
- renderer: am5xy.AxisRendererY.new(root, {
- labelFormat: "{value}%",
- }),
- min: 0,
- max: 100,
- })
- );
- // 创建X轴
- this.xAxis = this.chart.xAxes.push(
- am5xy.DateAxis.new(root, {
- baseInterval: { timeUnit: "second", count: 1 },
- renderer: am5xy.AxisRendererX.new(root, {
- minGridDistance: 200, // 增加标签之间的最小距离
- cellStartLocation: 0.2,
- cellEndLocation: 0.8,
- dateFormats: {
- second: "HH:mm",
- minute: "HH:mm",
- hour: "HH:mm",
- day: "HH:mm",
- },
- periodChangeDateFormats: {
- second: "HH:mm",
- minute: "HH:mm",
- hour: "HH:mm",
- day: "HH:mm",
- },
- // 减少标签数量
- maxDeviation: 0.5,
- // 只显示首尾和少量中间的标签
- minLabelGap: 50,
- }),
- // 控制要显示的标签数量
- gridIntervals: [
- { timeUnit: "second", count: 5 }, // 每5秒显示一个标签
- ],
- // 确保时间轴平滑滚动
- interpolationDuration: 500,
- interpolationEasing: am5.ease.cubic,
- })
- );
- // 创建第一条线(expected)
- this.series1 = this.chart.series.push(
- am5xy.SmoothedXLineSeries.new(root, {
- name: "CPU",
- xAxis: this.xAxis,
- yAxis: yAxis,
- valueYField: "value",
- valueXField: "date",
- tension: 0.8,
- stroke: am5.color("#FF005A"),
- strokeWidth: 3,
- tooltip: am5.Tooltip.new(root, {
- labelText: "{name}: {valueY}%",
- }),
- })
- );
- // 创建第二条线(actual)
- this.series2 = this.chart.series.push(
- am5xy.SmoothedXLineSeries.new(root, {
- name: "GPU",
- xAxis: this.xAxis,
- yAxis: yAxis,
- valueYField: "value",
- valueXField: "date",
- tension: 0.8,
- stroke: am5.color("#3888fa"),
- strokeWidth: 3,
- fill: am5.color("#f3f8ff"),
- tooltip: am5.Tooltip.new(root, {
- labelText: "{name}: {valueY}%",
- }),
- })
- );
- // 添加图例
- const legend = this.chart.children.push(
- am5.Legend.new(root, {
- centerX: am5.percent(50),
- x: am5.percent(50),
- layout: root.horizontalLayout,
- })
- );
- legend.data.setAll(this.chart.series.values);
- // 启用动画
- this.series1.appear(1000);
- this.series2.appear(1000);
- this.chart.appear(1000, 100);
- // 为 series1 添加圆点
- this.series1.bullets.push((root) => {
- const container = am5.Container.new(root, {});
- // 创建光晕效果(外圈)
- const halo = am5.Circle.new(root, {
- radius: 12,
- fill: am5.color("#FF005A"),
- fillOpacity: 0.3,
- stroke: am5.color("#FF005A"),
- strokeOpacity: 0.3,
- strokeWidth: 2,
- });
- // 创建主圆点(内圈)
- const circle = am5.Circle.new(root, {
- radius: 6,
- fill: am5.color("#FF005A"),
- fillOpacity: 0.8,
- stroke: am5.color("#fff"),
- strokeWidth: 2,
- });
- // 将两个圆形添加到容器
- container.children.push(halo);
- container.children.push(circle);
- // 创建光晕的呼吸动画
- halo.animate({
- key: "radius",
- from: 8,
- to: 16,
- duration: 1000,
- loops: Infinity,
- easing: am5.ease.cubic,
- direction: "alternate",
- });
- halo.animate({
- key: "fillOpacity",
- from: 0.3,
- to: 0.1,
- duration: 1000,
- loops: Infinity,
- easing: am5.ease.cubic,
- direction: "alternate",
- });
- return am5.Bullet.new(root, {
- sprite: container,
- });
- });
- // 为 series2 添加圆点
- this.series2.bullets.push((root) => {
- const container = am5.Container.new(root, {});
- // 创建光晕效果(外圈)
- const halo = am5.Circle.new(root, {
- radius: 12,
- fill: am5.color("#3888fa"),
- fillOpacity: 0.3,
- stroke: am5.color("#3888fa"),
- strokeOpacity: 0.3,
- strokeWidth: 2,
- });
- // 创建主圆点(内圈)
- const circle = am5.Circle.new(root, {
- radius: 6,
- fill: am5.color("#3888fa"),
- fillOpacity: 0.8,
- stroke: am5.color("#fff"),
- strokeWidth: 2,
- });
- // 将两个圆形添加到容器
- container.children.push(halo);
- container.children.push(circle);
- // 创建光晕的呼吸动画
- halo.animate({
- key: "radius",
- from: 8,
- to: 16,
- duration: 1000,
- loops: Infinity,
- easing: am5.ease.cubic,
- direction: "alternate",
- });
- halo.animate({
- key: "fillOpacity",
- from: 0.3,
- to: 0.1,
- duration: 1000,
- loops: Infinity,
- easing: am5.ease.cubic,
- direction: "alternate",
- });
- return am5.Bullet.new(root, {
- sprite: container,
- });
- });
- } catch (error) {
- console.error("初始化图表时出错:", error);
- }
- },
- async startLiveData() {
- // 如果已经不在dashboard页面,则停止请求
- if (!this.isOnDashboard) {
- return;
- }
- try {
- const response = await gpu_info();
- const currentTime = Date.now();
- // 更新数据
- this.data1.push({
- date: currentTime,
- value: Math.random() * 30 + 50,
- });
- this.data2.push({
- date: currentTime,
- value: response.data.data.cpu.total_usage,
- });
- // 限制只保留最新的6个数据点
- if (this.data1.length > 6) {
- this.data1 = this.data1.slice(-6);
- this.data2 = this.data2.slice(-6);
- }
- // 更新图表
- this.series1.data.setAll(this.data1);
- this.series2.data.setAll(this.data2);
- // 更新X轴范围
- if (this.data1.length > 1) {
- this.xAxis.zoomToDates(
- new Date(this.data1[0].date),
- new Date(this.data1[this.data1.length - 1].date)
- );
- }
- if (response.status !== 200) return;
- // 设置下一次请求的定时器
- this.timer = setTimeout(() => {
- this.startLiveData();
- }, 2000);
- } catch (error) {
- console.error("Failed to fetch data:", error);
- // 发生错误时,等待一段时间后重试
- this.timer = setTimeout(() => {
- this.startLiveData();
- }, 5000); // 错误后等待5秒重试
- }
- },
- stopLiveData() {
- if (this.timer) {
- clearTimeout(this.timer); // 改用clearTimeout
- this.timer = null;
- }
- },
- async cleanupResources() {
- this.isDestroyed = true;
- this.stopLiveData();
- if (this.timer) {
- clearInterval(this.timer);
- this.timer = null;
- }
- // 清空数据数组
- this.data1 = [];
- this.data2 = [];
- // 销毁图表实例
- if (this.root) {
- this.root.dispose();
- this.root = null;
- }
- this.chart = null;
- this.series1 = null;
- this.series2 = null;
- this.xAxis = null;
- },
- /*
-
- websocket请求
- websocket: null, // 新增 WebSocket 连接对象
- if (this.isOnDashboard) {
- this.initWebSocket(); // 替换原来的 startLiveData
- }
- initWebSocket() {
- // 初始化 WebSocket 连接
- this.websocket = new WebSocket('ws://192.168.1.199:8084/ws/gpu-info/');
-
- // WebSocket 连接成功回调
- this.websocket.onopen = () => {
- console.log('WebSocket 连接已建立');
- };
-
- // WebSocket 接收消息回调
- this.websocket.onmessage = (event) => {
- if (!this.isOnDashboard) {
- this.closeWebSocket();
- return;
- }
- try {
- const response = JSON.parse(event.data);
- const currentTime = Date.now();
- // 更新数据
- this.data1.push({
- date: currentTime,
- value: Math.random() * 30 + 50 // 模拟数据保持不变
- });
-
- this.data2.push({
- date: currentTime,
- value: response.data.cpu.total_usage
- });
- // 保持固定数量的数据点
- if (this.data1.length > 15) {
- this.data1.shift();
- this.data2.shift();
- }
- // 更新图表
- this.series1.data.setAll(this.data1);
- this.series2.data.setAll(this.data2);
- // 更新X轴范围
- if (this.data1.length > 1) {
- this.xAxis.zoomToDates(
- new Date(this.data1[0].date),
- new Date(this.data1[this.data1.length - 1].date)
- );
- }
- } catch (error) {
- console.error("处理 WebSocket 数据时出错:", error);
- }
- };
-
- // WebSocket 错误处理
- this.websocket.onerror = (error) => {
- console.error("WebSocket 错误:", error);
- };
-
- // WebSocket 连接关闭处理
- this.websocket.onclose = () => {
- console.log("WebSocket 连接已关闭");
- // 可以在这里添加重连逻辑
- if (this.isOnDashboard && !this.isDestroyed) {
- setTimeout(() => this.initWebSocket(), 3000);
- }
- };
- },
- closeWebSocket() {
- if (this.websocket) {
- this.websocket.close();
- this.websocket = null;
- }
- },
- cleanupResources() {
- this.isDestroyed = true;
- this.closeWebSocket(); // 替换原来的 stopLiveData
- if (this.root) {
- this.root.dispose();
- }
- } */
- },
- };
- </script>
|