From c43b5e5ac1f547615f69295375dc7ebccc7e81dc Mon Sep 17 00:00:00 2001 From: Cx330 <1487537121@qq.com> Date: Thu, 15 Jan 2026 22:49:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0AI=E6=96=87=E6=9C=AC=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 34 +++++++++++++------- scripts/ai.js | 16 +++++----- scripts/commands.js | 77 ++++++++++++++++++++------------------------- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/main.js b/main.js index d8a12f7..755175d 100644 --- a/main.js +++ b/main.js @@ -9,17 +9,30 @@ const init = async () => { let spaceTimer = null; let isRecording = false; + // 统一定义 UI 更新引用,方便 handleCommand 调用 + const uiRefs = { + updateStatus: (text) => { + const statusText = document.getElementById("statusText"); + if (statusText) { + statusText.innerText = text; + statusText.style.color = "#409eff"; // 使用蓝色区分对话与就绪状态 + } + } + }; + async function startProcess(text) { if (!text) return; ui.setLoading(true); try { - const localMatch = COMMANDS.find(c => text.includes(c.key)); - if (localMatch) { - handleCommand(localMatch.key); - } else { - const aiResult = await translateToCommand(text); - if (aiResult) handleCommand(aiResult); + // 无论是否本地匹配,统一走 translateToCommand 以获取对话回复 + // 如果你希望本地极速响应,可以在此保留逻辑,但建议统一走 AI 获取 + const aiResult = await translateToCommand(text); + if (aiResult) { + handleCommand(aiResult, uiRefs); } + } catch (err) { + console.error("处理流程错误:", err); + uiRefs.updateStatus("处理指令时出错"); } finally { ui.setLoading(false); } @@ -30,24 +43,23 @@ const init = async () => { startProcess(text); }); - // --- 逻辑 A: 保留原有按钮点击录音 --- + // 按钮触发录音 ui.btn.onclick = () => { if (!isRecording) { voiceCtrl.start(); ui.setRecording(true); isRecording = true; - // 按钮模式下 3秒后自动停止,或靠 recognition 自动结束 setTimeout(() => { if (isRecording) { voiceCtrl.stop(); ui.setRecording(false); isRecording = false; } - }, 3000); + }, 4000); // 按钮模式延长到 4s,确保说话完整 } }; - // --- 逻辑 B: 空格长按 0.5s 触发 --- + // 空格长按触发录音 window.addEventListener("keydown", (e) => { if (e.code === "Space" && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") { if (spaceTimer || isRecording) return; @@ -69,7 +81,6 @@ const init = async () => { spaceTimer = null; } if (isRecording) { - // 松开空格,延迟一小会儿停止,确保最后几个字能录进去 setTimeout(() => { voiceCtrl.stop(); ui.setRecording(false); @@ -80,6 +91,7 @@ const init = async () => { } }); + // 输入框回车触发 ui.input.onkeydown = (e) => { if (e.key === "Enter") { const val = ui.input.value; diff --git a/scripts/ai.js b/scripts/ai.js index edc1c52..5d5fad7 100644 --- a/scripts/ai.js +++ b/scripts/ai.js @@ -8,15 +8,13 @@ export async function translateToCommand(userInput) { 标准指令列表:[${availableKeys}] 任务规则: -1. 识别用户意图并按以下 XML 格式输出: - - 基础格式:标准指令 - - 带参数格式(如设备号、序列号):标准指令参数内容 -2. 示例: - - “帮我添加一台编号为20260114的设备” -> 输出 “添加设备20260114” - - “打开监控中心” -> 输出 “监控中心” -3. “主界面”、“主页”映射为“首页”。 -4. 只输出 XML 结果,不要输出任何解释说明或标点。 -5. 无法匹配请输出 UNKNOWN。`; +1. **必须包含对话**:无论是否执行指令,你都必须在 XXXX 标签中放入你对用户说的话。 +2. **意图识别**:如果用户意图匹配指令列表,输出 标准指令。如有参数(如设备号),输出 参数。 +3. **示例**: + - 用户:“打开监控中心” -> “好的,正在为您进入监控中心。监控中心” + - 用户:“你是谁” -> “我是您的自动化 AI 助手,可以帮您操作平台。” + - 用户:“添加设备 888” -> “没问题,正在为您添加编号为 888 的设备。添加设备888” +4. 只输出 XML 结果,不要输出任何解释说明,保持简洁。`; return new Promise((resolve) => { const handler = (event) => { diff --git a/scripts/commands.js b/scripts/commands.js index 987f38e..5a48f9c 100644 --- a/scripts/commands.js +++ b/scripts/commands.js @@ -1,3 +1,4 @@ +// scripts/commands.js export const COMMANDS = [ { key: "首页", menu: "首页", route: "/首页" }, { key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" }, @@ -53,28 +54,16 @@ function waitForElement(selector, callback, timeout = 5000) { }, 200); } -/** - * 自动化填写和点击逻辑 - */ function autoFillAndSubmit(value) { - // 1. 等待输入框出现 (针对 Element Plus 使用 .el-input__inner) - // 提示:如果有多个框,可改用 'input[placeholder*="上云码"]' 增加精度 waitForElement('.el-input__inner', (input) => { - console.log("🔍 已定位输入框,开始填充:", value); - - // 填写内容 input.value = value; - - // 关键:手动触发事件以同步 Vue 的双向绑定 (v-model) input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); - // 2. 自动点击“确认”按钮 (通常类名为 el-button--primary) const submitBtn = document.querySelector('.el-button--primary'); if (submitBtn) { setTimeout(() => { submitBtn.click(); - console.log("✅ 自动化流程已触发提交"); }, 600); } }); @@ -88,48 +77,50 @@ export function expandParentMenu(span) { } } -export function handleCommand(aiResult) { +/** + * 处理 AI 返回的混合指令 (对话 + 操作) + */ +export function handleCommand(aiResult, uiRefs) { if (!aiResult || aiResult === "UNKNOWN") return; - console.log("🚀 收到结构化指令:", aiResult); - // 使用正则解析 XML 标签内容 + // 1. 解析对话内容并反馈 + const commMatch = aiResult.match(/([\s\S]*?)<\/communication>/); + if (commMatch && commMatch[1]) { + const speechText = commMatch[1].trim(); + // 更新 UI 状态 + if (uiRefs && uiRefs.updateStatus) { + uiRefs.updateStatus(speechText); + } + // 语音播报 + const utterance = new SpeechSynthesisUtterance(speechText); + utterance.lang = "zh-CN"; + window.speechSynthesis.speak(utterance); + } + + // 2. 解析指令逻辑 const cmdMatch = aiResult.match(/([\s\S]*?)<\/cmd>/); const argMatch = aiResult.match(/([\s\S]*?)<\/arg>/); const key = cmdMatch ? cmdMatch[1].trim() : null; const arg = argMatch ? argMatch[1].trim() : null; - if (!key) { - console.warn("未能解析出有效的 标签"); - return; - } + if (key) { + const command = COMMANDS.find(c => c.key === key) || + COMMANDS.find(c => key.includes(c.key)); - // 在命令库中寻找匹配项 - const command = COMMANDS.find(c => c.key === key) || - COMMANDS.find(c => key.includes(c.key)); + if (command) { + const allSpans = Array.from(document.querySelectorAll("span")); + let span = allSpans.find(el => el.innerText.trim() === command.menu); - if (command) { - // 第一步:查找菜单并跳转 - const allSpans = Array.from(document.querySelectorAll("span")); - let span = allSpans.find(el => el.innerText.trim() === command.menu); - - if (span) { - expandParentMenu(span); - span.click(); - window.location.hash = command.route; - - // 第二步:如果有参数(如设备号),启动后续填充流程 - if (arg) { - console.log(`⏳ 检测到参数,准备自动填充: ${arg}`); - autoFillAndSubmit(arg); + if (span) { + expandParentMenu(span); + span.click(); + window.location.hash = command.route; + if (arg) autoFillAndSubmit(arg); + } else { + window.location.hash = command.route; + if (arg) autoFillAndSubmit(arg); } - } else { - // 容错:如果没找到菜单元素,依然尝试直接跳转路由 - console.log("未发现菜单 DOM,尝试直接通过路由跳转"); - window.location.hash = command.route; - if (arg) autoFillAndSubmit(arg); } - } else { - console.warn("未匹配到标准库指令:", key); } } \ No newline at end of file