@@ -34,16 +34,19 @@ const processAnsiColors = (text: string) => {
3434 // 移除时间戳前缀(如果存在)
3535 text = text . replace ( / ^ \d { 4 } - \d { 2 } - \d { 2 } \s \d { 2 } : \d { 2 } : \d { 2 } \. \d { 3 } \s / , '' ) ;
3636
37+ // 只移除 \u001B 字符,保留后面的颜色代码
38+ text = text . replace ( / \u001B / g, '' ) ; // 只移除 ESC 字符,保留 [32m 等
39+
3740 // 将 ANSI 颜色代码转换为 HTML span 标签
3841 const colorMap = new Map ( [
39- [ / \[ 3 2 m / g, '<span class="text-green-400">' ] ,
40- [ / \[ 3 1 m / g, '<span class="text-red-400">' ] ,
41- [ / \[ 3 3 m / g, '<span class="text-yellow-400">' ] ,
42- [ / \[ 3 4 m / g, '<span class="text-blue-400">' ] ,
43- [ / \[ 3 5 m / g, '<span class="text-purple-400">' ] ,
44- [ / \[ 3 6 m / g, '<span class="text-cyan-400">' ] ,
45- [ / \[ 3 7 m / g, '<span class="text-gray-400">' ] ,
46- [ / \[ 0 m / g, '</span>' ]
42+ [ / \[ 3 2 m / g, '<span class="text-green-400">' ] , // INFO - 绿色
43+ [ / \[ 3 1 m / g, '<span class="text-red-400">' ] , // ERROR - 红色
44+ [ / \[ 3 3 m / g, '<span class="text-yellow-400">' ] , // WARN - 黄色
45+ [ / \[ 3 4 m / g, '<span class="text-blue-400">' ] , // DEBUG - 蓝色
46+ [ / \[ 3 5 m / g, '<span class="text-purple-400">' ] , // 紫色
47+ [ / \[ 3 6 m / g, '<span class="text-cyan-400">' ] , // 青色
48+ [ / \[ 3 7 m / g, '<span class="text-gray-400">' ] , // 灰色
49+ [ / \[ 0 m / g, '</span>' ] // 结束标签
4750 ] ) ;
4851
4952 // 替换颜色代码
@@ -55,15 +58,16 @@ const processAnsiColors = (text: string) => {
5558 const openTags = ( text . match ( / < s p a n / g) || [ ] ) . length ;
5659 const closeTags = ( text . match ( / < \/ s p a n > / g) || [ ] ) . length ;
5760
58- // 如果开标签比闭标签多,添加缺少的闭标签
61+ // 如果开始标签多于结束标签,添加结束标签
5962 if ( openTags > closeTags ) {
60- text += '</span>' . repeat ( openTags - closeTags ) ;
63+ const missingCloseTags = openTags - closeTags ;
64+ text += '</span>' . repeat ( missingCloseTags ) ;
6165 }
6266
6367 return text ;
6468 } catch ( error ) {
65- console . error ( '处理日志颜色失败 :' , error ) ;
66- return text ; // 如果处理失败,返回原始文本
69+ console . error ( '处理ANSI颜色失败 :' , error ) ;
70+ return text ;
6771 }
6872} ;
6973
@@ -383,23 +387,31 @@ export default function TunnelDetailPage({ params }: { params: Promise<PageParam
383387 完整数据 : JSON . stringify ( data . tunnelInfo , null , 2 )
384388 } ) ;
385389
386- // 设置历史日志 - 处理字符串数组格式,启用HTML渲染
390+ // 设置历史日志 - 处理带时间信息的日志对象
387391 if ( data . logs && Array . isArray ( data . logs ) ) {
388392 // 初始化计数器为历史日志的数量,确保新日志ID不会与历史日志冲突
389393 logCounterRef . current = data . logs . length ;
390- const formattedLogs = data . logs . map ( ( message : string , index : number ) => ( {
391- id : index + 1 , // 从1开始计数,避免0值
392- message,
393- isHtml : true , // 启用HTML格式渲染
394- traffic : {
395- tcpRx : 0 ,
396- tcpTx : 0 ,
397- udpRx : 0 ,
398- udpTx : 0
399- } ,
400- timestamp : new Date ( ) // 使用当前时间作为占位符
401- } ) ) ;
402- setLogs ( formattedLogs ) ;
394+
395+ // 检查日志数据格式
396+ if ( data . logs . length > 0 && typeof data . logs [ 0 ] === 'object' ) {
397+ // 新格式:对象数组,包含时间信息
398+ setLogs ( data . logs ) ;
399+ } else {
400+ // 旧格式:字符串数组,需要转换
401+ const formattedLogs = data . logs . map ( ( message : string , index : number ) => ( {
402+ id : index + 1 ,
403+ message,
404+ isHtml : true ,
405+ traffic : {
406+ tcpRx : 0 ,
407+ tcpTx : 0 ,
408+ udpRx : 0 ,
409+ udpTx : 0
410+ } ,
411+ timestamp : new Date ( ) // 使用当前时间作为占位符
412+ } ) ) ;
413+ setLogs ( formattedLogs ) ;
414+ }
403415
404416 // 稍微延迟滚动,确保DOM更新完成
405417 setTimeout ( scrollToBottom , 100 ) ;
0 commit comments