From 52c931e7f581f008b56b05fec76f5411bad8e681 Mon Sep 17 00:00:00 2001 From: Cx330 <1487537121@qq.com> Date: Fri, 2 Jan 2026 21:06:54 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=84=E8=8C=83=E4=BB=A3=E7=A0=81=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands.js | 66 ++++++++++++++++++++++++ content.js | 136 +++----------------------------------------------- main.js | 21 ++++++++ manifest.json | 13 +++-- panel.js | 30 +++++++++++ voice.js | 36 +++++++++++++ 6 files changed, 169 insertions(+), 133 deletions(-) create mode 100644 commands.js create mode 100644 main.js create mode 100644 panel.js create mode 100644 voice.js diff --git a/commands.js b/commands.js new file mode 100644 index 0000000..826c063 --- /dev/null +++ b/commands.js @@ -0,0 +1,66 @@ +export const COMMANDS = [ + { key: "首页", menu: "首页", route: "/首页" }, + { key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" }, + { key: "监控中心", menu: "监控中心", route: "/监控中心/监控中心" }, + { key: "摄像头管理", menu: "摄像头管理", route: "/云视频管理/摄像头管理" }, + { key: "云视频监控", menu: "云视频监控", route: "/云视频监控/云视频监控" }, + { key: "GIS监控", menu: "GIS监控", route: "/GIS管理/GIS监控" }, + { key: "我的流量", menu: "我的流量", route: "/资源管理/我的流量" }, + { key: "我的资源", menu: "我的资源", route: "/资源管理/我的资源" }, + { key: "我的订单", menu: "我的订单", route: "/资源管理/我的订单" }, + { key: "仪表积分", menu: "仪表积分", route: "/资源管理/仪表积分" }, + { key: "报警设置", menu: "报警设置", route: "/报警管理/报警设置" }, + { key: "报警通知", menu: "报警通知", route: "/报警管理/报警通知" }, + { key: "报警记录", menu: "报警记录", route: "/报警管理/报警记录" }, + { key: "基础报表", menu: "基础报表", route: "/报表管理/基础报表" }, + { key: "高级报表配置", menu: "高级报表配置", route: "/报表管理/高级报表配置" }, + { key: "高级报表", menu: "高级报表", route: "/报表管理/高级报表" }, + { key: "应用场景", menu: "应用场景", route: "/仪表管理/应用场景" }, + { key: "仪表管理", menu: "仪表管理", route: "/仪表管理/仪表管理" }, + { key: "虚拟仪表", menu: "虚拟仪表", route: "/仪表管理/虚拟仪表" }, + { key: "物位监测", menu: "物位监测", route: "/场景管理/物位监测/物位监测" }, + { key: "物位监测配置", menu: "物位监测配置", route: "/场景管理/物位监测/物位监测配置" }, + { key: "车间看板", menu: "车间看板", route: "/场景管理/车间看板/车间看板" }, + { key: "车间看板配置", menu: "车间看板配置", route: "/场景管理/车间看板/车间看板配置" }, + { key: "能源结算", menu: "能源结算", route: "/场景管理/能源抄表/能源结算" }, + { key: "能源结算配置", menu: "能源结算配置", route: "/场景管理/能源抄表/能源结算配置" }, + { key: "多租户能源结算", menu: "多租户能源结算", route: "/场景管理/多租户结算/多租户能源结算" }, + { key: "租户管理", menu: "租户管理", route: "/场景管理/多租户结算/租户管理" }, + { key: "计价方式管理", menu: "计价方式管理", route: "/场景管理/多租户结算/计价方式管理" }, + { key: "单染缸印染结算", menu: "单染缸印染结算", route: "/场景管理/印染结算/单染缸印染结算" }, + { key: "多染缸印染结算", menu: "多染缸印染结算", route: "/场景管理/印染结算/多染缸印染结算" }, + { key: "染缸能耗一览表", menu: "染缸能耗一览表", route: "/场景管理/印染结算/染缸能耗一览表" }, + { key: "印染结算配置", menu: "印染结算配置", route: "/场景管理/印染结算/印染结算配置" }, + { key: "消息管理", menu: "消息管理", route: "/系统管理/消息管理" }, + { key: "数据服务", menu: "数据服务", route: "/系统管理/数据服务" }, + { key: "数据下云", menu: "数据下云", route: "/系统管理/数据下云" }, +]; + +export function expandParentMenu(span) { + const subMenu = span.closest(".el-sub-menu"); + if (subMenu) { + const title = subMenu.querySelector(".el-sub-menu__title"); + if (title && !subMenu.classList.contains("is-opened")) { + title.click(); + console.log("📂 已展开父菜单"); + } + } +} + +export function goToRoute(route) { + window.location.hash = route; + console.log("✅ 已跳转到路由:", route); +} + +export function handleCommand(text) { + console.log("📥 收到指令:", text); + const command = COMMANDS.find(c => text.includes(c.key)); + if (!command) { + alert("未识别指令:" + text); + return; + } + const span = [...document.querySelectorAll("span")] + .find(el => el.innerText.trim() === command.menu); + span && expandParentMenu(span); + goToRoute(command.route); +} \ No newline at end of file diff --git a/content.js b/content.js index abd8141..b05c89f 100644 --- a/content.js +++ b/content.js @@ -1,131 +1,7 @@ -console.log("✅ 语音 + 文字插件已运行"); +// content.js 是 content script 的入口,不直接写全部逻辑 +// 用 module 方式加载其他文件 -// ========= 1. 浏览器能力检测 ========= -const SpeechRecognition = - window.SpeechRecognition || window.webkitSpeechRecognition; - -const supportSpeech = !!SpeechRecognition; - -// ========= 2. 创建控制面板 ========= -const panel = document.createElement("div"); -panel.style.cssText = ` - position: fixed; - bottom: 24px; - right: 24px; - z-index: 999999; - background: white; - border-radius: 10px; - box-shadow: 0 4px 12px rgba(0,0,0,.15); - padding: 10px; - width: 220px; - font-size: 14px; -`; - -panel.innerHTML = ` -
- - -
-
- 支持语音 / 回车文字指令 -
-`; - -document.body.appendChild(panel); - -// ========= 3. 父菜单展开函数 ========= -function expandParentMenu(span) { - const subMenu = span.closest(".el-sub-menu"); - if (subMenu) { - const title = subMenu.querySelector(".el-sub-menu__title"); - if (title && !subMenu.classList.contains("is-opened")) { - title.click(); - console.log("📂 已展开父菜单"); - } - } -} - -// ========= 4. 路由跳转函数 ========= -function goToRoute(route) { - window.location.hash = route; - console.log("✅ 已跳转到路由:", route); -} - -// ========= 5. 指令配置 ========= -const COMMANDS = [ - { key: "首页", menu: "首页", route: "/首页" }, - { key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" }, - { key: "监控中心", menu: "监控中心", route: "/监控中心/监控中心" }, -]; - -// ========= 6. 指令解析(文字 & 语音共用) ========= -function handleCommand(text) { - console.log("📥 收到指令:", text); - - const command = COMMANDS.find(c => text.includes(c.key)); - - if (!command) { - alert("未识别指令:" + text); - return; - } - - // 找菜单 DOM 并展开父菜单 - const span = [...document.querySelectorAll("span")] - .find(el => el.innerText.trim() === command.menu); - - span && expandParentMenu(span); - - // 路由跳转 - goToRoute(command.route); -} - -// ========= 7. 文字输入支持 ========= -const input = panel.querySelector("#voiceTextInput"); -input.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - const value = input.value.trim(); - if (value) { - handleCommand(value); - input.value = ""; - } - } -}); - -// ========= 8. 语音识别支持 ========= -let recognition; -if (supportSpeech) { - recognition = new SpeechRecognition(); - recognition.lang = "zh-CN"; - recognition.continuous = false; - recognition.interimResults = false; - - recognition.onresult = (event) => { - const text = event.results[0][0].transcript.trim(); - handleCommand(text); - }; - - recognition.onerror = (event) => { - console.error("语音识别错误", event.error); - }; - - panel.querySelector("#voiceBtn").onclick = () => { - recognition.start(); - }; -} else { - panel.querySelector("#voiceBtn").disabled = true; - panel.querySelector("#voiceBtn").innerText = "❌"; -} - -// ========= 9. 快捷键 Alt+V 支持语音 ========= -document.addEventListener("keydown", (e) => { - if (e.altKey && e.key.toLowerCase() === "v") { - if (supportSpeech && recognition) recognition.start(); - } -}); +const script = document.createElement('script'); +script.type = 'module'; +script.src = chrome.runtime.getURL('main.js'); +document.head.appendChild(script); \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..9e3f4ad --- /dev/null +++ b/main.js @@ -0,0 +1,21 @@ +import { createPanel } from './panel.js'; +import { handleCommand } from './commands.js'; +import { initVoice } from './voice.js'; + +console.log("✅ 语音 + 文字插件已运行"); + +const panel = createPanel(); + +// 文字输入支持 +const input = panel.querySelector("#voiceTextInput"); +input.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + const value = input.value.trim(); + if (value) { + handleCommand(value); + input.value = ""; + } + } +}); + +initVoice(panel, handleCommand); \ No newline at end of file diff --git a/manifest.json b/manifest.json index e010519..14e167b 100644 --- a/manifest.json +++ b/manifest.json @@ -1,12 +1,19 @@ { "manifest_version": 3, "name": "语音界面跳转", - "version": "1.0", + "version": "0.0.1", "content_scripts": [ { "matches": ["*://1718cloud.com/*"], - "js": ["content.js"] + "js": ["content.js"], + "run_at": "document_idle" } ], - "permissions": ["activeTab"] + "permissions": ["activeTab"], + "web_accessible_resources": [ + { + "resources": ["*.js"], + "matches": ["*://1718cloud.com/*"] + } + ] } diff --git a/panel.js b/panel.js new file mode 100644 index 0000000..f225dfc --- /dev/null +++ b/panel.js @@ -0,0 +1,30 @@ +export function createPanel() { + const panel = document.createElement("div"); + panel.style.cssText = ` + position: fixed; + bottom: 24px; + right: 24px; + z-index: 999999; + background: white; + border-radius: 10px; + box-shadow: 0 4px 12px rgba(0,0,0,.15); + padding: 10px; + width: 220px; + font-size: 14px; + `; + panel.innerHTML = ` +
+ + +
+
+ 支持语音 / 回车文字指令 +
+ `; + document.body.appendChild(panel); + return panel; +} \ No newline at end of file diff --git a/voice.js b/voice.js new file mode 100644 index 0000000..48100e9 --- /dev/null +++ b/voice.js @@ -0,0 +1,36 @@ +// 语音识别 + +export function initVoice(panel, handleCommand) { + const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; + const supportSpeech = !!SpeechRecognition; + let recognition; + + if (supportSpeech) { + recognition = new SpeechRecognition(); + recognition.lang = "zh-CN"; + recognition.continuous = false; + recognition.interimResults = false; + + recognition.onresult = (event) => { + const text = event.results[0][0].transcript.trim(); + handleCommand(text); + }; + + recognition.onerror = (event) => { + console.error("语音识别错误", event.error); + }; + + panel.querySelector("#voiceBtn").onclick = () => recognition.start(); + } else { + panel.querySelector("#voiceBtn").disabled = true; + panel.querySelector("#voiceBtn").innerText = "❌"; + } + + document.addEventListener("keydown", (e) => { + if (e.altKey && e.key.toLowerCase() === "v" && supportSpeech && recognition) { + recognition.start(); + } + }); + + return supportSpeech; +} \ No newline at end of file