Эх сурвалжийг харах

添加websocket请求及修改部分样式布局

yangg 8 сар өмнө
parent
commit
cdf238bdeb

+ 4 - 2
.env.development

@@ -2,5 +2,7 @@
 ENV = 'development'
 port = 8080
 # base api
-VUE_APP_BASE_API = 'http://58.246.234.210:8084'
-#192.168.1.200
+VUE_APP_BASE_API = 'http://192.168.1.199:8084'
+#192.168.1.199
+
+#58.246.234.210

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/index.html


+ 1 - 0
dist/static/css/chunk-260f027d.8a2c9683.css

@@ -0,0 +1 @@
+.header[data-v-5f200d82]{margin-left:20px}.header h2[data-v-5f200d82]{margin-bottom:0}.center[data-v-5f200d82]{margin:10px}.center[data-v-5f200d82] .el-form--inline .el-form-item{display:block}.center[data-v-5f200d82] .el-input-number--medium{width:150px}.footer[data-v-5f200d82]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin:0 100px 20px}[data-v-5f200d82] .avatar-uploader .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-5f200d82]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-5f200d82]{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}.avatar[data-v-5f200d82]{width:100px;height:100px;display:block}.hint[data-v-5f200d82]{font-size:12px;color:#c0c4cc}

+ 0 - 1
dist/static/css/chunk-43013872.97cc4264.css

@@ -1 +0,0 @@
-.header[data-v-74a68649]{margin-left:20px}.header h2[data-v-74a68649]{margin-bottom:0}.center[data-v-74a68649]{margin:10px}.center[data-v-74a68649] .el-form--inline .el-form-item{display:block}.center[data-v-74a68649] .el-input-number--medium{width:150px}.footer[data-v-74a68649]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin:0 100px 20px}[data-v-74a68649] .avatar-uploader .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-74a68649]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-74a68649]{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}.avatar[data-v-74a68649]{width:100px;height:100px;display:block}.hint[data-v-74a68649]{font-size:12px;color:#c0c4cc}

+ 0 - 1
dist/static/css/chunk-4552e990.8677f655.css

@@ -1 +0,0 @@
-.midd-page[data-v-e429baea]{padding:20px}.el-icon-edit[data-v-e429baea]{font-size:14px;color:#409eff}[data-v-e429baea] .model-gen-1{background-color:#e6f3ff!important}[data-v-e429baea] .model-gen-1.el-table__cell{background-color:#e6f3ff!important;color:#333!important}[data-v-e429baea] .model-gen-2{background-color:#f0e6ff!important}[data-v-e429baea] .model-gen-2.el-table__cell{background-color:#f0e6ff!important;color:#333!important}.header[data-v-e429baea]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px 0}.h_left p[data-v-e429baea]{margin-top:0}.search-container[data-v-e429baea]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.search-box[data-v-e429baea]{width:350px}.custom-search-input[data-v-e429baea] :deep(.el-input__inner){height:40px;line-height:40px;border-right:none;border-radius:4px 0 0 4px}.custom-search-input[data-v-e429baea] :deep(.el-input-group__append){padding:0;border:none;background-color:transparent}.custom-search-input[data-v-e429baea] :deep(.el-input-group__append .el-button){height:40px;padding:0 20px;border-radius:0 4px 4px 0;margin:0}.custom-search-input[data-v-e429baea] :deep(.el-input__inner:hover){border-color:#dcdfe6}.custom-search-input[data-v-e429baea] :deep(.el-input__inner:focus){border-color:#409eff}

+ 1 - 0
dist/static/css/chunk-7d51ef16.e8dbdd23.css

@@ -0,0 +1 @@
+.midd-page[data-v-7a0ab417]{padding:20px}.el-icon-edit[data-v-7a0ab417]{font-size:14px;color:#409eff}[data-v-7a0ab417] .model-gen-1{background-color:#e6f3ff!important}[data-v-7a0ab417] .model-gen-1.el-table__cell{background-color:#e6f3ff!important;color:#333!important}[data-v-7a0ab417] .model-gen-2{background-color:#f0e6ff!important}[data-v-7a0ab417] .model-gen-2.el-table__cell{background-color:#f0e6ff!important;color:#333!important}.header[data-v-7a0ab417]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px 0}.h_left p[data-v-7a0ab417]{margin-top:0}.search-container[data-v-7a0ab417]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.search-box[data-v-7a0ab417]{width:350px}.custom-search-input[data-v-7a0ab417] :deep(.el-input__inner){height:40px;line-height:40px;border-right:none;border-radius:4px 0 0 4px}.custom-search-input[data-v-7a0ab417] :deep(.el-input-group__append){padding:0;border:none;background-color:transparent}.custom-search-input[data-v-7a0ab417] :deep(.el-input-group__append .el-button){height:40px;padding:0 20px;border-radius:0 4px 4px 0;margin:0}.custom-search-input[data-v-7a0ab417] :deep(.el-input__inner:hover){border-color:#dcdfe6}.custom-search-input[data-v-7a0ab417] :deep(.el-input__inner:focus){border-color:#409eff}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/css/chunk-cdbc4916.deff7366.css


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/app.efcd111d.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-260f027d.7325d8a9.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-386d37cb.fa7fb055.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-43013872.bc592c8a.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-4552e990.e867adf7.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-51fb63a4.bed252e5.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-5d225980.919681d9.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-708604c0.796da079.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-7d51ef16.a57f3cae.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-ca61fc30.bdecf75c.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/static/js/chunk-cdbc4916.8b99b1d6.js


+ 18 - 10
src/views/appConfig/index.vue

@@ -387,13 +387,16 @@
               </div>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+         
+        </el-row>
+        <el-row :gutter="24">
+           <el-col :span="24">
             <el-form-item label="自定义提示模板:" prop="custom_prompt">
               <el-input
-                style="width: 60%"
+                style="width: 100%"
                 v-model="AIform.custom_prompt"
                 type="textarea"
-                :rows="6"
+                :rows="12"
                 placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
                     上下文: {context}
                     人类: {question}
@@ -405,7 +408,6 @@
             </el-form-item>
           </el-col>
         </el-row>
-
         <el-row :gutter="24">
           <el-col :span="12">
             <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
@@ -831,13 +833,16 @@
               </div>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+          
+        </el-row>
+        <el-row :gutter="24">
+          <el-col :span="24">
             <el-form-item label="自定义提示模板:" prop="custom_prompt">
               <el-input
-                style="width: 60%"
+                style="width: 100%"
                 v-model="editForm.custom_prompt"
                 type="textarea"
-                :rows="6"
+                :rows="12"
                 placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
                     上下文: {context}
                     人类: {question}
@@ -1295,14 +1300,17 @@
               </div>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+          
+        </el-row>
+        <el-row :gutter="24">
+          <el-col :span="24">
             <el-form-item label="自定义提示模板:" prop="custom_prompt">
               <el-input
                 disabled
-                style="width: 60%"
+                style="width: 100%"
                 v-model="viewForm.custom_prompt"
                 type="textarea"
-                :rows="6"
+                :rows="12"
                 placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
                     上下文: {context}
                     人类: {question}

+ 230 - 213
src/views/dashboard/admin/components/LineChart.vue

@@ -1,142 +1,4 @@
-<!-- <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>
+<template>
   <div ref="chartDiv" style="width: 100%; height: 350px"></div>
 </template>
 
@@ -151,20 +13,24 @@ export default {
   props: {
     className: {
       type: String,
-      default: 'chart'
+      default: "chart",
     },
     width: {
       type: String,
-      default: '100%'
+      default: "100%",
     },
     height: {
       type: String,
-      default: '350px'
+      default: "350px",
     },
     autoResize: {
       type: Boolean,
-      default: true
-    }
+      default: true,
+    },
+    data: {
+      type: Object,
+      default: {},
+    },
   },
   data() {
     return {
@@ -177,34 +43,90 @@ export default {
       data2: [],
       timer: null,
       isDestroyed: false,
-      isOnDashboard:false
+      isOnDashboard: false,
     };
   },
   watch: {
     $route: {
       immediate: true,
       handler(newRoute) {
-        this.isOnDashboard = newRoute.path === '/dashboard';
+        this.isOnDashboard = newRoute.path === "/dashboard";
         if (this.isOnDashboard) {
-          this.startLiveData();
+          // 清理旧的资源
+          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, // 假设传入的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.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)]);
 
@@ -223,10 +145,10 @@ export default {
         let yAxis = this.chart.yAxes.push(
           am5xy.ValueAxis.new(root, {
             renderer: am5xy.AxisRendererY.new(root, {
-              labelFormat: "{value}%"
+              labelFormat: "{value}%",
             }),
             min: 0,
-            max: 100
+            max: 100,
           })
         );
 
@@ -235,36 +157,40 @@ export default {
           am5xy.DateAxis.new(root, {
             baseInterval: { timeUnit: "second", count: 1 },
             renderer: am5xy.AxisRendererX.new(root, {
-              minGridDistance: 100, // 增加标签之间的最小距离
+              minGridDistance: 200, // 增加标签之间的最小距离
               cellStartLocation: 0.2,
               cellEndLocation: 0.8,
               dateFormats: {
-                second: "mm:ss",
-                minute: "mm:ss"
+                second: "HH:mm",
+                minute: "HH:mm",
+                hour: "HH:mm",
+                day: "HH:mm",
               },
               periodChangeDateFormats: {
-                second: "mm:ss",
-                minute: "mm:ss"
+                second: "HH:mm",
+                minute: "HH:mm",
+                hour: "HH:mm",
+                day: "HH:mm",
               },
               // 减少标签数量
               maxDeviation: 0.5,
               // 只显示首尾和少量中间的标签
-              minLabelGap: 50
+              minLabelGap: 50,
             }),
             // 控制要显示的标签数量
             gridIntervals: [
-              { timeUnit: "second", count: 5 } // 每5秒显示一个标签
+              { timeUnit: "second", count: 5 }, // 每5秒显示一个标签
             ],
             // 确保时间轴平滑滚动
             interpolationDuration: 500,
-            interpolationEasing: am5.ease.cubic
+            interpolationEasing: am5.ease.cubic,
           })
         );
 
         // 创建第一条线(expected)
         this.series1 = this.chart.series.push(
           am5xy.SmoothedXLineSeries.new(root, {
-            name: "GPU",
+            name: "CPU",
             xAxis: this.xAxis,
             yAxis: yAxis,
             valueYField: "value",
@@ -273,15 +199,15 @@ export default {
             stroke: am5.color("#FF005A"),
             strokeWidth: 3,
             tooltip: am5.Tooltip.new(root, {
-              labelText: "{name}: {valueY}%"
-            })
+              labelText: "{name}: {valueY}%",
+            }),
           })
         );
 
         // 创建第二条线(actual)
         this.series2 = this.chart.series.push(
           am5xy.SmoothedXLineSeries.new(root, {
-            name: "CPU",
+            name: "GPU",
             xAxis: this.xAxis,
             yAxis: yAxis,
             valueYField: "value",
@@ -291,8 +217,8 @@ export default {
             strokeWidth: 3,
             fill: am5.color("#f3f8ff"),
             tooltip: am5.Tooltip.new(root, {
-              labelText: "{name}: {valueY}%"
-            })
+              labelText: "{name}: {valueY}%",
+            }),
           })
         );
 
@@ -301,7 +227,7 @@ export default {
           am5.Legend.new(root, {
             centerX: am5.percent(50),
             x: am5.percent(50),
-            layout: root.horizontalLayout
+            layout: root.horizontalLayout,
           })
         );
         legend.data.setAll(this.chart.series.values);
@@ -322,7 +248,7 @@ export default {
             fillOpacity: 0.3,
             stroke: am5.color("#FF005A"),
             strokeOpacity: 0.3,
-            strokeWidth: 2
+            strokeWidth: 2,
           });
 
           // 创建主圆点(内圈)
@@ -331,7 +257,7 @@ export default {
             fill: am5.color("#FF005A"),
             fillOpacity: 0.8,
             stroke: am5.color("#fff"),
-            strokeWidth: 2
+            strokeWidth: 2,
           });
 
           // 将两个圆形添加到容器
@@ -346,7 +272,7 @@ export default {
             duration: 1000,
             loops: Infinity,
             easing: am5.ease.cubic,
-            direction: "alternate"
+            direction: "alternate",
           });
 
           halo.animate({
@@ -356,11 +282,11 @@ export default {
             duration: 1000,
             loops: Infinity,
             easing: am5.ease.cubic,
-            direction: "alternate"
+            direction: "alternate",
           });
 
           return am5.Bullet.new(root, {
-            sprite: container
+            sprite: container,
           });
         });
 
@@ -375,7 +301,7 @@ export default {
             fillOpacity: 0.3,
             stroke: am5.color("#3888fa"),
             strokeOpacity: 0.3,
-            strokeWidth: 2
+            strokeWidth: 2,
           });
 
           // 创建主圆点(内圈)
@@ -384,7 +310,7 @@ export default {
             fill: am5.color("#3888fa"),
             fillOpacity: 0.8,
             stroke: am5.color("#fff"),
-            strokeWidth: 2
+            strokeWidth: 2,
           });
 
           // 将两个圆形添加到容器
@@ -399,7 +325,7 @@ export default {
             duration: 1000,
             loops: Infinity,
             easing: am5.ease.cubic,
-            direction: "alternate"
+            direction: "alternate",
           });
 
           halo.animate({
@@ -409,45 +335,132 @@ export default {
             duration: 1000,
             loops: Infinity,
             easing: am5.ease.cubic,
-            direction: "alternate"
+            direction: "alternate",
           });
 
           return am5.Bullet.new(root, {
-            sprite: container
+            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) {
-        clearInterval(this.timer);
+        clearTimeout(this.timer); // 改用clearTimeout
+        this.timer = null;
       }
+    },
+    async cleanupResources() {
+      this.isDestroyed = true;
       this.stopLiveData();
-      this.timer = setInterval(async () => {
+      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.stopLiveData();
+          this.closeWebSocket();
           return;
         }
+
         try {
-          const response = await gpu_info();
+          const response = JSON.parse(event.data);
           const currentTime = Date.now();
 
-          // 模拟两组数据
-          const expectedValue = Math.random() * 30 + 50;
-          const actualValue = response.data.data.cpu.total_usage;
-
           // 更新数据
           this.data1.push({
             date: currentTime,
-            value: expectedValue
+            value: Math.random() * 30 + 50 // 模拟数据保持不变
           });
+          
           this.data2.push({
             date: currentTime,
-            value: actualValue
+            value: response.data.cpu.total_usage
           });
 
           // 保持固定数量的数据点
@@ -467,35 +480,39 @@ export default {
               new Date(this.data1[this.data1.length - 1].date)
             );
           }
-
         } catch (error) {
-          console.error("Failed to fetch data:", error);
+          console.error("处理 WebSocket 数据时出错:", error);
         }
-      }, 2000);
-    },
-
-    stopLiveData() {
-      if (this.timer) {
-        clearInterval(this.timer);
-        this.timer = null;
+      };
+      
+      // 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;
       }
     },
