diff --git a/background/background.js b/background/background.js index a5ed3f9..86b431b 100644 --- a/background/background.js +++ b/background/background.js @@ -1,9 +1,7 @@ -// background/background.js - // 监听来自 content.js 的消息转发 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "AI_TRANSLATE") { - // 1. 同时获取 AI 配置信息和语音开关状态 + // 获取 AI 配置信息和语音开关状态 chrome.storage.sync.get(['aiConfig', 'voiceEnabled'], async (result) => { const config = result.aiConfig; const voiceEnabled = result.voiceEnabled || false; // 默认为关闭 @@ -14,7 +12,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { } try { - // 2. 向 AI 平台发起 fetch 请求 + // 向 AI 平台发起 fetch 请求 const response = await fetch(`${config.apiUrl}/chat/completions`, { method: 'POST', headers: { @@ -38,7 +36,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { const data = await response.json(); - // 3. 将 AI 结果和语音开关状态一并返回给 content.js + // 将 AI 结果和语音开关状态返回给 content.js sendResponse({ success: true, data: data, @@ -51,7 +49,6 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { } }); - // 返回 true 表示我们将异步发送响应 return true; } }); \ No newline at end of file diff --git a/content.js b/content.js index 833a27e..72cd1cf 100644 --- a/content.js +++ b/content.js @@ -3,7 +3,7 @@ script.type = 'module'; script.src = chrome.runtime.getURL('main.js'); document.head.appendChild(script); -// 监听来自网页(main.js)的请求 +// 监听来自 main.js 的请求 window.addEventListener("DO_AI_REQUEST", async (event) => { const { userInput, systemPrompt } = event.detail; @@ -13,7 +13,7 @@ window.addEventListener("DO_AI_REQUEST", async (event) => { userInput, systemPrompt }, (response) => { - // 将结果传回给网页(main.js) + // 将结果传回给 main.js window.dispatchEvent(new CustomEvent("AI_RESULT", { detail: response })); }); }); \ No newline at end of file diff --git a/main.js b/main.js index 95893e7..3a1e0cf 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,3 @@ -// main.js import { createPanel } from './scripts/panel.js'; import { handleCommand, COMMANDS } from './scripts/commands.js'; import { initVoice } from './scripts/voice.js'; @@ -9,29 +8,24 @@ const init = async () => { let spaceTimer = null; let isRecording = false; - // 统一定义 UI 更新引用,方便 handleCommand 调用 + // 统一定义 UI 更新引用 const uiRefs = { updateStatus: (text) => { const statusText = document.getElementById("statusText"); if (statusText) { statusText.innerText = text; - statusText.style.color = "#409eff"; // 使用蓝色区分对话与就绪状态 + statusText.style.color = "#409eff"; } } }; - /** - * 处理 AI 流程的主函数 - */ async function startProcess(text) { if (!text) return; ui.setLoading(true); try { - // 获取 AI 响应结果(包含内容和语音开关状态) const aiResponse = await translateToCommand(text); if (aiResponse && aiResponse.content) { - // 将内容、UI 引用以及语音开关状态传给指令处理器 handleCommand(aiResponse.content, uiRefs, aiResponse.voiceEnabled); } else { uiRefs.updateStatus("未识别到有效指令"); @@ -67,7 +61,6 @@ const init = async () => { voiceCtrl.start(); ui.setRecording(true); isRecording = true; - // 4秒自动停止录音保护 setTimeout(() => { if (isRecording) { voiceCtrl.stop(); @@ -82,18 +75,17 @@ const init = async () => { } }; - // 空格长按触发录音逻辑 + // 空格长按逻辑 window.addEventListener("keydown", (e) => { if (e.code === "Space" && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") { if (spaceTimer || isRecording) return; - spaceTimer = setTimeout(() => { if (voiceCtrl.supportSpeech) { voiceCtrl.start(); ui.setRecording(true); isRecording = true; } - }, 500); // 判定为长按 + }, 500); } }); @@ -104,7 +96,6 @@ const init = async () => { spaceTimer = null; } if (isRecording) { - // 延迟停止以捕捉最后一段语音 setTimeout(() => { voiceCtrl.stop(); ui.setRecording(false); @@ -115,7 +106,6 @@ const init = async () => { }); }; -// 启动初始化 if (document.readyState === "complete" || document.readyState === "interactive") { init(); } else { diff --git a/manifest.json b/manifest.json index 3c294e9..7debc83 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, - "name": "Supmea Automation AI", - "version": "0.1.0", + "name": "Agent_For_Supmea", + "version": "0.1.17", "permissions": ["activeTab", "storage"], "background": { "service_worker": "background/background.js" diff --git a/popup/popup.html b/popup/popup.html index 6c7960d..0da7708 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -30,7 +30,7 @@ -

