This commit is contained in:
cheney 2026-04-24 09:53:03 +08:00
parent 3459a389ac
commit 58533aa5d0

View File

@ -25,6 +25,15 @@ function App() {
const [isMobile, setIsMobile] = useState(false) const [isMobile, setIsMobile] = useState(false)
const [mobileTab, setMobileTab] = useState(getInitialTab()) const [mobileTab, setMobileTab] = useState(getInitialTab())
// 功能菜单状态
const [menuVisible, setMenuVisible] = useState(false)
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 })
const [currentInput, setCurrentInput] = useState<{
value: string;
setter: React.Dispatch<React.SetStateAction<string>>;
readOnly: boolean
} | null>(null)
// 检测是否为移动设备 // 检测是否为移动设备
useEffect(() => { useEffect(() => {
const checkMobile = () => { const checkMobile = () => {
@ -39,6 +48,73 @@ function App() {
} }
}, []) }, [])
// 点击外部关闭菜单
useEffect(() => {
const handleClickOutside = () => {
setMenuVisible(false)
}
if (menuVisible) {
document.addEventListener('click', handleClickOutside)
}
return () => {
document.removeEventListener('click', handleClickOutside)
}
}, [menuVisible])
// 打开功能菜单
const openMenu = (e: React.MouseEvent | React.TouchEvent, value: string, setter: React.Dispatch<React.SetStateAction<string>>, readOnly: boolean) => {
e.preventDefault()
let x = 0
let y = 0
if ('clientX' in e) {
// 鼠标事件
x = e.clientX
y = e.clientY
} else if ('touches' in e && e.touches.length > 0) {
// 触摸事件
x = e.touches[0].clientX
y = e.touches[0].clientY
}
setMenuPosition({ x, y })
setCurrentInput({ value, setter, readOnly })
setMenuVisible(true)
}
// 清空输入
const handleClear = () => {
if (currentInput) {
currentInput.setter('')
setMenuVisible(false)
}
}
// 复制全部
const handleCopyAll = () => {
if (currentInput && currentInput.value) {
navigator.clipboard.writeText(currentInput.value).catch(err => {
console.error('复制失败:', err)
})
setMenuVisible(false)
}
}
// 粘贴覆盖
const handlePaste = () => {
if (currentInput && !currentInput.readOnly) {
navigator.clipboard.readText().then(text => {
currentInput.setter(text)
}).catch(err => {
console.error('粘贴失败:', err)
})
setMenuVisible(false)
}
}
// 处理移动设备的 tab 切换 // 处理移动设备的 tab 切换
const handleMobileTabChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const handleMobileTabChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const tab = e.target.value const tab = e.target.value
@ -471,6 +547,13 @@ function App() {
<textarea <textarea
value={sm2PrivateKey} value={sm2PrivateKey}
onChange={(e) => setSm2PrivateKey(e.target.value)} onChange={(e) => setSm2PrivateKey(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2PrivateKey, setSm2PrivateKey, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2PrivateKey, setSm2PrivateKey, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入私钥 (hex) 或点击生成密钥对" placeholder="输入私钥 (hex) 或点击生成密钥对"
style={{ style={{
width: '100%', width: '100%',
@ -490,6 +573,13 @@ function App() {
<textarea <textarea
value={sm2PublicKey} value={sm2PublicKey}
onChange={(e) => setSm2PublicKey(e.target.value)} onChange={(e) => setSm2PublicKey(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2PublicKey, setSm2PublicKey, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2PublicKey, setSm2PublicKey, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入公钥 (hex) 或点击生成密钥对" placeholder="输入公钥 (hex) 或点击生成密钥对"
style={{ style={{
width: '100%', width: '100%',
@ -556,6 +646,13 @@ function App() {
<textarea <textarea
value={sm2Message} value={sm2Message}
onChange={(e) => setSm2Message(e.target.value)} onChange={(e) => setSm2Message(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2Message, setSm2Message, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2Message, setSm2Message, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入要加密的消息 (hex)" placeholder="输入要加密的消息 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -575,6 +672,13 @@ function App() {
<textarea <textarea
value={sm2Encrypted} value={sm2Encrypted}
onChange={(e) => setSm2Encrypted(e.target.value)} onChange={(e) => setSm2Encrypted(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2Encrypted, setSm2Encrypted, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2Encrypted, setSm2Encrypted, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入加密结果 (hex)" placeholder="输入加密结果 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -594,6 +698,13 @@ function App() {
<textarea <textarea
value={sm2Decrypted} value={sm2Decrypted}
readOnly readOnly
onContextMenu={(e) => openMenu(e, sm2Decrypted, setSm2Decrypted, true)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2Decrypted, setSm2Decrypted, true)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="解密结果将显示在这里" placeholder="解密结果将显示在这里"
style={{ style={{
width: '100%', width: '100%',
@ -656,6 +767,13 @@ function App() {
<textarea <textarea
value={sm2SignMessage} value={sm2SignMessage}
onChange={(e) => setSm2SignMessage(e.target.value)} onChange={(e) => setSm2SignMessage(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2SignMessage, setSm2SignMessage, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2SignMessage, setSm2SignMessage, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入要签名的消息 (hex)" placeholder="输入要签名的消息 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -675,6 +793,13 @@ function App() {
<textarea <textarea
value={sm2Signature} value={sm2Signature}
onChange={(e) => setSm2Signature(e.target.value)} onChange={(e) => setSm2Signature(e.target.value)}
onContextMenu={(e) => openMenu(e, sm2Signature, setSm2Signature, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm2Signature, setSm2Signature, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入签名 (hex)" placeholder="输入签名 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -760,6 +885,13 @@ function App() {
<textarea <textarea
value={sm3Message} value={sm3Message}
onChange={(e) => setSm3Message(e.target.value)} onChange={(e) => setSm3Message(e.target.value)}
onContextMenu={(e) => openMenu(e, sm3Message, setSm3Message, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm3Message, setSm3Message, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入要计算的消息" placeholder="输入要计算的消息"
style={{ style={{
width: '100%', width: '100%',
@ -780,6 +912,13 @@ function App() {
<textarea <textarea
value={sm3Result} value={sm3Result}
readOnly readOnly
onContextMenu={(e) => openMenu(e, sm3Result, setSm3Result, true)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm3Result, setSm3Result, true)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="SM3 计算结果将显示在这里" placeholder="SM3 计算结果将显示在这里"
style={{ style={{
width: '100%', width: '100%',
@ -818,6 +957,13 @@ function App() {
type="text" type="text"
value={sm3HmacKey} value={sm3HmacKey}
onChange={(e) => setSm3HmacKey(e.target.value)} onChange={(e) => setSm3HmacKey(e.target.value)}
onContextMenu={(e) => openMenu(e, sm3HmacKey, setSm3HmacKey, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm3HmacKey, setSm3HmacKey, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 HMAC 密钥 (hex)" placeholder="输入 HMAC 密钥 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -836,6 +982,13 @@ function App() {
<textarea <textarea
value={sm3HmacResult} value={sm3HmacResult}
readOnly readOnly
onContextMenu={(e) => openMenu(e, sm3HmacResult, setSm3HmacResult, true)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm3HmacResult, setSm3HmacResult, true)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="SM3 HMAC 计算结果将显示在这里" placeholder="SM3 HMAC 计算结果将显示在这里"
style={{ style={{
width: '100%', width: '100%',
@ -881,6 +1034,13 @@ function App() {
type="text" type="text"
value={sm4Key} value={sm4Key}
onChange={(e) => setSm4Key(e.target.value)} onChange={(e) => setSm4Key(e.target.value)}
onContextMenu={(e) => openMenu(e, sm4Key, setSm4Key, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm4Key, setSm4Key, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 16 字节密钥 (32 位 hex)" placeholder="输入 16 字节密钥 (32 位 hex)"
style={{ style={{
width: '100%', width: '100%',
@ -900,6 +1060,13 @@ function App() {
type="text" type="text"
value={sm4Iv} value={sm4Iv}
onChange={(e) => setSm4Iv(e.target.value)} onChange={(e) => setSm4Iv(e.target.value)}
onContextMenu={(e) => openMenu(e, sm4Iv, setSm4Iv, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm4Iv, setSm4Iv, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 16 字节 IV (32 位 hex)" placeholder="输入 16 字节 IV (32 位 hex)"
style={{ style={{
width: '100%', width: '100%',
@ -970,6 +1137,13 @@ function App() {
<textarea <textarea
value={sm4Message} value={sm4Message}
onChange={(e) => setSm4Message(e.target.value)} onChange={(e) => setSm4Message(e.target.value)}
onContextMenu={(e) => openMenu(e, sm4Message, setSm4Message, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm4Message, setSm4Message, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入要加密的消息" placeholder="输入要加密的消息"
style={{ style={{
width: '100%', width: '100%',
@ -990,6 +1164,13 @@ function App() {
<textarea <textarea
value={sm4Encrypted} value={sm4Encrypted}
onChange={(e) => setSm4Encrypted(e.target.value)} onChange={(e) => setSm4Encrypted(e.target.value)}
onContextMenu={(e) => openMenu(e, sm4Encrypted, setSm4Encrypted, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm4Encrypted, setSm4Encrypted, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入加密结果 (hex)" placeholder="输入加密结果 (hex)"
style={{ style={{
width: '100%', width: '100%',
@ -1010,6 +1191,13 @@ function App() {
<textarea <textarea
value={sm4Decrypted} value={sm4Decrypted}
readOnly readOnly
onContextMenu={(e) => openMenu(e, sm4Decrypted, setSm4Decrypted, true)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, sm4Decrypted, setSm4Decrypted, true)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="解密结果将显示在这里" placeholder="解密结果将显示在这里"
style={{ style={{
width: '100%', width: '100%',
@ -1078,6 +1266,13 @@ function App() {
<textarea <textarea
value={utf8Input} value={utf8Input}
onChange={(e) => setUtf8Input(e.target.value)} onChange={(e) => setUtf8Input(e.target.value)}
onContextMenu={(e) => openMenu(e, utf8Input, setUtf8Input, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, utf8Input, setUtf8Input, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 UTF8 字符串" placeholder="输入 UTF8 字符串"
style={{ style={{
height: '150px', height: '150px',
@ -1115,6 +1310,13 @@ function App() {
<textarea <textarea
value={hexInput} value={hexInput}
onChange={(e) => setHexInput(e.target.value)} onChange={(e) => setHexInput(e.target.value)}
onContextMenu={(e) => openMenu(e, hexInput, setHexInput, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, hexInput, setHexInput, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 hex 字符串" placeholder="输入 hex 字符串"
style={{ style={{
height: '150px', height: '150px',
@ -1158,6 +1360,13 @@ function App() {
<textarea <textarea
value={base64Input} value={base64Input}
onChange={(e) => setBase64Input(e.target.value)} onChange={(e) => setBase64Input(e.target.value)}
onContextMenu={(e) => openMenu(e, base64Input, setBase64Input, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, base64Input, setBase64Input, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 Base64 字符串" placeholder="输入 Base64 字符串"
style={{ style={{
height: '150px', height: '150px',
@ -1195,6 +1404,13 @@ function App() {
<textarea <textarea
value={hexInput} value={hexInput}
onChange={(e) => setHexInput(e.target.value)} onChange={(e) => setHexInput(e.target.value)}
onContextMenu={(e) => openMenu(e, hexInput, setHexInput, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, hexInput, setHexInput, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 hex 字符串" placeholder="输入 hex 字符串"
style={{ style={{
height: '150px', height: '150px',
@ -1333,6 +1549,58 @@ function App() {
</div> </div>
)} )}
{/* 功能菜单 */}
{menuVisible && currentInput && (
<div
style={{
position: 'fixed',
left: menuPosition.x,
top: menuPosition.y,
backgroundColor: '#2d2d2d',
border: '1px solid #333',
borderRadius: '4px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
zIndex: 1000,
minWidth: '150px'
}}
>
<div
style={{
padding: '8px 12px',
cursor: 'pointer',
color: '#e0e0e0',
borderBottom: '1px solid #333'
}}
onClick={handleClear}
>
</div>
<div
style={{
padding: '8px 12px',
cursor: 'pointer',
color: '#e0e0e0',
borderBottom: '1px solid #333'
}}
onClick={handleCopyAll}
>
</div>
{!currentInput.readOnly && (
<div
style={{
padding: '8px 12px',
cursor: 'pointer',
color: '#e0e0e0'
}}
onClick={handlePaste}
>
</div>
)}
</div>
)}
{/* 页脚 */} {/* 页脚 */}
<footer style={{ marginTop: '2rem', padding: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#e0e0e0' }}> <footer style={{ marginTop: '2rem', padding: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#e0e0e0' }}>
<p>© 2026 </p> <p>© 2026 </p>