-    async cleanupResources() {
-      this.isDestroyed = true;
-      this.stopLiveData();
-      if (this.timer) {
-        clearInterval(this.timer);
-        this.timer = null;
-      }
 
+    cleanupResources() {
+      this.isDestroyed = true;
+      this.closeWebSocket(); // 替换原来的 stopLiveData
       if (this.root) {
         this.root.dispose();
       }
-    }
-  }
+    } */
+  },
 };
-</script>
-
-
-
-
+</script>

+ 176 - 77
src/views/dashboard/admin/components/instruChart.vue

@@ -1,100 +1,199 @@
 <template>
-    <div id="chartdiv"></div>
-  </template>
-    
-    <script>
-  import * as am5 from "@amcharts/amcharts5";
-  import * as am5percent from "@amcharts/amcharts5/percent";
-  import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
+  <div id="chartdiv"></div>
+</template>
   
-  export default {
-    name: "DoublePieChart",
-    data() {
-      return {
-        root: null,
-        chart: null,
-        series: null,  // 添加 series 到 data 中以便在其他方法中访问
-        currentValue: 0,  // 添加当前值的追踪
-        timer: null,      // 添加定时器引用
-        data: [],
-      };
+  <script>
+import * as am5 from "@amcharts/amcharts5";
+import * as am5percent from "@amcharts/amcharts5/percent";
+import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
+
+import { gpu_info } from "@/api/knowledge";
+
+export default {
+  name: "DoublePieChart",
+  props: {
+    percentage: {
+        type: Number,
+        default: 0
+      }
+  },
+  data() {
+    return {
+      root: null,
+      chart: null,
+      series: null,
+      centerLabel: null,
+      currentPercentage: 0,
+      isOnDashboard: false,
+      timer: null, // 添加定时器变量
+      isRequesting: false, // 添加请求状态标记
+    };
+  },
+  watch: {
+    percentage: {
+      handler(newValue) {
+        this.updateChart(newValue);
+      },
     },
-    mounted() {
-      this.initChart();
+    $route: {
+      immediate: true,
+      handler(newRoute) {
+        this.isOnDashboard = newRoute.path === "/dashboard";
+       /*  if (this.isOnDashboard) {
+          this.startPolling();
+        } else {
+          this.stopPolling();
+        } */
+      },
     },
-    beforeDestroy() {
+  },
+  methods: {
+    initChart() {
+      // 确保在重新初始化前清理旧实例
       if (this.root) {
         this.root.dispose();
       }
-    },
-    methods: {
-      initChart() {
-        // Create root element
-        this.root = am5.Root.new("chartdiv");
-  
-        // Set themes
-        this.root.setThemes([am5themes_Animated.new(this.root)]);
-  
-        // Create chart
-        this.chart = this.root.container.children.push(
-          am5percent.PieChart.new(this.root, {
-            startAngle: 180,
-            endAngle: 360,
-            layout: this.root.verticalLayout,
-            innerRadius: am5.percent(80),
-          })
-        );
-  
+
+      // Create root element
+      this.root = am5.Root.new("chartdiv");
+
+      // Set themes
+      this.root.setThemes([am5themes_Animated.new(this.root)]);
+
+      // Create chart
+      this.chart = this.root.container.children.push(
+        am5percent.PieChart.new(this.root, {
+          startAngle: 180,
+          endAngle: 360,
+          layout: this.root.verticalLayout,
+          innerRadius: am5.percent(80),
+        })
+      );
+
+      // 确保 chart 创建完成后再创建 series
+      if (this.chart) {
         // Create series
-        const series = this.chart.series.push(
+        this.series = this.chart.series.push(
           am5percent.PieSeries.new(this.root, {
             valueField: "value",
             categoryField: "category",
             startAngle: 180,
             endAngle: 360,
-            radius: am5.percent(80), // 外圆半径
-            innerRadius: am5.percent(30), // 内圆半径,调小这个值会增加仪表的宽度
+            radius: am5.percent(80),
+            innerRadius: am5.percent(30),
           })
         );
-  
+
         // Set custom colors
-        series.set(
+        this.series.set(
           "colors",
           am5.ColorSet.new(this.root, {
-            colors: [
-              am5.color("#19bcbf"), // completed color
-              am5.color(0xeeeeee), // remaining color
-            ],
+            colors: [am5.color("#19bcbf"), am5.color("#F5F5F5")],
           })
         );
-  
+
         // Disable labels and ticks
-        series.labels.template.set("forceHidden", true);
-        series.ticks.template.set("forceHidden", true);
-  
-        // Add center label
-        this.chart.seriesContainer.children.push(
-          am5.Label.new(this.root, {
-            textAlign: "center",
-            centerY: am5.p50,
-            centerX: am5.p50,
-            text: "[fontSize:30px]90%[/]\n[fontSize:16px]Compiled Ticket[/]",
-          })
-        );
-  
-        // Set data
-        series.data.setAll([
-          { category: "Complete", value: 10 },
-          /*  { category: "Remaining", value: 1 }, */
+        this.series.labels.template.set("forceHidden", true);
+        this.series.ticks.template.set("forceHidden", true);
+
+        // Create center label
+        this.centerLabel = am5.Label.new(this.root, {
+          textAlign: "center",
+          centerY: am5.p50,
+          centerX: am5.p50,
+          /*   y: am5.percent(-35), // 负值会使文字向上移动 */
+          text: "[fontSize:24px]0%[/]\n[fontSize:14px]Compiled Ticket[/]",
+        });
+
+        this.chart.seriesContainer.children.push(this.centerLabel);
+
+        // 初始化完成后更新数据
+        this.$nextTick(() => {
+          this.updateChart(this.percentage);
+        });
+      }
+    },
+
+    updateChart(percentage) {
+      // 确保所有必要的组件都已创建
+      if (!this.series || !this.centerLabel) {
+        return;
+      }
+
+      try {
+        // Update data
+        this.series.data.setAll([
+          { category: "memory", value: percentage },
+          { category: "Remaining", value: 100 - percentage },
         ]);
-      },
+
+        // Update center label
+        this.centerLabel.set(
+          "text",
+          `[fontSize:24px]${percentage}%[/]\n[fontSize:14px]Compiled Ticket[/]`
+        );
+      } catch (error) {
+        console.error("Error updating chart:", error);
+      }
     },
-  };
-  </script>
-    
-    <style scoped>
-  #chartdiv {
-    width: 100%;
-    height: 150px;
-  }
-  </style>
+    startPolling() {
+      if (!this.isRequesting) {
+        this.fetchGpuInfo();
+      }
+    },
+
+    stopPolling() {
+      this.isRequesting = false;
+      if (this.timer) {
+        clearTimeout(this.timer);
+        this.timer = null;
+      }
+    },
+
+    fetchGpuInfo() {
+      if (!this.isOnDashboard) return;
+      
+      this.isRequesting = true;
+      gpu_info()
+        .then((res) => {
+          const total = res.data.data.cpu.memory.total;
+          const used = res.data.data.cpu.memory.used;
+          this.currentPercentage = Number(((used / total) * 100).toFixed(2));
+          
+          // 请求成功后,等待3秒再发起下一次请求
+          this.timer = setTimeout(() => {
+            this.isRequesting = false;
+            this.fetchGpuInfo();
+          }, 3000);
+        })
+        .catch((error) => {
+          console.error('Failed to fetch GPU info:', error);
+          // 请求失败后,等待3秒重试
+          this.timer = setTimeout(() => {
+            this.isRequesting = false;
+            this.fetchGpuInfo();
+          }, 3000);
+        });
+    },
+  },
+  mounted() {
+    // 使用 nextTick 确保 DOM 已经更新
+    this.$nextTick(() => {
+      this.initChart();
+    });
+  },
+  beforeDestroy() {
+    this.stopPolling();
+    if (this.root) {
+      this.root.dispose();
+    }
+  },
+};
+</script>
+  
+  <style scoped>
+#chartdiv {
+  width: 100%;
+  height: 150px;
+}
+</style>

+ 83 - 14
src/views/dashboard/admin/index.vue

@@ -7,12 +7,12 @@
       <el-col :span="8">
         <el-card class="box-card">
           <div slot="header" class="clearfixs">
-            <span>cpu使用率</span>
+            <span>memory</span>
           </div>
           <instruChart style="height: 350px" :percentage="currentPercentage" />
         </el-card>
       </el-col>
-      <el-col style="margin-bottom: 20px;" :span="16">
+      <el-col style="margin-bottom: 20px" :span="16">
         <el-card class="box-card">
           <div slot="header" class="clearfixs">
             <span>GPU使用率</span>
@@ -40,6 +40,7 @@
               </el-select>
             </div>
           </div>
+          <!-- <LineChart :data="chartData" style="height: 350px"></LineChart> -->
           <BarChart :kenValue="kenVal" style="height: 350px"></BarChart>
         </el-card>
       </el-col>
@@ -88,8 +89,13 @@ import BoxCard from "./components/BoxCard";
 import TagCloud from "./components/tagCloud.vue";
 import RecentFile from "./components/recentFile.vue";
 import instruChart from "./components/instruChart.vue";
-import { listBuckets, structure_count,gpu_info } from "@/api/knowledge";
-import { mapState } from 'vuex';
+import {
+  listBuckets,
+  structure_count,
+  GET_Rbq,
+  gpu_info,
+} from "@/api/knowledge";
+import { mapState } from "vuex";
 
 const lineChartData = {
   newVisitis: {
@@ -124,7 +130,7 @@ export default {
     BoxCard,
     TagCloud,
     RecentFile,
-    instruChart
+    instruChart,
   },
   data() {
     return {
@@ -148,36 +154,43 @@ export default {
       kenList: [],
       kenValue: null,
       kenVal: {},
+      currentPercentage: 11,
       refreshInterval: null,
       isOnDashboard: false,
+      ws: null, // WebSocket 实例
+      chartData: {},
     };
   },
   computed: {
     ...mapState({
-      route: state => state.tagsView.visitedViews
-    })
+      route: (state) => state.tagsView.visitedViews,
+    }),
   },
   watch: {
     $route: {
       immediate: true,
       handler(newRoute) {
-        this.isOnDashboard = newRoute.path === '/dashboard';
+        this.isOnDashboard = newRoute.path === "/dashboard";
         this.handleRouteChange();
-      }
-    }
+      },
+    },
   },
   mounted() {
     this.init();
-   /*  this.startRefreshInterval(); */
+    this.initWebSocket(); // 初始化 WebSocket
+    /*  this.startRefreshInterval(); */
   },
   beforeDestroy() {
     this.stopRefreshInterval();
+    this.closeWebSocket(); // 组件销毁前关闭 WebSocket
   },
   methods: {
     handleRouteChange() {
       if (this.isOnDashboard) {
         this.refreshData();
-      } 
+      } else {
+        this.closeWebSocket();
+      }
     },
     /* 选择知识库 */
     kenChange(val) {
@@ -207,16 +220,72 @@ export default {
           this.kenVal = this.kenList[0];
         }
       });
-      gpu_info().then((res) => {
+      GET_Rbq().then((res) => {
+        console.log(res);
+      });
+      /* gpu_info().then((res) => {
         const total = res.data.data.cpu.memory.total;
         const used = res.data.data.cpu.memory.used;
         this.currentPercentage = Number((used / total).toFixed(2));
-      });
+      }); */
     },
 
     init() {
       this.refreshData();
     },
+    initWebSocket() {
+      // 获取基础 API 地址并替换 http/https 为 ws/wss
+      const wsUrl = process.env.VUE_APP_BASE_API.replace(/^http/, "ws");
+      // 创建 WebSocket 连接
+      this.ws = new WebSocket(`${wsUrl}/ws/monitoring`);
+
+      // 连接成功回调
+      this.ws.onopen = () => {
+        console.log("WebSocket连接成功");
+      };
+
+      // 接收消息回调
+      this.ws.onmessage = (event) => {
+        try {
+          const data = JSON.parse(event.data);
+          // 检查数据结构是否完整
+          if (
+            data &&
+            data.data &&
+            data.data.system &&
+            data.data.system.cpu &&
+            data.data.system.cpu.memory
+          ) {
+            // 更新图表数据
+            this.chartData = data;
+            const total = data.data.system.cpu.memory.total;
+            const used = data.data.system.cpu.memory.used;
+            this.currentPercentage = Number((used / total).toFixed(2)) * 100;
+          } else {
+            console.warn("Received incomplete data structure:", data);
+          }
+        } catch (error) {
+          console.error("Error processing WebSocket message:", error);
+        }
+      };
+
+      // 连接关闭回调
+      this.ws.onclose = () => {
+        console.log("WebSocket连接关闭");
+      };
+
+      // 连接错误回调
+      this.ws.onerror = (error) => {
+        console.error("WebSocket连接错误:", error);
+      };
+    },
+
+    closeWebSocket() {
+      if (this.ws) {
+        this.ws.close();
+        this.ws = null;
+      }
+    },
   },
 };
 </script>

+ 270 - 165
src/views/middPage/index.vue

@@ -2,32 +2,38 @@
   <div class="midd-page">
     <!-- <el-tabs v-model="activeName" @tab-click="handleClick">
       <el-tab-pane label="AED列表" name="first"> -->
-        <div class="header">
-          <div class="h_left">
-            <p class="current-doc">
-              当前数据表: {{ firstForm.currentDocument }}
-            </p>
-            <div class="search-box">
-              <el-input
-                v-model="firstForm.searchQuery"
-                placeholder="请输入搜索内容"
-                class="custom-search-input"
-                clearable
-                @clear="handleSearchClear"
-                @keyup.enter.native="handleSearch"
+    <div class="header">
+      <div class="h_left">
+        <p class="current-doc">
+          <strong>当前文献</strong>:
+          <span style="color: #97a8be">{{ firstForm.currentDocument }}</span>
+        </p>
+        <div class="search-box">
+          <p>
+            <strong>所属标签</strong>:<span style="color: #97a8be">{{
+              firstForm.tag || '暂无标签'
+            }}</span>
+          </p>
+          <!-- <el-input
+            v-model="firstForm.searchQuery"
+            placeholder="请输入搜索内容"
+            class="custom-search-input"
+            clearable
+            @clear="handleSearchClear"
+            @keyup.enter.native="handleSearch"
+          >
+            <template #append>
+              <el-button
+                type="primary"
+                icon="el-icon-search"
+                @click="handleSearch"
+                >搜索</el-button
               >
-                <template #append>
-                  <el-button
-                    type="primary"
-                    icon="el-icon-search"
-                    @click="handleSearch"
-                    >搜索</el-button
-                  >
-                </template>
-              </el-input>
-            </div>
-          </div>
-          <!-- <div class="h_right">
+            </template>
+          </el-input> -->
+        </div>
+      </div>
+      <!-- <div class="h_right">
             <el-pagination
               @size-change="handleSizeChange"
               @current-change="handleCurrentChange"
@@ -38,9 +44,9 @@
               >
             </el-pagination>
           </div> -->
-        </div>
-        <el-table :data="firstTableData" style="width: 100%">
-          <!-- <el-table-column
+    </div>
+    <el-table :data="firstTableData" style="width: 100%">
+      <!-- <el-table-column
             label="工作类型"
             prop="workType"
             :filters="firstForm.workTypeFilters"
@@ -61,136 +67,130 @@
               <span v-else>{{ scope.row.workType }}</span>
             </template>
           </el-table-column> -->
-          <el-table-column
-            label="项目"
-            prop="name"
-            :filters="firstForm.projectFilters"
-            :filter-method="firstFilterProject"
-            filter-placement="bottom-end"
-            width="140"
-          >
-            <template #default="scope">
-              <el-input
-                v-model="scope.row.name"
-                v-if="scope.row.isEdit"
-              ></el-input>
-              <span v-else>{{ scope.row.name }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="说明" prop="description">
-            <template #default="scope">
-              <el-input
-                v-model="scope.row.description"
-                v-if="scope.row.isEdit"
-              ></el-input>
-              <span v-else>{{ scope.row.description }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="范例" prop="example">
-            <template #default="scope">
-              <el-input
-                v-model="scope.row.example"
-                v-if="scope.row.isEdit"
-              ></el-input>
-              <span v-else>
-                {{ scope.row.example }}
-                <i
-                  class="el-icon-edit"
-                  @click="openExampleDialog(scope.row)"
-                  style="cursor: pointer; margin-left: 5px"
-                ></i>
-              </span>
-            </template>
-          </el-table-column>
-          <el-table-column
-            label="模型生成1"
-            prop="generation_1"
-            class-name="model-gen-1"
-          >
-            <template #default="scope">
-              <el-input
-                v-model="scope.row.generation_1"
-                v-if="scope.row.isEdit"
-              ></el-input>
-              <span v-else>
-                {{ scope.row.generation_1 }}
-                <i
-                  class="el-icon-refresh"
-                  @click="openModel1Dialog(scope.row)"
-                  style="cursor: pointer; margin-left: 5px"
-                ></i>
-              </span>
-            </template>
-          </el-table-column>
-          <el-table-column
-            label="模型生成2"
-            prop="generation_2"
-            class-name="model-gen-2"
-          >
-            <template #default="scope">
-              <el-input
-                v-model="scope.row.generation_2"
-                v-if="scope.row.isEdit"
-              ></el-input>
-              <span v-else>
-                {{ scope.row.generation_2 }}
-                <i
-                  class="el-icon-refresh"
-                  @click="openModel2Dialog(scope.row)"
-                  style="cursor: pointer; margin-left: 5px"
-                ></i>
-              </span>
-            </template>
-          </el-table-column>
-          <el-table-column label="操作" width="100">
-            <template #default="scope">
-              <el-dropdown trigger="click">
-                <el-button
-                  type="text"
-                  size="small"
-                  circle
-                  class="operation-button"
-                  style="font-size: 15px; margin-left: 10px"
-                  icon="el-icon-more"
-                ></el-button>
-                <template #dropdown>
-                  <el-dropdown-menu>
-                    <el-dropdown-item
-                      @click.native="firstHandleEdit(scope.$index, scope.row)"
-                      v-if="!scope.row.isEdit"
-                    >
-                      <i class="el-icon-edit"></i> 编辑
-                    </el-dropdown-item>
-                    <el-dropdown-item
-                      @click.native="firstHandleSave(scope.$index, scope.row)"
-                      v-if="scope.row.isEdit"
-                    >
-                      <i class="el-icon-check"></i> 保存
-                    </el-dropdown-item>
-                    <!--  <el-dropdown-item
+      <el-table-column
+        label="项目"
+        prop="name"
+        :filters="firstForm.projectFilters"
+        :filter-method="firstFilterProject"
+        filter-placement="bottom-end"
+        width="140"
+      >
+        <template #default="scope">
+          <el-input v-model="scope.row.name" v-if="scope.row.isEdit"></el-input>
+          <span v-else>{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="说明" prop="description">
+        <template #default="scope">
+          <el-input
+            v-model="scope.row.description"
+            v-if="scope.row.isEdit"
+          ></el-input>
+          <span v-else>{{ scope.row.description }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="范例" prop="example">
+        <template #default="scope">
+          <el-input
+            v-model="scope.row.example"
+            v-if="scope.row.isEdit"
+          ></el-input>
+          <span v-else>
+            {{ scope.row.example }}
+            <i
+              class="el-icon-edit"
+              @click="openExampleDialog(scope.row)"
+              style="cursor: pointer; margin-left: 5px"
+            ></i>
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="模型生成1"
+        prop="generation_1"
+        class-name="model-gen-1"
+      >
+        <template #default="scope">
+          <el-input
+            v-model="scope.row.generation_1"
+            v-if="scope.row.isEdit"
+          ></el-input>
+          <span v-else>
+            {{ scope.row.generation_1 }}
+            <i
+              class="el-icon-refresh"
+              @click="openModel1Dialog(scope.row)"
+              style="cursor: pointer; margin-left: 5px"
+            ></i>
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="模型生成2"
+        prop="generation_2"
+        class-name="model-gen-2"
+      >
+        <template #default="scope">
+          <el-input
+            v-model="scope.row.generation_2"
+            v-if="scope.row.isEdit"
+          ></el-input>
+          <span v-else>
+            {{ scope.row.generation_2 }}
+            <i
+              class="el-icon-refresh"
+              @click="openModel2Dialog(scope.row)"
+              style="cursor: pointer; margin-left: 5px"
+            ></i>
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="100">
+        <template #default="scope">
+          <el-dropdown trigger="click">
+            <el-button
+              type="text"
+              size="small"
+              circle
+              class="operation-button"
+              style="font-size: 15px; margin-left: 10px"
+              icon="el-icon-more"
+            ></el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item
+                  @click.native="firstHandleEdit(scope.$index, scope.row)"
+                  v-if="!scope.row.isEdit"
+                >
+                  <i class="el-icon-edit"></i> 编辑
+                </el-dropdown-item>
+                <el-dropdown-item
+                  @click.native="firstHandleSave(scope.$index, scope.row)"
+                  v-if="scope.row.isEdit"
+                >
+                  <i class="el-icon-check"></i> 保存
+                </el-dropdown-item>
+                <!--  <el-dropdown-item
                       @click.native="firstHandleUpload(scope.$index, scope.row)"
                     >
                       <i class="el-icon-refresh"></i> 更新内容
                     </el-dropdown-item> -->
-                    <el-dropdown-item
-                      @click.native="firstHandleDelete(scope.$index, scope.row)"
-                    >
-                      <i class="el-icon-delete"></i> 删除
-                    </el-dropdown-item>
-                  </el-dropdown-menu>
-                </template>
-              </el-dropdown>
+                <el-dropdown-item
+                  @click.native="firstHandleDelete(scope.$index, scope.row)"
+                >
+                  <i class="el-icon-delete"></i> 删除
+                </el-dropdown-item>
+              </el-dropdown-menu>
             </template>
-          </el-table-column>
-        </el-table>
-        <el-button
-          type="primary"
-          @click="firstHandleAdd"
-          style="margin-top: 20px"
-          >新增行</el-button
-        >
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-button type="primary" @click="firstHandleAdd" style="margin-top: 20px"
+      >新增行</el-button
+    >
     <!--   </el-tab-pane> -->
-     <!--  <el-tab-pane label="SOTA详情" name="second">
+    <!--  <el-tab-pane label="SOTA详情" name="second">
         <el-table :data="secondTableData" style="width: 100%">
           <el-table-column
             label="Identifier"
@@ -390,7 +390,7 @@
           >新增行</el-button
         >
       </el-tab-pane> -->
-   <!--  </el-tabs> -->
+    <!--  </el-tabs> -->
 
     <el-dialog :visible.sync="dialogVisible" title="编辑范例" width="30%">
       <el-input v-model="currentExample" type="textarea" :rows="4"></el-input>
@@ -540,12 +540,13 @@ export default {
           modelValue: "",
         },
         currentDocument: "AED数据表", // 当前文档名称
+        tag:[],
         searchQuery: "", // 搜索关键词
         currentPage: 1, // 当前页码
         pageSize: 10, // 每页显示条数
         total: 3, // 总条数
       },
-     /*  secondForm: {
+      /*  secondForm: {
         tableData: [
           {
             workType: "SOTA-E1-01",
@@ -651,12 +652,13 @@ export default {
     this.updateProjectFilters();
   },
   mounted() {
-    this.firstForm.currentDocument=this.$route.query.name
+    this.firstForm.currentDocument = this.$route.query.name;
+    this.firstForm.tag= JSON.parse(this.$route.query.tag).join(",")
     this.firstInit();
   },
   methods: {
     /* SOTA汇总表(第三) */
-   /*  thirdHandleEdit(index, row) {
+    /*  thirdHandleEdit(index, row) {
       row.isEdit = true;
     },
     thirdHandleSave(index, row) {
@@ -694,7 +696,7 @@ export default {
       }));
     }, */
     /* SOTA数据表(第二) */
-   /*  secondHandleEdit(index, row) {
+    /*  secondHandleEdit(index, row) {
       row.isEdit = true;
     },
     secondHandleSave(index, row) {
@@ -877,18 +879,121 @@ export default {
     /* 获取详细信息 */
     async firstInit() {
       const res = await getDetail({ id: this.$route.query.id });
-      console.log("详情数据:", res.data);
+
+      // 定义字段映射关系
+      const fieldMappings = {
+        identifier: {
+          description: "SOTA-Xx-Xx",
+          example: "文献唯一标识符",
+        },
+        literature_type: {
+          description:
+            "1-Meta-Analysis 2-Systematic Review(Systematic Review有非常严格的检索、选择、评价标准)3-Review4-Randomized Controlled Trials5-Cohort Study6-Case-Report7-Background Information/Expert Opinion8-Animal Research/Lab Studies可以从文献的:标题(1,2在标题出现的概率高),abstract,method中识别文献类型",
+          example: "第一作者",
+        },
+        first_author: {
+          description: "overall_quality",
+          example: "",
+        },
+        study_design: {
+          description: "可以从文献的method部分提取实验设计/方法可以是Randomized controlled方式.文献搜索,数据库提取文献,干预分组……",
+          example: "searched Medline, Embase, the Web of Science, and the Cochrane Library for randomized trials and observational studies",
+        },
+        population: {
+          description: "Population information; including: patient quantify(N, N of male/female); age(mean,range) Adult/children eg:60 adults with T1D (mean age 38years), 20 male",
+          example: "Type II Diabetes: n=10282 Age: ≥18 years; Naive to CGM; Between 2017 to 2019",
+        },
+        region_country: {
+          description: "患者/试验者的人种,国家地区,以查阅是否存在数据上人种差异",
+          example: "white non-hispanic: n=247; african american:n=25; hispanic or latino: n=34",
+        },
+        indication: {
+          description: "器械在文献中使用的适应症,按照器械分析,也需要添加器械使用部位以区分",
+          example: "t1d: n=191;t2d: n=152;",
+        },
+        intervention: {
+          description: "Group 1: xx; Group 2: xx 写清对照组和实验组分组的情况,如:有无干预,分适应症,分部位……",
+          example: "CGM(N=XX) vs. SMBG(N=XX); CGM (N=XX) vs. venous blood glucose test(N=XX); CGM+SMBG(N=XX) vs. SMBG(N=XX)",
+        },
+        subject_device: {
+          description: "Device name + model name 如无,可写疗法(以评价的器械所用疗法为主)",
+          example: "Dexcom G6(此处为CGM器械名字)",
+        },
+        alternative_device: {
+          description: "Device name + model name 如无,可写疗法(以评价的器械的替代疗法)",
+          example: "Capillary blood by BMG; Venous blood by YSI (此处为CGM的替代疗法)",
+        },
+        outcomes: {
+          description: "Scope: tools (follow-up); such as 'Pain: VAS (day 3)'",
+          example: "a1c;tir;tbr;tar",
+        },
+        risk_adverse_event: {
+          description: "Type of side effect",
+          example: "Serious Hypoglycemia (SH);Diabetic Ketoacidosis (DKA)",
+        },
+        follow_up: {
+          description: "随访时间",
+          example: "8 weeks",
+        },
+        clinical_benefit: {
+          description: "临床收益(文献中提到研究器械的好处,或者可以达到什么效果,可以摘抄过来可以从result,discussion中提取detailed can be refer to MEDDEV 2.7.1 rev4,b. Evaluation of the device’s benefits to the patient",
+          example: "the pediatric age span who used a CGM have improved glycemic control compared with patients who did patients who use insulin pumps along with CGM did achieve a lower HbA1c and better CGM metrics compared with the reference groups.",
+        },
+        disadvantage: {
+          description: "临床劣势(文献中提到研究器械的劣势,比如在什么情况下疗效不好)eg:导致出血,导致uncomfortable,导致预后不好……可以从result,discussion中提取",
+          example: "/",
+        },
+        conclusion: {
+          description: "结论",
+          example: "CGM use was associated with a lower HbA1c in both MDI and pump users. Pump use was only associated with a lower HbA1c if used with CGM. HCL was associated with the lowest HbA1c. Spanish language and minority race/ethnicity were associated with lower rates of pump and CGM use, highlighting the need to reduce disparities.",
+        },
+        clinical_guidelines: {
+          description: "识别文献中提到的临床实践指南",
+          example: "",
+        },
+        clinical_background: {
+          description: "描述有关器械的临床背景,包括发展的概述等",
+          example: "",
+        },
+        medical_field: {
+          description: "识别该器械是用于哪个医疗领域?例如骨科,外科,牙科,放射诊断等,包括已确定的医疗领域的历史方面的概述",
+          example: "",
+        },
+        associated_conditions: {
+          description: "1 讨论该设备拟治疗的疾病的所有方面,包括但不限于流行病学、发病机制、临床表现和共病2 临床条件的描述,如:2.1 自然病程和结果。2.2 医疗条件是否有不同的临床方式、阶段和严重性。2.3 总体人群中的频率,通过年龄、性别、种族划分,熟悉的诱因、遗传方面等。",
+          example: "",
+        },
+        suitability_criteria: {
+          description: "D1 Actual device D2 Equivalent device D3 Other device A1 Same use A2 Minor deviation A3 Major deviation P1 Applicable P2 Limited P3 Different population R1 High quality R2 Minor deficiencies R3 Insufficient information",
+          example: "D2,A1,P1,R1",
+        },
+        contribution_criteria: {
+          description: "1 Yes; 2 No T:Was the design of the study appropriate? O:Do the outcome measures reported reflect the intended purpose of the device? F:Is the duration of the follow-up long enough to access whether duration of treatment effects and identify complications? S:Has a statistical analysis of the data been provided and is it appropriate?  C:Was the magnitude of the treatment effect observed clinically significant?  ",
+          example: "T1,O1,F1,S1,C1",
+        },
+        oxford_evidence: {
+          description: "根据Literature type,对照Oxford Centre for Evidence-Based Medicine, Levels of Evidence (2011)填写基本level 1:Systematic review of randomized trials level 2:Randomized trial or observational study level 3:Non-randomized controlled cohort/follow-upstudy (PMS) level 4:Case report level 5:Mechanism-based reasoning",
+          example: "level 2",
+        },
+        overall_quality: {
+          description: "将前面数字加起来的总和 9-14 Accepted and Pivotal 15-23 Accepted but not Pivotal 24-27 Excluded",
+          example: "12",
+        },
+      };
 
       // Transform data into array of objects with name property
-      const processedData = Object.entries(res.data)
-        .filter(([key]) => !["id", "create_time", "update_time"].includes(key)) // Exclude non-field properties
-        .map(([key, value]) => ({
+      const processedData = Object.entries(res.data.data.datalist)
+        .filter(([key]) => !["id", "create_time", "update_time"].includes(key))
+        .map(([key]) => ({
           name: key,
-          isEdit:false,
-          ...value,
+          isEdit: false,
+          description: fieldMappings[key]?.description || "",
+          example: fieldMappings[key]?.example || "",
+          generation_1: "", // 为模型生成1预留空间
+          generation_2: "", // 为模型生成2预留空间
         }));
-        console.log(processedData);
-        this.firstForm.tableData=processedData;
+
+      this.firstForm.tableData = processedData;
     },
   },
   watch: {

+ 49 - 5
src/views/middPage/list.vue

@@ -120,9 +120,9 @@
           <el-table-column type="selection" width="55" />
           <el-table-column prop="id" label="ID" width="80" />
           <el-table-column prop="name" label="名称">
-            <template #default="{ row }">
+            <!-- <template #default="{ row }">
               <el-link @click="handleNameClick(row)">{{ row.name }}</el-link>
-            </template>
+            </template> -->
           </el-table-column>
           <el-table-column prop="tags" label="已绑定标签">
             <template #default="{ row }">
@@ -137,7 +137,7 @@
             </template>
           </el-table-column>
           <el-table-column prop="type" label="文档类型" width="100" />
-          <el-table-column prop="create_time" label="创建时间" width="200"/>
+          <el-table-column prop="create_time" label="创建时间" width="200" />
           <el-table-column prop="status" label="处理状态" width="100">
             <template #default="{ row }">
               <el-tag :type="getStatusType(row.status)">
@@ -145,6 +145,42 @@
               </el-tag>
             </template>
           </el-table-column>
+          <el-table-column label="操作" width="100">
+            <template #default="{ row }">
+              <el-dropdown trigger="click">
+                <el-button
+                  type="text"
+                  size="small"
+                  circle
+                  class="operation-button"
+                  style="font-size: 15px; margin-left: 10px"
+                  icon="el-icon-more"
+                ></el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item @click.native="handleNameClick(row)">
+                      <i class="el-icon-view"></i> 查看
+                    </el-dropdown-item>
+                    <el-dropdown-item
+                      @click.native="handlePreview(row)"
+                    >
+                      <i class="el-icon-tickets"></i> 预览
+                    </el-dropdown-item>
+                    <el-dropdown-item
+                      @click.native="firstHandleUpload(scope.$index, scope.row)"
+                    >
+                      <i class="el-icon-refresh"></i> 批量处理
+                    </el-dropdown-item>
+                    <!--  <el-dropdown-item
+                      @click.native="firstHandleDelete(scope.$index, scope.row)"
+                    >
+                      <i class="el-icon-delete"></i> 删除
+                    </el-dropdown-item> -->
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
         </el-table>
 
         <!-- 分页器 -->
@@ -515,7 +551,15 @@ export default {
       this.currentPage = val;
       this.debouncedFetchGpuList();
     },
-
+    /* 预览 */
+    handlePreview(row) {
+      console.log(row);
+      let _this = this;
+      let a = document.createElement("a");
+      a.href = `#/infoList?id=${row.id}&type=${row.type.toLowerCase()}`; // 使用 hash
+      a.target = "_blank";
+      a.click();
+    },
     // 处理名称点击
     async handleNameClick(row) {
       try {
@@ -523,7 +567,7 @@ export default {
         console.log('详情数据:', res) */
         this.$router.push({
           path: "/knowledge/middPage/index",
-          query: { id: row.id, name: row.name },
+          query: { id: row.id, name: row.name, tag: JSON.stringify(row.tags) },
         });
         // 这里可以添加详情处理逻辑
       } catch (error) {

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно