ソースを参照

⚡ feat: Add speed test functionality to API info display

- Add speed test tag with gauge icon for each API route
- Integrate tcptest.cn service for API endpoint performance testing
- Implement handleSpeedTest callback to open speed test in new tab
- Add Tag component import from @douyinfe/semi-ui
- Use Gauge icon with white circular tag styling
- Position speed test tag before API route for better visibility
- URL encoding handles special characters for proper test URL generation
- Remove unused IconTestScoreStroked import and clean up comments

The speed test feature allows users to quickly test API endpoint
performance by clicking a small circular tag that opens the
tcptest.cn speed testing service with the encoded API URL.
Apple\Apple 9 ヶ月 前
コミット
845b748ffe
2 ファイル変更21 行追加11 行削除
  1. 2 1
      web/src/i18n/locales/en.json
  2. 19 10
      web/src/pages/Detail/index.js

+ 2 - 1
web/src/i18n/locales/en.json

@@ -1585,5 +1585,6 @@
   "请输入说明": "Please enter the description",
   "请输入说明": "Please enter the description",
   "如:香港线路": "e.g. Hong Kong line",
   "如:香港线路": "e.g. Hong Kong line",
   "请联系管理员在系统设置中配置API信息": "Please contact the administrator to configure API information in the system settings.",
   "请联系管理员在系统设置中配置API信息": "Please contact the administrator to configure API information in the system settings.",
-  "确定要删除此API信息吗?": "Are you sure you want to delete this API information?"
+  "确定要删除此API信息吗?": "Are you sure you want to delete this API information?",
+  "测速": "Speed Test"
 }
 }

+ 19 - 10
web/src/pages/Detail/index.js

@@ -13,6 +13,7 @@ import {
   Tabs,
   Tabs,
   TabPane,
   TabPane,
   Empty,
   Empty,
+  Tag
 } from '@douyinfe/semi-ui';
 } from '@douyinfe/semi-ui';
 import {
 import {
   IconRefresh,
   IconRefresh,
@@ -25,7 +26,7 @@ import {
   IconPulse,
   IconPulse,
   IconStopwatchStroked,
   IconStopwatchStroked,
   IconTypograph,
   IconTypograph,
-  IconPieChart2Stroked,
+  IconPieChart2Stroked
 } from '@douyinfe/semi-icons';
 } from '@douyinfe/semi-icons';
 import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
 import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
 import { VChart } from '@visactor/react-vchart';
 import { VChart } from '@visactor/react-vchart';
@@ -495,6 +496,12 @@ const Detail = (props) => {
     }
     }
   }, [t]);
   }, [t]);
 
 
+  const handleSpeedTest = useCallback((apiUrl) => {
+    const encodedUrl = encodeURIComponent(apiUrl);
+    const speedTestUrl = `https://www.tcptest.cn/http/${encodedUrl}`;
+    window.open(speedTestUrl, '_blank');
+  }, []);
+
   const handleInputChange = useCallback((value, name) => {
   const handleInputChange = useCallback((value, name) => {
     if (name === 'data_export_default_time') {
     if (name === 'data_export_default_time') {
       setDataExportDefaultTime(value);
       setDataExportDefaultTime(value);
@@ -698,22 +705,17 @@ const Detail = (props) => {
   }, [dataExportDefaultTime, getTimeInterval]);
   }, [dataExportDefaultTime, getTimeInterval]);
 
 
   const updateChartData = useCallback((data) => {
   const updateChartData = useCallback((data) => {
-    // 处理原始数据
     const processedData = processRawData(data);
     const processedData = processRawData(data);
     const { totalQuota, totalTimes, totalTokens, uniqueModels, timePoints, timeQuotaMap, timeTokensMap, timeCountMap } = processedData;
     const { totalQuota, totalTimes, totalTokens, uniqueModels, timePoints, timeQuotaMap, timeTokensMap, timeCountMap } = processedData;
 
 
-    // 计算趋势数据
     const trendDataResult = calculateTrendData(timePoints, timeQuotaMap, timeTokensMap, timeCountMap);
     const trendDataResult = calculateTrendData(timePoints, timeQuotaMap, timeTokensMap, timeCountMap);
     setTrendData(trendDataResult);
     setTrendData(trendDataResult);
 
 
-    // 生成模型颜色映射
     const newModelColors = generateModelColors(uniqueModels);
     const newModelColors = generateModelColors(uniqueModels);
     setModelColors(newModelColors);
     setModelColors(newModelColors);
 
 
-    // 聚合数据
     const aggregatedData = aggregateDataByTimeAndModel(data);
     const aggregatedData = aggregateDataByTimeAndModel(data);
 
 
-    // 生成饼图数据
     const modelTotals = new Map();
     const modelTotals = new Map();
     for (let [_, value] of aggregatedData) {
     for (let [_, value] of aggregatedData) {
       updateMapValue(modelTotals, value.model, value.count);
       updateMapValue(modelTotals, value.model, value.count);
@@ -724,7 +726,6 @@ const Detail = (props) => {
       value: count,
       value: count,
     })).sort((a, b) => b.value - a.value);
     })).sort((a, b) => b.value - a.value);
 
 
-    // 生成线图数据
     const chartTimePoints = generateChartTimePoints(aggregatedData, data);
     const chartTimePoints = generateChartTimePoints(aggregatedData, data);
     let newLineData = [];
     let newLineData = [];
 
 
@@ -748,7 +749,6 @@ const Detail = (props) => {
 
 
     newLineData.sort((a, b) => a.Time.localeCompare(b.Time));
     newLineData.sort((a, b) => a.Time.localeCompare(b.Time));
 
 
-    // 更新图表配置
     updateChartSpec(
     updateChartSpec(
       setSpecPie,
       setSpecPie,
       newPieData,
       newPieData,
@@ -765,7 +765,6 @@ const Detail = (props) => {
       'barData'
       'barData'
     );
     );
 
 
-    // 更新状态
     setPieData(newPieData);
     setPieData(newPieData);
     setLineData(newLineData);
     setLineData(newLineData);
     setConsumeQuota(totalQuota);
     setConsumeQuota(totalQuota);
@@ -994,7 +993,17 @@ const Detail = (props) => {
                             </Avatar>
                             </Avatar>
                           </div>
                           </div>
                           <div className="flex-1">
                           <div className="flex-1">
-                            <div className="text-sm font-medium text-gray-900 mb-1">
+                            <div className="text-sm font-medium text-gray-900 mb-1 !font-bold flex items-center gap-2">
+                              <Tag
+                                prefixIcon={<Gauge size={12} />}
+                                size="small"
+                                color="white"
+                                shape='circle'
+                                onClick={() => handleSpeedTest(api.url)}
+                                className="cursor-pointer hover:opacity-80 text-xs"
+                              >
+                                {t('测速')}
+                              </Tag>
                               {api.route}
                               {api.route}
                             </div>
                             </div>
                             <div
                             <div