⚙️ AI 模型配置

+

AI 模型配置

diff --git a/popup/popup.js b/popup/popup.js index b315933..5400b15 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -1,15 +1,14 @@ // 页面加载时读取存储的配置 document.addEventListener('DOMContentLoaded', () => { - // 同时获取 aiConfig 和 voiceEnabled 状态 + chrome.storage.sync.get(['aiConfig', 'voiceEnabled'], (result) => { const config = result.aiConfig || {}; - // 填充 API 配置 document.getElementById('apiUrl').value = config.apiUrl || DEFAULT_URL; document.getElementById('apiKey').value = config.apiKey || ""; document.getElementById('modelName').value = config.modelName || DEFAULT_MODEL; - // 填充语音开关状态(默认为关闭 false) + // 语音开关状态(默认为关闭) document.getElementById('voiceEnabled').checked = result.voiceEnabled || false; }); }); @@ -26,7 +25,7 @@ document.getElementById('save').addEventListener('click', () => { // 验证 API Key 是否填写 if (!config.apiKey) { - showStatus("❌ 请输入 API Key", "#f56c6c"); + showStatus("请输入 API Key", "#f56c6c"); return; } @@ -39,7 +38,6 @@ document.getElementById('save').addEventListener('click', () => { }); }); -// 状态提示函数 function showStatus(text, color) { const status = document.getElementById('status'); status.textContent = text; diff --git a/scripts/ai.js b/scripts/ai.js index c4da5d8..124921d 100644 --- a/scripts/ai.js +++ b/scripts/ai.js @@ -1,10 +1,9 @@ -// scripts/ai.js import { COMMANDS } from './commands.js'; export async function translateToCommand(userInput) { const availableKeys = COMMANDS.map(c => c.key).join(', '); - const systemPrompt = `你是一个工业自动化系统助手。 + const systemPrompt = `你是美小智,是仪表云平台的自动化小助手。 标准指令列表:[${availableKeys}] 任务规则: @@ -12,7 +11,6 @@ export async function translateToCommand(userInput) { 2. **意图识别**:如果用户意图匹配指令列表,输出 标准指令。如有参数(如设备号),输出 参数。 3. **示例**: - 用户:“打开监控中心” -> “好的,正在为您进入监控中心。监控中心” - - 用户:“你是谁” -> “我是您的自动化 AI 助手,可以帮您操作平台。” - 用户:“添加设备 888” -> “没问题,正在为您添加编号为 888 的设备。添加设备888” 4. 只输出 XML 结果,不要输出任何解释说明,保持简洁。`; @@ -30,7 +28,7 @@ export async function translateToCommand(userInput) { // 返回包含内容和语音开关状态的对象 resolve({ content: content === "UNKNOWN" ? null : content, - voiceEnabled: response.voiceEnabled // 从 background.js 透传回来的开关状态 + voiceEnabled: response.voiceEnabled // 从 background.js 透传回来的开关状态 }); } else { resolve(null); diff --git a/scripts/commands.js b/scripts/commands.js index d677e77..4d5798d 100644 --- a/scripts/commands.js +++ b/scripts/commands.js @@ -1,6 +1,3 @@ -// scripts/commands.js - -// 标准指令路由映射表 export const COMMANDS = [ { key: "首页", menu: "首页", route: "/首页" }, { key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" }, @@ -16,15 +13,32 @@ export const COMMANDS = [ { 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: "/系统管理/消息管理" }, + { key: "数据服务", menu: "数据服务", route: "/系统管理/数据服务" }, + { key: "数据下云", menu: "数据下云", route: "/系统管理/数据下云" }, ]; -/** - * 展开侧边栏父级菜单 - */ function expandParentMenu(span) { let parent = span.closest('li'); while (parent) { @@ -38,42 +52,52 @@ function expandParentMenu(span) { } } -/** - * 自动填充并提交搜索(模拟针对某些页面的操作) - */ function autoFillAndSubmit(arg) { if (!arg) return; + // 等待页面跳转和组件渲染 setTimeout(() => { - const input = document.querySelector('input[placeholder*="名称"], input[placeholder*="编号"]'); + // 尝试寻找各种可能的输入框(针对 Element UI) + const input = document.querySelector('input[placeholder*="编号"]') || + document.querySelector('input[placeholder*="名称"]') || + document.querySelector('.el-input__inner'); + if (input) { input.value = arg; + // 触发 input 事件让 Vue 监听到数据变化 input.dispatchEvent(new Event('input', { bubbles: true })); - const searchBtn = document.querySelector('button.el-button--primary'); - if (searchBtn) searchBtn.click(); + + // 寻找包含“添加”、“查询” + setTimeout(() => { + const buttons = Array.from(document.querySelectorAll('button')); + const submitBtn = buttons.find(btn => + btn.innerText.includes("添加") || + btn.innerText.includes("查询") || + btn.innerText.includes("确认") || + btn.classList.contains('el-button--primary') + ); + + if (submitBtn) { + submitBtn.click(); + console.log("已自动点击按钮:", submitBtn.innerText); + } + }, 500); } - }, 1000); + }, 1200); } -/** - * 核心处理器:根据 AI 结果执行动作 - * @param {string} aiResult AI 返回的 XML 字符串 - * @param {object} uiRefs UI 更新引用的对象 - * @param {boolean} voiceEnabled 是否允许播放语音 (由 ai.js 传入) - */ export function handleCommand(aiResult, uiRefs, voiceEnabled = false) { - if (!aiResult || aiResult === "UNKNOWN") return; - // 1. 解析对话内容并反馈 + if (typeof aiResult !== 'string' || aiResult === "UNKNOWN") return; + + // 解析对话内容 const commMatch = aiResult.match(/([\s\S]*?)<\/communication>/); if (commMatch && commMatch[1]) { const speechText = commMatch[1].trim(); - - // 始终更新 UI 界面上的文字状态 if (uiRefs && uiRefs.updateStatus) { uiRefs.updateStatus(speechText); } - // 仅在语音开关开启时播报语音 + // 语音播放判断 if (voiceEnabled) { const utterance = new SpeechSynthesisUtterance(speechText); utterance.lang = "zh-CN"; @@ -81,7 +105,7 @@ export function handleCommand(aiResult, uiRefs, voiceEnabled = false) { } } - // 2. 解析指令逻辑 + // 解析指令和参数 const cmdMatch = aiResult.match(/([\s\S]*?)<\/cmd>/); const argMatch = aiResult.match(/([\s\S]*?)<\/arg>/); @@ -89,14 +113,11 @@ export function handleCommand(aiResult, uiRefs, voiceEnabled = false) { const arg = argMatch ? argMatch[1].trim() : null; if (key) { - // 模糊匹配指令 const command = COMMANDS.find(c => c.key === key) || COMMANDS.find(c => key.includes(c.key)); if (command) { - console.log("🚀 执行指令:", command.key, "参数:", arg); - - // 尝试点击侧边栏菜单(针对 Element UI 结构) + const allSpans = Array.from(document.querySelectorAll("span")); let span = allSpans.find(el => el.innerText.trim() === command.menu); @@ -105,15 +126,13 @@ export function handleCommand(aiResult, uiRefs, voiceEnabled = false) { span.click(); } - // 路由跳转 + // 执行跳转 window.location.hash = command.route; - // 自动填充搜索参数 + // 如果有参数(如设备编号),执行自动填充 if (arg) { autoFillAndSubmit(arg); } - } else { - console.warn("⚠️ 未找到匹配指令:", key); } } } \ No newline at end of file diff --git a/scripts/panel.js b/scripts/panel.js index b924575..438fa3a 100644 --- a/scripts/panel.js +++ b/scripts/panel.js @@ -1,4 +1,3 @@ -// scripts/panel.js export function createPanel() { const panelId = "automation-ai-panel"; let panel = document.getElementById(panelId); diff --git a/scripts/voice.js b/scripts/voice.js index 5e9de82..0dad37e 100644 --- a/scripts/voice.js +++ b/scripts/voice.js @@ -1,17 +1,16 @@ -// scripts/voice.js export function initVoice(panel, onResultCallback) { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) return { supportSpeech: false }; const recognition = new SpeechRecognition(); recognition.lang = "zh-CN"; - recognition.continuous = false; // 改为 false,确保每次停止都能立即结算 + recognition.continuous = false; recognition.interimResults = false; recognition.onresult = (event) => { const text = event.results[0][0].transcript.trim(); if (text) { - console.log("🎤 识别结果:", text); + console.log("识别结果:", text); onResultCallback(text); } };