add url coder tools

This commit is contained in:
2025-11-28 19:23:35 +08:00
parent 9194e364dc
commit 902f360f71
2 changed files with 704 additions and 0 deletions

690
url_encoder.html Normal file
View File

@@ -0,0 +1,690 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>URL 编解码工具 - 功能工具箱</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
color: #333;
overflow: hidden;
}
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: #f5f5f5;
padding: 15px 20px;
text-align: center;
border-bottom: 1px solid #ddd;
flex-shrink: 0;
}
.header h1 {
font-size: 1.5em;
margin-bottom: 5px;
font-weight: 600;
}
.header p {
font-size: 0.9em;
color: #666;
margin: 0;
}
.controls {
padding: 10px 15px;
background: #f9f9f9;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 10px;
flex-shrink: 0;
}
.control-group {
display: flex;
gap: 10px;
align-items: center;
}
button {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
background: white;
}
button:hover {
background: #f0f0f0;
}
.btn-primary {
background: #007bff;
color: white;
border-color: #007bff;
}
.btn-primary:hover {
background: #0056b3;
}
.btn-secondary {
background: #6c757d;
color: white;
border-color: #6c757d;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-success {
background: #28a745;
color: white;
border-color: #28a745;
}
.btn-success:hover {
background: #218838;
}
.editor-container {
display: flex;
flex: 1;
min-height: 400px;
}
.editor-panel {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
.editor-header {
padding: 10px 15px;
background: #f5f5f5;
border-bottom: 1px solid #ddd;
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
}
.editor-content {
flex: 1;
position: relative;
}
textarea {
width: 100%;
height: 100%;
border: none;
outline: none;
padding: 15px;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 13px;
line-height: 1.4;
resize: none;
background: white;
color: #333;
}
.output-area {
background: white;
padding: 15px;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 13px;
line-height: 1.4;
white-space: pre-wrap;
overflow: auto;
height: 100%;
color: #333;
word-break: break-all;
}
.error-message {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 8px;
margin: 20px;
border: 1px solid #f5c6cb;
display: none;
}
.success-message {
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 8px;
margin: 20px;
border: 1px solid #c3e6cb;
display: none;
}
.divider {
width: 2px;
background: #ddd;
cursor: col-resize;
}
.divider:hover {
background: #bbb;
}
.status-bar {
background: #f5f5f5;
border-top: 1px solid #ddd;
padding: 6px 15px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #666;
flex-shrink: 0;
}
.char-count {
color: #999;
}
.encoding-options {
display: flex;
gap: 15px;
align-items: center;
}
.option-group {
display: flex;
align-items: center;
gap: 5px;
}
.option-group label {
font-size: 14px;
color: #666;
}
.option-group select {
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 14px;
}
@media (max-width: 768px) {
.editor-container {
flex-direction: column;
height: auto;
}
.divider {
width: 100%;
height: 4px;
cursor: row-resize;
}
.divider::before {
content: "⋯";
}
.editor-panel {
min-height: 300px;
}
.controls {
flex-direction: column;
align-items: stretch;
}
.control-group {
justify-content: center;
}
}
.loading {
display: none;
color: #007bff;
font-style: italic;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔗 URL 编解码工具</h1>
<p>URL 编码和解码工具,支持多种编码格式和实时转换</p>
</div>
<div class="controls">
<div class="control-group">
<div
class="mode-buttons"
style="
display: flex;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
"
>
<button
id="encodeBtn"
class="btn-primary"
onclick="setMode('encode')"
style="border-radius: 0; border: none; margin: 0"
>
编码
</button>
<button
id="decodeBtn"
class="btn-secondary"
onclick="setMode('decode')"
style="
border-radius: 0;
border: none;
margin: 0;
border-left: 1px solid #ddd;
"
>
解码
</button>
</div>
<button class="btn-success" onclick="copyResult()">复制结果</button>
<button class="btn-secondary" onclick="clearAll()">清空</button>
</div>
<div class="encoding-options">
<div class="option-group">
<label for="encodingType">编码类型:</label>
<select id="encodingType" onchange="handleInputChange()">
<option value="component">URIComponent (推荐)</option>
<option value="full">完整 URL</option>
<option value="form">表单编码</option>
<option value="base64">Base64</option>
</select>
</div>
</div>
</div>
<div class="editor-container">
<div class="editor-panel">
<div class="editor-header">
<span>输入 URL/文本</span>
<span class="char-count" id="inputCount">0 字符</span>
</div>
<div class="editor-content">
<textarea
id="urlInput"
placeholder="在此输入要编码或解码的 URL 或文本...
例如: https://example.com/search?q=测试参数&lang=zh-CN"
oninput="handleInputChange()"
></textarea>
</div>
</div>
<div class="divider"></div>
<div class="editor-panel">
<div class="editor-header">
<span>处理结果</span>
<span class="char-count" id="outputCount">0 字符</span>
</div>
<div class="editor-content">
<div id="urlOutput" class="output-area">
编码/解码结果将显示在这里...
</div>
</div>
</div>
</div>
<div class="error-message" id="errorMessage"></div>
<div class="success-message" id="successMessage"></div>
<div class="status-bar">
<span id="status">就绪</span>
<span id="processingStatus" class="loading">正在处理...</span>
</div>
</div>
<script>
let isAutoProcessing = true;
let currentMode = "encode"; // 'encode' 或 'decode'
function setMode(mode) {
currentMode = mode;
const encodeBtn = document.getElementById("encodeBtn");
const decodeBtn = document.getElementById("decodeBtn");
if (mode === "encode") {
encodeBtn.className = "btn-primary";
decodeBtn.className = "btn-secondary";
} else {
encodeBtn.className = "btn-secondary";
decodeBtn.className = "btn-primary";
}
// 重新处理当前输入
const input = document.getElementById("urlInput").value.trim();
if (input) {
processInput();
}
}
function handleInputChange() {
updateCharCount();
if (isAutoProcessing) {
// 延迟处理,避免频繁操作
clearTimeout(window.processTimeout);
window.processTimeout = setTimeout(() => {
processInput();
}, 300);
}
}
function processInput() {
const input = document.getElementById("urlInput").value.trim();
const output = document.getElementById("urlOutput");
if (!input) {
output.textContent = "处理结果将显示在这里...";
updateCharCount();
return;
}
if (currentMode === "encode") {
encodeURL(false);
} else {
decodeURL(false);
}
}
function updateCharCount() {
const input = document.getElementById("urlInput");
const output = document.getElementById("urlOutput");
const inputCount = document.getElementById("inputCount");
const outputCount = document.getElementById("outputCount");
inputCount.textContent = input.value.length + " 字符";
outputCount.textContent = output.textContent.length + " 字符";
}
function encodeURL(showMessages = true) {
const input = document.getElementById("urlInput").value.trim();
const output = document.getElementById("urlOutput");
const errorMessage = document.getElementById("errorMessage");
const successMessage = document.getElementById("successMessage");
const processingStatus = document.getElementById("processingStatus");
const encodingType = document.getElementById("encodingType").value;
// 清除之前的消息
if (showMessages) {
errorMessage.style.display = "none";
successMessage.style.display = "none";
processingStatus.style.display = "block";
}
if (!input) {
output.textContent = "请输入要编码的内容";
if (showMessages) {
processingStatus.style.display = "none";
}
updateCharCount();
return;
}
try {
let result = "";
switch (encodingType) {
case "component":
result = encodeURIComponent(input);
break;
case "full":
result = encodeURI(input);
break;
case "form":
result = formEncode(input);
break;
case "base64":
result = btoa(unescape(encodeURIComponent(input)));
break;
default:
result = encodeURIComponent(input);
}
output.textContent = result;
if (showMessages) {
// 显示成功消息
successMessage.textContent = `${getEncodingName(encodingType)}编码成功!`;
successMessage.style.display = "block";
// 3秒后隐藏成功消息
setTimeout(() => {
successMessage.style.display = "none";
}, 3000);
}
} catch (error) {
output.textContent = "";
if (showMessages) {
errorMessage.innerHTML = `❌ 编码错误: ${error.message}`;
errorMessage.style.display = "block";
}
}
if (showMessages) {
processingStatus.style.display = "none";
}
updateCharCount();
}
function decodeURL(showMessages = true) {
const input = document.getElementById("urlInput").value.trim();
const output = document.getElementById("urlOutput");
const errorMessage = document.getElementById("errorMessage");
const successMessage = document.getElementById("successMessage");
const processingStatus = document.getElementById("processingStatus");
const encodingType = document.getElementById("encodingType").value;
// 清除之前的消息
if (showMessages) {
errorMessage.style.display = "none";
successMessage.style.display = "none";
processingStatus.style.display = "block";
}
if (!input) {
output.textContent = "请输入要解码的内容";
if (showMessages) {
processingStatus.style.display = "none";
}
updateCharCount();
return;
}
try {
let result = "";
switch (encodingType) {
case "component":
result = decodeURIComponent(input);
break;
case "full":
result = decodeURI(input);
break;
case "form":
result = formDecode(input);
break;
case "base64":
result = decodeURIComponent(escape(atob(input)));
break;
default:
result = decodeURIComponent(input);
}
output.textContent = result;
if (showMessages) {
// 显示成功消息
successMessage.textContent = `${getEncodingName(encodingType)}解码成功!`;
successMessage.style.display = "block";
// 3秒后隐藏成功消息
setTimeout(() => {
successMessage.style.display = "none";
}, 3000);
}
} catch (error) {
output.textContent = "";
if (showMessages) {
errorMessage.innerHTML = `❌ 解码错误: ${error.message}`;
errorMessage.style.display = "block";
}
}
if (showMessages) {
processingStatus.style.display = "none";
}
updateCharCount();
}
function formEncode(str) {
return encodeURIComponent(str).replace(/%20/g, "+");
}
function formDecode(str) {
return decodeURIComponent(str.replace(/\+/g, "%20"));
}
function getEncodingName(type) {
const names = {
component: "URIComponent",
full: "完整 URL",
form: "表单",
base64: "Base64",
};
return names[type] || "URL";
}
function copyResult() {
const output = document.getElementById("urlOutput");
const successMessage = document.getElementById("successMessage");
if (
!output.textContent ||
output.textContent === "编码/解码结果将显示在这里..." ||
output.textContent === "请输入要编码的内容" ||
output.textContent === "请输入要解码的内容"
) {
alert("没有可复制的内容!");
return;
}
// 获取纯文本内容
const textToCopy = output.textContent || output.innerText;
navigator.clipboard
.writeText(textToCopy)
.then(() => {
successMessage.textContent = "📋 已复制到剪贴板!";
successMessage.style.display = "block";
setTimeout(() => {
successMessage.style.display = "none";
}, 2000);
})
.catch(() => {
// 降级方案
const textArea = document.createElement("textarea");
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
successMessage.textContent = "📋 已复制到剪贴板!";
successMessage.style.display = "block";
setTimeout(() => {
successMessage.style.display = "none";
}, 2000);
});
}
function clearAll() {
document.getElementById("urlInput").value = "";
document.getElementById("urlOutput").textContent =
"编码/解码结果将显示在这里...";
document.getElementById("errorMessage").style.display = "none";
document.getElementById("successMessage").style.display = "none";
updateCharCount();
}
// 键盘快捷键
document.addEventListener("keydown", function (e) {
// Ctrl/Cmd + Enter 根据当前模式处理
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
e.preventDefault();
if (currentMode === "encode") {
encodeURL();
} else {
decodeURL();
}
}
// Ctrl/Cmd + Shift + Enter 切换模式
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "Enter") {
e.preventDefault();
setMode(currentMode === "encode" ? "decode" : "encode");
}
// Ctrl/Cmd + Shift + C 复制结果
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "C") {
e.preventDefault();
copyResult();
}
});
// 页面加载完成后的初始化
document.addEventListener("DOMContentLoaded", function () {
const input = document.getElementById("urlInput");
input.focus();
updateCharCount();
// 初始化为编码模式
setMode("encode");
});
// 示例 URL
const exampleURL =
"https://example.com/search?q=测试参数&lang=zh-CN&page=1";
// 如果需要加载示例,可以取消下面的注释
// document.getElementById('urlInput').value = exampleURL;
// encodeURL();
</script>
</body>
</html>