This commit is contained in:
liangbin 2025-08-28 08:33:50 +08:00
commit c3cdb0d0a1
3 changed files with 157 additions and 119 deletions

View File

@ -19,7 +19,7 @@ const showSpeechControl = ref(true);
//
router.afterEach((to) => {
if (to.path === "/LargeScreen") {
showSpeechControl.value = false;
showSpeechControl.value = true;
} else {
showSpeechControl.value = true;
}

View File

@ -3,7 +3,7 @@
<!-- <div class="TopBox">
<h1>仓储可视化数据大屏</h1>
</div> -->
<iframe src="http://172.16.1.130:81/sz/" frameborder="0" style="width: 100%;height: 100%;"></iframe>
<iframe src="http://172.16.1.130:81/sz/" id="suzhoudaping" frameborder="0" style="width: 100%;height: 100%;"></iframe>
</div>
</template>

View File

@ -59,7 +59,7 @@
</div>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { ref, onMounted, watch, nextTick } from "vue";
const config = {
page: "Ecommerce Home",
@ -71,9 +71,98 @@ const config = {
selector: "#ai-speech-add-project",
params: [],
},
{
command: "switch_jsc",
description: "驾驶舱",
action: "click",
selector: "#ai_speech_jsc",
params: [],
isIframe: true,
},
{
command: "switch_zngk",
description: "智能管控",
action: "click",
selector: "#ai_speech_zngk",
params: [],
isIframe: true,
},
{
command: "switch_zndd",
description: "智能调度",
action: "click",
selector: "#ai_speech_zndd",
params: [],
isIframe: true,
},
{
command: "switch_znyw",
description: "智能运维",
action: "click",
selector: "#ai_speech_znyw",
params: [],
isIframe: true,
},
{
command: "switch_nyszzx",
description: "能源数据中心",
action: "click",
selector: "#ai_speech_nyszzx",
params: [],
isIframe: true,
},
{
command: "switch_spjkzx",
description: "碳排放分析",
action: "click",
selector: "#ai_speech_spjkzx",
params: [],
isIframe: true,
},
{
command: "ai_speech_rygl",
description: "人员管理",
action: "click",
selector: "#ai_speech_rygl",
params: [],
isIframe: true,
},
{
command: "ai_speech_sbgl",
description: "设备管理",
action: "click",
selector: "#ai_speech_sbgl",
params: [],
isIframe: true,
},
{
command: "ai_speech_gjgl",
description: "告警管理",
action: "click",
selector: "#ai_speech_gjgl",
params: [],
isIframe: true,
},
{
command: "ai_speech_zsk",
description: "知识库",
action: "click",
selector: "#ai_speech_zsk",
params: [],
isIframe: true,
},
{
command: "ai_speech_xcgl",
description: "巡查管理",
action: "click",
selector: "#ai_speech_xcgl",
params: [],
isIframe: true,
},
{
command: "search_project",
description: "搜索项目",
description: "搜索",
action: "input",
selector: "#search_project",
params: [
@ -86,7 +175,7 @@ const config = {
},
{
command: "input_project_name",
description: "项目名称",
description: "名称",
action: "input",
selector: "#ai-speech-project-name",
params: [
@ -97,14 +186,20 @@ const config = {
},
],
},
{
command: "close_build_project",
description: "取消",
action: "click",
selector: "#ai-speech-close_buildproject",
params: [],
},
{
command: "build_project",
description: "创建项目",
description: "创建",
action: "click",
selector: "#ai-speech-buildproject",
params: [],
},
],
};
@ -118,16 +213,15 @@ const props = defineProps({
});
watch(config, (newVal, oldVal) => {
if (voiceControl.value && voiceControl.value.isListening) {
voiceControl.value.stopListening();
}
initVoiceControl();
// if (voiceControl.value && voiceControl.value.isListening) {
// voiceControl.value.stopListening();
// }
// initVoiceControl();
});
class VoiceControl {
constructor(callback) {
this.config = config;
this.isListening = false;
this.apiKey = "sk-020189889aac40f3b050f7c60ca597f8";
this.setupSpeechRecognition();
this.updateUI();
@ -145,13 +239,13 @@ class VoiceControl {
this.recognition = new SpeechRecognition();
//
this.recognition.continuous = true; //
this.recognition.continuous = false; //
this.recognition.interimResults = true; //
this.recognition.maxAlternatives = 1; //
this.recognition.lang = "zh-CN"; //
//
this.recognition.energy_threshold = 300; //
this.recognition.energy_threshold = 500; //
this.recognition.pause_threshold = 0.3; //
this.recognition.phrase_threshold = 0.2; //
@ -159,10 +253,7 @@ class VoiceControl {
this.isProcessing = false; //
this.recognition.onstart = () => {
this.isListening = true;
this.updateUI();
this.showStatus("正在聆听...", "info");
document.querySelector(".status-text").textContent = "正在聆听...";
};
this.recognition.onresult = async (event) => {
@ -174,15 +265,13 @@ class VoiceControl {
const result = event.results[lastResultIndex];
const transcript = result[0].transcript;
console.log(event, "-------event");
this.showTranscript(transcript);
this.showStatus("正在处理指令...", "info");
//
const commandDisplay = document.querySelector(".command-display");
const commandText = commandDisplay.querySelector(".command-text");
console.log(result.isFinal, "----result.isFinal");
console.log(result.isFinal, "=====> isFinal");
if (!result.isFinal) {
// -
@ -199,14 +288,13 @@ class VoiceControl {
commandText.style.color = "#00000085";
}
console.log(transcript, "----");
console.log(transcript, "=====> 识别文字");
try {
// 使setTimeoutUI
setTimeout(async () => {
if (transcript) {
const sequence = await this.queryDeepSeek(transcript);
this.showStatus("指令执行完成", "success");
this.executeSequence(sequence);
@ -220,36 +308,33 @@ class VoiceControl {
}, 0);
} catch (error) {
console.error("处理过程中出错:", error);
this.showError(`执行出错: ${error.message}`);
this.isProcessing = false;
}
};
this.recognition.onerror = (event) => {
console.log(event);
this.isListening = false;
this.updateUI();
this.showError(`语音识别错误: ${event.error}`);
document.querySelector(".status-text").textContent = "点击开始语音识别";
this.isProcessing = false;
};
// this.recognition.onerror = (event) => {
// console.log(event, "=====> error event");
// setTimeout(() => {
// this.recognition.start();
// }, 100); //
// };
this.recognition.onend = () => {
//
if (this.isListening) {
console.log("====> 走到这 ", listenStatus.value);
if (listenStatus.value) {
//
setTimeout(() => {
this.recognition.start();
}, 100); //
} else {
this.updateUI();
document.querySelector(".status-text").textContent = "点击开始语音识别";
setTimeout(() => {
document.querySelector(".status-text").classList.remove("show");
}, 3000);
}
this.isProcessing = false;
// this.isListening = false;
// this.updateUI();
// document.querySelector(".status-text").textContent = "";
@ -317,7 +402,7 @@ class VoiceControl {
],
temperature: 0.1,
stream: false,
max_tokens: 200,
max_tokens: 500,
}),
}
);
@ -334,7 +419,7 @@ class VoiceControl {
// JSON
let result = JSON.parse(content);
console.log(result, "---result");
console.log(result, "=====> deepseek 返回");
//
if (!result.sequence || !Array.isArray(result.sequence)) {
throw new Error("DeepSeek返回了无效的指令序列格式");
@ -353,9 +438,8 @@ class VoiceControl {
}
stopListening() {
if (this.recognition && this.isListening) {
this.recognition.stop();
}
this.updateUI();
}
async executeSequence(sequence) {
@ -377,18 +461,47 @@ class VoiceControl {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// iframe
sendCommand(command) {
const targetFrame = document.getElementById("suzhoudaping");
nextTick(() => {
const message = {
type: "CONTROL_COMMAND",
action: command.action,
selector: command.selector,
value: command.value,
timestamp: Date.now(),
};
try {
targetFrame.contentWindow.postMessage(message, "*");
console.log("=====> 发送到iframe");
} catch (error) {
console.log(error, "=====> 发送到iframe");
}
});
}
async executeInstruction(instruction) {
const commandConfig = this.config.commands.find(
(c) => c.command === instruction.command
);
console.log(commandConfig, "----commandConfig");
if (!commandConfig) {
throw new Error(`未知指令: ${instruction.command}`);
}
if (commandConfig && commandConfig.isIframe) {
this.sendCommand(commandConfig);
return;
}
const element = document.querySelector(commandConfig.selector);
console.log(element, "-----element");
console.log(element, "====> 控制元素");
if (!element) {
throw new Error(`找不到元素: ${commandConfig.selector}`);
@ -455,32 +568,22 @@ class VoiceControl {
updateUI() {
const voiceBtn = document.getElementById("voice-btn");
if (voiceBtn) {
if (this.isListening) {
if (listenStatus.value) {
voiceBtn.classList.add("listening");
document.querySelector(".status-text").textContent = "正在聆听...";
} else {
voiceBtn.classList.remove("listening");
document.querySelector(".status-text").textContent = "点击开始语音识别";
}
}
}
showStatus(message, type = "info") {
const statusEl = document.getElementById("status");
if (statusEl) {
statusEl.textContent = `状态: ${message}`;
statusEl.className = `status ${type}`;
}
}
showTranscript(text) {
const transcriptEl = document.getElementById("transcript");
if (transcriptEl) {
transcriptEl.textContent = `识别结果: ${text}`;
}
}
showError(message) {
this.showStatus(message, "error");
}
}
const voiceControl = ref(null);
@ -492,7 +595,6 @@ const initVoiceControl = () => {
const callBackFun = (data) => {
action.value = data;
console.log(data);
};
const toggleListening = () => {
if (!voiceControl.value) {
@ -500,7 +602,7 @@ const toggleListening = () => {
return;
}
if (voiceControl.value.isListening) {
if (listenStatus.value) {
listenStatus.value = false;
voiceControl.value.stopListening();
const voiceBtn = document.getElementById("voice-btn");
@ -535,22 +637,6 @@ body {
color: #fff;
}
.container {
}
h1 {
font-size: 2.8rem;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
p {
font-size: 1.2rem;
max-width: 800px;
margin: 0 auto 30px;
line-height: 1.6;
}
/* 固定在左下角的语音控制组件 */
.voice-control-container {
position: fixed;
@ -676,34 +762,6 @@ p {
font-weight: 500;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
background: rgba(255, 255, 255, 0.15);
color: #fff;
}
.info {
background: rgba(0, 123, 255, 0.2);
border-left: 4px solid #007bff;
}
.success {
background: rgba(40, 167, 69, 0.2);
border-left: 4px solid #28a745;
}
.error {
background: rgba(220, 53, 69, 0.2);
border-left: 4px solid #dc3545;
}
.warning {
background: rgba(255, 193, 7, 0.2);
border-left: 4px solid #ffc107;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 94, 98, 0.7);
@ -735,24 +793,4 @@ p {
transform: scale(1.3);
}
}
@media (max-width: 768px) {
.voice-control-container {
left: 20px;
bottom: 20px;
}
.command-display {
left: 100px;
max-width: calc(100vw - 120px);
}
h1 {
font-size: 2.2rem;
}
p {
font-size: 1rem;
}
}
</style>