159 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState, useRef, useEffect } from 'react'
interface DecodedLine {
content: string
isError: boolean
}
export default function DecodePage() {
const [decodedLines, setDecodedLines] = useState<DecodedLine[]>([])
const [errorMessage, setErrorMessage] = useState('')
const resultRef = useRef<HTMLDivElement>(null)
// 转换 Unix 时间戳为本地时间字符串
const formatTimestamp = (line: string): string => {
const match = line.match(/^\[(\d+)\](.*)/)
if (match) {
const timestamp = parseInt(match[1])
const date = new Date(timestamp * 1000)
const formattedTime = date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
return `[${formattedTime}]${match[2]}`
}
return line
}
const decodeBase64 = (base64String: string): string => {
try {
const binaryString = atob(base64String.trim())
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
const decoder = new TextDecoder('utf-8')
return decoder.decode(bytes)
} catch (e) {
throw new Error(`解码失败: ${(e as Error).message}`)
}
}
const scrollToBottom = () => {
if (resultRef.current) {
resultRef.current.scrollTop = resultRef.current.scrollHeight
}
}
// 当解码行数更新时滚动到底部
useEffect(() => {
if (decodedLines.length > 0) {
setTimeout(scrollToBottom, 100)
}
}, [decodedLines])
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (!file) {
setErrorMessage('请选择文件')
return
}
try {
const text = await file.text()
const lines = text.split('\n')
const processedLines: DecodedLine[] = lines
.filter(line => line.trim())
.map((line, index) => {
try {
const decodedLine = decodeBase64(line)
const formattedLine = formatTimestamp(decodedLine)
return {
content: formattedLine,
isError: false
}
} catch (e) {
console.error(`${index + 1} 行解码失败:`, e)
return {
content: `${index + 1} 行解码失败: ${line}`,
isError: true
}
}
})
setDecodedLines(processedLines)
setErrorMessage('')
} catch (e) {
setErrorMessage('文件读取失败,请重试')
console.error('文件读取错误:', e)
}
}
return (
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-2xl font-bold text-gray-800 mb-6">Base64 </h1>
{/* 文件上传区域 */}
<div className="mb-8">
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
<label className="block mb-4 text-sm font-medium text-gray-700">
</label>
<input
type="file"
accept=".txt,.log"
onChange={handleFileChange}
className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4
file:rounded-md file:border-0 file:text-sm file:font-semibold
file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100
cursor-pointer"
/>
</div>
</div>
{/* 错误提示 */}
{errorMessage && (
<div className="mb-6 p-4 bg-red-50 text-red-600 rounded-md">
{errorMessage}
</div>
)}
{/* 解码结果 */}
{decodedLines.length > 0 && (
<div className="space-y-2">
<h2 className="text-lg font-semibold text-gray-700 mb-4"></h2>
<div
ref={resultRef}
className="bg-white shadow rounded-lg overflow-auto max-h-[600px]"
>
<div className="divide-y divide-gray-100">
{decodedLines.map((line, index) => (
<div
key={index}
className={`py-1.5 px-4 hover:bg-gray-50 leading-tight ${
line.isError ? 'bg-red-50' : ''
}`}
>
<pre className="whitespace-pre-wrap font-mono text-sm text-gray-800">
{line.content}
</pre>
</div>
))}
</div>
</div>
</div>
)}
</div>
)
}