保存
This commit is contained in:
parent
3459a389ac
commit
58533aa5d0
268
src/App.tsx
268
src/App.tsx
@ -25,6 +25,15 @@ function App() {
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
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(() => {
|
||||
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 切换
|
||||
const handleMobileTabChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const tab = e.target.value
|
||||
@ -471,6 +547,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2PrivateKey}
|
||||
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) 或点击生成密钥对"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -490,6 +573,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2PublicKey}
|
||||
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) 或点击生成密钥对"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -556,6 +646,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2Message}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -575,6 +672,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2Encrypted}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -594,6 +698,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2Decrypted}
|
||||
readOnly
|
||||
onContextMenu={(e) => openMenu(e, sm2Decrypted, setSm2Decrypted, true)}
|
||||
onTouchStart={(e) => {
|
||||
const touchTimer = setTimeout(() => {
|
||||
openMenu(e, sm2Decrypted, setSm2Decrypted, true)
|
||||
}, 500)
|
||||
return () => clearTimeout(touchTimer)
|
||||
}}
|
||||
placeholder="解密结果将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -656,6 +767,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2SignMessage}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -675,6 +793,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm2Signature}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -760,6 +885,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm3Message}
|
||||
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="输入要计算的消息"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -780,6 +912,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm3Result}
|
||||
readOnly
|
||||
onContextMenu={(e) => openMenu(e, sm3Result, setSm3Result, true)}
|
||||
onTouchStart={(e) => {
|
||||
const touchTimer = setTimeout(() => {
|
||||
openMenu(e, sm3Result, setSm3Result, true)
|
||||
}, 500)
|
||||
return () => clearTimeout(touchTimer)
|
||||
}}
|
||||
placeholder="SM3 计算结果将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -818,6 +957,13 @@ function App() {
|
||||
type="text"
|
||||
value={sm3HmacKey}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -836,6 +982,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm3HmacResult}
|
||||
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 计算结果将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -881,6 +1034,13 @@ function App() {
|
||||
type="text"
|
||||
value={sm4Key}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -900,6 +1060,13 @@ function App() {
|
||||
type="text"
|
||||
value={sm4Iv}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -970,6 +1137,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm4Message}
|
||||
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="输入要加密的消息"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -990,6 +1164,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm4Encrypted}
|
||||
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)"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -1010,6 +1191,13 @@ function App() {
|
||||
<textarea
|
||||
value={sm4Decrypted}
|
||||
readOnly
|
||||
onContextMenu={(e) => openMenu(e, sm4Decrypted, setSm4Decrypted, true)}
|
||||
onTouchStart={(e) => {
|
||||
const touchTimer = setTimeout(() => {
|
||||
openMenu(e, sm4Decrypted, setSm4Decrypted, true)
|
||||
}, 500)
|
||||
return () => clearTimeout(touchTimer)
|
||||
}}
|
||||
placeholder="解密结果将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -1078,6 +1266,13 @@ function App() {
|
||||
<textarea
|
||||
value={utf8Input}
|
||||
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 字符串"
|
||||
style={{
|
||||
height: '150px',
|
||||
@ -1115,6 +1310,13 @@ function App() {
|
||||
<textarea
|
||||
value={hexInput}
|
||||
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 字符串"
|
||||
style={{
|
||||
height: '150px',
|
||||
@ -1158,6 +1360,13 @@ function App() {
|
||||
<textarea
|
||||
value={base64Input}
|
||||
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 字符串"
|
||||
style={{
|
||||
height: '150px',
|
||||
@ -1195,6 +1404,13 @@ function App() {
|
||||
<textarea
|
||||
value={hexInput}
|
||||
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 字符串"
|
||||
style={{
|
||||
height: '150px',
|
||||
@ -1333,6 +1549,58 @@ function App() {
|
||||
</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' }}>
|
||||
<p>© 2026 安全算法计算工具</p>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user