更新 UI

This commit is contained in:
cheney 2026-04-17 13:34:35 +08:00
parent 826419fdbc
commit 9f6df6bbd8
3 changed files with 401 additions and 78 deletions

View File

@ -1,4 +1,5 @@
# TODO # TODO
- [ ] CI 集成,将 dist 传输到指定服务器指定目录。 - [x] CI 集成,将 dist 传输到指定服务器指定目录。
- [x] 添加 SM2 算法

View File

@ -1,4 +1,4 @@
import { useState } from 'react' import { useState, useEffect } from 'react'
import { VERSION } from './version' import { VERSION } from './version'
import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3' import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3'
@ -7,6 +7,33 @@ import { hexToUtf8, utf8ToHex } from './utils/convert'
function App() { function App() {
const [activeTab, setActiveTab] = useState('sm3') const [activeTab, setActiveTab] = useState('sm3')
const [isMobile, setIsMobile] = useState(false)
const [mobileTab, setMobileTab] = useState('sm3')
// 检测是否为移动设备
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768)
}
checkMobile()
window.addEventListener('resize', checkMobile)
return () => {
window.removeEventListener('resize', checkMobile)
}
}, [])
// 处理移动设备的 tab 切换
const handleMobileTabChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setMobileTab(e.target.value)
setActiveTab(e.target.value)
}
// 处理桌面端的 tab 切换
const handleTabChange = (tab: string) => {
setActiveTab(tab)
}
@ -92,113 +119,280 @@ function App() {
} }
return ( return (
<div className="app"> <div className="app" style={{ maxWidth: '800px', margin: '0 auto', padding: '20px', fontFamily: 'Arial, sans-serif', backgroundColor: '#121212', color: '#e0e0e0', minHeight: '100vh' }}>
<h1></h1> <h1 style={{ textAlign: 'center', color: '#f59e0b' }}></h1>
<div className="tabs"> {/* 桌面端 Tab 选择 */}
<button {!isMobile && (
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`} <div className="tabs" style={{ display: 'flex', borderBottom: '1px solid #333', marginBottom: '20px', backgroundColor: '#1e1e1e', borderRadius: '8px 8px 0 0', overflow: 'hidden' }}>
onClick={() => setActiveTab('sm3')} <button
> className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
SM3 onClick={() => handleTabChange('sm3')}
</button> style={{
<button padding: '12px 24px',
className={`tab ${activeTab === 'sm4' ? 'active' : ''}`} border: 'none',
onClick={() => setActiveTab('sm4')} background: activeTab === 'sm3' ? '#333' : '#1e1e1e',
> cursor: 'pointer',
SM4 fontSize: '16px',
</button> fontWeight: activeTab === 'sm3' ? 'bold' : 'normal',
<button color: activeTab === 'sm3' ? '#f59e0b' : '#e0e0e0',
className={`tab ${activeTab === 'convert' ? 'active' : ''}`} outline: 'none',
onClick={() => setActiveTab('convert')} transition: 'all 0.3s ease',
> borderBottom: activeTab === 'sm3' ? '3px solid #f59e0b' : 'none'
}}
</button> >
</div> SM3
</button>
<button
className={`tab ${activeTab === 'sm4' ? 'active' : ''}`}
onClick={() => handleTabChange('sm4')}
style={{
padding: '12px 24px',
border: 'none',
background: activeTab === 'sm4' ? '#333' : '#1e1e1e',
cursor: 'pointer',
fontSize: '16px',
fontWeight: activeTab === 'sm4' ? 'bold' : 'normal',
color: activeTab === 'sm4' ? '#f59e0b' : '#e0e0e0',
outline: 'none',
transition: 'all 0.3s ease',
borderBottom: activeTab === 'sm4' ? '3px solid #f59e0b' : 'none'
}}
>
SM4
</button>
<button
className={`tab ${activeTab === 'convert' ? 'active' : ''}`}
onClick={() => handleTabChange('convert')}
style={{
padding: '12px 24px',
border: 'none',
background: activeTab === 'convert' ? '#333' : '#1e1e1e',
cursor: 'pointer',
fontSize: '16px',
fontWeight: activeTab === 'convert' ? 'bold' : 'normal',
color: activeTab === 'convert' ? '#f59e0b' : '#e0e0e0',
outline: 'none',
transition: 'all 0.3s ease',
borderBottom: activeTab === 'convert' ? '3px solid #f59e0b' : 'none'
}}
>
</button>
</div>
)}
{/* 移动端下拉选择 */}
{isMobile && (
<div className="mobile-tab-selector" style={{ marginBottom: '20px' }}>
<select
value={mobileTab}
onChange={handleMobileTabChange}
className="mobile-select"
style={{
width: '100%',
padding: '12px',
fontSize: '16px',
border: '1px solid #333',
borderRadius: '4px',
background: '#1e1e1e',
color: '#e0e0e0',
outline: 'none',
appearance: 'none',
cursor: 'pointer'
}}
>
<option value="sm3" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>SM3</option>
<option value="sm4" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>SM4</option>
<option value="convert" style={{ background: '#1e1e1e', color: '#e0e0e0' }}></option>
</select>
</div>
)}
{/* SM3 面板 */} {/* SM3 面板 */}
{activeTab === 'sm3' && ( {activeTab === 'sm3' && (
<div className="panel"> <div className="panel" style={{ backgroundColor: '#1e1e1e', padding: '20px', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.3)' }}>
<h2>SM3 </h2> <h2 style={{ color: '#f59e0b', marginBottom: '20px' }}>SM3 </h2>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<textarea <textarea
value={sm3Message} value={sm3Message}
onChange={(e) => setSm3Message(e.target.value)} onChange={(e) => setSm3Message(e.target.value)}
placeholder="输入要计算的消息" placeholder="输入要计算的消息"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label>SM3 </label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>SM3 </label>
<textarea <textarea
value={sm3Result} value={sm3Result}
readOnly readOnly
placeholder="SM3 计算结果将显示在这里" placeholder="SM3 计算结果将显示在这里"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<button onClick={handleCalculateSm3}> SM3</button> <button
onClick={handleCalculateSm3}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '16px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease'
}}
>
SM3
</button>
<div className="form-group" style={{ marginTop: '1.5rem' }}> <div className="form-group" style={{ marginTop: '1.5rem', marginBottom: '15px' }}>
<label>HMAC </label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>HMAC </label>
<input <input
type="text" type="text"
value={sm3HmacKey} value={sm3HmacKey}
onChange={(e) => setSm3HmacKey(e.target.value)} onChange={(e) => setSm3HmacKey(e.target.value)}
placeholder="输入 HMAC 密钥 (hex)" placeholder="输入 HMAC 密钥 (hex)"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label>SM3 HMAC </label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>SM3 HMAC </label>
<textarea <textarea
value={sm3HmacResult} value={sm3HmacResult}
readOnly readOnly
placeholder="SM3 HMAC 计算结果将显示在这里" placeholder="SM3 HMAC 计算结果将显示在这里"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<button onClick={calculateSm3Hmac}> SM3 HMAC</button> <button
onClick={calculateSm3Hmac}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '16px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease'
}}
>
SM3 HMAC
</button>
</div> </div>
)} )}
{/* SM4 面板 */} {/* SM4 面板 */}
{activeTab === 'sm4' && ( {activeTab === 'sm4' && (
<div className="panel"> <div className="panel" style={{ backgroundColor: '#1e1e1e', padding: '20px', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.3)' }}>
<h2>SM4 </h2> <h2 style={{ color: '#f59e0b', marginBottom: '20px' }}>SM4 </h2>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label> (16 hex)</label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}> (16 hex)</label>
<input <input
type="text" type="text"
value={sm4Key} value={sm4Key}
onChange={(e) => setSm4Key(e.target.value)} onChange={(e) => setSm4Key(e.target.value)}
placeholder="输入 16 字节密钥 (32 位 hex)" placeholder="输入 16 字节密钥 (32 位 hex)"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label>IV (16 hex, CBC/CFB/GCM )</label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>IV (16 hex, CBC/CFB/GCM )</label>
<input <input
type="text" type="text"
value={sm4Iv} value={sm4Iv}
onChange={(e) => setSm4Iv(e.target.value)} onChange={(e) => setSm4Iv(e.target.value)}
placeholder="输入 16 字节 IV (32 位 hex)" placeholder="输入 16 字节 IV (32 位 hex)"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
{['ecb', 'cbc', 'gcm'].map(mode => ( {['ecb', 'cbc', 'gcm'].map(mode => (
<button <button
key={mode} key={mode}
className={sm4Mode === mode ? 'primary' : ''} className={sm4Mode === mode ? 'primary' : ''}
onClick={() => setSm4Mode(mode)} onClick={() => setSm4Mode(mode)}
style={{
padding: '8px 16px',
border: '1px solid #333',
borderRadius: '4px',
background: sm4Mode === mode ? '#f59e0b' : '#2d2d2d',
color: sm4Mode === mode ? '#121212' : '#e0e0e0',
cursor: 'pointer',
fontSize: '14px',
fontWeight: sm4Mode === mode ? 'bold' : 'normal',
transition: 'all 0.3s ease'
}}
> >
{mode.toUpperCase()} {mode.toUpperCase()}
</button> </button>
@ -206,14 +400,25 @@ function App() {
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
{(['pkcs7', 'none'] as const).map(paddingMode => ( {(['pkcs7', 'none'] as const).map(paddingMode => (
<button <button
key={paddingMode} key={paddingMode}
className={sm4PaddingMode === paddingMode ? 'primary' : ''} className={sm4PaddingMode === paddingMode ? 'primary' : ''}
onClick={() => setSm4PaddingMode(paddingMode)} onClick={() => setSm4PaddingMode(paddingMode)}
style={{
padding: '8px 16px',
border: '1px solid #333',
borderRadius: '4px',
background: sm4PaddingMode === paddingMode ? '#f59e0b' : '#2d2d2d',
color: sm4PaddingMode === paddingMode ? '#121212' : '#e0e0e0',
cursor: 'pointer',
fontSize: '14px',
fontWeight: sm4PaddingMode === paddingMode ? 'bold' : 'normal',
transition: 'all 0.3s ease'
}}
> >
{paddingMode} {paddingMode}
</button> </button>
@ -221,73 +426,190 @@ function App() {
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<textarea <textarea
value={sm4Message} value={sm4Message}
onChange={(e) => setSm4Message(e.target.value)} onChange={(e) => setSm4Message(e.target.value)}
placeholder="输入要加密的消息" placeholder="输入要加密的消息"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<textarea <textarea
value={sm4Encrypted} value={sm4Encrypted}
onChange={(e) => setSm4Encrypted(e.target.value)} onChange={(e) => setSm4Encrypted(e.target.value)}
placeholder="输入加密结果 (hex)" placeholder="输入加密结果 (hex)"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ marginBottom: '15px' }}>
<label></label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}></label>
<textarea <textarea
value={sm4Decrypted} value={sm4Decrypted}
readOnly readOnly
placeholder="解密结果将显示在这里" placeholder="解密结果将显示在这里"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '100px'
}}
/> />
</div> </div>
<div className="button-group"> <div className="button-group" style={{ display: 'flex', gap: '10px' }}>
<button onClick={handleEncryptSm4}></button> <button
<button onClick={handleDecryptSm4}></button> onClick={handleEncryptSm4}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '16px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease',
flex: 1
}}
>
</button>
<button
onClick={handleDecryptSm4}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '16px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease',
flex: 1
}}
>
</button>
</div> </div>
</div> </div>
)} )}
{/* 转换工具面板 */} {/* 转换工具面板 */}
{activeTab === 'convert' && ( {activeTab === 'convert' && (
<div className="panel"> <div className="panel" style={{ backgroundColor: '#1e1e1e', padding: '20px', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.3)' }}>
<h2>Hex / UTF8 </h2> <h2 style={{ color: '#f59e0b', marginBottom: '20px' }}>Hex / UTF8 </h2>
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}> <div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem', flexDirection: isMobile ? 'column' : 'row' }}>
<div className="form-group" style={{ flex: 1 }}> <div className="form-group" style={{ flex: 1 }}>
<label>UTF8 </label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>UTF8 </label>
<textarea <textarea
value={utf8Input} value={utf8Input}
onChange={(e) => setUtf8Input(e.target.value)} onChange={(e) => setUtf8Input(e.target.value)}
placeholder="输入 UTF8 字符串" placeholder="输入 UTF8 字符串"
style={{ height: '200px' }} style={{
height: '200px',
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical'
}}
/> />
<button onClick={handleUtf8ToHex} style={{ marginTop: '0.5rem' }}>UTF8 Hex</button> <button
onClick={handleUtf8ToHex}
style={{
marginTop: '0.5rem',
padding: '8px 16px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '14px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease'
}}
>
UTF8 Hex
</button>
</div> </div>
<div className="form-group" style={{ flex: 1 }}> <div className="form-group" style={{ flex: 1 }}>
<label>Hex </label> <label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>Hex </label>
<textarea <textarea
value={hexInput} value={hexInput}
onChange={(e) => setHexInput(e.target.value)} onChange={(e) => setHexInput(e.target.value)}
placeholder="输入 hex 字符串" placeholder="输入 hex 字符串"
style={{ height: '200px' }} style={{
height: '200px',
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#2d2d2d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical'
}}
/> />
<button onClick={handleHexToUtf8} style={{ marginTop: '0.5rem' }}>UTF8 Hex</button> <button
onClick={handleHexToUtf8}
style={{
marginTop: '0.5rem',
padding: '8px 16px',
border: 'none',
borderRadius: '4px',
background: '#f59e0b',
color: '#121212',
fontSize: '14px',
fontWeight: 'bold',
cursor: 'pointer',
transition: 'background 0.3s ease'
}}
>
UTF8 Hex
</button>
</div> </div>
</div> </div>
</div> </div>
)} )}
{/* 页脚 */} {/* 页脚 */}
<footer style={{ marginTop: '2rem', padding: '1rem', borderTop: '1px solid #ccc', textAlign: 'center' }}> <footer style={{ marginTop: '2rem', padding: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#e0e0e0' }}>
<p>© 2026 </p> <p>© 2026 </p>
<p>: {VERSION}</p> <p>: {VERSION}</p>
</footer> </footer>

View File

@ -1 +1 @@
export const VERSION = "V1.0-20260417022550"; export const VERSION = "V1.0-20260417024927";