部分界面预览




1. 整体架构设计
1.1 HTML结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- 元数据和样式 -->
</head>
<body>
<div class="container">
<div class="header-actions">
<h1>全能前端处理工具</h1>
<div class="theme-switch">
<!-- 暗黑模式切换 -->
</div>
</div>
<div class="tabs">
<!-- 6个功能标签页 -->
</div>
<!-- 各个功能区域 -->
<div id="text-tools" class="tab-content active">
<!-- 文本处理功能 -->
</div>
<!-- 其他5个功能区域 -->
</div>
<script>
// 所有JavaScript代码
</script>
</body>
</html>
- 使用语义化的
HTML5文档结构 - 采用标签页(tab)设计,将功能分为六大类:
- 文本处理
- JSON工具
- Base64工具
- 编码/加密
- ASCII工具
- 时间工具
1.2 核心功能分析
文本处理功能
- 文本统计:实时计算字符数、单词数、行数等
- 大小写转换:支持多种格式转换
- 正则表达式测试:支持多种标志和替换功能
- HTML转义/反转义
- URL编码/解码
JSON工具
- 格式化与压缩:美化或压缩JSON数据
- 格式转换:JSON转XML、CSV、YAML
- 路径查询:简单的JSON路径查询功能
Base64工具
- 文本编码/解码
- 文件编码:将文件转换为Base64
- 图片预览:直接预览Base64编码的图片
编码/加密工具
- 哈希计算:支持MD5、SHA-1、SHA-256、SHA-512
- AES加密/解密:使用Web Crypto API实现
ASCII工具
- 文本与ASCII码互转:支持多种输出格式
- ASCII码表:动态生成完整的ASCII码表
时间工具
- 时间戳转换:Unix时间戳与日期互转
- 日期计算:计算两个日期之间的差值
2. CSS设计
2. CSS设计系统
- 使用CSS变量定义主题色彩,便于维护和切换
- 响应式设计,适配不同屏幕尺寸
- 暗黑模式支持
- 清晰的视觉层次和交互反馈
CSS变量定义
css
:root {
--primary-color: #4a6fa5;
--secondary-color: #6b8cae;
--light-color: #f8f9fa;
--dark-color: #343a40;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
}
使用CSS变量统一管理主题色,便于维护和主题切换。
响应式设计
css
@media (max-width: 768px) {
.container {
padding: 15px;
}
.split-container {
flex-direction: column;
}
.header-actions {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
}
媒体查询实现移动端适配,在小屏幕上调整布局。
暗黑模式实现
css
body.dark-mode {
background-color: #1a1a1a;
color: #e0e0e0;
}
body.dark-mode .container {
background-color: #2d2d2d;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
通过body类名切换实现完整的暗黑主题。
3. JavaScript核心功能实现
3.1 初始化与状态管理
javascript
document.addEventListener('DOMContentLoaded', function() {
// 暗黑模式切换
const darkModeToggle = document.getElementById('dark-mode-toggle');
darkModeToggle.addEventListener('change', () => {
document.body.classList.toggle('dark-mode', darkModeToggle.checked);
localStorage.setItem('darkMode', darkModeToggle.checked);
});
// 初始化暗黑模式状态
if (localStorage.getItem('darkMode')) {
darkModeToggle.checked = localStorage.getItem('darkMode') === 'true';
document.body.classList.toggle('dark-mode', darkModeToggle.checked);
}
});
使用localStorage持久化用户偏好设置。
3.2 标签页系统
javascript
// 标签页切换
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// 移除所有active类
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
// 添加active类到当前标签和内容
tab.classList.add('active');
const tabId = tab.getAttribute('data-tab');
document.getElementById(tabId).classList.add('active');
// 保存当前标签页
localStorage.setItem('lastTab', tabId);
});
});
通过data属性关联标签和内容,实现简单的SPA效果。
3.3 文本处理功能详解
实时文本统计
javascript
statsInput.addEventListener('input', () => {
const text = statsInput.value;
document.getElementById('char-count').textContent = text.length;
const words = text.trim() ? text.trim().split(/\s+/) : [];
document.getElementById('word-count').textContent = words.length;
const lines = text.split('\n');
document.getElementById('line-count').textContent = lines.length;
const nonEmptyLines = lines.filter(line => line.trim());
document.getElementById('non-empty-line-count').textContent = nonEmptyLines.length;
});
使用input事件实时更新统计信息,正则表达式/\s+/分割单词。
正则表达式测试
javascript
document.getElementById('test-regex').addEventListener('click', () => {
try {
const text = regexInput.value;
const pattern = document.getElementById('regex-pattern').value;
const flags = document.getElementById('regex-flags').value;
const regex = new RegExp(pattern, flags);
const matches = text.match(regex);
if (matches) {
regexOutput.value = `找到 ${matches.length} 处匹配:\n${matches.join('\n')}`;
// 高亮显示匹配内容
let highlighted = text;
matches.forEach(match => {
highlighted = highlighted.replaceAll(match, `<span class="match-highlight">${match}</span>`);
});
regexMatches.innerHTML = `<h4>匹配位置:</h4><div style="border:1px solid #ddd;padding:10px;">${highlighted}</div>`;
}
} catch (e) {
// 错误处理
}
});
动态创建正则表达式,提供可视化匹配结果。
3.4 JSON工具实现
JSON格式化与验证
javascript
document.getElementById('format-json').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(jsonInput.value);
jsonOutput.value = JSON.stringify(jsonObj, null, 2); // 缩进2空格
showStatus(jsonStatus, 'JSON格式化成功!', 'success');
} catch (e) {
showStatus(jsonStatus, `JSON格式化失败: ${e.message}`, 'error');
}
});
利用JSON.parse和JSON.stringify实现格式化和验证。
JSON转XML
javascript
function jsonToXml(jsonObj, nodeName = 'root') {
let xml = '';
if (typeof jsonObj === 'object' && jsonObj !== null) {
if (Array.isArray(jsonObj)) {
jsonObj.forEach((item, index) => {
xml += jsonToXml(item, nodeName + '_' + index);
});
} else {
xml += '<' + nodeName + '>';
for (const key in jsonObj) {
if (jsonObj.hasOwnProperty(key)) {
xml += jsonToXml(jsonObj[key], key);
}
}
xml += '</' + nodeName + '>';
}
} else {
xml += '<' + nodeName + '>' + jsonObj + '</' + nodeName + '>';
}
return xml;
}
递归遍历JSON对象,构建XML字符串。
3.5 Base64文件处理
文件编码
javascript
document.getElementById('encode-file').addEventListener('click', () => {
const file = fileUpload.files[0];
if (!file) {
showStatus(document.getElementById('file-status'), '请先选择文件', 'error');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result.split(',')[1]; // 移除data URL前缀
document.getElementById('file-output').value = base64;
showStatus(document.getElementById('file-status'), '文件编码成功!', 'success');
};
reader.readAsDataURL(file);
});
使用FileReader API读取文件并转换为Base64。
3.6 加密功能实现
哈希计算
javascript
async function calculateHash(text, algorithm) {
// 使用Web Crypto API计算哈希
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest(algorithm, data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
使用现代Web Crypto API,支持多种哈希算法。
AES加密
javascript
async function aesEncrypt(text, key, iv) {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(key),
{ name: 'AES-CBC' },
false,
['encrypt']
);
const ivArray = iv ? encoder.encode(iv) : crypto.getRandomValues(new Uint8Array(16));
const data = encoder.encode(text);
const encrypted = await crypto.subtle.encrypt(
{
name: 'AES-CBC',
iv: ivArray
},
keyMaterial,
data
);
// 返回IV和加密数据的Base64组合
const result = new Uint8Array(ivArray.length + encrypted.byteLength);
result.set(ivArray, 0);
result.set(new Uint8Array(encrypted), ivArray.length);
return btoa(String.fromCharCode.apply(null, result));
}
完整的AES-CBC加密实现,包含IV处理。
3.7 辅助函数系统
状态显示
javascript
function showStatus(element, message, type) {
element.textContent = message;
element.className = 'status-message ' + type;
setTimeout(() => {
element.textContent = '';
element.className = 'status-message';
}, 3000);
}
统一的用户反馈机制,3秒后自动消失。
剪贴板操作
javascript
function copyToClipboard(element) {
element.select();
document.execCommand('copy');
// 取消选中
if (window.getSelection) {
window.getSelection().removeAllRanges();
} else if (document.selection) {
document.selection.empty();
}
}
兼容多种浏览器的复制功能实现。
4. 技术亮点总结
-
用户体验优化
- 实时统计更新
- 操作状态反馈
- 一键复制功能
- 暗黑模式切换
-
代码质量
- 功能模块化
- 错误处理完善
- 代码复用度高
-
现代Web技术应用
- 使用
SS Grid和Flexbox布局 - 利用
Web Crypto API进行加密 - 使用
File API处理文件
- 使用
内部细节
- 完整的错误处理:所有操作都有try-catch包装
- 用户体验优化:实时反馈、状态提示、一键复制
- 现代API使用 :
FileReader、Crypto、localStorage等 - 代码复用性:辅助函数封装良好
- 响应式设计:完美适配各种屏幕尺寸
- 主题系统:完整的亮色/暗色主题支持
这个工具网页展示了前端开发的多个重要方面:DOM操作、事件处理、数据转换、文件处理、加密算法等,功能全面,是一个很好的学习范例。
5. 源代码
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全能前端处理工具</title>
<style>
:root {
--primary-color: #4a6fa5;
--secondary-color: #6b8cae;
--light-color: #f8f9fa;
--dark-color: #343a40;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
background-color: #f5f5f5;
color: var(--dark-color);
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
margin-bottom: 30px;
color: var(--primary-color);
}
.header-actions {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
align-items: center;
}
.theme-switch {
display: flex;
align-items: center;
}
.theme-switch label {
margin-right: 10px;
}
.tabs {
display: flex;
margin-bottom: 20px;
border-bottom: 1px solid #ddd;
flex-wrap: wrap;
}
.tab {
padding: 10px 20px;
cursor: pointer;
background-color: #4a6fa5;
border: none;
border-radius: 5px 5px 0 0;
margin-right: 5px;
transition: all 0.3s;
margin-bottom: 5px;
}
.tab:hover {
background-color: #1782B8;
}
.tab.active {
background-color: var(--primary-color);
background-color: #28A745;
color: white;
}
.tab-content {
display: none;
padding: 20px;
border: 1px solid #ddd;
border-radius: 0 0 5px 5px;
background-color: white;
}
.tab-content.active {
display: block;
}
.tool-section {
margin-bottom: 30px;
border-bottom: 1px dashed #eee;
padding-bottom: 20px;
}
.tool-section h2 {
margin-bottom: 15px;
color: var(--secondary-color);
font-size: 1.3rem;
}
textarea {
width: 100%;
min-height: 150px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 14px;
}
.input-group {
display: flex;
gap: 10px;
margin: 10px 0;
align-items: center;
}
.input-group input, .input-group select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.button-group {
display: flex;
gap: 10px;
margin: 10px 0;
flex-wrap: wrap;
}
button {
padding: 8px 15px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 5px;
}
button:hover {
background-color: var(--secondary-color);
}
button.secondary {
background-color: #6c757d;
}
button.secondary:hover {
background-color: #5a6268;
}
button.success {
background-color: var(--success-color);
}
button.success:hover {
background-color: #218838;
}
button.danger {
background-color: var(--danger-color);
}
button.danger:hover {
background-color: #c82333;
}
button.warning {
background-color: var(--warning-color);
color: var(--dark-color);
}
button.warning:hover {
background-color: #e0a800;
}
button.info {
background-color: var(--info-color);
}
button.info:hover {
background-color: #138496;
}
.result-area {
margin-top: 20px;
}
.copy-btn {
margin-top: 10px;
}
.status-message {
margin-top: 10px;
padding: 8px;
border-radius: 4px;
display: none;
}
.status-message.success {
display: block;
background-color: #d4edda;
color: #155724;
}
.status-message.error {
display: block;
background-color: #f8d7da;
color: #721c24;
}
.status-message.info {
display: block;
background-color: #d1ecf1;
color: #0c5460;
}
.match-highlight {
background-color: yellow;
padding: 0 2px;
}
.file-upload {
display: none;
}
.file-upload-label {
display: inline-block;
padding: 8px 15px;
background-color: var(--info-color);
color: white;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.file-upload-label:hover {
background-color: #138496;
}
.file-info {
margin-left: 10px;
font-size: 0.9em;
color: #666;
}
.split-container {
display: flex;
gap: 20px;
}
.split-panel {
flex: 1;
}
.stats-container {
display: flex;
gap: 15px;
margin-top: 10px;
flex-wrap: wrap;
}
.stat-box {
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
padding: 8px 12px;
min-width: 120px;
}
.stat-label {
font-size: 0.8em;
color: #666;
}
.stat-value {
font-weight: bold;
font-size: 1.2em;
}
/* ASCII码表样式 */
.ascii-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.ascii-table th, .ascii-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
.ascii-table th {
background-color: var(--primary-color);
color: white;
}
.ascii-table tr:nth-child(even) {
background-color: #f2f2f2;
}
.ascii-table tr:hover {
background-color: #e9e9e9;
}
.ascii-char {
font-weight: bold;
}
.ascii-control {
color: #666;
font-style: italic;
}
/* 暗黑模式 */
body.dark-mode {
background-color: #1a1a1a;
color: #e0e0e0;
}
body.dark-mode .container {
background-color: #2d2d2d;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
body.dark-mode .tab {
background-color: #3d3d3d;
color: #e0e0e0;
}
body.dark-mode .tab:hover {
background-color: #4d4d4d;
}
body.dark-mode .tab.active {
background-color: var(--primary-color);
}
body.dark-mode .tab-content {
background-color: #2d2d2d;
border-color: #444;
}
body.dark-mode textarea {
background-color: #333;
color: #e0e0e0;
border-color: #444;
}
body.dark-mode .input-group input,
body.dark-mode .input-group select {
background-color: #333;
color: #e0e0e0;
border-color: #444;
}
body.dark-mode .stat-box {
background-color: #333;
border-color: #444;
}
body.dark-mode .match-highlight {
background-color: #705700;
color: #fff;
}
body.dark-mode .ascii-table th,
body.dark-mode .ascii-table td {
border-color: #444;
}
body.dark-mode .ascii-table th {
background-color: #3d3d3d;
}
body.dark-mode .ascii-table tr:nth-child(even) {
background-color: #333;
}
body.dark-mode .ascii-table tr:hover {
background-color: #3a3a3a;
}
body.dark-mode .ascii-control {
color: #aaa;
}
@media (max-width: 768px) {
.container {
padding: 15px;
}
.split-container {
flex-direction: column;
}
.header-actions {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.input-group {
flex-direction: column;
align-items: flex-start;
}
.input-group input, .input-group select {
width: 100%;
}
.ascii-table {
font-size: 0.8em;
}
.ascii-table th, .ascii-table td {
padding: 4px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header-actions">
<h1>全能前端处理工具</h1>
<div class="theme-switch">
<label for="dark-mode-toggle">暗黑模式:</label>
<label class="switch">
<input type="checkbox" id="dark-mode-toggle">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="tabs">
<button class="tab active" data-tab="text-tools">文本处理</button>
<button class="tab" data-tab="json-tools">JSON工具</button>
<button class="tab" data-tab="base64-tools">Base64工具</button>
<button class="tab" data-tab="encode-tools">编码/加密</button>
<button class="tab" data-tab="ascii-tools">ASCII工具</button>
<button class="tab" data-tab="date-tools">时间工具</button>
</div>
<!-- 文本处理工具 -->
<div id="text-tools" class="tab-content active">
<div class="tool-section">
<h2>文本统计</h2>
<textarea id="stats-input" placeholder="输入要统计的文本..."></textarea>
<div class="stats-container">
<div class="stat-box">
<div class="stat-label">字符数</div>
<div class="stat-value" id="char-count">0</div>
</div>
<div class="stat-box">
<div class="stat-label">单词数</div>
<div class="stat-value" id="word-count">0</div>
</div>
<div class="stat-box">
<div class="stat-label">行数</div>
<div class="stat-value" id="line-count">0</div>
</div>
<div class="stat-box">
<div class="stat-label">非空行</div>
<div class="stat-value" id="non-empty-line-count">0</div>
</div>
</div>
</div>
<div class="tool-section">
<h2>文本大小写转换</h2>
<textarea id="text-input" placeholder="请输入要处理的文本..."></textarea>
<div class="button-group">
<button id="to-upper">转换为大写</button>
<button id="to-lower">转换为小写</button>
<button id="capitalize">首字母大写</button>
<button id="title-case">标题格式</button>
<button id="trim-text">去除空格</button>
<button id="reverse-text">反转文本</button>
</div>
<div class="result-area">
<label for="text-output">处理结果:</label>
<textarea id="text-output" readonly></textarea>
<button id="copy-text" class="copy-btn">复制结果</button>
</div>
<div id="text-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>正则表达式测试</h2>
<textarea id="regex-input" placeholder="输入要测试的文本..."></textarea>
<div class="input-group">
<input type="text" id="regex-pattern" placeholder="正则表达式" style="flex: 2;">
<input type="text" id="regex-replace" placeholder="替换文本(可选)" style="flex: 1;">
<select id="regex-flags">
<option value="g">全局(g)</option>
<option value="i">不区分大小写(i)</option>
<option value="m">多行(m)</option>
<option value="gi">全局+不区分大小写(gi)</option>
<option value="gm">全局+多行(gm)</option>
<option value="im">不区分大小写+多行(im)</option>
<option value="gim">全部(gim)</option>
</select>
</div>
<div class="button-group">
<button id="test-regex" class="success">测试匹配</button>
<button id="replace-regex">替换文本</button>
<button id="regex-help" class="info">正则帮助</button>
</div>
<div class="result-area">
<label for="regex-output">结果:</label>
<textarea id="regex-output" readonly></textarea>
<div id="regex-matches" style="margin-top: 10px;"></div>
<button id="copy-regex" class="copy-btn">复制结果</button>
</div>
<div id="regex-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>HTML转义/反转义</h2>
<div class="split-container">
<div class="split-panel">
<label for="html-raw">原始HTML:</label>
<textarea id="html-raw" placeholder="输入原始HTML..."></textarea>
</div>
<div class="split-panel">
<label for="html-escaped">转义结果:</label>
<textarea id="html-escaped" placeholder="转义后的HTML..."></textarea>
</div>
</div>
<div class="button-group">
<button id="escape-html" class="success">转义HTML</button>
<button id="unescape-html" class="secondary">反转义HTML</button>
<button id="copy-html-escaped" class="copy-btn">复制转义结果</button>
<button id="copy-html-raw" class="copy-btn">复制原始HTML</button>
</div>
</div>
<div class="tool-section">
<h2>URL编码/解码</h2>
<div class="split-container">
<div class="split-panel">
<label for="url-decoded">原始URL:</label>
<textarea id="url-decoded" placeholder="输入原始URL..."></textarea>
</div>
<div class="split-panel">
<label for="url-encoded">编码结果:</label>
<textarea id="url-encoded" placeholder="编码后的URL..."></textarea>
</div>
</div>
<div class="button-group">
<button id="encode-url" class="success">编码URL</button>
<button id="decode-url" class="secondary">解码URL</button>
<button id="copy-url-encoded" class="copy-btn">复制编码结果</button>
<button id="copy-url-decoded" class="copy-btn">复制原始URL</button>
</div>
</div>
</div>
<!-- JSON工具 -->
<div id="json-tools" class="tab-content">
<div class="tool-section">
<h2>JSON格式化与压缩</h2>
<textarea id="json-input" placeholder='请输入JSON字符串,例如: {"name":"John","age":30}'></textarea>
<div class="button-group">
<button id="format-json" class="success">格式化JSON</button>
<button id="minify-json" class="secondary">压缩JSON</button>
<button id="validate-json" class="secondary">验证JSON</button>
<button id="clear-json" class="danger">清空</button>
</div>
<div class="result-area">
<label for="json-output">处理结果:</label>
<textarea id="json-output" readonly></textarea>
<button id="copy-json" class="copy-btn">复制结果</button>
</div>
<div id="json-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>JSON转换</h2>
<textarea id="json-convert-input" placeholder='输入要转换的JSON...'></textarea>
<div class="button-group">
<button id="json-to-xml" class="success">转XML</button>
<button id="json-to-csv" class="success">转CSV</button>
<button id="json-to-yaml" class="success">转YAML</button>
<button id="clear-json-convert" class="danger">清空</button>
</div>
<div class="result-area">
<label for="json-convert-output">转换结果:</label>
<textarea id="json-convert-output" readonly></textarea>
<button id="copy-json-convert" class="copy-btn">复制结果</button>
</div>
<div id="json-convert-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>JSON路径查询</h2>
<textarea id="json-path-input" placeholder='输入JSON数据...'></textarea>
<div class="input-group">
<input type="text" id="json-path" placeholder="JSON路径 (如 $.store.book[0].title)" style="flex: 1;">
<button id="execute-json-path" class="success">查询</button>
</div>
<div class="result-area">
<label for="json-path-output">查询结果:</label>
<textarea id="json-path-output" readonly></textarea>
<button id="copy-json-path" class="copy-btn">复制结果</button>
</div>
<div id="json-path-status" class="status-message"></div>
</div>
</div>
<!-- Base64工具 -->
<div id="base64-tools" class="tab-content">
<div class="tool-section">
<h2>Base64文本编码/解码</h2>
<textarea id="base64-input" placeholder="请输入要编码或解码的文本..."></textarea>
<div class="button-group">
<button id="encode-base64" class="success">Base64编码</button>
<button id="decode-base64" class="secondary">Base64解码</button>
<button id="clear-base64" class="danger">清空</button>
</div>
<div class="result-area">
<label for="base64-output">处理结果:</label>
<textarea id="base64-output" readonly></textarea>
<button id="copy-base64" class="copy-btn">复制结果</button>
</div>
<div id="base64-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>Base64文件编码</h2>
<div class="input-group">
<label for="file-upload" class="file-upload-label">
<span>选择文件</span>
<input type="file" id="file-upload" class="file-upload">
</label>
<span id="file-info" class="file-info">未选择文件</span>
</div>
<div class="button-group">
<button id="encode-file" class="success">编码文件</button>
<button id="clear-file" class="danger">清空</button>
</div>
<div class="result-area">
<label for="file-output">Base64编码结果:</label>
<textarea id="file-output" readonly></textarea>
<button id="copy-file" class="copy-btn">复制结果</button>
</div>
<div id="file-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>Base64图片预览</h2>
<textarea id="image-base64-input" placeholder="输入Base64编码的图片数据 (以data:image/开头)..."></textarea>
<div class="button-group">
<button id="preview-image" class="success">预览图片</button>
<button id="clear-image" class="danger">清空</button>
</div>
<div class="result-area">
<label>图片预览:</label>
<div id="image-preview-container" style="margin-top: 10px; text-align: center;">
<img id="image-preview" style="max-width: 100%; max-height: 300px; display: none;">
<div id="image-error" style="color: var(--danger-color); display: none;"></div>
</div>
</div>
</div>
</div>
<!-- 编码/加密工具 -->
<div id="encode-tools" class="tab-content">
<div class="tool-section">
<h2>哈希计算</h2>
<textarea id="hash-input" placeholder="输入要计算哈希的文本..."></textarea>
<div class="button-group">
<button id="md5-hash" class="success">MD5</button>
<button id="sha1-hash" class="success">SHA-1</button>
<button id="sha256-hash" class="success">SHA-256</button>
<button id="sha512-hash" class="success">SHA-512</button>
<button id="clear-hash" class="danger">清空</button>
</div>
<div class="result-area">
<label for="hash-output">哈希结果:</label>
<textarea id="hash-output" readonly></textarea>
<button id="copy-hash" class="copy-btn">复制结果</button>
</div>
</div>
<div class="tool-section">
<h2>AES加密/解密</h2>
<textarea id="aes-input" placeholder="输入要加密/解密的文本..."></textarea>
<div class="input-group">
<input type="password" id="aes-key" placeholder="密钥" style="flex: 1;">
<input type="text" id="aes-iv" placeholder="IV (可选)" style="flex: 1;">
</div>
<div class="button-group">
<button id="aes-encrypt" class="success">AES加密</button>
<button id="aes-decrypt" class="secondary">AES解密</button>
<button id="clear-aes" class="danger">清空</button>
</div>
<div class="result-area">
<label for="aes-output">结果:</label>
<textarea id="aes-output" readonly></textarea>
<button id="copy-aes" class="copy-btn">复制结果</button>
</div>
<div id="aes-status" class="status-message"></div>
</div>
</div>
<!-- ASCII工具 -->
<div id="ascii-tools" class="tab-content">
<div class="tool-section">
<h2>字符串与ASCII码转换</h2>
<div class="split-container">
<div class="split-panel">
<label for="ascii-text-input">文本:</label>
<textarea id="ascii-text-input" placeholder="输入要转换的文本..."></textarea>
</div>
<div class="split-panel">
<label for="ascii-code-input">ASCII码 (空格或逗号分隔):</label>
<textarea id="ascii-code-input" placeholder="输入ASCII码,如: 72 101 108 108 111 或 72,101,108,108,111"></textarea>
</div>
</div>
<div class="button-group">
<button id="text-to-ascii" class="success">文本 → ASCII码</button>
<button id="ascii-to-text" class="secondary">ASCII码 → 文本</button>
<button id="clear-ascii" class="danger">清空</button>
<button id="copy-ascii-text" class="copy-btn">复制文本</button>
<button id="copy-ascii-code" class="copy-btn">复制ASCII码</button>
</div>
<div class="input-group">
<label for="ascii-format">输出格式:</label>
<select id="ascii-format">
<option value="space">空格分隔 (72 101 108 108 111)</option>
<option value="comma">逗号分隔 (72,101,108,108,111)</option>
<option value="hex">十六进制 (0x48 0x65 0x6C 0x6C 0x6F)</option>
<option value="hex-no-prefix">十六进制无前缀 (48 65 6C 6C 6F)</option>
</select>
</div>
<div id="ascii-status" class="status-message"></div>
</div>
<div class="tool-section">
<h2>ASCII码表</h2>
<div id="ascii-table" style="overflow-x: auto;">
<!-- ASCII码表将通过JavaScript动态生成 -->
</div>
</div>
</div>
<!-- 时间工具 -->
<div id="date-tools" class="tab-content">
<div class="tool-section">
<h2>时间戳转换</h2>
<div class="input-group">
<input type="number" id="timestamp-input" placeholder="输入Unix时间戳...">
<button id="timestamp-to-date" class="success">转日期</button>
<button id="current-timestamp" class="secondary">当前时间戳</button>
</div>
<div class="input-group" style="margin-top: 10px;">
<input type="datetime-local" id="date-input">
<button id="date-to-timestamp" class="success">转时间戳</button>
</div>
<div class="result-area">
<label for="timestamp-output">结果:</label>
<textarea id="timestamp-output" readonly></textarea>
<button id="copy-timestamp" class="copy-btn">复制结果</button>
</div>
</div>
<div class="tool-section">
<h2>日期计算</h2>
<div class="input-group">
<input type="datetime-local" id="date-start">
<input type="datetime-local" id="date-end">
<button id="calculate-diff" class="success">计算差值</button>
</div>
<div class="result-area">
<label for="date-diff-output">时间差:</label>
<textarea id="date-diff-output" readonly></textarea>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 暗黑模式切换
const darkModeToggle = document.getElementById('dark-mode-toggle');
darkModeToggle.addEventListener('change', () => {
document.body.classList.toggle('dark-mode', darkModeToggle.checked);
localStorage.setItem('darkMode', darkModeToggle.checked);
});
// 初始化暗黑模式状态
if (localStorage.getItem('darkMode')) {
darkModeToggle.checked = localStorage.getItem('darkMode') === 'true';
document.body.classList.toggle('dark-mode', darkModeToggle.checked);
}
// 标签页切换
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// 移除所有active类
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
// 添加active类到当前标签和内容
tab.classList.add('active');
const tabId = tab.getAttribute('data-tab');
document.getElementById(tabId).classList.add('active');
// 保存当前标签页
localStorage.setItem('lastTab', tabId);
});
});
// 恢复上次打开的标签页
const lastTab = localStorage.getItem('lastTab') || 'text-tools';
document.querySelector(`.tab[data-tab="${lastTab}"]`).click();
// 文本处理功能
const textInput = document.getElementById('text-input');
const textOutput = document.getElementById('text-output');
const textStatus = document.getElementById('text-status');
document.getElementById('to-upper').addEventListener('click', () => {
textOutput.value = textInput.value.toUpperCase();
showStatus(textStatus, '转换为大写成功!', 'success');
});
document.getElementById('to-lower').addEventListener('click', () => {
textOutput.value = textInput.value.toLowerCase();
showStatus(textStatus, '转换为小写成功!', 'success');
});
document.getElementById('capitalize').addEventListener('click', () => {
if (textInput.value.length > 0) {
textOutput.value = textInput.value.charAt(0).toUpperCase() + textInput.value.slice(1).toLowerCase();
showStatus(textStatus, '首字母大写成功!', 'success');
}
});
document.getElementById('title-case').addEventListener('click', () => {
textOutput.value = textInput.value.toLowerCase().split(' ').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ');
showStatus(textStatus, '标题格式转换成功!', 'success');
});
document.getElementById('trim-text').addEventListener('click', () => {
textOutput.value = textInput.value.replace(/\s/g, '');
showStatus(textStatus, '去除空格成功!', 'success');
});
document.getElementById('reverse-text').addEventListener('click', () => {
textOutput.value = textInput.value.split('').reverse().join('');
showStatus(textStatus, '文本反转成功!', 'success');
});
document.getElementById('copy-text').addEventListener('click', () => {
if (textOutput.value) {
copyToClipboard(textOutput);
showStatus(textStatus, '结果已复制到剪贴板!', 'success');
}
});
// 文本统计功能
const statsInput = document.getElementById('stats-input');
statsInput.addEventListener('input', () => {
const text = statsInput.value;
document.getElementById('char-count').textContent = text.length;
const words = text.trim() ? text.trim().split(/\s+/) : [];
document.getElementById('word-count').textContent = words.length;
const lines = text.split('\n');
document.getElementById('line-count').textContent = lines.length;
const nonEmptyLines = lines.filter(line => line.trim());
document.getElementById('non-empty-line-count').textContent = nonEmptyLines.length;
});
// 正则表达式功能
const regexInput = document.getElementById('regex-input');
const regexOutput = document.getElementById('regex-output');
const regexStatus = document.getElementById('regex-status');
const regexMatches = document.getElementById('regex-matches');
document.getElementById('test-regex').addEventListener('click', () => {
try {
const text = regexInput.value;
const pattern = document.getElementById('regex-pattern').value;
const flags = document.getElementById('regex-flags').value;
const regex = new RegExp(pattern, flags);
const matches = text.match(regex);
if (matches) {
regexOutput.value = `找到 ${matches.length} 处匹配:\n${matches.join('\n')}`;
// 高亮显示匹配内容
let highlighted = text;
matches.forEach(match => {
highlighted = highlighted.replaceAll(match, `<span class="match-highlight">${match}</span>`);
});
regexMatches.innerHTML = `<h4>匹配位置:</h4><div style="border:1px solid #ddd;padding:10px;">${highlighted}</div>`;
} else {
regexOutput.value = "没有找到匹配内容";
regexMatches.innerHTML = '';
}
showStatus(regexStatus, '正则匹配成功!', 'success');
} catch (e) {
regexOutput.value = `正则表达式错误: ${e.message}`;
showStatus(regexStatus, `正则匹配失败: ${e.message}`, 'error');
}
});
document.getElementById('replace-regex').addEventListener('click', () => {
try {
const text = regexInput.value;
const pattern = document.getElementById('regex-pattern').value;
const replacement = document.getElementById('regex-replace').value || '';
const flags = document.getElementById('regex-flags').value;
const regex = new RegExp(pattern, flags);
regexOutput.value = text.replace(regex, replacement);
showStatus(regexStatus, '替换成功!', 'success');
} catch (e) {
regexOutput.value = `替换错误: ${e.message}`;
showStatus(regexStatus, `替换失败: ${e.message}`, 'error');
}
});
document.getElementById('regex-help').addEventListener('click', () => {
const helpText = `
常用正则表达式示例:
• 数字: \\d 或 [0-9]
• 非数字: \\D 或 [^0-9]
• 单词字符: \\w 或 [a-zA-Z0-9_]
• 非单词字符: \\W
• 空白字符: \\s
• 非空白字符: \\S
• 开始: ^
• 结束: $
• 任意字符: .
• 量词: * (0或多个), + (1或多个), ? (0或1个), {n} (n个), {n,} (至少n个), {n,m} (n到m个)
示例:
• 邮箱: ^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$
• 手机号: ^1[3-9]\\d{9}$
• URL: ^https?:\\/\\/[\\w.-]+\\.[a-zA-Z]{2,}(\\/\\S*)?$
`;
regexOutput.value = helpText;
showStatus(regexStatus, '正则表达式帮助已显示', 'info');
});
document.getElementById('copy-regex').addEventListener('click', () => {
if (regexOutput.value) {
copyToClipboard(regexOutput);
showStatus(regexStatus, '结果已复制到剪贴板!', 'success');
}
});
// HTML转义/反转义
document.getElementById('escape-html').addEventListener('click', () => {
const raw = document.getElementById('html-raw').value;
document.getElementById('html-escaped').value = escapeHtml(raw);
});
document.getElementById('unescape-html').addEventListener('click', () => {
const escaped = document.getElementById('html-escaped').value;
document.getElementById('html-raw').value = unescapeHtml(escaped);
});
document.getElementById('copy-html-escaped').addEventListener('click', () => {
copyToClipboard(document.getElementById('html-escaped'));
});
document.getElementById('copy-html-raw').addEventListener('click', () => {
copyToClipboard(document.getElementById('html-raw'));
});
// URL编码/解码
document.getElementById('encode-url').addEventListener('click', () => {
const decoded = document.getElementById('url-decoded').value;
document.getElementById('url-encoded').value = encodeURIComponent(decoded);
});
document.getElementById('decode-url').addEventListener('click', () => {
const encoded = document.getElementById('url-encoded').value;
document.getElementById('url-decoded').value = decodeURIComponent(encoded);
});
document.getElementById('copy-url-encoded').addEventListener('click', () => {
copyToClipboard(document.getElementById('url-encoded'));
});
document.getElementById('copy-url-decoded').addEventListener('click', () => {
copyToClipboard(document.getElementById('url-decoded'));
});
// JSON处理功能
const jsonInput = document.getElementById('json-input');
const jsonOutput = document.getElementById('json-output');
const jsonStatus = document.getElementById('json-status');
document.getElementById('format-json').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(jsonInput.value);
jsonOutput.value = JSON.stringify(jsonObj, null, 2);
showStatus(jsonStatus, 'JSON格式化成功!', 'success');
} catch (e) {
showStatus(jsonStatus, `JSON格式化失败: ${e.message}`, 'error');
}
});
document.getElementById('minify-json').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(jsonInput.value);
jsonOutput.value = JSON.stringify(jsonObj);
showStatus(jsonStatus, 'JSON压缩成功!', 'success');
} catch (e) {
showStatus(jsonStatus, `JSON压缩失败: ${e.message}`, 'error');
}
});
document.getElementById('validate-json').addEventListener('click', () => {
try {
JSON.parse(jsonInput.value);
showStatus(jsonStatus, 'JSON验证成功!', 'success');
} catch (e) {
showStatus(jsonStatus, `JSON验证失败: ${e.message}`, 'error');
}
});
document.getElementById('clear-json').addEventListener('click', () => {
jsonInput.value = '';
jsonOutput.value = '';
jsonStatus.textContent = '';
jsonStatus.className = 'status-message';
});
document.getElementById('copy-json').addEventListener('click', () => {
if (jsonOutput.value) {
copyToClipboard(jsonOutput);
showStatus(jsonStatus, '结果已复制到剪贴板!', 'success');
}
});
// JSON转换功能
document.getElementById('json-to-xml').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(document.getElementById('json-convert-input').value);
document.getElementById('json-convert-output').value = jsonToXml(jsonObj);
showStatus(document.getElementById('json-convert-status'), 'JSON转XML成功!', 'success');
} catch (e) {
showStatus(document.getElementById('json-convert-status'), `JSON转XML失败: ${e.message}`, 'error');
}
});
document.getElementById('json-to-csv').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(document.getElementById('json-convert-input').value);
document.getElementById('json-convert-output').value = jsonToCsv(jsonObj);
showStatus(document.getElementById('json-convert-status'), 'JSON转CSV成功!', 'success');
} catch (e) {
showStatus(document.getElementById('json-convert-status'), `JSON转CSV失败: ${e.message}`, 'error');
}
});
document.getElementById('json-to-yaml').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(document.getElementById('json-convert-input').value);
document.getElementById('json-convert-output').value = jsonToYaml(jsonObj);
showStatus(document.getElementById('json-convert-status'), 'JSON转YAML成功!', 'success');
} catch (e) {
showStatus(document.getElementById('json-convert-status'), `JSON转YAML失败: ${e.message}`, 'error');
}
});
document.getElementById('clear-json-convert').addEventListener('click', () => {
document.getElementById('json-convert-input').value = '';
document.getElementById('json-convert-output').value = '';
document.getElementById('json-convert-status').textContent = '';
document.getElementById('json-convert-status').className = 'status-message';
});
document.getElementById('copy-json-convert').addEventListener('click', () => {
if (document.getElementById('json-convert-output').value) {
copyToClipboard(document.getElementById('json-convert-output'));
showStatus(document.getElementById('json-convert-status'), '结果已复制到剪贴板!', 'success');
}
});
// JSON路径查询
document.getElementById('execute-json-path').addEventListener('click', () => {
try {
const jsonObj = JSON.parse(document.getElementById('json-path-input').value);
const path = document.getElementById('json-path').value;
if (!path) {
showStatus(document.getElementById('json-path-status'), '请输入JSON路径', 'error');
return;
}
// 简单的JSON路径查询实现
const result = evaluateJsonPath(jsonObj, path);
document.getElementById('json-path-output').value = JSON.stringify(result, null, 2);
showStatus(document.getElementById('json-path-status'), '查询成功!', 'success');
} catch (e) {
showStatus(document.getElementById('json-path-status'), `查询失败: ${e.message}`, 'error');
}
});
document.getElementById('copy-json-path').addEventListener('click', () => {
if (document.getElementById('json-path-output').value) {
copyToClipboard(document.getElementById('json-path-output'));
showStatus(document.getElementById('json-path-status'), '结果已复制到剪贴板!', 'success');
}
});
// Base64处理功能
const base64Input = document.getElementById('base64-input');
const base64Output = document.getElementById('base64-output');
const base64Status = document.getElementById('base64-status');
document.getElementById('encode-base64').addEventListener('click', () => {
try {
base64Output.value = btoa(unescape(encodeURIComponent(base64Input.value)));
showStatus(base64Status, 'Base64编码成功!', 'success');
} catch (e) {
showStatus(base64Status, `Base64编码失败: ${e.message}`, 'error');
}
});
document.getElementById('decode-base64').addEventListener('click', () => {
try {
base64Output.value = decodeURIComponent(escape(atob(base64Input.value)));
showStatus(base64Status, 'Base64解码成功!', 'success');
} catch (e) {
showStatus(base64Status, `Base64解码失败: ${e.message}`, 'error');
}
});
document.getElementById('clear-base64').addEventListener('click', () => {
base64Input.value = '';
base64Output.value = '';
base64Status.textContent = '';
base64Status.className = 'status-message';
});
document.getElementById('copy-base64').addEventListener('click', () => {
if (base64Output.value) {
copyToClipboard(base64Output);
showStatus(base64Status, '结果已复制到剪贴板!', 'success');
}
});
// Base64文件编码
const fileUpload = document.getElementById('file-upload');
const fileInfo = document.getElementById('file-info');
fileUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
fileInfo.textContent = `${file.name} (${formatFileSize(file.size)})`;
} else {
fileInfo.textContent = '未选择文件';
}
});
document.getElementById('encode-file').addEventListener('click', () => {
const file = fileUpload.files[0];
if (!file) {
showStatus(document.getElementById('file-status'), '请先选择文件', 'error');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result.split(',')[1];
document.getElementById('file-output').value = base64;
showStatus(document.getElementById('file-status'), '文件编码成功!', 'success');
};
reader.onerror = () => {
showStatus(document.getElementById('file-status'), '文件读取失败', 'error');
};
reader.readAsDataURL(file);
});
document.getElementById('clear-file').addEventListener('click', () => {
fileUpload.value = '';
fileInfo.textContent = '未选择文件';
document.getElementById('file-output').value = '';
document.getElementById('file-status').textContent = '';
document.getElementById('file-status').className = 'status-message';
});
document.getElementById('copy-file').addEventListener('click', () => {
if (document.getElementById('file-output').value) {
copyToClipboard(document.getElementById('file-output'));
showStatus(document.getElementById('file-status'), '结果已复制到剪贴板!', 'success');
}
});
// Base64图片预览
document.getElementById('preview-image').addEventListener('click', () => {
const base64 = document.getElementById('image-base64-input').value.trim();
const imgPreview = document.getElementById('image-preview');
const imgError = document.getElementById('image-error');
if (!base64) {
imgError.textContent = '请输入Base64编码的图片数据';
imgError.style.display = 'block';
imgPreview.style.display = 'none';
return;
}
try {
imgPreview.src = base64.startsWith('data:image/') ? base64 : `data:image/png;base64,${base64}`;
imgPreview.style.display = 'block';
imgError.style.display = 'none';
} catch (e) {
imgError.textContent = `图片预览失败: ${e.message}`;
imgError.style.display = 'block';
imgPreview.style.display = 'none';
}
});
document.getElementById('clear-image').addEventListener('click', () => {
document.getElementById('image-base64-input').value = '';
document.getElementById('image-preview').style.display = 'none';
document.getElementById('image-error').style.display = 'none';
});
// 哈希计算
document.getElementById('md5-hash').addEventListener('click', async () => {
const text = document.getElementById('hash-input').value;
if (!text) {
document.getElementById('hash-output').value = '请输入要计算哈希的文本';
return;
}
try {
const hash = await calculateHash(text, 'MD5');
document.getElementById('hash-output').value = hash;
} catch (e) {
document.getElementById('hash-output').value = `MD5计算失败: ${e.message}`;
}
});
document.getElementById('sha1-hash').addEventListener('click', async () => {
const text = document.getElementById('hash-input').value;
if (!text) {
document.getElementById('hash-output').value = '请输入要计算哈希的文本';
return;
}
try {
const hash = await calculateHash(text, 'SHA-1');
document.getElementById('hash-output').value = hash;
} catch (e) {
document.getElementById('hash-output').value = `SHA-1计算失败: ${e.message}`;
}
});
document.getElementById('sha256-hash').addEventListener('click', async () => {
const text = document.getElementById('hash-input').value;
if (!text) {
document.getElementById('hash-output').value = '请输入要计算哈希的文本';
return;
}
try {
const hash = await calculateHash(text, 'SHA-256');
document.getElementById('hash-output').value = hash;
} catch (e) {
document.getElementById('hash-output').value = `SHA-256计算失败: ${e.message}`;
}
});
document.getElementById('sha512-hash').addEventListener('click', async () => {
const text = document.getElementById('hash-input').value;
if (!text) {
document.getElementById('hash-output').value = '请输入要计算哈希的文本';
return;
}
try {
const hash = await calculateHash(text, 'SHA-512');
document.getElementById('hash-output').value = hash;
} catch (e) {
document.getElementById('hash-output').value = `SHA-512计算失败: ${e.message}`;
}
});
document.getElementById('clear-hash').addEventListener('click', () => {
document.getElementById('hash-input').value = '';
document.getElementById('hash-output').value = '';
});
document.getElementById('copy-hash').addEventListener('click', () => {
copyToClipboard(document.getElementById('hash-output'));
});
// AES加密/解密
document.getElementById('aes-encrypt').addEventListener('click', async () => {
try {
const text = document.getElementById('aes-input').value;
const key = document.getElementById('aes-key').value;
const iv = document.getElementById('aes-iv').value || undefined;
if (!text || !key) {
showStatus(document.getElementById('aes-status'), '请输入文本和密钥', 'error');
return;
}
const encrypted = await aesEncrypt(text, key, iv);
document.getElementById('aes-output').value = encrypted;
showStatus(document.getElementById('aes-status'), 'AES加密成功!', 'success');
} catch (e) {
document.getElementById('aes-output').value = `AES加密失败: ${e.message}`;
showStatus(document.getElementById('aes-status'), `AES加密失败: ${e.message}`, 'error');
}
});
document.getElementById('aes-decrypt').addEventListener('click', async () => {
try {
const text = document.getElementById('aes-input').value;
const key = document.getElementById('aes-key').value;
const iv = document.getElementById('aes-iv').value || undefined;
if (!text || !key) {
showStatus(document.getElementById('aes-status'), '请输入文本和密钥', 'error');
return;
}
const decrypted = await aesDecrypt(text, key, iv);
document.getElementById('aes-output').value = decrypted;
showStatus(document.getElementById('aes-status'), 'AES解密成功!', 'success');
} catch (e) {
document.getElementById('aes-output').value = `AES解密失败: ${e.message}`;
showStatus(document.getElementById('aes-status'), `AES解密失败: ${e.message}`, 'error');
}
});
document.getElementById('clear-aes').addEventListener('click', () => {
document.getElementById('aes-input').value = '';
document.getElementById('aes-output').value = '';
document.getElementById('aes-key').value = '';
document.getElementById('aes-iv').value = '';
document.getElementById('aes-status').textContent = '';
document.getElementById('aes-status').className = 'status-message';
});
document.getElementById('copy-aes').addEventListener('click', () => {
copyToClipboard(document.getElementById('aes-output'));
});
// 时间戳转换
document.getElementById('timestamp-to-date').addEventListener('click', () => {
const timestamp = parseInt(document.getElementById('timestamp-input').value);
if (isNaN(timestamp)) {
document.getElementById('timestamp-output').value = '请输入有效的时间戳';
return;
}
const date = new Date(timestamp * 1000);
document.getElementById('timestamp-output').value = date.toLocaleString();
document.getElementById('date-input').value = formatDateTimeLocal(date);
});
document.getElementById('current-timestamp').addEventListener('click', () => {
const now = Math.floor(Date.now() / 1000);
document.getElementById('timestamp-input').value = now;
document.getElementById('timestamp-output').value = new Date(now * 1000).toLocaleString();
document.getElementById('date-input').value = formatDateTimeLocal(new Date());
});
document.getElementById('date-to-timestamp').addEventListener('click', () => {
const dateStr = document.getElementById('date-input').value;
if (!dateStr) {
document.getElementById('timestamp-output').value = '请选择日期时间';
return;
}
const date = new Date(dateStr);
const timestamp = Math.floor(date.getTime() / 1000);
document.getElementById('timestamp-input').value = timestamp;
document.getElementById('timestamp-output').value = `时间戳: ${timestamp}\n本地时间: ${date.toLocaleString()}`;
});
document.getElementById('copy-timestamp').addEventListener('click', () => {
copyToClipboard(document.getElementById('timestamp-output'));
});
// 日期计算
document.getElementById('calculate-diff').addEventListener('click', () => {
const startStr = document.getElementById('date-start').value;
const endStr = document.getElementById('date-end').value;
if (!startStr || !endStr) {
document.getElementById('date-diff-output').value = '请选择开始和结束日期时间';
return;
}
const start = new Date(startStr);
const end = new Date(endStr);
if (start > end) {
document.getElementById('date-diff-output').value = '结束时间必须晚于开始时间';
return;
}
const diffMs = end - start;
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHour = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHour / 24);
const result = [
`总毫秒数: ${diffMs}`,
`总秒数: ${diffSec}`,
`总分钟数: ${diffMin}`,
`总小时数: ${diffHour}`,
`总天数: ${diffDay}`,
`详细: ${diffDay}天 ${diffHour % 24}小时 ${diffMin % 60}分钟 ${diffSec % 60}秒`
].join('\n');
document.getElementById('date-diff-output').value = result;
});
// 初始化日期时间输入为当前时间
document.getElementById('date-input').value = formatDateTimeLocal(new Date());
document.getElementById('date-start').value = formatDateTimeLocal(new Date());
document.getElementById('date-end').value = formatDateTimeLocal(new Date(Date.now() + 3600000)); // 1小时后
// ASCII工具功能
const asciiTextInput = document.getElementById('ascii-text-input');
const asciiCodeInput = document.getElementById('ascii-code-input');
const asciiStatus = document.getElementById('ascii-status');
// 文本转ASCII码
document.getElementById('text-to-ascii').addEventListener('click', () => {
const text = asciiTextInput.value;
if (!text) {
showStatus(asciiStatus, '请输入要转换的文本', 'error');
return;
}
const format = document.getElementById('ascii-format').value;
let asciiCodes = [];
for (let i = 0; i < text.length; i++) {
asciiCodes.push(text.charCodeAt(i));
}
let result;
switch (format) {
case 'space':
result = asciiCodes.join(' ');
break;
case 'comma':
result = asciiCodes.join(',');
break;
case 'hex':
result = asciiCodes.map(c => '0x' + c.toString(16).toUpperCase()).join(' ');
break;
case 'hex-no-prefix':
result = asciiCodes.map(c => c.toString(16).toUpperCase()).join(' ');
break;
default:
result = asciiCodes.join(' ');
}
asciiCodeInput.value = result;
showStatus(asciiStatus, '文本转换为ASCII码成功!', 'success');
});
// ASCII码转文本
document.getElementById('ascii-to-text').addEventListener('click', () => {
const codeStr = asciiCodeInput.value.trim();
if (!codeStr) {
showStatus(asciiStatus, '请输入要转换的ASCII码', 'error');
return;
}
try {
// 处理不同格式的ASCII码输入
let codes = [];
if (codeStr.includes(',')) {
// 逗号分隔
codes = codeStr.split(',').map(s => parseInt(s.trim()));
} else if (codeStr.includes(' ')) {
// 空格分隔
codes = codeStr.split(/\s+/).map(s => {
// 处理十六进制格式 (0x... 或 纯十六进制)
if (s.startsWith('0x') || s.startsWith('0X')) {
return parseInt(s, 16);
} else if (/^[0-9A-Fa-f]+$/.test(s)) {
// 可能是十六进制无前缀
return parseInt(s, 16);
} else {
return parseInt(s);
}
});
} else {
// 单个ASCII码
codes = [parseInt(codeStr)];
}
// 检查是否有无效的ASCII码
if (codes.some(isNaN)) {
throw new Error('包含无效的ASCII码');
}
// 将ASCII码转换为字符串
let text = '';
for (const code of codes) {
if (code < 0 || code > 255) {
throw new Error(`ASCII码 ${code} 超出范围 (0-255)`);
}
text += String.fromCharCode(code);
}
asciiTextInput.value = text;
showStatus(asciiStatus, 'ASCII码转换为文本成功!', 'success');
} catch (e) {
showStatus(asciiStatus, `转换失败: ${e.message}`, 'error');
}
});
// 清空ASCII工具
document.getElementById('clear-ascii').addEventListener('click', () => {
asciiTextInput.value = '';
asciiCodeInput.value = '';
asciiStatus.textContent = '';
asciiStatus.className = 'status-message';
});
// 复制ASCII文本
document.getElementById('copy-ascii-text').addEventListener('click', () => {
if (asciiTextInput.value) {
copyToClipboard(asciiTextInput);
showStatus(asciiStatus, '文本已复制到剪贴板!', 'success');
}
});
// 复制ASCII码
document.getElementById('copy-ascii-code').addEventListener('click', () => {
if (asciiCodeInput.value) {
copyToClipboard(asciiCodeInput);
showStatus(asciiStatus, 'ASCII码已复制到剪贴板!', 'success');
}
});
// 生成ASCII码表
generateAsciiTable();
// 辅助函数
function showStatus(element, message, type) {
element.textContent = message;
element.className = 'status-message ' + type;
setTimeout(() => {
element.textContent = '';
element.className = 'status-message';
}, 3000);
}
function copyToClipboard(element) {
element.select();
document.execCommand('copy');
// 取消选中
if (window.getSelection) {
window.getSelection().removeAllRanges();
} else if (document.selection) {
document.selection.empty();
}
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function unescapeHtml(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function jsonToXml(jsonObj, nodeName = 'root') {
let xml = '';
if (typeof jsonObj === 'object' && jsonObj !== null) {
if (Array.isArray(jsonObj)) {
jsonObj.forEach((item, index) => {
xml += jsonToXml(item, nodeName + '_' + index);
});
} else {
xml += '<' + nodeName + '>';
for (const key in jsonObj) {
if (jsonObj.hasOwnProperty(key)) {
xml += jsonToXml(jsonObj[key], key);
}
}
xml += '</' + nodeName + '>';
}
} else {
xml += '<' + nodeName + '>' + jsonObj + '</' + nodeName + '>';
}
return xml;
}
function jsonToCsv(jsonObj) {
if (Array.isArray(jsonObj)) {
if (jsonObj.length === 0) return '';
// 获取所有可能的键作为表头
const headers = new Set();
jsonObj.forEach(item => {
if (typeof item === 'object' && item !== null) {
Object.keys(item).forEach(key => headers.add(key));
}
});
const headerArray = Array.from(headers);
let csv = headerArray.join(',') + '\n';
// 添加数据行
jsonObj.forEach(item => {
const row = headerArray.map(header => {
const value = item[header];
if (value === undefined || value === null) return '';
// 处理包含逗号或引号的值
const strValue = String(value).replace(/"/g, '""');
return `"${strValue}"`;
});
csv += row.join(',') + '\n';
});
return csv;
} else if (typeof jsonObj === 'object' && jsonObj !== null) {
// 单个对象转换为单行CSV
const headers = Object.keys(jsonObj);
const row = headers.map(header => {
const value = jsonObj[header];
if (value === undefined || value === null) return '';
const strValue = String(value).replace(/"/g, '""');
return `"${strValue}"`;
});
return headers.join(',') + '\n' + row.join(',');
} else {
return String(jsonObj);
}
}
function jsonToYaml(jsonObj, indent = 0) {
const spaces = ' '.repeat(indent);
if (typeof jsonObj === 'object' && jsonObj !== null) {
if (Array.isArray(jsonObj)) {
if (jsonObj.length === 0) return '[]';
let yaml = '';
jsonObj.forEach(item => {
yaml += spaces + '- ' + jsonToYaml(item, indent + 1).trimStart() + '\n';
});
return yaml;
} else {
const keys = Object.keys(jsonObj);
if (keys.length === 0) return '{}';
let yaml = '';
keys.forEach(key => {
const value = jsonObj[key];
yaml += spaces + key + ': ' + jsonToYaml(value, indent + 1).trimStart() + '\n';
});
return yaml;
}
} else if (typeof jsonObj === 'string') {
// 简单字符串处理
if (jsonObj.includes('\n')) {
return '|\n' + spaces + ' ' + jsonObj.replace(/\n/g, '\n' + spaces + ' ');
}
return jsonObj;
} else if (jsonObj === null) {
return 'null';
} else {
return String(jsonObj);
}
}
function evaluateJsonPath(obj, path) {
// 简单实现,不支持完整JSONPath语法
try {
// 尝试作为JavaScript表达式执行
const func = new Function('obj', 'return obj' + (path.startsWith('$') ? path.slice(1) : path));
return func(obj);
} catch (e) {
throw new Error('不支持的JSON路径表达式');
}
}
async function calculateHash(text, algorithm) {
// 使用Web Crypto API计算哈希
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest(algorithm, data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
async function aesEncrypt(text, key, iv) {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(key),
{ name: 'AES-CBC' },
false,
['encrypt']
);
const ivArray = iv ? encoder.encode(iv) : crypto.getRandomValues(new Uint8Array(16));
const data = encoder.encode(text);
const encrypted = await crypto.subtle.encrypt(
{
name: 'AES-CBC',
iv: ivArray
},
keyMaterial,
data
);
// 返回IV和加密数据的Base64组合
const result = new Uint8Array(ivArray.length + encrypted.byteLength);
result.set(ivArray, 0);
result.set(new Uint8Array(encrypted), ivArray.length);
return btoa(String.fromCharCode.apply(null, result));
}
async function aesDecrypt(encryptedText, key) {
try {
const encoder = new TextEncoder();
const decoder = new TextDecoder();
// 解码Base64
const binaryString = atob(encryptedText);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// 提取IV (前16字节)和加密数据
const iv = bytes.slice(0, 16);
const data = bytes.slice(16);
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(key),
{ name: 'AES-CBC' },
false,
['decrypt']
);
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-CBC',
iv: iv
},
keyMaterial,
data
);
return decoder.decode(decrypted);
} catch (e) {
throw new Error('解密失败: ' + e.message);
}
}
function formatDateTimeLocal(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
function generateAsciiTable() {
const table = document.createElement('table');
table.className = 'ascii-table';
// 表头
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
['十进制', '十六进制', '字符', '描述'].forEach(text => {
const th = document.createElement('th');
th.textContent = text;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// 表体
const tbody = document.createElement('tbody');
// 生成0-127的ASCII码
for (let i = 0; i <= 127; i++) {
const tr = document.createElement('tr');
// 十进制
const tdDec = document.createElement('td');
tdDec.textContent = i;
tr.appendChild(tdDec);
// 十六进制
const tdHex = document.createElement('td');
tdHex.textContent = '0x' + i.toString(16).toUpperCase();
tr.appendChild(tdHex);
// 字符
const tdChar = document.createElement('td');
const charSpan = document.createElement('span');
charSpan.className = i < 32 ? 'ascii-control' : 'ascii-char';
if (i < 32) {
// 控制字符
charSpan.textContent = getControlCharName(i);
} else if (i === 32) {
charSpan.textContent = 'Space';
} else if (i === 127) {
charSpan.textContent = 'DEL';
} else {
charSpan.textContent = String.fromCharCode(i);
}
tdChar.appendChild(charSpan);
tr.appendChild(tdChar);
// 描述
const tdDesc = document.createElement('td');
tdDesc.textContent = getAsciiDescription(i);
tr.appendChild(tdDesc);
tbody.appendChild(tr);
}
table.appendChild(tbody);
document.getElementById('ascii-table').appendChild(table);
}
function getControlCharName(code) {
const controlChars = [
'NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL',
'BS', 'HT', 'LF', 'VT', 'FF', 'CR', 'SO', 'SI',
'DLE', 'DC1', 'DC2', 'DC3', 'DC4', 'NAK', 'SYN', 'ETB',
'CAN', 'EM', 'SUB', 'ESC', 'FS', 'GS', 'RS', 'US'
];
return controlChars[code] || '';
}
function getAsciiDescription(code) {
const descriptions = {
0: '空字符',
1: '标题开始',
2: '正文开始',
3: '正文结束',
4: '传输结束',
5: '请求',
6: '确认回应',
7: '响铃',
8: '退格',
9: '水平制表符',
10: '换行',
11: '垂直制表符',
12: '换页',
13: '回车',
14: '取消变换',
15: '启用变换',
16: '数据链路转义',
17: '设备控制1',
18: '设备控制2',
19: '设备控制3',
20: '设备控制4',
21: '拒绝接收',
22: '同步空闲',
23: '传输块结束',
24: '取消',
25: '介质中断',
26: '替换',
27: '逃逸',
28: '文件分隔符',
29: '组分隔符',
30: '记录分隔符',
31: '单元分隔符',
32: '空格',
127: '删除'
};
return descriptions[code] || (code >= 33 && code <= 126 ? '可打印字符' : '');
}
});
</script>
</body>
</html>