默认关闭语音播放功能
This commit is contained in:
parent
c43b5e5ac1
commit
1b8f833789
@ -1,23 +1,20 @@
|
|||||||
// background.js
|
// background/background.js
|
||||||
|
|
||||||
// 监听来自 main.js 的消息
|
// 监听来自 content.js 的消息转发
|
||||||
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
|
|
||||||
// 这种方式需要知道 Extension ID,更简单的方法是统一由 content.js 转发
|
|
||||||
});
|
|
||||||
|
|
||||||
// 通用监听(推荐)
|
|
||||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
if (request.type === "AI_TRANSLATE") {
|
if (request.type === "AI_TRANSLATE") {
|
||||||
// 1. 先从 storage 获取配置
|
// 1. 同时获取 AI 配置信息和语音开关状态
|
||||||
chrome.storage.sync.get(['aiConfig'], async (result) => {
|
chrome.storage.sync.get(['aiConfig', 'voiceEnabled'], async (result) => {
|
||||||
const config = result.aiConfig;
|
const config = result.aiConfig;
|
||||||
|
const voiceEnabled = result.voiceEnabled || false; // 默认为关闭
|
||||||
|
|
||||||
if (!config || !config.apiKey) {
|
if (!config || !config.apiKey) {
|
||||||
sendResponse({ success: false, error: "未配置 API Key" });
|
sendResponse({ success: false, error: "请先在扩展图标中配置 API Key" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. 发起请求
|
// 2. 向 AI 平台发起 fetch 请求
|
||||||
const response = await fetch(`${config.apiUrl}/chat/completions`, {
|
const response = await fetch(`${config.apiUrl}/chat/completions`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -34,12 +31,27 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.error?.message || "网络请求失败");
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
sendResponse({ success: true, data: data });
|
|
||||||
|
// 3. 将 AI 结果和语音开关状态一并返回给 content.js
|
||||||
|
sendResponse({
|
||||||
|
success: true,
|
||||||
|
data: data,
|
||||||
|
voiceEnabled: voiceEnabled
|
||||||
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error("AI Request Error:", err);
|
||||||
sendResponse({ success: false, error: err.message });
|
sendResponse({ success: false, error: err.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true; // 保持异步
|
|
||||||
|
// 返回 true 表示我们将异步发送响应
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
64
main.js
64
main.js
@ -20,15 +20,21 @@ const init = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 AI 流程的主函数
|
||||||
|
*/
|
||||||
async function startProcess(text) {
|
async function startProcess(text) {
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
ui.setLoading(true);
|
ui.setLoading(true);
|
||||||
try {
|
try {
|
||||||
// 无论是否本地匹配,统一走 translateToCommand 以获取对话回复
|
// 获取 AI 响应结果(包含内容和语音开关状态)
|
||||||
// 如果你希望本地极速响应,可以在此保留逻辑,但建议统一走 AI 获取 <communication>
|
const aiResponse = await translateToCommand(text);
|
||||||
const aiResult = await translateToCommand(text);
|
|
||||||
if (aiResult) {
|
if (aiResponse && aiResponse.content) {
|
||||||
handleCommand(aiResult, uiRefs);
|
// 将内容、UI 引用以及语音开关状态传给指令处理器
|
||||||
|
handleCommand(aiResponse.content, uiRefs, aiResponse.voiceEnabled);
|
||||||
|
} else {
|
||||||
|
uiRefs.updateStatus("未识别到有效指令");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("处理流程错误:", err);
|
console.error("处理流程错误:", err);
|
||||||
@ -38,28 +44,45 @@ const init = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const voiceCtrl = initVoice(document.getElementById("automation-ai-panel"), (text) => {
|
// 初始化语音识别模块
|
||||||
|
const voiceCtrl = initVoice(ui, (text) => {
|
||||||
|
ui.setRecording(false);
|
||||||
|
isRecording = false;
|
||||||
ui.input.value = text;
|
ui.input.value = text;
|
||||||
startProcess(text);
|
startProcess(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 按钮触发录音
|
// 输入框回车触发
|
||||||
|
ui.input.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
const text = ui.input.value.trim();
|
||||||
|
ui.input.value = "";
|
||||||
|
startProcess(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按钮点击触发录音
|
||||||
ui.btn.onclick = () => {
|
ui.btn.onclick = () => {
|
||||||
if (!isRecording) {
|
if (!isRecording) {
|
||||||
voiceCtrl.start();
|
voiceCtrl.start();
|
||||||
ui.setRecording(true);
|
ui.setRecording(true);
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
|
// 4秒自动停止录音保护
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
voiceCtrl.stop();
|
voiceCtrl.stop();
|
||||||
ui.setRecording(false);
|
ui.setRecording(false);
|
||||||
isRecording = false;
|
isRecording = false;
|
||||||
}
|
}
|
||||||
}, 4000); // 按钮模式延长到 4s,确保说话完整
|
}, 4000);
|
||||||
|
} else {
|
||||||
|
voiceCtrl.stop();
|
||||||
|
ui.setRecording(false);
|
||||||
|
isRecording = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 空格长按触发录音
|
// 空格长按触发录音逻辑
|
||||||
window.addEventListener("keydown", (e) => {
|
window.addEventListener("keydown", (e) => {
|
||||||
if (e.code === "Space" && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
if (e.code === "Space" && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
||||||
if (spaceTimer || isRecording) return;
|
if (spaceTimer || isRecording) return;
|
||||||
@ -70,7 +93,7 @@ const init = async () => {
|
|||||||
ui.setRecording(true);
|
ui.setRecording(true);
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500); // 判定为长按
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,25 +104,20 @@ const init = async () => {
|
|||||||
spaceTimer = null;
|
spaceTimer = null;
|
||||||
}
|
}
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
|
// 延迟停止以捕捉最后一段语音
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
voiceCtrl.stop();
|
voiceCtrl.stop();
|
||||||
ui.setRecording(false);
|
ui.setRecording(false);
|
||||||
isRecording = false;
|
isRecording = false;
|
||||||
}, 200);
|
}, 300);
|
||||||
e.preventDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 输入框回车触发
|
|
||||||
ui.input.onkeydown = (e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
const val = ui.input.value;
|
|
||||||
ui.input.value = "";
|
|
||||||
startProcess(val);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (document.readyState === "complete") init();
|
// 启动初始化
|
||||||
else window.addEventListener("load", init);
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
window.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
||||||
@ -13,6 +13,20 @@
|
|||||||
.btn:hover { background: #66b1ff; }
|
.btn:hover { background: #66b1ff; }
|
||||||
#status { font-size: 12px; text-align: center; margin-top: 8px; height: 14px; }
|
#status { font-size: 12px; text-align: center; margin-top: 8px; height: 14px; }
|
||||||
.note { font-size: 11px; color: #999; margin-top: 4px; }
|
.note { font-size: 11px; color: #999; margin-top: 4px; }
|
||||||
|
|
||||||
|
/* 语音开关专属样式 */
|
||||||
|
.switch-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.switch-item label { margin-bottom: 0; cursor: pointer; flex: 1; }
|
||||||
|
.switch-item input { width: auto; cursor: pointer; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -21,7 +35,6 @@
|
|||||||
<div class="item">
|
<div class="item">
|
||||||
<label>API Base URL</label>
|
<label>API Base URL</label>
|
||||||
<input type="text" id="apiUrl" placeholder="https://api-inference.modelscope.cn/v1">
|
<input type="text" id="apiUrl" placeholder="https://api-inference.modelscope.cn/v1">
|
||||||
<div class="note">通常使用兼容模式地址</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
@ -31,12 +44,20 @@
|
|||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<label>Model Name</label>
|
<label>Model Name</label>
|
||||||
<input type="text" id="modelName" placeholder="deepseek-ai/DeepSeek-V3.2">
|
<input type="text" id="modelName" placeholder="deepseek-ai/DeepSeek-V3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item switch-item">
|
||||||
|
<label for="voiceEnabled">开启语音回复</label>
|
||||||
|
<input type="checkbox" id="voiceEnabled">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="save" class="btn">保存并生效</button>
|
<button id="save" class="btn">保存并生效</button>
|
||||||
|
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
|
|
||||||
|
<div class="note">提示:保存配置后,请刷新目标页面使设置生效。</div>
|
||||||
|
|
||||||
<script src="popup.js"></script>
|
<script src="popup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,12 +1,16 @@
|
|||||||
// 页面加载时读取存储的配置
|
// 页面加载时读取存储的配置
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
chrome.storage.sync.get(['aiConfig'], (result) => {
|
// 同时获取 aiConfig 和 voiceEnabled 状态
|
||||||
|
chrome.storage.sync.get(['aiConfig', 'voiceEnabled'], (result) => {
|
||||||
const config = result.aiConfig || {};
|
const config = result.aiConfig || {};
|
||||||
|
|
||||||
// 如果没有存储的值,则显示默认占位符或默认值
|
// 填充 API 配置
|
||||||
document.getElementById('apiUrl').value = config.apiUrl || DEFAULT_URL;
|
document.getElementById('apiUrl').value = config.apiUrl || DEFAULT_URL;
|
||||||
document.getElementById('apiKey').value = config.apiKey || "";
|
document.getElementById('apiKey').value = config.apiKey || "";
|
||||||
document.getElementById('modelName').value = config.modelName || DEFAULT_MODEL;
|
document.getElementById('modelName').value = config.modelName || DEFAULT_MODEL;
|
||||||
|
|
||||||
|
// 填充语音开关状态(默认为关闭 false)
|
||||||
|
document.getElementById('voiceEnabled').checked = result.voiceEnabled || false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -18,14 +22,19 @@ document.getElementById('save').addEventListener('click', () => {
|
|||||||
modelName: document.getElementById('modelName').value.trim() || DEFAULT_MODEL
|
modelName: document.getElementById('modelName').value.trim() || DEFAULT_MODEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isVoiceEnabled = document.getElementById('voiceEnabled').checked;
|
||||||
|
|
||||||
// 验证 API Key 是否填写
|
// 验证 API Key 是否填写
|
||||||
if (!config.apiKey) {
|
if (!config.apiKey) {
|
||||||
showStatus("❌ 请输入 API Key", "#f56c6c");
|
showStatus("❌ 请输入 API Key", "#f56c6c");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储到 chrome.storage
|
// 存储到 chrome.storage.sync
|
||||||
chrome.storage.sync.set({ aiConfig: config }, () => {
|
chrome.storage.sync.set({
|
||||||
|
aiConfig: config,
|
||||||
|
voiceEnabled: isVoiceEnabled
|
||||||
|
}, () => {
|
||||||
showStatus("✅ 配置已保存,刷新页面生效", "#67c23a");
|
showStatus("✅ 配置已保存,刷新页面生效", "#67c23a");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -35,6 +44,8 @@ function showStatus(text, color) {
|
|||||||
const status = document.getElementById('status');
|
const status = document.getElementById('status');
|
||||||
status.textContent = text;
|
status.textContent = text;
|
||||||
status.style.color = color;
|
status.style.color = color;
|
||||||
|
|
||||||
|
// 3秒后清除提示
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
status.textContent = '';
|
status.textContent = '';
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|||||||
@ -26,7 +26,12 @@ export async function translateToCommand(userInput) {
|
|||||||
if (response.data.choices && response.data.choices.length > 0) {
|
if (response.data.choices && response.data.choices.length > 0) {
|
||||||
const content = response.data.choices[0].message.content.trim();
|
const content = response.data.choices[0].message.content.trim();
|
||||||
console.log("📥 AI 原始响应:", content);
|
console.log("📥 AI 原始响应:", content);
|
||||||
resolve(content === "UNKNOWN" ? null : content);
|
|
||||||
|
// 返回包含内容和语音开关状态的对象
|
||||||
|
resolve({
|
||||||
|
content: content === "UNKNOWN" ? null : content,
|
||||||
|
voiceEnabled: response.voiceEnabled // 从 background.js 透传回来的开关状态
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
@ -35,11 +40,15 @@ export async function translateToCommand(userInput) {
|
|||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.error("AI 请求失败:", response?.error);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 监听来自 content.js 的结果反馈
|
||||||
window.addEventListener("AI_RESULT", handler);
|
window.addEventListener("AI_RESULT", handler);
|
||||||
|
|
||||||
|
// 触发自定义事件,由 content.js 转发给 background.js
|
||||||
window.dispatchEvent(new CustomEvent("DO_AI_REQUEST", {
|
window.dispatchEvent(new CustomEvent("DO_AI_REQUEST", {
|
||||||
detail: { userInput, systemPrompt }
|
detail: { userInput, systemPrompt }
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
// scripts/commands.js
|
// scripts/commands.js
|
||||||
|
|
||||||
|
// 标准指令路由映射表
|
||||||
export const COMMANDS = [
|
export const COMMANDS = [
|
||||||
{ key: "首页", menu: "首页", route: "/首页" },
|
{ key: "首页", menu: "首页", route: "/首页" },
|
||||||
{ key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" },
|
{ key: "添加设备", menu: "添加设备", route: "/添加设备/添加设备" },
|
||||||
@ -14,88 +16,70 @@ 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: "/场景管理/尘埃粒子/尘埃粒子配置" },
|
|
||||||
{ key: "消息管理", menu: "消息管理", route: "/系统管理/消息管理" },
|
|
||||||
{ key: "数据服务", menu: "数据服务", route: "/系统管理/数据服务" },
|
|
||||||
{ key: "数据下云", menu: "数据下云", route: "/系统管理/数据下云" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function waitForElement(selector, callback, timeout = 5000) {
|
/**
|
||||||
const start = Date.now();
|
* 展开侧边栏父级菜单
|
||||||
const timer = setInterval(() => {
|
*/
|
||||||
const el = document.querySelector(selector);
|
function expandParentMenu(span) {
|
||||||
if (el) {
|
let parent = span.closest('li');
|
||||||
clearInterval(timer);
|
while (parent) {
|
||||||
callback(el);
|
if (parent.classList.contains('el-submenu')) {
|
||||||
} else if (Date.now() - start > timeout) {
|
const title = parent.querySelector('.el-submenu__title');
|
||||||
clearInterval(timer);
|
if (title && !parent.classList.contains('is-opened')) {
|
||||||
console.warn("等待元素超时:", selector);
|
title.click();
|
||||||
}
|
}
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
function autoFillAndSubmit(value) {
|
|
||||||
waitForElement('.el-input__inner', (input) => {
|
|
||||||
input.value = value;
|
|
||||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
||||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
||||||
|
|
||||||
const submitBtn = document.querySelector('.el-button--primary');
|
|
||||||
if (submitBtn) {
|
|
||||||
setTimeout(() => {
|
|
||||||
submitBtn.click();
|
|
||||||
}, 600);
|
|
||||||
}
|
}
|
||||||
});
|
parent = parent.parentElement.closest('li');
|
||||||
}
|
|
||||||
|
|
||||||
export function expandParentMenu(span) {
|
|
||||||
const subMenu = span.closest(".el-sub-menu");
|
|
||||||
if (subMenu && !subMenu.classList.contains("is-opened")) {
|
|
||||||
const title = subMenu.querySelector(".el-sub-menu__title");
|
|
||||||
if (title) title.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 AI 返回的混合指令 (对话 + 操作)
|
* 自动填充并提交搜索(模拟针对某些页面的操作)
|
||||||
*/
|
*/
|
||||||
export function handleCommand(aiResult, uiRefs) {
|
function autoFillAndSubmit(arg) {
|
||||||
|
if (!arg) return;
|
||||||
|
setTimeout(() => {
|
||||||
|
const input = document.querySelector('input[placeholder*="名称"], input[placeholder*="编号"]');
|
||||||
|
if (input) {
|
||||||
|
input.value = arg;
|
||||||
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
const searchBtn = document.querySelector('button.el-button--primary');
|
||||||
|
if (searchBtn) searchBtn.click();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心处理器:根据 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;
|
if (!aiResult || aiResult === "UNKNOWN") return;
|
||||||
|
|
||||||
// 1. 解析对话内容并反馈
|
// 1. 解析对话内容并反馈
|
||||||
const commMatch = aiResult.match(/<communication>([\s\S]*?)<\/communication>/);
|
const commMatch = aiResult.match(/<communication>([\s\S]*?)<\/communication>/);
|
||||||
if (commMatch && commMatch[1]) {
|
if (commMatch && commMatch[1]) {
|
||||||
const speechText = commMatch[1].trim();
|
const speechText = commMatch[1].trim();
|
||||||
// 更新 UI 状态
|
|
||||||
|
// 始终更新 UI 界面上的文字状态
|
||||||
if (uiRefs && uiRefs.updateStatus) {
|
if (uiRefs && uiRefs.updateStatus) {
|
||||||
uiRefs.updateStatus(speechText);
|
uiRefs.updateStatus(speechText);
|
||||||
}
|
}
|
||||||
// 语音播报
|
|
||||||
|
// 仅在语音开关开启时播报语音
|
||||||
|
if (voiceEnabled) {
|
||||||
const utterance = new SpeechSynthesisUtterance(speechText);
|
const utterance = new SpeechSynthesisUtterance(speechText);
|
||||||
utterance.lang = "zh-CN";
|
utterance.lang = "zh-CN";
|
||||||
window.speechSynthesis.speak(utterance);
|
window.speechSynthesis.speak(utterance);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 解析指令逻辑
|
// 2. 解析指令逻辑
|
||||||
const cmdMatch = aiResult.match(/<cmd>([\s\S]*?)<\/cmd>/);
|
const cmdMatch = aiResult.match(/<cmd>([\s\S]*?)<\/cmd>/);
|
||||||
@ -105,22 +89,31 @@ export function handleCommand(aiResult, uiRefs) {
|
|||||||
const arg = argMatch ? argMatch[1].trim() : null;
|
const arg = argMatch ? argMatch[1].trim() : null;
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
|
// 模糊匹配指令
|
||||||
const command = COMMANDS.find(c => c.key === key) ||
|
const command = COMMANDS.find(c => c.key === key) ||
|
||||||
COMMANDS.find(c => key.includes(c.key));
|
COMMANDS.find(c => key.includes(c.key));
|
||||||
|
|
||||||
if (command) {
|
if (command) {
|
||||||
|
console.log("🚀 执行指令:", command.key, "参数:", arg);
|
||||||
|
|
||||||
|
// 尝试点击侧边栏菜单(针对 Element UI 结构)
|
||||||
const allSpans = Array.from(document.querySelectorAll("span"));
|
const allSpans = Array.from(document.querySelectorAll("span"));
|
||||||
let span = allSpans.find(el => el.innerText.trim() === command.menu);
|
let span = allSpans.find(el => el.innerText.trim() === command.menu);
|
||||||
|
|
||||||
if (span) {
|
if (span) {
|
||||||
expandParentMenu(span);
|
expandParentMenu(span);
|
||||||
span.click();
|
span.click();
|
||||||
window.location.hash = command.route;
|
|
||||||
if (arg) autoFillAndSubmit(arg);
|
|
||||||
} else {
|
|
||||||
window.location.hash = command.route;
|
|
||||||
if (arg) autoFillAndSubmit(arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 路由跳转
|
||||||
|
window.location.hash = command.route;
|
||||||
|
|
||||||
|
// 自动填充搜索参数
|
||||||
|
if (arg) {
|
||||||
|
autoFillAndSubmit(arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn("⚠️ 未找到匹配指令:", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user