From 0bb1ddbf88ed32e97ca2abdec4de79196d23887b Mon Sep 17 00:00:00 2001 From: scout <1134087124@qq.com> Date: Wed, 13 Nov 2024 11:55:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 71 +++++++--- background.js | 342 +++++++++++++++++++++++++++++++++++++++++++++++ content.css | 30 +++++ content.js | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 33 ++++- md5.js | 19 +++ popup.html | 23 ++++ popup.js | 42 +++++- styles.css | 85 +++++++++++- 9 files changed, 972 insertions(+), 32 deletions(-) create mode 100644 background.js create mode 100644 content.css create mode 100644 content.js create mode 100644 md5.js diff --git a/README.md b/README.md index 9fe2775..4c1fe3a 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,85 @@ # 丰链工具箱 Chrome 扩展 -一个实用的 Chrome 扩展工具集合,目前包含二维码生成器和 Word 转 HTML 功能。 +一个实用的 Chrome 扩展工具集合,集成了二维码生成、Word 转 HTML、中英文翻译等实用功能。 ## 功能特性 ### 二维码生成器 🔲 - 自动获取当前标签页 URL 并生成二维码 - 支持手动输入文本/链接生成二维码 -- 高清晰度二维码输出 +- 生成高清晰度二维码,支持长文本 +- 即时预览生成结果 ### Word 转 HTML 转换器 📄 - 支持 .docx 文件转换为 HTML +- 保留原文档格式和样式 - 实时预览转换结果 -- 保留文档格式和样式 -- 支持下载转换后的 HTML 文件 +- 支持一键下载转换后的 HTML 文件 +- 自动处理图片和表格 + +### 中英文翻译 🔤 +- 支持 Windows(Ctrl+Q) 和 Mac(Command+Q) 快捷键激活 +- 框选任意区域中文文本进行翻译 +- 支持中英互译和繁体转换 +- 翻译结果即时显示在选区旁边 +- 点击任意位置关闭翻译结果 + +## 系统要求 + +- Chrome 88+ 或其他基于 Chromium 的浏览器 +- Windows/MacOS/Linux 系统均可运行 ## 安装方法 +### 从 Chrome 商店安装 +1. 访问 Chrome 网上应用店 +2. 搜索"丰链工具箱" +3. 点击"添加至 Chrome" + +### 开发者模式安装 1. 下载本项目代码 2. 打开 Chrome 浏览器,进入扩展程序页面 (chrome://extensions/) -3. 开启"开发者模式" +3. 开启右上角的"开发者模式" 4. 点击"加载已解压的扩展程序" 5. 选择项目文件夹即可完成安装 ## 使用方法 -1. 点击 Chrome 工具栏中的扩展图标打开工具箱 -2. 选择需要使用的功能标签页 -3. 根据需要使用相应功能 - ### 二维码生成 -- 自动显示当前页面的二维码 -- 可以在输入框中输入其他文本/链接并点击"生成二维码" +1. 点击扩展图标打开工具箱 +2. 默认显示当前页面的二维码 +3. 可在输入框中输入任意文本/链接 +4. 点击"生成二维码"按钮即可 ### Word 转 HTML -- 点击选择文件上传 Word 文档 -- 等待转换完成后预览效果 -- 点击"下载 HTML"保存转换结果 +1. 点击"Word转HTML"标签页 +2. 点击选择或拖拽上传 Word 文档 +3. 等待转换完成后可预览效果 +4. 点击"下载HTML"保存文件 + +### 翻译功能 +1. 点击"翻译设置"标签页开启功能 +2. 选择目标语言(英文/繁体中文) +3. 在任意页面按下快捷键(Ctrl+Q/Command+Q) +4. 框选需要翻译的文本 +5. 翻译结果会显示在选区旁边 ## 技术栈 -- HTML5 -- CSS3 -- JavaScript +- HTML5/CSS3 +- JavaScript ES6+ - Chrome Extension API -- QRCode.js -- Mammoth.js +- QRCode.js - 二维码生成 +- Mammoth.js - Word 文档转换 +- 百度翻译 API - 文本翻译 ## 开发者 Made by Scout -## 许可证 +## 更新日志 -[添加许可证类型] \ No newline at end of file +### v1.0.0 +- 初始版本发布 +- 实现基础功能:二维码生成、Word转HTML、翻译 +- 支持 Windows/Mac 快捷键 diff --git a/background.js b/background.js new file mode 100644 index 0000000..8a4feac --- /dev/null +++ b/background.js @@ -0,0 +1,342 @@ +// 存储注入状态 +let injectedTabs = new Set(); + +// 监听快捷键命令 +chrome.commands.onCommand.addListener(async (command) => { + if (command === 'trigger-select') { + try { + const result = await chrome.storage.sync.get(['translateEnabled']); + console.log('Translation enabled status:', result.translateEnabled); + + // 只有当明确设置为 false 时才禁用 + if (result.translateEnabled === false) { + console.log('Translation is disabled'); + return; + } + + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (tab) { + await injectScriptsIfNeeded(tab.id); + } + } catch (error) { + console.error('Error checking translation status:', error); + } + } +}); + +// 处理翻译请求 +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === 'translate') { + handleTranslation(request.text) + .then(sendResponse) + .catch(error => { + console.error('Translation error:', error); + sendResponse({ error: 'Translation failed' }); + }); + return true; + } +}); + +// 注入脚本的函数 +async function injectScriptsIfNeeded(tabId) { + try { + // 如果已经注入过,直接发送消息 + if (injectedTabs.has(tabId)) { + await chrome.tabs.sendMessage(tabId, { action: 'startSelection' }); + return; + } + + // 注入 CSS + await chrome.scripting.insertCSS({ + target: { tabId }, + files: ['content.css'] + }); + + // 先注入 MD5 + await chrome.scripting.executeScript({ + target: { tabId }, + files: ['md5.js'] + }); + + // 再注入 content script + await chrome.scripting.executeScript({ + target: { tabId }, + files: ['content.js'] + }); + + // 记录已注入状态 + injectedTabs.add(tabId); + + // 等待一小段时间确保脚本加载完成 + await new Promise(resolve => setTimeout(resolve, 100)); + + // 发送消息 + await chrome.tabs.sendMessage(tabId, { action: 'startSelection' }); + + } catch (error) { + console.error('Injection error:', error); + } +} + +// 监听标签页更新,清除注入状态 +chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === 'loading') { + injectedTabs.delete(tabId); + } +}); + +// 监听标签页移除,清除注入状态 +chrome.tabs.onRemoved.addListener((tabId) => { + injectedTabs.delete(tabId); +}); + +// 添加 MD5 函数 +function md5(string) { + function md5_RotateLeft(lValue, iShiftBits) { + return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); + } + + function md5_AddUnsigned(lX, lY) { + var lX4, lY4, lX8, lY8, lResult; + lX8 = (lX & 0x80000000); + lY8 = (lY & 0x80000000); + lX4 = (lX & 0x40000000); + lY4 = (lY & 0x40000000); + lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); + if (lX4 & lY4) { + return (lResult ^ 0x80000000 ^ lX8 ^ lY8); + } + if (lX4 | lY4) { + if (lResult & 0x40000000) { + return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); + } else { + return (lResult ^ 0x40000000 ^ lX8 ^ lY8); + } + } else { + return (lResult ^ lX8 ^ lY8); + } + } + + function md5_F(x, y, z) { return (x & y) | ((~x) & z); } + function md5_G(x, y, z) { return (x & z) | (y & (~z)); } + function md5_H(x, y, z) { return (x ^ y ^ z); } + function md5_I(x, y, z) { return (y ^ (x | (~z))); } + + function md5_FF(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_GG(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_HH(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_II(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_ConvertToWordArray(string) { + var lWordCount; + var lMessageLength = string.length; + var lNumberOfWords_temp1 = lMessageLength + 8; + var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; + var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; + var lWordArray = Array(lNumberOfWords - 1); + var lBytePosition = 0; + var lByteCount = 0; + while (lByteCount < lMessageLength) { + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); + lByteCount++; + } + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); + lWordArray[lNumberOfWords - 2] = lMessageLength << 3; + lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; + return lWordArray; + } + + function md5_WordToHex(lValue) { + var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount; + for (lCount = 0; lCount <= 3; lCount++) { + lByte = (lValue >>> (lCount * 8)) & 255; + WordToHexValue_temp = "0" + lByte.toString(16); + WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2); + } + return WordToHexValue; + } + + function md5_Utf8Encode(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + return utftext; + } + + var x = Array(); + var k, AA, BB, CC, DD, a, b, c, d; + var S11 = 7, S12 = 12, S13 = 17, S14 = 22; + var S21 = 5, S22 = 9, S23 = 14, S24 = 20; + var S31 = 4, S32 = 11, S33 = 16, S34 = 23; + var S41 = 6, S42 = 10, S43 = 15, S44 = 21; + + string = md5_Utf8Encode(string); + x = md5_ConvertToWordArray(string); + a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; + + for (k = 0; k < x.length; k += 16) { + AA = a; BB = b; CC = c; DD = d; + a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); + d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); + c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB); + b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); + a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); + d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); + c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613); + b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501); + a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8); + d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); + c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); + b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); + a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122); + d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193); + c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E); + b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821); + a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); + d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340); + c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); + b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); + a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); + d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453); + c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); + b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); + a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); + d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); + c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); + b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); + a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); + d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); + c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); + b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); + a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); + d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681); + c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); + b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); + a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); + d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); + c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); + b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); + a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); + d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); + c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); + b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05); + a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); + d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); + c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); + b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); + a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244); + d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97); + c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); + b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039); + a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3); + d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); + c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); + b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1); + a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); + d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); + c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314); + b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); + a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82); + d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); + c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); + b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391); + a = md5_AddUnsigned(a, AA); + b = md5_AddUnsigned(b, BB); + c = md5_AddUnsigned(c, CC); + d = md5_AddUnsigned(d, DD); + } + return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase(); +} + +async function handleTranslation(text) { + try { + // 获取目标语言设置 + const { targetLang } = await chrome.storage.sync.get(['targetLang']); + const to = targetLang || 'en'; + + const appid = '20241112002200806'; + const key = 'preM0becByYCdotRTP_a'; + const salt = Date.now().toString(); + const from = 'zh'; + + const str1 = appid + text + salt + key; + const sign = md5(str1); + + const params = new URLSearchParams({ + q: text, + from: from, + to: to, + appid: appid, + salt: salt, + sign: sign + }); + + const url = 'https://api.fanyi.baidu.com/api/trans/vip/translate'; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: params.toString() + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + console.log('Translation result:', result); + + if (result.error_code) { + throw new Error(result.error_msg || 'Translation failed'); + } + + return result; + } catch (error) { + console.error('Translation error:', error); + return { + error: true, + message: error.message || 'Translation failed', + trans_result: [{ + dst: 'Translation failed: ' + (error.message || 'Unknown error') + }] + }; + } +} + +// 监听扩展安装或更新 +chrome.runtime.onInstalled.addListener(() => { + console.log('Extension installed/updated'); +}); \ No newline at end of file diff --git a/content.css b/content.css new file mode 100644 index 0000000..25d6cfa --- /dev/null +++ b/content.css @@ -0,0 +1,30 @@ +.selection-overlay { + position: fixed; + border: 2px dashed #4285f4; + background: rgba(66, 133, 244, 0.1); + z-index: 10000; + pointer-events: none; +} + +.translation-popup { + position: fixed; + background: white; + padding: 10px; + border-radius: 4px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + z-index: 10001; + max-width: 300px; + font-size: 14px; + line-height: 1.4; +} + +.page-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.1); + z-index: 9999; + cursor: crosshair; +} \ No newline at end of file diff --git a/content.js b/content.js new file mode 100644 index 0000000..4fda2ed --- /dev/null +++ b/content.js @@ -0,0 +1,359 @@ +// 检查是否已经初始化 +if (window.translationToolInitialized) { + throw new Error('Translation tool already initialized'); +} +window.translationToolInitialized = true; + +let isSelecting = false; +let startX, startY; +let selectionElement = null; +let translationPopup = null; + +// 监听来自background的消息 +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === 'startSelection') { + startSelectionMode(); + } +}); + +function startSelectionMode() { + if (isSelecting) return; + + isSelecting = true; + + const overlay = document.createElement('div'); + overlay.className = 'page-overlay'; + document.body.appendChild(overlay); + + selectionElement = document.createElement('div'); + selectionElement.className = 'selection-overlay'; + document.body.appendChild(selectionElement); + + document.addEventListener('mousedown', handleMouseDown); + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + + document.addEventListener('keydown', handleKeyDown); +} + +function handleMouseDown(e) { + if (!isSelecting) return; + startX = e.clientX; + startY = e.clientY; + updateSelectionBox(e); +} + +function handleMouseMove(e) { + if (!isSelecting || !startX) return; + updateSelectionBox(e); +} + +function handleMouseUp(e) { + if (!isSelecting) return; + + const selectedText = getTextFromArea( + Math.min(startX, e.clientX), + Math.min(startY, e.clientY), + Math.abs(e.clientX - startX), + Math.abs(e.clientY - startY) + ); + + if (selectedText.trim()) { + translateText(selectedText.trim(), e.clientX, e.clientY); + } + + cleanup(); +} + +function updateSelectionBox(e) { + const left = Math.min(startX, e.clientX); + const top = Math.min(startY, e.clientY); + const width = Math.abs(e.clientX - startX); + const height = Math.abs(e.clientY - startY); + + selectionElement.style.left = left + 'px'; + selectionElement.style.top = top + 'px'; + selectionElement.style.width = width + 'px'; + selectionElement.style.height = height + 'px'; +} + +function getTextFromArea(left, top, width, height) { + const elements = document.elementsFromPoint(left + width/2, top + height/2); + for (const element of elements) { + if (element.textContent && element !== selectionElement) { + return element.textContent; + } + } + return ''; +} + +async function translateText(text, x, y) { + try { + // 通过 chrome.runtime.sendMessage 发送翻译请求 + const result = await chrome.runtime.sendMessage({ + action: 'translate', + text: text + }); + + if (result && result.trans_result && result.trans_result[0]) { + showTranslation(result.trans_result[0].dst, x, y); + } else { + showTranslation('Translation failed', x, y); + } + } catch (error) { + console.error('Translation failed:', error); + showTranslation('Translation failed', x, y); + } +} + +function showTranslation(translatedText, x, y) { + if (translationPopup) { + translationPopup.remove(); + } + + translationPopup = document.createElement('div'); + translationPopup.className = 'translation-popup'; + translationPopup.textContent = translatedText; + + translationPopup.style.left = x + 'px'; + translationPopup.style.top = (y + 20) + 'px'; + + document.body.appendChild(translationPopup); + + // 点击其他地方关闭翻译框 + document.addEventListener('click', function closePopup(e) { + if (e.target !== translationPopup) { + translationPopup.remove(); + document.removeEventListener('click', closePopup); + } + }); +} + +function cleanup() { + isSelecting = false; + + const overlay = document.querySelector('.page-overlay'); + if (overlay) { + overlay.remove(); + } + + if (selectionElement) { + selectionElement.remove(); + selectionElement = null; + } + + startX = startY = null; + + document.removeEventListener('mousedown', handleMouseDown); + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + document.removeEventListener('keydown', handleKeyDown); +} + +function handleKeyDown(e) { + // 检测 Mac 和 Windows 的按键 + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + const triggerKey = isMac ? e.metaKey : e.ctrlKey; + + if (e.key === 'Escape' || (triggerKey && e.key === 'q')) { + cleanup(); + } +} + +// 添加 MD5 函数 +function md5(string) { + function md5_RotateLeft(lValue, iShiftBits) { + return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); + } + + function md5_AddUnsigned(lX, lY) { + var lX4, lY4, lX8, lY8, lResult; + lX8 = (lX & 0x80000000); + lY8 = (lY & 0x80000000); + lX4 = (lX & 0x40000000); + lY4 = (lY & 0x40000000); + lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); + if (lX4 & lY4) { + return (lResult ^ 0x80000000 ^ lX8 ^ lY8); + } + if (lX4 | lY4) { + if (lResult & 0x40000000) { + return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); + } else { + return (lResult ^ 0x40000000 ^ lX8 ^ lY8); + } + } else { + return (lResult ^ lX8 ^ lY8); + } + } + + function md5_F(x, y, z) { + return (x & y) | ((~x) & z); + } + + function md5_G(x, y, z) { + return (x & z) | (y & (~z)); + } + + function md5_H(x, y, z) { + return (x ^ y ^ z); + } + + function md5_I(x, y, z) { + return (y ^ (x | (~z))); + } + + function md5_FF(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_GG(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_HH(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_II(a, b, c, d, x, s, ac) { + a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac)); + return md5_AddUnsigned(md5_RotateLeft(a, s), b); + } + + function md5_ConvertToWordArray(string) { + var lWordCount; + var lMessageLength = string.length; + var lNumberOfWords_temp1 = lMessageLength + 8; + var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; + var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; + var lWordArray = Array(lNumberOfWords - 1); + var lBytePosition = 0; + var lByteCount = 0; + while (lByteCount < lMessageLength) { + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); + lByteCount++; + } + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); + lWordArray[lNumberOfWords - 2] = lMessageLength << 3; + lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; + return lWordArray; + } + + function md5_WordToHex(lValue) { + var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount; + for (lCount = 0; lCount <= 3; lCount++) { + lByte = (lValue >>> (lCount * 8)) & 255; + WordToHexValue_temp = "0" + lByte.toString(16); + WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2); + } + return WordToHexValue; + } + + function md5_Utf8Encode(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + return utftext; + } + + var x = Array(); + var k, AA, BB, CC, DD, a, b, c, d; + var S11 = 7, S12 = 12, S13 = 17, S14 = 22; + var S21 = 5, S22 = 9, S23 = 14, S24 = 20; + var S31 = 4, S32 = 11, S33 = 16, S34 = 23; + var S41 = 6, S42 = 10, S43 = 15, S44 = 21; + string = md5_Utf8Encode(string); + x = md5_ConvertToWordArray(string); + a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; + for (k = 0; k < x.length; k += 16) { + AA = a; BB = b; CC = c; DD = d; + a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); + d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); + c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB); + b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); + a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); + d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); + c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613); + b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501); + a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8); + d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); + c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); + b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); + a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122); + d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193); + c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E); + b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821); + a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); + d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340); + c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); + b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); + a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); + d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453); + c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); + b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); + a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); + d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); + c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); + b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); + a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); + d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); + c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); + b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); + a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); + d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681); + c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); + b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); + a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); + d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); + c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); + b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); + a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); + d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); + c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); + b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05); + a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); + d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); + c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); + b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); + a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244); + d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97); + c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); + b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039); + a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3); + d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); + c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); + b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1); + a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); + d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); + c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314); + b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); + a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82); + d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); + c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); + b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391); + a = md5_AddUnsigned(a, AA); + b = md5_AddUnsigned(b, BB); + c = md5_AddUnsigned(c, CC); + d = md5_AddUnsigned(d, DD); + } + return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase(); +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 89607b9..6e1279d 100644 --- a/manifest.json +++ b/manifest.json @@ -16,8 +16,33 @@ "128": "icons/icon128.png" } }, - "permissions": ["activeTab", "downloads"], - "content_security_policy": { - "extension_pages": "script-src 'self'; object-src 'self'" - } + "permissions": [ + "activeTab", + "downloads", + "scripting", + "storage", + "tabs" + ], + "host_permissions": [ + "https://api.fanyi.baidu.com/*", + "" + ], + "commands": { + "trigger-select": { + "suggested_key": { + "default": "Ctrl+Q", + "mac": "Command+Q" + }, + "description": "触发框选功能" + } + }, + "background": { + "service_worker": "background.js", + "type": "module" + }, + "content_scripts": [{ + "matches": [""], + "js": ["md5.js", "content.js"], + "css": ["content.css"] + }] } \ No newline at end of file diff --git a/md5.js b/md5.js new file mode 100644 index 0000000..95df281 --- /dev/null +++ b/md5.js @@ -0,0 +1,19 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +var CryptoJS=CryptoJS||function(s,p){var m={},l=m.lib={},n=function(){},r=l.Base={extend:function(b){n.prototype=this;var h=new n;b&&h.mixIn(b);h.hasOwnProperty("init")||(h.init=function(){h.$super.init.apply(this,arguments)});h.init.prototype=h;h.$super=this;return h},create:function(){var b=this.extend();b.init.apply(b,arguments);return b},init:function(){},mixIn:function(b){for(var h in b)b.hasOwnProperty(h)&&(this[h]=b[h]);b.hasOwnProperty("toString")&&(this.toString=b.toString)},clone:function(){return this.init.prototype.extend(this)}}, +q=l.WordArray=r.extend({init:function(b,h){b=this.words=b||[];this.sigBytes=h!=p?h:4*b.length},toString:function(b){return(b||t).stringify(this)},concat:function(b){var h=this.words,a=b.words,j=this.sigBytes;b=b.sigBytes;this.clamp();if(j%4)for(var g=0;g>>2]|=(a[g>>>2]>>>24-8*(g%4)&255)<<24-8*((j+g)%4);else if(65535>>2]=a[g>>>2];else h.push.apply(h,a);this.sigBytes+=b;return this},clamp:function(){var b=this.words,h=this.sigBytes;b[h>>>2]&=4294967295<< +32-8*(h%4);b.length=s.ceil(h/4)},clone:function(){var b=r.clone.call(this);b.words=this.words.slice(0);return b},random:function(b){for(var h=[],a=0;a>>2]>>>24-8*(j%4)&255;g.push((k>>>4).toString(16));g.push((k&15).toString(16))}return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j>>3]|=parseInt(b.substr(j, +2),16)<<24-4*(j%8);return new q.init(g,a/2)}},a=v.Latin1={stringify:function(b){var a=b.words;b=b.sigBytes;for(var g=[],j=0;j>>2]>>>24-8*(j%4)&255));return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j>>2]|=(b.charCodeAt(j)&255)<<24-8*(j%4);return new q.init(g,a)}},u=v.Utf8={stringify:function(b){try{return decodeURIComponent(escape(a.stringify(b)))}catch(g){throw Error("Malformed UTF-8 data");}},parse:function(b){return a.parse(unescape(encodeURIComponent(b)))}}, +g=l.BufferedBlockAlgorithm=r.extend({reset:function(){this._data=new q.init;this._nDataBytes=0},_append:function(b){"string"==typeof b&&(b=u.parse(b));this._data.concat(b);this._nDataBytes+=b.sigBytes},_process:function(b){var a=this._data,g=a.words,j=a.sigBytes,k=this.blockSize,m=j/(4*k),m=b?s.ceil(m):s.max((m|0)-this._minBufferSize,0);b=m*k;j=s.min(4*b,j);if(b){for(var l=0;l>>32-j)+k}function m(a,k,b,h,l,j,m){a=a+(k&h|b&~h)+l+m;return(a<>>32-j)+k}function l(a,k,b,h,l,j,m){a=a+(k^b^h)+l+m;return(a<>>32-j)+k}function n(a,k,b,h,l,j,m){a=a+(b^(k|~h))+l+m;return(a<>>32-j)+k}for(var r=CryptoJS,q=r.lib,v=q.WordArray,t=q.Hasher,q=r.algo,a=[],u=0;64>u;u++)a[u]=4294967296*s.abs(s.sin(u+1))|0;q=q.MD5=t.extend({_doReset:function(){this._hash=new v.init([1732584193,4023233417,2562383102,271733878])}, +_doProcessBlock:function(g,k){for(var b=0;16>b;b++){var h=k+b,w=g[h];g[h]=(w<<8|w>>>24)&16711935|(w<<24|w>>>8)&4278255360}var b=this._hash.words,h=g[k+0],w=g[k+1],j=g[k+2],q=g[k+3],r=g[k+4],s=g[k+5],t=g[k+6],u=g[k+7],v=g[k+8],x=g[k+9],y=g[k+10],z=g[k+11],A=g[k+12],B=g[k+13],C=g[k+14],D=g[k+15],c=b[0],d=b[1],e=b[2],f=b[3],c=p(c,d,e,f,h,7,a[0]),f=p(f,c,d,e,w,12,a[1]),e=p(e,f,c,d,j,17,a[2]),d=p(d,e,f,c,q,22,a[3]),c=p(c,d,e,f,r,7,a[4]),f=p(f,c,d,e,s,12,a[5]),e=p(e,f,c,d,t,17,a[6]),d=p(d,e,f,c,u,22,a[7]), +c=p(c,d,e,f,v,7,a[8]),f=p(f,c,d,e,x,12,a[9]),e=p(e,f,c,d,y,17,a[10]),d=p(d,e,f,c,z,22,a[11]),c=p(c,d,e,f,A,7,a[12]),f=p(f,c,d,e,B,12,a[13]),e=p(e,f,c,d,C,17,a[14]),d=p(d,e,f,c,D,22,a[15]),c=m(c,d,e,f,w,5,a[16]),f=m(f,c,d,e,t,9,a[17]),e=m(e,f,c,d,z,14,a[18]),d=m(d,e,f,c,h,20,a[19]),c=m(c,d,e,f,s,5,a[20]),f=m(f,c,d,e,y,9,a[21]),e=m(e,f,c,d,D,14,a[22]),d=m(d,e,f,c,r,20,a[23]),c=m(c,d,e,f,x,5,a[24]),f=m(f,c,d,e,C,9,a[25]),e=m(e,f,c,d,q,14,a[26]),d=m(d,e,f,c,v,20,a[27]),c=m(c,d,e,f,B,5,a[28]),f=m(f,c, +d,e,j,9,a[29]),e=m(e,f,c,d,u,14,a[30]),d=m(d,e,f,c,A,20,a[31]),c=l(c,d,e,f,s,4,a[32]),f=l(f,c,d,e,v,11,a[33]),e=l(e,f,c,d,z,16,a[34]),d=l(d,e,f,c,C,23,a[35]),c=l(c,d,e,f,w,4,a[36]),f=l(f,c,d,e,r,11,a[37]),e=l(e,f,c,d,u,16,a[38]),d=l(d,e,f,c,y,23,a[39]),c=l(c,d,e,f,B,4,a[40]),f=l(f,c,d,e,h,11,a[41]),e=l(e,f,c,d,q,16,a[42]),d=l(d,e,f,c,t,23,a[43]),c=l(c,d,e,f,x,4,a[44]),f=l(f,c,d,e,A,11,a[45]),e=l(e,f,c,d,D,16,a[46]),d=l(d,e,f,c,j,23,a[47]),c=n(c,d,e,f,h,6,a[48]),f=n(f,c,d,e,u,10,a[49]),e=n(e,f,c,d, +C,15,a[50]),d=n(d,e,f,c,s,21,a[51]),c=n(c,d,e,f,A,6,a[52]),f=n(f,c,d,e,q,10,a[53]),e=n(e,f,c,d,y,15,a[54]),d=n(d,e,f,c,w,21,a[55]),c=n(c,d,e,f,v,6,a[56]),f=n(f,c,d,e,D,10,a[57]),e=n(e,f,c,d,t,15,a[58]),d=n(d,e,f,c,B,21,a[59]),c=n(c,d,e,f,r,6,a[60]),f=n(f,c,d,e,z,10,a[61]),e=n(e,f,c,d,j,15,a[62]),d=n(d,e,f,c,x,21,a[63]);b[0]=b[0]+c|0;b[1]=b[1]+d|0;b[2]=b[2]+e|0;b[3]=b[3]+f|0},_doFinalize:function(){var a=this._data,k=a.words,b=8*this._nDataBytes,h=8*a.sigBytes;k[h>>>5]|=128<<24-h%32;var l=s.floor(b/ +4294967296);k[(h+64>>>9<<4)+15]=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360;k[(h+64>>>9<<4)+14]=(b<<8|b>>>24)&16711935|(b<<24|b>>>8)&4278255360;a.sigBytes=4*(k.length+1);this._process();a=this._hash;k=a.words;for(b=0;4>b;b++)h=k[b],k[b]=(h<<8|h>>>24)&16711935|(h<<24|h>>>8)&4278255360;return a},clone:function(){var a=t.clone.call(this);a._hash=this._hash.clone();return a}});r.MD5=t._createHelper(q);r.HmacMD5=t._createHmacHelper(q)})(Math); \ No newline at end of file diff --git a/popup.html b/popup.html index 51f52d8..adcb093 100644 --- a/popup.html +++ b/popup.html @@ -18,6 +18,10 @@ 📄 Word转HTML +
@@ -43,6 +47,25 @@ 下载HTML
+ + diff --git a/popup.js b/popup.js index eb7e6ff..f981ced 100644 --- a/popup.js +++ b/popup.js @@ -10,12 +10,15 @@ document.addEventListener('DOMContentLoaded', function() { let qr = null; let convertedHtml = ''; + // 加载翻译设置 + loadTranslationSettings(); + const tabs = document.querySelectorAll('.tablinks'); const tabContents = document.querySelectorAll('.tabcontent'); tabs.forEach(tab => { tab.addEventListener('click', (e) => { - const tabName = e.target.getAttribute('data-tab'); + const tabName = e.currentTarget.getAttribute('data-tab'); tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); @@ -131,4 +134,41 @@ document.addEventListener('DOMContentLoaded', function() { saveAs: true }); }); + + // 翻译设置相关 + function loadTranslationSettings() { + const translateEnabled = document.getElementById('translateEnabled'); + const targetLang = document.getElementById('targetLang'); + + // 检测操作系统 + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + const shortcutText = isMac ? 'Command+Q' : 'Ctrl+Q'; + + // 更新显示的快捷键文本 + const settingLabel = document.querySelector('.setting-label'); + if (settingLabel) { + settingLabel.textContent = `启用翻译功能 (${shortcutText})`; + } + + // 加载保存的设置 + chrome.storage.sync.get(['translateEnabled', 'targetLang'], function(result) { + translateEnabled.checked = result.translateEnabled !== false; // 默认为 true + targetLang.value = result.targetLang || 'en'; + }); + + // 监听设置变更 + translateEnabled.addEventListener('change', function(e) { + const enabled = e.target.checked; + chrome.storage.sync.set({ translateEnabled: enabled }, function() { + console.log('Translation enabled:', enabled); + }); + }); + + targetLang.addEventListener('change', function(e) { + const lang = e.target.value; + chrome.storage.sync.set({ targetLang: lang }, function() { + console.log('Target language:', lang); + }); + }); + } }); \ No newline at end of file diff --git a/styles.css b/styles.css index 28de6b8..ee72a1a 100644 --- a/styles.css +++ b/styles.css @@ -10,7 +10,7 @@ body { } .container { - width: 380px; + width: 400px; padding: 20px; background: white; box-shadow: 0 2px 10px rgba(0,0,0,0.1); @@ -26,27 +26,30 @@ h1 { .tab { display: flex; - gap: 10px; + gap: 8px; margin-bottom: 20px; background: #f5f5f5; padding: 5px; border-radius: 8px; + min-height: 45px; } .tab button { flex: 1; - padding: 12px; + padding: 8px 4px; border: none; background: transparent; color: #666; cursor: pointer; border-radius: 6px; transition: all 0.3s ease; - font-size: 14px; + font-size: 13px; display: flex; align-items: center; justify-content: center; - gap: 8px; + gap: 4px; + white-space: nowrap; + min-width: 80px; } .tab button:hover { @@ -148,7 +151,8 @@ button:hover { } .icon { - font-size: 18px; + font-size: 16px; + flex-shrink: 0; } /* 美化滚动条 */ @@ -241,4 +245,73 @@ button:hover { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +/* 翻译设置样式 */ +.translate-settings { + padding: 15px; +} + +.setting-item { + margin-bottom: 15px; + display: flex; + align-items: center; +} + +.setting-label { + margin-left: 10px; +} + +/* 开关按钮样式 */ +.switch { + position: relative; + display: inline-block; + width: 50px; + height: 24px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + border-radius: 24px; +} + +.slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:checked + .slider:before { + transform: translateX(26px); +} + +/* 下拉菜单样式 */ +select { + padding: 5px; + border-radius: 4px; + border: 1px solid #ddd; + margin-left: 10px; } \ No newline at end of file