|
|
@@ -9,7 +9,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 收音状态显示区域 - 添加这段代码 -->
|
|
|
+ <!-- 收音状态显示区域 -->
|
|
|
<div v-if="isRecording" class="recording-status-container">
|
|
|
<div class="recording-text">正在收音...</div>
|
|
|
<div class="equalizer">
|
|
|
@@ -27,8 +27,8 @@
|
|
|
<!-- 智能家居 -->
|
|
|
<div class="image-overlay-container">
|
|
|
<img :src="baseMap" alt="智能家居" class="full-screen-image base-image" />
|
|
|
- <img v-if="state.showCurtainLeft" :src="curtainLeft" alt="白窗帘左" class="full-screen-image overlay-image" />
|
|
|
- <img v-if="state.showCurtainRight" :src="curtainRight" alt="白窗帘右" class="full-screen-image overlay-image" />
|
|
|
+ <img v-if="state.showCurtainLeft" :src="curtainLeft" alt="白窗帘左" class="full-screen-image overlay-image curtain-left" :class="{ 'curtain-animate-left': state.animateCurtainLeft }" />
|
|
|
+ <img v-if="state.showCurtainRight" :src="curtainRight" alt="白窗帘右" class="full-screen-image overlay-image curtain-right" :class="{ 'curtain-animate-right': state.animateCurtainRight }" />
|
|
|
<img :src="curtainFront" alt="窗帘前" class="full-screen-image overlay-image" />
|
|
|
<img v-if="state.showTelevision" :src="television" alt="电视画面" class="full-screen-image overlay-image" />
|
|
|
<img v-if="state.showLightOpen" :src="lightOpen" alt="灯光打开" class="full-screen-image overlay-image" />
|
|
|
@@ -39,7 +39,7 @@
|
|
|
|
|
|
<!-- 右下角按钮组 -->
|
|
|
<div class="button-group">
|
|
|
- <el-button class="control-button run-button" @click="toggleLight">运行</el-button>
|
|
|
+ <el-button class="control-button run-button" @click="runCode">运行</el-button>
|
|
|
<el-button class="control-button code-button" @click="handleViewCode">代码</el-button>
|
|
|
</div>
|
|
|
|
|
|
@@ -75,7 +75,7 @@
|
|
|
<h2>工具箱</h2>
|
|
|
<div id="toolbox" style="display: none;">
|
|
|
|
|
|
- <!-- 添加AI模块分类 -->
|
|
|
+ <!-- AI模块分类 -->
|
|
|
<category name="AI模块" categorystyle="ai_category">
|
|
|
<block type="ai_voice_input"></block>
|
|
|
<block type="ai_text_to_image"></block>
|
|
|
@@ -259,7 +259,7 @@ const device = ref({
|
|
|
});
|
|
|
|
|
|
// 台灯预览显示状态
|
|
|
-const showLampPreview = ref(false);
|
|
|
+const showLampPreview = ref(true);
|
|
|
// 语音识别
|
|
|
const isRecording = ref(false);
|
|
|
const recordingCountdown = ref(10);
|
|
|
@@ -281,12 +281,12 @@ const toggleLight = () => {
|
|
|
runCode();
|
|
|
};
|
|
|
|
|
|
-// 添加开始录音状态函数
|
|
|
+// 开始录音状态函数
|
|
|
const handleMusicEnded = () => {
|
|
|
onMusicEnded(state);
|
|
|
};
|
|
|
|
|
|
-// 添加开始录音状态函数
|
|
|
+// 开始录音状态函数
|
|
|
function startRecordingStatus() {
|
|
|
isRecording.value = true;
|
|
|
recordingCountdown.value = 10;
|
|
|
@@ -300,13 +300,13 @@ function startRecordingStatus() {
|
|
|
countdownInterval = setInterval(() => {
|
|
|
recordingCountdown.value--;
|
|
|
if (recordingCountdown.value <= 0) {
|
|
|
-clearInterval(countdownInterval);
|
|
|
+ clearInterval(countdownInterval);
|
|
|
endRecordingStatus();
|
|
|
}
|
|
|
}, 1000);
|
|
|
}
|
|
|
|
|
|
-// 添加结束录音状态函数
|
|
|
+// 结束录音状态函数
|
|
|
function endRecordingStatus() {
|
|
|
isRecording.value = false;
|
|
|
if (countdownInterval) {
|
|
|
@@ -331,7 +331,7 @@ const jsonDataString = computed({
|
|
|
device.value.jsonData = JSON.parse(value);
|
|
|
} catch (e) {
|
|
|
console.error("无效的JSON格式", e);
|
|
|
- // 可以添加错误提示给用户
|
|
|
+ // 可以错误提示给用户
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
@@ -375,12 +375,15 @@ const state = reactive({
|
|
|
colorLog: "白", // 默认颜色白色
|
|
|
},
|
|
|
|
|
|
- // 智能家居图片显示控制
|
|
|
- showCurtainLeft: false,
|
|
|
- showCurtainRight: false,
|
|
|
+ // 智能家居图片显示控制
|
|
|
+ showCurtainLeft: true,
|
|
|
+ showCurtainRight: true,
|
|
|
showTelevision: false,
|
|
|
showLightOpen: false,
|
|
|
showLightClose: false,
|
|
|
+ // 窗帘动画状态
|
|
|
+ animateCurtainLeft: false,
|
|
|
+ animateCurtainRight: false,
|
|
|
|
|
|
// 【文本文】对话相关状态
|
|
|
activeConversationId: null,
|
|
|
@@ -528,18 +531,18 @@ const aiService = {
|
|
|
const speechResult = event.results[0][0].transcript;
|
|
|
console.log("语音识别结果:", speechResult);
|
|
|
showStatus("语音识别完成");
|
|
|
- endRecordingStatus(); // 添加这行,在识别成功时结束语音状态
|
|
|
+ endRecordingStatus(); // 在识别成功时结束语音状态
|
|
|
resolve(speechResult);
|
|
|
};
|
|
|
|
|
|
recognition.onerror = (event) => {
|
|
|
console.error("语音识别错误:", event.error);
|
|
|
showStatus("语音识别发生错误: " + event.error, 'error');
|
|
|
- endRecordingStatus(); // 添加这行,在识别错误时结束语音状态
|
|
|
+ endRecordingStatus(); // 在识别错误时结束语音状态
|
|
|
resolve("");
|
|
|
};
|
|
|
|
|
|
- // 添加onend事件处理程序,确保语音识别无论如何结束都会清除状态
|
|
|
+ // onend事件处理程序,确保语音识别无论如何结束都会清除状态
|
|
|
recognition.onend = () => {
|
|
|
console.log("语音识别结束");
|
|
|
endRecordingStatus(); // 确保在识别结束时调用
|
|
|
@@ -846,19 +849,93 @@ const aiService = {
|
|
|
// 打开窗帘
|
|
|
openCurtain: withErrorHandling('打开窗帘', async function() {
|
|
|
console.log("打开窗帘");
|
|
|
- // 这里可以添加实际的窗帘控制逻辑
|
|
|
- showStatus("窗帘已打开");
|
|
|
- return true;
|
|
|
+
|
|
|
+ if (!state.showCurtainLeft && !state.showCurtainRight) {
|
|
|
+ console.log("窗帘已经是打开的状态");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 重置窗帘状态
|
|
|
+ state.showCurtainLeft = true;
|
|
|
+ state.showCurtainRight = true;
|
|
|
+ state.animateCurtainLeft = false;
|
|
|
+ state.animateCurtainRight = false;
|
|
|
+
|
|
|
+ // 确保Vue有足够时间更新DOM
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 50));
|
|
|
+
|
|
|
+ // 先设置显示窗帘(此时窗帘是闭合状态)
|
|
|
+ state.showCurtainLeft = true;
|
|
|
+ state.showCurtainRight = true;
|
|
|
+ state.animateCurtainLeft = true;
|
|
|
+ state.animateCurtainRight = true;
|
|
|
+
|
|
|
+ // 强制Vue更新DOM
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 50));
|
|
|
+
|
|
|
+ // 触发相反方向的消失动画:移除动画类,让窗帘回到初始隐藏状态
|
|
|
+ state.animateCurtainLeft = false;
|
|
|
+ state.animateCurtainRight = false;
|
|
|
+
|
|
|
+ // 等待动画完成 - 3秒
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 3000));
|
|
|
+
|
|
|
+ // 完全隐藏窗帘
|
|
|
+ state.showCurtainLeft = false;
|
|
|
+ state.showCurtainRight = false;
|
|
|
+
|
|
|
+ showStatus("窗帘已打开");
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("打开窗帘失败:", error);
|
|
|
+ showStatus("打开窗帘失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}, '打开窗帘失败'),
|
|
|
|
|
|
// 关闭窗帘
|
|
|
closeCurtain: withErrorHandling('关闭窗帘', async function() {
|
|
|
console.log("关闭窗帘");
|
|
|
- // 这里可以添加实际的窗帘控制逻辑
|
|
|
- showStatus("窗帘已关闭");
|
|
|
- return true;
|
|
|
+ if (state.showCurtainLeft && state.showCurtainRight) {
|
|
|
+ console.log("窗帘已经是关闭的状态");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 重置窗帘状态
|
|
|
+ state.showCurtainLeft = false;
|
|
|
+ state.showCurtainRight = false;
|
|
|
+ state.animateCurtainLeft = false;
|
|
|
+ state.animateCurtainRight = false;
|
|
|
+
|
|
|
+ // 确保Vue有足够时间更新DOM
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 50));
|
|
|
+
|
|
|
+ // 先设置显示窗帘
|
|
|
+ state.showCurtainLeft = true;
|
|
|
+ state.showCurtainRight = true;
|
|
|
+
|
|
|
+ // 强制Vue更新DOM
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 50));
|
|
|
+
|
|
|
+ // 再触发动画
|
|
|
+ state.animateCurtainLeft = true;
|
|
|
+ state.animateCurtainRight = true;
|
|
|
+
|
|
|
+ // 等待动画完成 - 3秒
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 3000));
|
|
|
+
|
|
|
+ showStatus("窗帘已关闭"); // 修正状态消息
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("关闭窗帘失败:", error);
|
|
|
+ showStatus("关闭窗帘失败"); // 修正错误消息
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}, '关闭窗帘失败'),
|
|
|
|
|
|
+
|
|
|
// 智能音箱命令处理 - 单参数版本
|
|
|
controlSmartSpeaker: withErrorHandling('智能音箱命令处理(单参数)', async function(params) {
|
|
|
console.log(`智能音箱执行命令: ${params}`);
|
|
|
@@ -984,7 +1061,7 @@ onMounted(() => {
|
|
|
device.value.image = router.currentRoute.value.query.image || "";
|
|
|
device.value.jsonData = JSON.parse(router.currentRoute.value.query.jsonData || {});
|
|
|
|
|
|
- // 注册AI语音识别积木
|
|
|
+ // 注册AI语音识别积木
|
|
|
Blockly.Blocks["ai_voice_input"] = {
|
|
|
init: function () {
|
|
|
this.appendDummyInput().appendField("语音识别");
|
|
|
@@ -1805,7 +1882,7 @@ const showStatus = (message, type = 'success') => {
|
|
|
background-size: cover;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
- /* 添加黑色透明遮挡层 */
|
|
|
+ /* 黑色透明遮挡层 */
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
background-blend-mode: overlay;
|
|
|
overflow-y: auto;
|
|
|
@@ -2131,6 +2208,31 @@ textarea {
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
|
|
|
+/* 窗帘动画样式 */
|
|
|
+/* 左侧窗帘初始状态 */
|
|
|
+.curtain-left {
|
|
|
+ clip-path: inset(0 100% 0 0);
|
|
|
+ transition: clip-path 3s cubic-bezier(0, 0.8, 1, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+/* 左侧窗帘动画效果 */
|
|
|
+.curtain-animate-left {
|
|
|
+ clip-path: inset(0 0 0 0);
|
|
|
+ -webkit-clip-path: inset(0 0 0 0);
|
|
|
+}
|
|
|
+
|
|
|
+/* 右侧窗帘初始状态 */
|
|
|
+.curtain-right {
|
|
|
+ clip-path: inset(0 0 0 100%);
|
|
|
+ transition: clip-path 3s cubic-bezier(0, 0.8, 1, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+/* 右侧窗帘动画效果 */
|
|
|
+.curtain-animate-right {
|
|
|
+ clip-path: inset(0 0 0 0);
|
|
|
+ -webkit-clip-path: inset(0 0 0 0);
|
|
|
+}
|
|
|
+
|
|
|
/* 台灯灯光样式 - 使用动态颜色 */
|
|
|
.lamp-light-mask {
|
|
|
position: absolute;
|