فهرست منبع

Merge branch 'master' of http://59.110.91.129:3000/zhangmengying/AIClass into wanzi

丸子 6 ماه پیش
والد
کامیت
7c07c01f34

+ 10 - 0
src/api/ai/virtualDevice.js

@@ -0,0 +1,10 @@
+import axios from '@/utils/request';
+
+// 获取虚拟设备列表
+export function getVirtualDeviceList(data) {
+  return axios({
+    url: 'bjdxWeb/ai/selectVirtualDevice',
+    method: 'get',
+    data
+  });
+}

+ 10 - 8
src/components/blockly/BlocklyEditor.vue

@@ -279,7 +279,7 @@ const handleViewCode = () => {
 };
 
 // 响应式变量
-const jsonData = ref(`{
+const jsonData = ref({
   "blocks": {
     "languageVersion": 0,
     "blocks": [
@@ -290,7 +290,7 @@ const jsonData = ref(`{
         "y": 130,
         "fields": {
           "VAR": {
-            "id": "MHW(ZbOKhL!/An\`5N@6\`"
+            "id": "MHW(ZbOKhL!/An`5N@6`"
           }
         },
         "inputs": {
@@ -318,7 +318,7 @@ const jsonData = ref(`{
         "next": {
           "block": {
             "type": "variables_set",
-            "id": "]g.xbBe.i=a9B*Kfw@|\`",
+            "id": "]g.xbBe.i=a9B*Kfw@|`",
             "fields": {
               "VAR": {
                 "id": "zn.7{ZqbUaH1?P,R05hF"
@@ -336,7 +336,7 @@ const jsonData = ref(`{
                         "id": "h$S$nt)3VU.=nX*W-mo~",
                         "fields": {
                           "VAR": {
-                            "id": "MHW(ZbOKhL!/An\`5N@6\`"
+                            "id": "MHW(ZbOKhL!/An`5N@6`"
                           }
                         }
                       }
@@ -381,14 +381,14 @@ const jsonData = ref(`{
   "variables": [
     {
       "name": "inputText",
-      "id": "MHW(ZbOKhL!/An\`5N@6\`"
+      "id": "MHW(ZbOKhL!/An`5N@6`"
     },
     {
       "name": "lampConfig",
       "id": "zn.7{ZqbUaH1?P,R05hF"
     }
   ]
-}`);
+});
 
 //输出结果
 const output = ref('');
@@ -1369,7 +1369,8 @@ function registerPythonGenerators() {
 // 从JSON加载工作区
 const loadWorkspaceFromJson = () => {
   try {
-    const json = JSON.parse(jsonData.value);
+    // const json = JSON.parse(jsonData.value);
+    const json = jsonData.value;
     Blockly.serialization.workspaces.load(json, workspace);
     showStatus('工作区已成功从JSON加载!');
   } catch (error) {
@@ -1382,7 +1383,8 @@ const loadWorkspaceFromJson = () => {
 const exportWorkspaceToJson = () => {
   try {
     const state = Blockly.serialization.workspaces.save(workspace);
-    jsonData.value = JSON.stringify(state, null, 2);
+    // jsonData.value = JSON.stringify(state, null, 2);
+    jsonData.value = state;
     showStatus('工作区已成功导出为JSON!');
   } catch (error) {
     showStatus('导出错误: ' + error.message, 'error');

+ 2 - 8
src/router/index.js

@@ -8,9 +8,6 @@ const routes = [
   { path: '/login', component: () => import('../views/Login.vue') },
   // 免登录
   { path: '/quick-login', component: () => import('../views/QuickLogin.vue') },
-  // Blockly
-  { path: '/blockly', component: () => import('../views/Blockly.vue') },
-  { path: '/blockly2', component: () => import('../components/blockly/BlocklyEditor.vue') },
   // 首页
   {
     path: '/home',
@@ -81,11 +78,8 @@ const routes = [
     path: '/virtual-laboratory',
     component: () => import('../views/virtuallaboratory/index.vue')
   },
-  // 智能台灯
-  //  {
-  //   path: '/desk-lamp-view',
-  //   component: () => import('../views/virtuallaboratory/DeskLampView.vue')
-  // }
+  // Blockly
+  { path: '/blockly', component: () => import('../views/block/Blockly.vue') },
 ]
 const router = createRouter({
   history: createWebHistory(),

+ 0 - 2697
src/views/Blockly.vue

@@ -1,2697 +0,0 @@
-<template>
-    <!-- 智能台灯 -->
-  <div v-if="showLampPreview" class="desk-lamp-container">
-    <!-- 标题框 -->
-    <div class="desk-lamp-title-box">
-      <div class="desk-lamp-box-icon" @click="goBackLab">
-        <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
-        返回虚拟实验室
-      </div>
-    </div>
-
-    <img src="@/assets/images/desklamp.png" alt="智能台灯" class="full-screen-image" />
-    <!-- 使用动态样式设置灯光遮罩 -->
-     <div v-if="state.lamp.isLightOn"  :style="{ '--lamp-color': state.lamp.color,'--lamp-opacity': state.lamp.brightness / 100 }" class="lamp-light-mask"></div>
-
-    <!-- 右下角按钮组 -->
-    <div class="button-group">
-      <el-button class="control-button run-button" @click="toggleLight">运行</el-button>
-      <el-button class="control-button code-button" @click="handleViewCode">智能台灯</el-button>
-    </div>
-
-    <!-- 显示当前灯光信息 -->
-    <div v-if="state.lamp.isLightOn" class="lamp-info">
-      <p>颜色: {{ state.lamp.colorLog }}色</p>
-      <p>亮度: {{ state.lamp.brightness }}%</p>
-    </div>
-  </div>
-
-  <!-- Blockly -->
-  <div class="page-container">
-    <!-- 返回 -->
-    <div class="title-box">
-      <div class="box-icon" @click="goBack">
-        <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
-        返回智能台灯
-      </div>
-    </div>
-
-    <div class="home-container">
-      <div class="box-blockly">
-        <div
-          id="blocklyDiv"
-          style="height: 100%; width: 100%; background-color: #cccccc"
-        ></div>
-
-      <!-- blockly工具栏 -->
-      <template>
-        <xml id="toolbox" style="display: none">
-          <!-- AI模块分类 - 扩展更多功能 -->
-          <category name="AI模块" categorystyle="ai_category">
-            <block type="ai_voice_input"></block>
-            <block type="ai_text_to_image"></block>
-            <block type="ai_text_to_video"></block>
-            <block type="ai_text_to_text"></block>
-            <block type="ai_smart_lamp_single_param"></block>
-            <block type="ai_smart_lamp"></block>
-            <block type="ai_lamp_set_brightness"></block>
-            <block type="ai_lamp_set_color"></block>
-            <block type="ai_music_play"></block>
-          </category>
-
-            <!-- 其他原有分类保持不变 -->
-            <category name="逻辑" categorystyle="logic_category">
-              <block type="controls_if"></block>
-              <block type="logic_compare"></block>
-              <block type="logic_operation"></block>
-              <block type="logic_negate"></block>
-              <block type="logic_boolean"></block>
-              <block type="logic_null" disabled="true"></block>
-              <block type="logic_ternary"></block>
-            </category>
-            <category name="循环" categorystyle="loop_category">
-              <block type="controls_repeat_ext">
-                <value name="TIMES">
-                  <shadow type="math_number">
-                    <field name="NUM">10</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="controls_repeat" disabled="true"></block>
-              <block type="controls_whileUntil"></block>
-              <block type="controls_for">
-                <value name="FROM">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-                <value name="TO">
-                  <shadow type="math_number">
-                    <field name="NUM">10</field>
-                  </shadow>
-                </value>
-                <value name="BY">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="controls_forEach"></block>
-              <block type="controls_flow_statements"></block>
-            </category>
-            <category name="数值" categorystyle="math_category">
-              <block type="math_number" gap="32">
-                <field name="NUM">123</field>
-              </block>
-              <block type="math_arithmetic">
-                <value name="A">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-                <value name="B">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_single">
-                <value name="NUM">
-                  <shadow type="math_number">
-                    <field name="NUM">9</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_trig">
-                <value name="NUM">
-                  <shadow type="math_number">
-                    <field name="NUM">45</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_constant"></block>
-              <block type="math_number_property">
-                <value name="NUMBER_TO_CHECK">
-                  <shadow type="math_number">
-                    <field name="NUM">0</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_round">
-                <value name="NUM">
-                  <shadow type="math_number">
-                    <field name="NUM">3.1</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_on_list"></block>
-              <block type="math_modulo">
-                <value name="DIVIDEND">
-                  <shadow type="math_number">
-                    <field name="NUM">64</field>
-                  </shadow>
-                </value>
-                <value name="DIVISOR">
-                  <shadow type="math_number">
-                    <field name="NUM">10</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_constrain">
-                <value name="VALUE">
-                  <shadow type="math_number">
-                    <field name="NUM">50</field>
-                  </shadow>
-                </value>
-                <value name="LOW">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-                <value name="HIGH">
-                  <shadow type="math_number">
-                    <field name="NUM">100</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_random_int">
-                <value name="FROM">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-                <value name="TO">
-                  <shadow type="math_number">
-                    <field name="NUM">100</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="math_random_float"></block>
-              <block type="math_atan2">
-                <value name="X">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-                <value name="Y">
-                  <shadow type="math_number">
-                    <field name="NUM">1</field>
-                  </shadow>
-                </value>
-              </block>
-            </category>
-            <category name="文本" categorystyle="text_category">
-              <block type="text"></block>
-              <block type="text_join"></block>
-              <block type="text_append">
-                <value name="TEXT">
-                  <shadow type="text"></shadow>
-                </value>
-              </block>
-              <block type="text_length">
-                <value name="VALUE">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_isEmpty">
-                <value name="VALUE">
-                  <shadow type="text">
-                    <field name="TEXT"></field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_indexOf">
-                <value name="VALUE">
-                  <block type="variables_get">
-                    <field name="VAR">text</field>
-                  </block>
-                </value>
-                <value name="FIND">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_charAt">
-                <value name="VALUE">
-                  <block type="variables_get">
-                    <field name="VAR">text</field>
-                  </block>
-                </value>
-              </block>
-              <block type="text_getSubstring">
-                <value name="STRING">
-                  <block type="variables_get">
-                    <field name="VAR">text</field>
-                  </block>
-                </value>
-              </block>
-              <block type="text_changeCase">
-                <value name="TEXT">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_trim">
-                <value name="TEXT">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_count">
-                <value name="SUB">
-                  <shadow type="text"></shadow>
-                </value>
-                <value name="TEXT">
-                  <shadow type="text"></shadow>
-                </value>
-              </block>
-              <block type="text_replace">
-                <value name="FROM">
-                  <shadow type="text"></shadow>
-                </value>
-                <value name="TO">
-                  <shadow type="text"></shadow>
-                </value>
-                <value name="TEXT">
-                  <shadow type="text"></shadow>
-                </value>
-              </block>
-              <block type="text_reverse">
-                <value name="TEXT">
-                  <shadow type="text"></shadow>
-                </value>
-              </block>
-              <label text="Input/Output:" web-class="ioLabel"></label>
-              <block type="text_print">
-                <value name="TEXT">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="text_prompt_ext">
-                <value name="TEXT">
-                  <shadow type="text">
-                    <field name="TEXT">abc</field>
-                  </shadow>
-                </value>
-              </block>
-            </category>
-            <category name="列表" categorystyle="list_category">
-              <block type="lists_create_with">
-                <mutation items="0"></mutation>
-              </block>
-              <block type="lists_create_with"></block>
-              <block type="lists_repeat">
-                <value name="NUM">
-                  <shadow type="math_number">
-                    <field name="NUM">5</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="lists_length"></block>
-              <block type="lists_isEmpty"></block>
-              <block type="lists_indexOf">
-                <value name="VALUE">
-                  <block type="variables_get">
-                    <field name="VAR">list</field>
-                  </block>
-                </value>
-              </block>
-              <block type="lists_getIndex">
-                <value name="VALUE">
-                  <block type="variables_get">
-                    <field name="VAR">list</field>
-                  </block>
-                </value>
-              </block>
-              <block type="lists_setIndex">
-                <value name="LIST">
-                  <block type="variables_get">
-                    <field name="VAR">list</field>
-                  </block>
-                </value>
-              </block>
-              <block type="lists_getSublist">
-                <value name="LIST">
-                  <block type="variables_get">
-                    <field name="VAR">list</field>
-                  </block>
-                </value>
-              </block>
-              <block type="lists_split">
-                <value name="DELIM">
-                  <shadow type="text">
-                    <field name="TEXT">,</field>
-                  </shadow>
-                </value>
-              </block>
-              <block type="lists_sort"></block>
-              <block type="lists_reverse"></block>
-            </category>
-            <sep></sep>
-            <category
-              name="Variables"
-              categorystyle="variable_category"
-              custom="VARIABLE"
-            ></category>
-            <category
-              name="Functions"
-              categorystyle="procedure_category"
-              custom="PROCEDURE"
-            ></category>
-          </xml>
-        </template>
-      </div>
-
-      <div class="box-code">
-        <div class="button-container">
-          <el-button type="danger" plain id="run-code" @click="runCode()"
-            >运行代码</el-button
-          >
-          <el-button type="warning" plain id="import" @click="load()"
-            >重新加载</el-button
-          >
-        </div>
-        <br />
-
-        <div class="button-container">
-          <el-button
-            type="primary"
-            plain
-            id="to-code-js"
-            @click="generateCode('javascript')"
-            >生成 JavaScript</el-button
-          >
-          <el-button
-            type="success"
-            plain
-            id="to-code-py"
-            @click="generateCode('python')"
-            >生成 Python</el-button
-          >
-          <el-button type="info" plain id="save-json" @click="saveJson()"
-            >保存 JSON</el-button
-          >
-          <el-button type="info" plain id="save-xml" @click="saveXml()"
-            >保存 XML</el-button
-          >
-        </div>
-
-        <div class="json-section">
-          <h3>JSON 数据</h3>
-          <textarea v-model="jsonData" placeholder="在此输入JSON格式的积木块数据..."></textarea>
-          <div class="controls">
-            <button @click="loadWorkspaceFromJson">加载JSON到工作区</button>
-            <button @click="exportWorkspaceToJson">导出工作区为JSON</button>
-          </div>
-          <div v-if="statusMessage" :class="['status', statusType]" style="color: #1a1a1a">
-            {{ statusMessage }}
-          </div>
-        </div>
-
-        <!-- 代码显示区 -->
-        <div class="code-section">
-          <h4>生成的代码</h4>
-          <textarea name="" id="textarea" class="box-code-textarea"></textarea>
-        </div>
-
-        <!-- 运行结果区 -->
-        <div class="result-section">
-          <h4>运行结果</h4>
-          <div id="run-result" class="run-result-content"></div>
-
-          <!-- 额外的图片预览区域 -->
-          <div
-            v-if="state.generatedContent.imageUrl"
-            class="extra-image-preview"
-          >
-            <h5>生成的图片:</h5>
-            <img
-              :src="state.generatedContent.imageUrl"
-              class="extra-preview-image"
-              alt="AI生成图片"
-            />
-          </div>
-          <div
-            v-if="state.generatedContent.videoUrl"
-            class="extra-image-preview"
-          >
-            <h5>生成的视频:</h5>
-            <video
-              :src="state.generatedContent.videoUrl"
-              controls
-              class="preview-video"
-              alt="AI生成视频"
-            ></video>
-          </div>
-
-          <!-- 在template部分的适当位置音频播放器组件 -->
-          <div class="music-player-container" v-if="state.currentMusicUrl">
-            <h5>音乐播放</h5>
-            <audio
-                ref="musicPlayer"
-                :src="state.currentMusicUrl"
-                @ended="handleMusicEnded"
-                preload="metadata">
-              您的浏览器不支持音频元素
-            </audio>
-            <div class="music-status">
-              <p v-if="state.isMusicPlaying">正在播放: {{ state.currentMusicName }}</p>
-              <p v-else>准备就绪</p>
-            </div>
-            <!-- 停止播放按钮 - 修复点击事件 -->
-            <el-button
-                v-if="state.isMusicPlaying"
-                type="danger"
-                size="small"
-                @click="handleStopMusic"
-                style="margin-top: 10px;">
-              停止播放
-            </el-button>
-          </div>
-
-        </div>
-      </div>
-
-      <!-- AI结果预览模态框 -->
-      <el-dialog
-        v-if="state.previewConten"
-        title="AI生成结果"
-        width="80%"
-        :before-close="handleClosePreview"
-      >
-        <div
-          v-if="state.previewType === 'image'"
-          class="preview-image-container"
-        >
-          <img
-            :src="state.previewContent"
-            class="preview-image"
-            alt="生成的图片"
-          />
-        </div>
-        <div
-          v-if="state.previewType === 'video'"
-          class="preview-video-container"
-        >
-          <video
-            :src="state.previewContent"
-            controls
-            class="preview-video"
-          ></video>
-        </div>
-        <div v-if="state.previewType === 'text'" class="preview-text-container">
-          <p>{{ state.previewContent }}</p>
-        </div>
-        <span slot="footer" class="dialog-footer">
-          <el-button @click="handleClosePreview">关闭</el-button>
-        </span>
-      </el-dialog>
-    </div>
-
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted, onUnmounted, reactive } from "vue";
-import { useRouter } from "vue-router";
-import { ArrowLeftBold } from "@element-plus/icons-vue";
-import * as Blockly from "blockly";
-import { javascriptGenerator } from "blockly/javascript";
-import { pythonGenerator } from "blockly/python";
-import * as hans from "blockly/msg/zh-hans";
-import { ElDialog, ElButton, ElMessage } from "element-plus";
-
-//【文生图】文生图
-import {
-  AiImageStatusEnum,
-  CreatePainting,
-  PaintingGetMys,
-  CreateVideo,
-  VideoGetMys,
-  sendChatMessageStream,
-  CreateDialogue,
-} from "@/api/questions.js";
-import { getModelIdByType, ModelPlatformEnum } from "@/api/teachers.js";
-import { ModelTypeEnum } from "@/api/teachers.js";
-import { globalState } from "@/utils/globalState.js";
-//音乐
-import { playMusic, stopMusic, onMusicEnded } from "@/api/blockly/music.js";
-
-
-const router = useRouter();
-// 台灯预览显示状态
-const showLampPreview = ref(false)
-
-// 返回虚拟实验室
-const goBackLab = () => {
-  router.push("/virtual-laboratory");
-};
-const goBack = () => {
-  showLampPreview.value = true
-};
-// 切换灯光状态
-const toggleLight = () => {
-  state.lamp.isLightOn = true;
-  generateCode('javascript');
-  runCode()
-};
-// 查看代码编程界面显示状态
-const handleViewCode = () =>{
-  showLampPreview.value = false
-};
-
-Blockly.setLocale(hans);
-
-// 状态管理
-const state = reactive({
-  workspace: null,
-  generatedContent: {
-    imageUrl: "",
-    videoUrl: "",
-    text: "",
-  },
-  previewVisible: false,
-  previewType: "",
-  previewContent: "",
-  isProcessing: false,
-
-  //年级
-  gradeId: "",
-
-  //【文生图】文生图
-  inProgressImageMap: {},
-
-  //【文生视频】文生视频
-  inProgressVideoMap: {},
-
-  // 台灯状态
-  lamp: {
-    isLightOn: false,// 台灯是否亮着
-    brightness: 0, // 默认亮度50%
-    color: "#ffffff", // 默认颜色白色
-    colorLog: "白", // 默认颜色白色
-  },
-
-  // 【文本文】对话相关状态
-  activeConversationId: null,
-  conversationInAbortController: null,
-
-  // 独立的音乐播放状态
-  currentMusicUrl: '',
-  currentMusicName: '',
-  isMusicPlaying: false,
-});
-
-// 创建音乐播放器引用
-const musicPlayer = ref(null);
-// 音乐相关的处理函数
-const handleMusicEnded = () => {
-  onMusicEnded(state);
-};
-
-// 专门的停止音乐处理函数
-const handleStopMusic = () => {
-  // 直接调用导入的stopMusic函数并传递正确的参数
-  stopMusic(state, musicPlayer);
-  // 提示信息
-  ElMessage.success('音乐已停止播放');
-};
-
-// 统一轮询管理器
-const pollingManager = {
-  timers: {},
-
-  // 启动轮询
-  startPolling(type, callback, interval = 3000) {
-    // 如果已有相同类型的轮询,先清除
-    this.stopPolling(type);
-
-    this.timers[type] = setInterval(async () => {
-      try {
-        await callback();
-      } catch (error) {
-        console.error(`${type}轮询失败:`, error);
-      }
-    }, interval);
-
-    return this.timers[type];
-  },
-
-  // 停止轮询
-  stopPolling(type) {
-    if (this.timers[type]) {
-      clearInterval(this.timers[type]);
-      this.timers[type] = null;
-    }
-  },
-
-  // 停止所有轮询
-  stopAll() {
-    Object.keys(this.timers).forEach(type => this.stopPolling(type));
-  }
-};
-
-// 统一的错误处理包装器
-function withErrorHandling(operationName, fn, errorMessage = null) {
-  return async function(...args) {
-    try {
-      state.isProcessing = true;
-      return await fn.apply(this, args);
-    } catch (error) {
-      console.error(`${operationName}失败:`, error);
-      ElMessage.error(errorMessage || `${operationName}发生错误: ${error.message || '未知错误'}`);
-      return null;
-    } finally {
-      state.isProcessing = false;
-    }
-  };
-}
-
-// 任务状态轮询公共函数
-async function pollTaskStatus(taskType, taskIds, fetchApi, onSuccess, onFailure) {
-  if (taskIds.length === 0) {
-    pollingManager.stopPolling(taskType);
-    return {};
-  }
-
-  try {
-    const list = await fetchApi(taskIds);
-    const activeTasks = {};
-
-    list.data.forEach((task) => {
-      if (task.status === AiImageStatusEnum.IN_PROGRESS) {
-        activeTasks[task.id] = task;
-      } else if (task.status === AiImageStatusEnum.SUCCESS) {
-        // 任务成功完成
-        if (onSuccess) {
-          onSuccess(task);
-        }
-      } else if (task.status === AiImageStatusEnum.FAIL) {
-        // 任务失败
-        if (onFailure) {
-          onFailure(task);
-        }
-      }
-    });
-
-    return activeTasks;
-  } catch (error) {
-    console.error(`${taskType}状态轮询失败:`, error);
-    return {};
-  }
-}
-
-// AI服务模块 - 统一管理
-const aiService = {
-  // 语音识别
-  recognizeVoice: withErrorHandling('语音识别', async function(promptText = "", language = "zh-CN") {
-    console.log("语音识别开始");
-    // 前端语音采集
-    const recognitionResult = await this.captureVoice(language, promptText);
-    return recognitionResult || "";
-  }, '语音识别失败'),
-
-  // 前端语音采集
-  captureVoice(language, promptText) {
-    return new Promise((resolve) => {
-      if (
-          !"webkitSpeechRecognition" in window &&
-          !"SpeechRecognition" in window
-      ) {
-        ElMessage.warning("您的浏览器不支持语音识别功能");
-        resolve("");
-        return;
-      }
-
-      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
-      const recognition = new SpeechRecognition();
-
-      recognition.lang = language;
-      recognition.interimResults = false;
-      recognition.maxAlternatives = 1;
-
-      let countdown = 10;
-
-      // 固定的消息提示框
-      const messageText = promptText ? `${promptText}\n请开始说话...(${countdown}秒)` : `请开始说话...(${countdown}秒)`;
-      const messageInstance = ElMessage.info({
-        message: messageText,
-        duration: 0 // 0表示不自动关闭
-      });
-
-      // 隐藏的div来更新倒计时显示
-      const timer = setInterval(() => {
-        countdown--;
-        if (countdown > 0) {
-          // 找到消息提示框中的文本元素并更新
-          const messageElement = document.querySelector('.el-message__content');
-          if (messageElement) {
-            const newText = promptText ? `${promptText}\n请开始说话...(${countdown}秒)` : `请开始说话...(${countdown}秒)`;
-            messageElement.textContent = newText;
-          }
-        } else {
-          clearInterval(timer);
-          // 倒计时结束后关闭提示框
-          if (messageInstance && messageInstance.close) {
-            messageInstance.close();
-          }
-        }
-      }, 1000);
-
-
-      recognition.onresult = (event) => {
-        const speechResult = event.results[0][0].transcript;
-        console.log("语音识别结果:", speechResult);
-        resolve(speechResult);
-      };
-
-      recognition.onerror = (event) => {
-        console.error("语音识别错误:", event.error);
-        ElMessage.error("语音识别发生错误: " + event.error);
-        resolve("");
-      };
-
-      // recognition.onend = () => {
-      //   console.log("语音识别已结束");
-      // };
-
-      recognition.start();
-    });
-  },
-
-  // 文本生成图片
-  textToImage: withErrorHandling('AI图片生成', async function(prompt, waitForCompletion = true) {
-    console.log("AI图片生成中,提示词:", prompt);
-
-    //获取文生图-模型id
-    const modelRes = await getModelIdByType({
-      type: ModelTypeEnum.TEXT_TO_IMAGE,
-      platform: ModelPlatformEnum.DOUBAO,
-    });
-    if (!modelRes.data) {
-      ElMessage.error("获取模型ID失败");
-      return null;
-    }
-
-    // 使用CreatePainting API创建图片任务
-    const createRes = await CreatePainting({
-      modelId: modelRes.data,
-      prompt: prompt,
-      width: 1024,
-      height: 1024,
-    });
-
-    // 记录任务ID到映射中
-    state.inProgressImageMap[createRes.data] = {
-      id: createRes.data,
-      status: AiImageStatusEnum.IN_PROGRESS,
-    };
-
-    // 开始轮询任务状态
-    this.startPollingTasks('image');
-
-    // 如果需要等待完成,等待图片生成完成
-    if (waitForCompletion) {
-      console.log("AI图片生成中,请等待。。。:");
-      return await this.waitForImageCompletion(createRes.data);
-    }
-
-    return createRes.data; // 返回任务ID
-  }, '生成图片失败'),
-
-  // 【文生图】等待图片生成完成
-  waitForImageCompletion(imageId) {
-    return new Promise((resolve, reject) => {
-      const checkInterval = setInterval(async () => {
-        try {
-          const list = await PaintingGetMys([imageId]);
-          if (list.data && list.data.length > 0) {
-            const image = list.data[0];
-            if (image.status === AiImageStatusEnum.SUCCESS) {
-              clearInterval(checkInterval);
-              resolve(image.picUrl);
-            } else if (image.status === AiImageStatusEnum.FAIL) {
-              clearInterval(checkInterval);
-              reject(new Error(image.error || "图片生成失败"));
-            }
-          }
-        } catch (error) {
-          clearInterval(checkInterval);
-          reject(error);
-        }
-      }, 3000);
-    });
-  },
-
-  // 文本生成视频
-  textToVideo: withErrorHandling('AI视频生成', async function(prompt, waitForCompletion = true) {
-    console.log("AI视频生成中,提示词:", prompt);
-
-    //获取视频生成模型id
-    const modelRes = await getModelIdByType({
-      type: ModelTypeEnum.IMAGE_TO_VIDEO,
-      platform: ModelPlatformEnum.DOUBAO,
-    });
-
-    if (!modelRes.data) {
-      ElMessage.error("获取模型ID失败");
-      return null;
-    }
-
-    // 使用CreateVideo API创建视频任务
-    const createRes = await CreateVideo({
-      modelId: modelRes.data,
-      prompt: prompt,
-      duration: 4,
-      resolution: "1080P",
-    });
-
-    // 记录任务ID
-    state.inProgressVideoMap[createRes.data] = {
-      id: createRes.data,
-      status: AiImageStatusEnum.IN_PROGRESS,
-    };
-
-    console.log("AI视频生成中,请等待。。。");
-    // 启动统一的轮询机制
-    this.startPollingTasks('video');
-
-    // 如果需要等待完成,使用Promise封装结果
-    if (waitForCompletion) {
-      return new Promise((resolve, reject) => {
-        // 设置一次性的状态检查
-        const checkStatus = () => {
-          const videoInfo = state.generatedContent.videoUrl;
-          if (videoInfo && videoInfo.includes(createRes.data)) {
-            resolve(videoInfo);
-          } else if (state.inProgressVideoMap[createRes.data]?.status === AiImageStatusEnum.FAIL) {
-            reject(new Error("视频生成失败"));
-          } else if (!state.inProgressVideoMap[createRes.data]) {
-            reject(new Error("视频任务已不存在"));
-          } else {
-            // 继续检查
-            setTimeout(checkStatus, 1000);
-          }
-        };
-        checkStatus();
-      });
-    }
-
-    return createRes.data; // 返回任务ID
-  }, '生成视频失败'),
-
-  // 文本生成文本(如AI对话)
-  textToText: withErrorHandling('AI大模型调用', async function(prompt, model = "default") {
-    console.log("AI智能体请求,输入文本:", prompt);
-
-    // 如果没有活跃的对话ID,创建新对话
-    if (!state.activeConversationId) {
-      // 使用与TextToText.vue相同的方式创建对话
-      const res = await CreateDialogue({ roleId: 75 });
-      state.activeConversationId = res.data;
-      console.log("AI智能体创建成功,请等待。。。");
-    }
-
-    // 创建AbortController实例
-    state.conversationInAbortController = new AbortController();
-
-    // 使用流式API发送消息
-    let resultText = "";
-    let isFirstChunk = true;
-
-    await sendChatMessageStream(
-        state.activeConversationId,
-        prompt,
-        null,
-        state.conversationInAbortController,
-        true, // 启用上下文
-        async (res) => {
-          try {
-            const { code, data, msg } = JSON.parse(res.data);
-            if (code !== 0) {
-              console.log(`对话异常! ${msg}`);
-              return;
-            }
-            // 根据事件类型处理
-            if (data.eventType === "TEXT") {
-              // 如果内容为空,就不处理
-              if (data.receive?.content === "") {
-                return;
-              }
-
-              // 处理文本消息
-              resultText += data.receive.content;
-
-              // 首次返回时更新预览内容
-              if (isFirstChunk) {
-                isFirstChunk = false;
-                // 设置预览内容
-                state.generatedContent.text = resultText;
-                state.previewType = "text";
-                state.previewContent = resultText;
-                if (!state.previewVisible) {
-                  state.previewVisible = true;
-                }
-              } else {
-                // 更新预览内容
-                state.generatedContent.text = resultText;
-                state.previewContent = resultText;
-              }
-            }
-          } catch (error) {
-            console.error("处理流式响应失败:", error);
-          }
-        },
-        (error) => {
-          console.log(`对话异常! ${error}`);
-          this.stopTextToTextStream();
-          throw error;
-        },
-        () => {
-          // console.log(`结束对话!`);
-          this.stopTextToTextStream();
-        }
-    );
-
-    // 确保最终结果被设置
-    if (resultText) {
-      console.log("AI大模型调用成功,返回结果:", resultText);
-      state.generatedContent.text = resultText;
-      state.previewType = "text";
-      state.previewContent = resultText;
-      if (!state.previewVisible) {
-        state.previewVisible = true;
-      }
-    }
-
-    return resultText;
-  }, 'AI大模型调用失败'),
-
-  // 停止文本生成流
-  stopTextToTextStream() {
-    if (state.conversationInAbortController) {
-      state.conversationInAbortController.abort();
-    }
-  },
-
-  // 设置台灯亮度
-  setLampBrightness: withErrorHandling('设置台灯亮度', async function(brightness) {
-    // 验证亮度值在0-100之间
-    const validBrightness = Math.max(0, Math.min(100, parseInt(brightness) || 0));
-
-    // 更新状态
-    state.lamp.brightness = validBrightness;
-
-    // 模拟API调用(实际项目中可替换为真实API)
-    console.log(`智能台灯亮度已设置为: ${validBrightness}%`);
-
-    return validBrightness;
-  }, '设置台灯亮度失败'),
-
-  // 设置台灯颜色
-  setLampColor: withErrorHandling('设置台灯颜色', async function(color) {
-    // 预定义的颜色映射
-    const colorMap = {
-      '紫': '#D886F0',
-      '橙': '#F89E35',
-      '黄': '#F9E67E',
-      '青': '#6BF5E6',
-      '白': '#ffffff',
-    };
-
-    // 获取有效的颜色值
-    let validColor = colorMap[color] || color;
-
-    // 检查是否是有效的颜色格式
-    if (!/^#[0-9A-F]{6}$/i.test(validColor)) {
-      validColor = "#ffffff"; // 默认白色
-    }
-
-    // 更新状态
-    state.lamp.color = validColor;
-    state.lamp.colorLog = color;
-
-    // 模拟API调用(实际项目中可替换为真实API)
-    console.log(`智能台灯颜色已设置为: ${color}`);
-
-    return validColor;
-  }, '设置台灯颜色失败'),
-
-  // 音乐播放相关方法
-  playMusic: withErrorHandling('播放音乐', async function(musicType) {
-    return playMusic(musicType, state, musicPlayer);
-  }, '播放音乐失败'),
-
-  stopMusic: withErrorHandling('停止音乐', async function() {
-    return stopMusic(state, musicPlayer);
-  }, '停止音乐失败'),
-
-  // 综合控制台灯(参数格式:"颜色, 亮度, 音乐")
-  controlLampWithSingleParam: withErrorHandling('智能台灯综合控制', async function(params) {
-    // 解析参数字符串
-    let color = '白'; // 默认颜色
-    let brightness = 0; // 默认亮度
-    let music = ''; // 音乐信息
-
-    if (params && typeof params === 'string') {
-      // 根据逗号分割参数
-      const paramArray = params.split(',').map(p => p.trim());
-
-      // 提取颜色(第一个参数)
-      if (paramArray.length > 0 && paramArray[0]) {
-        color = paramArray[0];
-      }
-
-      // 提取亮度(第二个参数)
-      if (paramArray.length > 1 && paramArray[1]) {
-        brightness = paramArray[1];
-      }
-
-      // 提取音乐(第三个参数)
-      if (paramArray.length > 2 && paramArray[2]) {
-        music = paramArray[2];
-
-        // 调用音乐播放函数
-        await this.playMusic(music);
-      }
-    }
-    // 调用控制台灯方法
-    return await this.controlLamp(brightness, color);
-  }, '智能台灯综合控制失败'),
-
-  // 综合控制台灯(参数格式:"颜色, 亮度")
-  controlLamp: withErrorHandling('智能台灯控制', async function(brightness, color) {
-    // 先设置亮度
-    await this.setLampBrightness(brightness);
-
-    // 再设置颜色
-    await this.setLampColor(color);
-
-    return { brightness: state.lamp.brightness, color: state.lamp.color };
-  }, '智能台灯控制失败'),
-
-  // 启动任务轮询
-  startPollingTasks(type) {
-    if (type === 'image' || type === 'all') {
-      pollingManager.startPolling('image', async () => {
-        const imageIds = Object.keys(state.inProgressImageMap).map(Number);
-        state.inProgressImageMap = await pollTaskStatus(
-            'image',
-            imageIds,
-            PaintingGetMys,
-            (image) => {
-              state.generatedContent.imageUrl = image.picUrl;
-              state.previewType = "image";
-              state.previewContent = image.picUrl;
-              state.previewVisible = true;
-              console.log("AI图片生成完成:", image.picUrl);
-            },
-            (image) => {
-              ElMessage.error("图片生成失败: " + (image.error || "未知错误"));
-              console.error("图片生成失败:", image.id, image.error);
-            }
-        );
-      });
-    }
-
-    if (type === 'video' || type === 'all') {
-      pollingManager.startPolling('video', async () => {
-        const videoIds = Object.keys(state.inProgressVideoMap).map(Number);
-        state.inProgressVideoMap = await pollTaskStatus(
-            'video',
-            videoIds,
-            VideoGetMys,
-            (video) => {
-              state.generatedContent.videoUrl = video.videoUrl;
-              state.previewType = "video";
-              state.previewContent = video.videoUrl;
-              state.previewVisible = true;
-              console.log("AI视频生成完成:", video.videoUrl);
-            },
-            (video) => {
-              ElMessage.error("视频生成失败: " + (video.error || "未知错误"));
-              console.error("视频生成失败:", video.id, video.error);
-            }
-        );
-      });
-    }
-  }
-};
-
-// 初始化Blockly工作区和自定义积木
-onMounted(async () => {
-  // 从全局状态初始化年级ID
-  state.gradeId = globalState.initGradeId();
-
-  // 注册AI语音输入积木
-  Blockly.Blocks["ai_voice_input"] = {
-    init: function () {
-      this.appendDummyInput().appendField("语音输入");
-      this.appendValueInput("PROMPT")
-          .setCheck("String")
-          .appendField("提示文字:");
-      this.appendDummyInput()
-          .appendField("语言:")
-          .appendField(
-              new Blockly.FieldDropdown([
-                ["中文", "zh-CN"],
-                ["英文", "en-US"],
-              ]),
-              "LANGUAGE"
-          );
-      this.setOutput(true, "String");
-      this.setColour(310);
-      this.setTooltip("使用语音识别获取文本输入");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册AI文本生成图片积木
-  Blockly.Blocks["ai_text_to_image"] = {
-    init: function () {
-      this.appendDummyInput().appendField("AI生成图片");
-      this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
-      this.appendDummyInput()
-          .appendField("等待完成:", "WAIT_LABEL")
-          .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(340);
-      this.setTooltip("使用AI将文本描述转换为图片");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册AI文本生成视频积木
-  Blockly.Blocks["ai_text_to_video"] = {
-    init: function () {
-      this.appendDummyInput().appendField("AI生成视频");
-      this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
-      this.appendDummyInput()
-          .appendField("等待完成:", "WAIT_LABEL")
-          .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(340);
-      this.setTooltip("使用AI将文本描述转换为视频");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册AI文本生成文本积木
-  Blockly.Blocks["ai_text_to_text"] = {
-    init: function () {
-      this.appendDummyInput().appendField("AI大模型调用");
-      this.appendValueInput("PROMPT")
-          .setCheck("String")
-          .appendField("输入文本:");
-      this.appendValueInput("提示词")
-          .setCheck("String")
-          .appendField("提示词:");
-      this.setOutput(true, "String");
-      this.setColour(300);
-      this.setTooltip("使用AI大模型调用并返回结果");
-      this.setHelpUrl("");
-    },
-  };
-
-  //AI智能台灯单参数积木
-  Blockly.Blocks['ai_smart_lamp_single_param'] = {
-    init: function() {
-      this.appendDummyInput()
-          .appendField('智能台灯控制(单参数)');
-      this.appendValueInput('PARAMS')
-          .setCheck('String')
-          .appendField('参数(格式: 颜色,亮度,音乐):');
-      this.appendDummyInput()
-          .appendField('例如: 蓝,50,平静');
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(280);
-      this.setTooltip('通过一个参数字符串控制智能台灯的亮度、颜色和音乐\n格式: 颜色,亮度,音乐\n例如: 蓝,50,平静');
-      this.setHelpUrl('');
-    }
-  };
-
-  // 注册AI智能台灯积木
-  Blockly.Blocks["ai_smart_lamp"] = {
-    init: function () {
-      this.appendDummyInput().appendField("智能台灯控制");
-      this.appendValueInput("BRIGHTNESS")
-          .setCheck(["Number", "String"])
-          .appendField("亮度 (0-100):");
-      this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(280);
-      this.setTooltip("控制智能台灯的亮度和颜色");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册AI台灯设置亮度积木
-  Blockly.Blocks["ai_lamp_set_brightness"] = {
-    init: function () {
-      this.appendDummyInput().appendField("设置台灯亮度");
-      this.appendValueInput("BRIGHTNESS")
-          .setCheck(["Number", "String"])
-          .appendField("亮度 (0-100):");
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(270);
-      this.setTooltip("设置智能台灯的亮度");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册AI台灯设置颜色积木
-  Blockly.Blocks["ai_lamp_set_color"] = {
-    init: function () {
-      this.appendDummyInput().appendField("设置台灯颜色");
-      this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
-      this.appendDummyInput().appendField("可选颜色: 红,蓝,绿,黄,青,靛,紫");
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(275);
-      this.setTooltip("设置智能台灯的颜色");
-      this.setHelpUrl("");
-    },
-  };
-
-  // 注册音乐播放积木
-  Blockly.Blocks['ai_music_play'] = {
-    init: function() {
-      this.appendDummyInput()
-          .appendField('播放音乐');
-      this.appendDummyInput()
-          .appendField('音乐类型:')
-          .appendField(
-              new Blockly.FieldDropdown([
-                ['热闹', '热闹'],
-                ['舒缓', '舒缓'],
-              ]),
-              'MUSIC_TYPE'
-          );
-      this.setInputsInline(false);
-      this.setPreviousStatement(true, null);
-      this.setNextStatement(true, null);
-      this.setColour(290);
-      this.setTooltip('播放指定类型的音乐');
-      this.setHelpUrl('');
-    }
-  };
-
-  // 注册JavaScript代码生成器
-  registerJavaScriptGenerators();
-
-  // 注册Python代码生成器
-  registerPythonGenerators();
-
-  // 初始化工作区 - 简化且标准化的实现
-  try {
-    // 使用标准方式初始化Blockly工作区,参考BlocklyEditor_2.vue的配置
-    const blocklyDiv = document.getElementById('blocklyDiv');
-    const toolbox = document.getElementById('toolbox');
-
-    state.workspace = Blockly.inject(blocklyDiv, {
-      toolbox: toolbox,
-      collapse: true,
-      comments: true,
-      disable: false, // 设为false以允许编辑
-      maxBlocks: Infinity,
-      trashcan: true,
-      horizontalLayout: false,
-      toolboxPosition: 'start',
-      css: true,
-      media: 'https://unpkg.com/blockly/media/',
-      rtl: false,
-      scrollbars: true,
-      sounds: false, // 禁用声音以提高性能
-      oneBasedIndex: true,
-      grid: {
-        spacing: 20,
-        length: 3,
-        colour: "#ccc",
-        snap: true
-      },
-      zoom: {
-        controls: true,
-        wheel: true,
-        startScale: 1.0,
-        maxScale: 3,
-        minScale: 0.3,
-        scaleSpeed: 1.2
-      }
-    });
-
-    // 使用合并的监听器函数
-    addCombinedChangeListener();
-
-    // 使用当前Blockly版本支持的标准方法加载初始内容
-    /*const initialXml = "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n" +
-        "      <variables>\n" +
-        "        <variable id=\"kAVG*zJLw/q)l/(/eIMM\">inputText</variable>\n" +
-        "        <variable id=\"[7F(niLz{.fvWvY.VT/N\">lampConfig</variable>\n" +
-        "      </variables>\n" +
-        "      <block type=\"variables_set\" id=\"XIsa=sV/8ImVP5P3:ol4\" x=\"90\" y=\"90\">\n" +
-        "        <field name=\"VAR\" id=\"kAVG*zJLw/q)l/(/eIMM\">inputText</field>\n" +
-        "        <value name=\"VALUE\">\n" +
-        "          <block type=\"ai_voice_input\" id=\"Ql).^+/uT(P~$aMg2OEA\">\n" +
-        "            <field name=\"LANGUAGE\">zh-CN</field>\n" +
-        "            <value name=\"PROMPT\">\n" +
-        "              <block type=\"text\" id=\"kXjnnUncUu7p$a[zz)da\">\n" +
-        "                <field name=\"TEXT\">请说话:</field>\n" +
-        "              </block>\n" +
-        "            </value>\n" +
-        "          </block>\n" +
-        "        </value>\n" +
-        "        <next>\n" +
-        "          <block type=\"variables_set\" id=\"AP0CO$VPeM0*PS*CKT,V\">\n" +
-        "            <field name=\"VAR\" id=\"[7F(niLz{.fvWvY.VT/N\">lampConfig</field>\n" +
-        "            <value name=\"VALUE\">\n" +
-        "              <block type=\"ai_text_to_text\" id=\"XYG.cN_=CX5:Vd_)wdZ7\" x=\"190\" y=\"250\">\n" +
-        "                <value name=\"PROMPT\">\n" +
-        "                  <block type=\"variables_get\" id=\"(3Sd)lTX],5xho{p_=s!\">\n" +
-        "                    <field name=\"VAR\" id=\"8(,gJ5{2y*8olrD{^G.P\">inputText</field>\n" +
-        "                  </block>\n" +
-        "                </value>\n" +
-        "                <value name=\"提示词\">\n" +
-        "                  <block type=\"text\" id=\"ubD*WPRnwBC|BxGraKX3\">\n" +
-        "                    <field name=\"TEXT\">请只回复我指定格式:白,100,热闹</field>\n" +
-        "                  </block>\n" +
-        "                </value>\n" +
-        "              </block>\n" +
-        "            </value>\n" +
-        "            <next>\n" +
-        "              <block type=\"ai_smart_lamp_single_param\" id=\"ZTAwt~MMm(t5w:$$Azt`\">\n" +
-        "    <value name=\"PARAMS\">\n" +
-        "        <block type=\"variables_get\" id=\"X):w|X4hYE~ru1r9`W8f\">\n" +
-        "        <field name=\"VAR\" id=\"[7F(niLz{.fvWvY.VT/N\">lampConfig</field>\n" +
-        "  </block>\n" +
-        "  </value>\n" +
-        "  </block>\n" +
-        "  </next>\n" +
-        "  </block>\n" +
-        "  </next>\n" +
-        "  </block>\n" +
-        "  </xml>";
-
-
-    // const initialXml = "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n" +
-    //     "  </xml>";
-
-    try {
-      // 尝试使用现代API加载工作区
-      const parsedXml = Blockly.utils.xml.textToDom(initialXml);
-      Blockly.Xml.domToWorkspace(parsedXml, state.workspace);
-      console.log('XML加载成功');
-    } catch (xmlError) {
-      console.warn('XML加载失败,尝试清空工作区:', xmlError);
-      // 如果XML加载失败,清空工作区
-      state.workspace.clear();
-    }*/
-
-    // 添加拖拽修复
-    if (state.workspace) {
-      // 确保所有块都可以正确拖拽
-      state.workspace.addChangeListener((event) => {
-        if (event.type === Blockly.Events.BLOCK_CREATE) {
-          // 对于新创建的块,确保它们是可拖拽的
-          const block = state.workspace.getBlockById(event.blockId);
-          if (block && block.editable) {
-            // 确保块是可编辑的
-            block.setEditable(true);
-          }
-        }
-      });
-    }
-
-    // 初始化完成后手动生成一次代码
-    generateCode("javascript");
-
-  } catch (error) {
-    console.error('初始化Blockly工作区失败:', error);
-    ElMessage.error('Blockly初始化失败,请刷新页面重试');
-  }
-
-  // 修改工作区变化监听器,使其包含拖拽修复逻辑
-  state.workspace.addChangeListener((event) => {
-    // 生成代码
-    generateCode("javascript");
-
-    // 拖拽修复逻辑
-    if (event.type === Blockly.Events.BLOCK_CREATE) {
-      const block = state.workspace.getBlockById(event.blockId);
-      if (block) {
-        block.setEditable(true);
-      }
-    }
-  });
-
-  // 加载初始JSON数据
-  // workspace = state.workspace;
-  // loadWorkspaceFromJson();
-
-  // 添加工作区变化监听器
-  //   state.workspace.addChangeListener(() => generateCode("javascript"));
-});
-
-// 组件卸载时清除所有资源
-onUnmounted(() => {
-  // 统一的轮询管理器停止所有轮询
-  pollingManager.stopAll();
-
-  // 停止音乐播放
-  aiService.stopMusic();
-
-  // 停止文本生成流
-  aiService.stopTextToTextStream();
-
-  // 释放工作区资源
-  if (state.workspace) {
-    state.workspace.dispose();
-  }
-});
-
-
-
-
-// 响应式变量
-const jsonData = ref(`{
-  "blocks": {
-    "languageVersion": 0,
-    "blocks": [
-      {
-        "type": "text_print",
-        "x": 100,
-        "y": 100,
-        "inputs": {
-          "TEXT": {
-            "block": {
-              "type": "text",
-              "fields": {
-                "TEXT": "Hello, Blockly!"
-              }
-            }
-          }
-        }
-      },
-      {
-        "type": "controls_if",
-        "x": 100,
-        "y": 200,
-        "inputs": {
-          "IF0": {
-            "block": {
-              "type": "logic_compare",
-              "fields": {
-                "OP": "EQ"
-              },
-              "inputs": {
-                "A": {
-                  "block": {
-                    "type": "math_number",
-                    "fields": {
-                      "NUM": 5
-                    }
-                  }
-                },
-                "B": {
-                  "block": {
-                    "type": "math_number",
-                    "fields": {
-                      "NUM": 5
-                    }
-                  }
-                }
-              }
-            }
-          },
-          "DO0": {
-            "block": {
-              "type": "text_print",
-              "inputs": {
-                "TEXT": {
-                  "block": {
-                    "type": "text",
-                    "fields": {
-                      "TEXT": "条件成立!"
-                    }
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-    ]
-  }
-}`);
-const statusMessage = ref('');
-const statusType = ref('');
-// let workspace = null;
-
-// 从JSON加载工作区 - 改进版
-const loadWorkspaceFromJson = () => {
-  try {
-    const json = JSON.parse(jsonData.value);
-
-    // 1. 确保工作区存在
-    if (!state.workspace) {
-      showStatus('工作区未初始化', 'error');
-      console.error('工作区未初始化');
-      return;
-    }
-
-    console.log('开始加载JSON数据:', JSON.stringify(json).substring(0, 200) + '...');
-
-    // 无论使用哪种策略加载成功后,确保所有块可拖拽
-    const ensureBlocksDraggable = () => {
-      const allBlocks = state.workspace.getAllBlocks();
-      allBlocks.forEach(block => {
-        // 强制设置为可编辑状态,不做任何条件检查
-        block.setEditable(true);
-        // 添加调试日志,确认每个块都被设置为可编辑
-        console.log(`设置块${block.id}为可编辑状态`);
-      });
-      return allBlocks.length;
-    };
-
-    // 2. 尝试多种加载策略
-    try {
-      // 策略1: 先清空工作区再加载
-      state.workspace.clear();
-      Blockly.serialization.workspaces.load(json, state.workspace);
-      const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
-      showStatus(`工作区已成功从JSON加载!共加载${blockCount}个积木。`, 'success');
-      console.log(`策略1成功: 加载了${blockCount}个积木`);
-      return;
-    } catch (error1) {
-      console.warn('策略1失败,尝试策略2:', error1);
-
-      try {
-        // 策略2: 清理JSON后再加载
-        state.workspace.clear();
-        const cleanedJson = deepCleanJsonForConnectionIssues(json);
-        Blockly.serialization.workspaces.load(cleanedJson, state.workspace);
-        const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
-        showStatus(`工作区已通过清理后的JSON成功加载!共加载${blockCount}个积木。`, 'success');
-        console.log(`策略2成功: 加载了${blockCount}个积木`);
-        return;
-      } catch (error2) {
-        console.warn('策略2失败,尝试策略3:', error2);
-
-        try {
-          // 策略3: 直接使用XML序列化API作为中间格式
-          state.workspace.clear();
-
-          // 先将JSON转换为XML字符串
-          const xmlText = jsonToXml(json);
-          console.log('生成的XML:', xmlText.substring(0, 200) + '...');
-
-          // 加载XML到工作区
-          const xmlDom = Blockly.utils.xml.textToDom(xmlText);
-          Blockly.Xml.domToWorkspace(xmlDom, state.workspace);
-
-          const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
-          showStatus(`工作区已通过XML中间格式成功加载!共加载${blockCount}个积木。`, 'success');
-          console.log(`策略3成功: 加载了${blockCount}个积木`);
-          return;
-        } catch (error3) {
-          console.warn('策略3失败,尝试策略4:', error3);
-
-          try {
-            // 策略4: 创建新的工作区实例并使用XML导入
-            // 先释放旧工作区资源
-            if (state.workspace) {
-              state.workspace.dispose();
-            }
-
-            // 创建新的工作区
-            const blocklyDiv = document.getElementById('blocklyDiv');
-            const toolbox = document.getElementById('toolbox');
-
-            state.workspace = Blockly.inject(blocklyDiv, {
-              toolbox: toolbox,
-              collapse: true,
-              comments: true,
-              disable: false,
-              maxBlocks: Infinity,
-              trashcan: true,
-              horizontalLayout: false,
-              toolboxPosition: 'start',
-              css: true,
-              media: 'https://unpkg.com/blockly/media/',
-              rtl: false,
-              scrollbars: true,
-              sounds: false,
-              oneBasedIndex: true,
-              grid: {
-                spacing: 20,
-                length: 3,
-                colour: "#ccc",
-                snap: true
-              },
-              zoom: {
-                controls: true,
-                wheel: true,
-                startScale: 1.0,
-                maxScale: 3,
-                minScale: 0.3,
-                scaleSpeed: 1.2
-              }
-            });
-
-            // 重新添加工作区变化监听器 - 使用合并后的监听器
-            addCombinedChangeListener();
-
-            // 使用XML作为中间格式导入
-            const xmlText = jsonToXml(json);
-            console.log('创建新工作区,生成的XML:', xmlText.substring(0, 200) + '...');
-            const xmlDom = Blockly.utils.xml.textToDom(xmlText);
-            Blockly.Xml.domToWorkspace(xmlDom, state.workspace);
-
-            const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
-            showStatus(`工作区已通过创建新实例并使用XML格式成功加载!共加载${blockCount}个积木。`, 'success');
-            console.log(`策略4成功: 加载了${blockCount}个积木`);
-            return;
-          } catch (error4) {
-            // 所有策略都失败
-            showStatus('JSON解析错误: ' + error4.message, 'error');
-            console.error('所有加载策略均失败:', error4);
-          }
-        }
-      }
-    }
-  } catch (error) {
-    showStatus('JSON解析错误: ' + error.message, 'error');
-    console.error('JSON解析错误:', error);
-  }
-};
-
-// 2. 创建一个合并的工作区变化监听器函数
-function addCombinedChangeListener() {
-  if (!state.workspace) return;
-
-  // 移除旧的监听器(如果有)
-  state.workspace.removeAllChangeListeners();
-
-  // 添加新的合并监听器
-  state.workspace.addChangeListener((event) => {
-    // 生成代码
-    generateCode("javascript");
-
-    // 拖拽修复逻辑
-    if (event.type === Blockly.Events.BLOCK_CREATE || event.type === Blockly.Events.BLOCK_ADD)
-    {
-      const block = state.workspace.getBlockById(event.blockId);
-      if (block) {
-        // 强制设置为可编辑状态
-        block.setEditable(true);
-        console.log(`监听器设置块${block.id}为可编辑状态`);
-      }
-    }
-  });
-}
-
-// 导出工作区为JSON - 增强版
-const exportWorkspaceToJson = () => {
-  try {
-    if (!state.workspace) {
-      showStatus('工作区未初始化', 'error');
-      return;
-    }
-
-    // 使用state.workspace替代workspace
-    const stateJson = Blockly.serialization.workspaces.save(state.workspace);
-    jsonData.value = JSON.stringify(stateJson, null, 2);
-
-    // 显示导出的积木数量
-    const blockCount = state.workspace.getAllBlocks().length;
-    showStatus(`工作区已成功导出为JSON!共导出${blockCount}个积木。`, 'success');
-    console.log(`导出JSON成功: 导出了${blockCount}个积木`);
-  } catch (error) {
-    showStatus('导出错误: ' + error.message, 'error');
-    console.error('导出错误:', error);
-  }
-};
-
-// 深度清理JSON中可能导致连接问题的部分
-function deepCleanJsonForConnectionIssues(json) {
-  const cleanedJson = JSON.parse(JSON.stringify(json));
-
-  // 1. 清理blocks结构
-  if (cleanedJson.blocks && cleanedJson.blocks.blocks) {
-    cleanedJson.blocks.blocks.forEach(block => {
-      // 2. 移除inputs中的connection引用
-      if (block.inputs) {
-        Object.keys(block.inputs).forEach(inputKey => {
-          const input = block.inputs[inputKey];
-          if (input.connection) {
-            delete input.connection;
-          }
-
-          // 3. 递归清理嵌套的block
-          if (input.block) {
-            cleanBlockConnections(input.block);
-          }
-        });
-      }
-
-      // 4. 清理next和previous连接
-      if (block.next) {
-        if (block.next.connection) {
-          delete block.next.connection;
-        }
-        if (block.next.block) {
-          cleanBlockConnections(block.next.block);
-        }
-      }
-
-      if (block.previous) {
-        if (block.previous.connection) {
-          delete block.previous.connection;
-        }
-      }
-    });
-  }
-
-  return cleanedJson;
-}
-
-// 递归清理单个block的连接信息
-function cleanBlockConnections(block) {
-  if (!block) return;
-
-  // 清理inputs中的连接
-  if (block.inputs) {
-    Object.keys(block.inputs).forEach(inputKey => {
-      const input = block.inputs[inputKey];
-      if (input.connection) {
-        delete input.connection;
-      }
-      if (input.block) {
-        cleanBlockConnections(input.block);
-      }
-    });
-  }
-
-  // 清理next和previous连接
-  if (block.next) {
-    if (block.next.connection) {
-      delete block.next.connection;
-    }
-    if (block.next.block) {
-      cleanBlockConnections(block.next.block);
-    }
-  }
-
-  if (block.previous) {
-    if (block.previous.connection) {
-      delete block.previous.connection;
-    }
-  }
-}
-
-// 将JSON转换为XML(增强版)
-function jsonToXml(json) {
-  // 先检查JSON是否有效
-  if (!json || typeof json !== 'object') {
-    console.error('无效的JSON数据');
-    return '<xml xmlns="https://developers.google.com/blockly/xml"></xml>';
-  }
-
-  let xml = '<xml xmlns="https://developers.google.com/blockly/xml">';
-
-  // 1. 处理变量
-  if (json.variables && json.variables.variables) {
-    xml += '<variables>';
-    json.variables.variables.forEach(variable => {
-      xml += `<variable id="${escapeXml(variable.id)}">${escapeXml(variable.name)}</variable>`;
-    });
-    xml += '</variables>';
-  }
-
-  // 2. 处理积木块 - 这是关键部分
-  if (json.blocks && json.blocks.blocks) {
-    // 找出所有根节点积木(没有previous连接的积木)
-    const rootBlocks = json.blocks.blocks.filter(block => !block.previous);
-    console.log(`找到${rootBlocks.length}个根积木块`);
-
-    // 递归生成每个根积木块的XML
-    rootBlocks.forEach(block => {
-      xml += generateBlockXml(block);
-    });
-  } else {
-    console.warn('JSON中没有找到blocks数据');
-  }
-
-  xml += '</xml>';
-  return xml;
-}
-
-// 递归生成单个积木块的XML
-function generateBlockXml(block) {
-  if (!block) return '';
-
-  let blockXml = `<block type="${escapeXml(block.type)}" id="${escapeXml(block.id)}" x="${block.x || 0}" y="${block.y || 0}">`;
-
-  // 处理fields
-  if (block.fields) {
-    Object.keys(block.fields).forEach(fieldName => {
-      const fieldValue = block.fields[fieldName];
-      blockXml += `<field name="${escapeXml(fieldName)}">${escapeXml(String(fieldValue))}</field>`;
-    });
-  }
-
-  // 处理inputs
-  if (block.inputs) {
-    Object.keys(block.inputs).forEach(inputName => {
-      const input = block.inputs[inputName];
-      if (input.block) {
-        blockXml += `<value name="${escapeXml(inputName)}">`;
-        blockXml += generateBlockXml(input.block);
-        blockXml += '</value>';
-      }
-    });
-  }
-
-  // 处理嵌套的next积木
-  if (block.next && block.next.block) {
-    blockXml += '<next>';
-    blockXml += generateBlockXml(block.next.block);
-    blockXml += '</next>';
-  }
-
-  blockXml += '</block>';
-  return blockXml;
-}
-
-// XML转义函数
-function escapeXml(text) {
-  if (typeof text !== 'string') {
-    text = String(text);
-  }
-  return text
-      .replace(/&/g, '&amp;')
-      .replace(/</g, '&lt;')
-      .replace(/>/g, '&gt;')
-      .replace(/"/g, '&quot;')
-      .replace(/'/g, '&apos;');
-}
-
-
-
-// 显示状态消息
-const showStatus = (message, type) => {
-  statusMessage.value = message;
-  statusType.value = type;
-
-  // 3秒后自动清除状态消息
-  setTimeout(() => {
-    statusMessage.value = '';
-  }, 3000);
-};
-
-
-
-
-
-// 注册JavaScript代码生成器
-function registerJavaScriptGenerators() {
-  // 语音输入
-  javascriptGenerator.forBlock['ai_voice_input'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
-    const language = block.getFieldValue('LANGUAGE');
-    const code = `await aiService.recognizeVoice(${prompt || "''"}, '${language}')`;
-    return [code, javascriptGenerator.ORDER_ATOMIC];
-  };
-
-  // 文本生成图片
-  javascriptGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
-    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
-    const code = `await aiService.textToImage(${prompt}, ${waitForCompletion});`;
-    return code;
-  };
-
-  // 文本生成视频
-  javascriptGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
-    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
-    const code = `await aiService.textToVideo(${prompt}, ${waitForCompletion});`;
-    return code;
-  };
-
-  // 文本生成文本
-  javascriptGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
-    const model = block.getFieldValue('MODEL');
-    const code = `await aiService.textToText(${prompt}, '${model}')`;
-    return [code, javascriptGenerator.ORDER_ATOMIC];
-  };
-
-  // 智能台灯控制(单参数)
-  javascriptGenerator.forBlock['ai_smart_lamp_single_param'] = function(block, generator) {
-    const params = generator.valueToCode(block, 'PARAMS', javascriptGenerator.ORDER_ATOMIC);
-    const code = `await aiService.controlLampWithSingleParam(${params || "'白,0,平静'"});`;
-    return code;
-  };
-
-  // 智能台灯控制(多参数)
-  javascriptGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
-    const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
-    const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
-    const code = `await aiService.controlLamp(${brightness || '0'}, ${color || "'白'"});`;
-    return code;
-  };
-
-  // 设置台灯亮度
-  javascriptGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
-    const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
-    const code = `await aiService.setLampBrightness(${brightness || '0'});`;
-    return code;
-  };
-
-  // 设置台灯颜色
-  javascriptGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
-    const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
-    const code = `await aiService.setLampColor(${color || "'白'"});`;
-    return code;
-  };
-
-  // 音乐播放
-  javascriptGenerator.forBlock['ai_music_play'] = function(block, generator) {
-    const musicType = block.getFieldValue('MUSIC_TYPE');
-    const code = `await aiService.playMusic('${musicType}');`;
-    return code;
-  };
-}
-
-// 注册Python代码生成器
-function registerPythonGenerators() {
-  // 语音输入
-  pythonGenerator.forBlock['ai_voice_input'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
-    const language = block.getFieldValue('LANGUAGE');
-    const code = `ai_service.recognize_voice(${prompt || "''"}, '${language}')`;
-    return [code, pythonGenerator.ORDER_ATOMIC];
-  };
-
-  // 文本生成图片
-  pythonGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
-    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
-    const code = `ai_service.text_to_image(${prompt}, ${waitForCompletion})\n`;
-    return code;
-  };
-
-  // 文本生成视频
-  pythonGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
-    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
-    const code = `ai_service.text_to_video(${prompt}, ${waitForCompletion})\n`;
-    return code;
-  };
-
-  // 文本生成文本
-  pythonGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
-    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
-    const model = block.getFieldValue('MODEL');
-    const code = `ai_service.text_to_text(${prompt}, '${model}')`;
-    return [code, pythonGenerator.ORDER_ATOMIC];
-  };
-
-  // 智能台灯控制(单参数)
-  pythonGenerator.forBlock['ai_smart_lamp_single_param'] = function(block, generator) {
-    const params = generator.valueToCode(block, 'PARAMS', pythonGenerator.ORDER_ATOMIC);
-    const code = `ai_service.control_lamp_with_single_param(${params || "'白,0,平静'"})\n`;
-    return code;
-  };
-
-  // 智能台灯控制
-  pythonGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
-    const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
-    const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
-    const code = `ai_service.control_lamp(${brightness || '0'}, ${color || "'白'"})\n`;
-    return code;
-  };
-
-  // 设置台灯亮度
-  pythonGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
-    const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
-    const code = `ai_service.set_lamp_brightness(${brightness || '0'})\n`;
-    return code;
-  };
-
-  // 设置台灯颜色
-  pythonGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
-    const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
-    const code = `ai_service.set_lamp_color(${color || "'白'"})\n`;
-    return code;
-  };
-
-  // 音乐播放
-  pythonGenerator.forBlock['ai_music_play'] = function(block, generator) {
-    const musicType = block.getFieldValue('MUSIC_TYPE');
-    const code = `ai_service.play_music('${musicType}')\n`;
-    return code;
-  };
-}
-
-// 生成代码
-const generateCode = (language) => {
-  if (!state.workspace) {
-    console.error("workspace 未正确初始化");
-    return;
-  }
-
-  let generator;
-  if (language === "javascript") {
-    generator = javascriptGenerator;
-  } else if (language === "python") {
-    generator = pythonGenerator;
-  } else {
-    console.error("不支持的语言类型");
-    return;
-  }
-
-  const code = generator.workspaceToCode(state.workspace);
-  document.getElementById("textarea").value = code;
-};
-
-// 运行代码
-async function runCode() {
-  try {
-    const codeTextarea = document.getElementById("textarea");
-    const code = codeTextarea.value;
-    const resultElement = document.getElementById("run-result");
-
-    // 清空之前的结果
-    resultElement.innerHTML = '<div class="running-indicator">代码运行中...</div>';
-
-    // 保存原始console方法
-    const originalConsoleLog = console.log;
-    const originalConsoleError = console.error;
-    const originalConsoleWarn = console.warn;
-
-    // 创建输出缓冲区
-    let outputBuffer = "";
-
-    // 重定义console方法 - 修复console.log不更新DOM的问题
-    console.log = (...args) => {
-      // 过滤掉包含Vue警告的日志
-      const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
-
-      // 只显示不包含Vue warn的日志
-      if (!message.includes('[Vue warn]')) {
-        outputBuffer += '<div class="log-message">' + message + '</div>';
-        resultElement.innerHTML = outputBuffer; // 关键修复:立即更新DOM
-      }
-      originalConsoleLog.apply(console, args);
-    };
-
-    console.error = (...args) => {
-      // 过滤掉包含Vue警告的日志
-      const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
-
-      // 只显示不包含Vue warn的日志
-      if (!message.includes('[Vue warn]')) {
-        outputBuffer += '<div class="error-message">' + message + '</div>';
-        resultElement.innerHTML = outputBuffer;
-      }
-      originalConsoleError.apply(console, args);
-    };
-
-    console.warn = (...args) => {
-      // 过滤掉包含Vue警告的日志
-      const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
-
-      // 只显示不包含Vue warn的日志
-      if (!message.includes('[Vue warn]')) {
-        outputBuffer += '<div class="warn-message">' + message + '</div>';
-        resultElement.innerHTML = outputBuffer;
-      }
-      originalConsoleWarn.apply(console, args);
-    };
-
-    try {
-      // 添加安全检查
-      if (code.includes('eval(') || code.includes('Function(') ||
-          code.includes('document.write') || code.includes('window.location')) {
-        throw new Error('代码包含不安全的操作');
-      }
-
-      // 包装代码为异步函数执行,支持await
-      const wrappedCode = `(async () => { ${code} })()`;
-      await new Function(wrappedCode)();
-
-      // outputBuffer += '<div class="success-message">代码执行成功</div>';
-      resultElement.innerHTML = outputBuffer;
-    } catch (error) {
-      outputBuffer += `<div class="error-message">执行错误: ${error.message}</div>`;
-      resultElement.innerHTML = outputBuffer;
-    } finally {
-      // 恢复原始console方法
-      // console.log = originalConsoleLog;
-      // console.error = originalConsoleError;
-      // console.warn = originalConsoleWarn;
-    }
-  } catch (err) {
-    console.error('运行代码时发生错误:', err);
-    document.getElementById('run-result').innerHTML =
-        `<div class="error-message">运行代码时发生错误: ${err.message}</div>`;
-  }
-}
-
-// 保存JSON
-function saveJson() {
-  try {
-    const output = document.getElementById('textarea');
-    const json = Blockly.serialization.workspaces.save(state.workspace);
-    output.value = JSON.stringify(json, null, 2);
-    output.focus();
-    output.select();
-    taChange();
-    ElMessage.success('JSON保存成功');
-  } catch (error) {
-    console.error('保存JSON失败:', error);
-    ElMessage.error('保存JSON失败: ' + error.message);
-  }
-}
-
-// 保存XML
-function saveXml() {
-  try {
-    const output = document.getElementById('textarea');
-    const xml = Blockly.Xml.workspaceToDom(state.workspace);
-    output.value = Blockly.Xml.domToPrettyText(xml);
-    output.focus();
-    output.select();
-    taChange();
-    ElMessage.success('XML保存成功');
-  } catch (error) {
-    console.error('保存XML失败:', error);
-    ElMessage.error('保存XML失败: ' + error.message);
-  }
-}
-
-// 重新加载
-function load() {
-  const input = document.getElementById('textarea');
-  if (!input.value) return;
-
-  try {
-    //关闭预览
-    handleClosePreview();
-
-    const valid = saveIsValid(input.value);
-    if (valid.json) {
-      const parsedState = JSON.parse(input.value);
-      Blockly.serialization.workspaces.load(parsedState, state.workspace);
-    } else if (valid.xml) {
-      const xml = Blockly.utils.xml.textToDom(input.value);
-      Blockly.Xml.domToWorkspace(xml, state.workspace);
-    }
-    taChange();
-  } catch (error) {
-    console.error("加载失败:", error);
-    ElMessage.error("加载失败: " + error.message);
-  }
-}
-
-// 文本域变化时触发
-function taChange() {
-  const textarea = document.getElementById("textarea");
-  if (sessionStorage) {
-    sessionStorage.setItem("textarea", textarea.value);
-  }
-  const valid = saveIsValid(textarea.value);
-  document.getElementById("import").disabled = !valid.json && !valid.xml;
-}
-
-// 检查保存内容是否为有效JSON或XML
-function saveIsValid(save) {
-  let validJson = true;
-  try {
-    JSON.parse(save);
-  } catch (e) {
-    validJson = false;
-  }
-
-  let validXml = true;
-  try {
-    Blockly.utils.xml.textToDom(save);
-  } catch (e) {
-    validXml = false;
-  }
-
-  return { json: validJson, xml: validXml };
-}
-
-// 关闭预览
-function handleClosePreview() {
-  state.previewVisible = false;
-  state.previewContent = "";
-  state.previewType = "";
-  state.generatedContent.text = null;
-  state.generatedContent.imageUrl = null;
-  state.generatedContent.videoUrl = null;
-}
-
-// 将aiService挂载到window,以便执行生成的代码时可以访问
-window.aiService = aiService;
-</script>
-
-<style scoped lang="scss">
-/* 原有样式保持不变 */
-@use "sass:math";
-
-@function rpx($px) {
-  @return math.div($px, 750) * 100vw;
-}
-// 智能台灯
-.desk-lamp-container {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  overflow: hidden;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  z-index: 1000;
-}
-
-.full-screen-image {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-}
-
-/* 台灯灯光样式 - 使用动态颜色 */
-.lamp-light-mask {
-  position: absolute;
-  width: rpx(78);
-  height: rpx(250);
-  margin-top: rpx(49);
-  margin-left: rpx(192);
-  background: linear-gradient(to bottom, var(--lamp-color) 0%, var(--lamp-color) 10%, rgba(255, 255, 255, 0) 90%);
-  filter: blur(60px);
-  transform: rotate(9deg);
-  transform-origin: top center;
-  opacity: var(--lamp-opacity, 0.6);
-  /* 创建扇形效果 */
-  clip-path: polygon(0% 0%, 100% 0%, 250% 100%, -150% 100%);
-}
-
-/* 灯光信息显示 */
-.lamp-info {
-  position: absolute;
-  bottom: 100px;
-  right: 30px;
-  background-color: rgba(255, 255, 255, 0.2);
-  padding: 10px 20px;
-  border-radius: 8px;
-  backdrop-filter: blur(10px);
-  color: white;
-  font-size: 14px;
-  z-index: 1000;
-}
-
-/* 标题框样式 */
-.desk-lamp-title-box {
-  position: absolute;
-  top: 20px;
-  left: 20px;
-  z-index: 1000;
-}
-
-.desk-lamp-box-icon {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  padding: 10px 20px;
-  background-color: rgba(255, 255, 255, 0.2);
-  border-radius: 30px;
-  backdrop-filter: blur(10px);
-  cursor: pointer;
-  transition: all 0.3s ease;
-  font-size: 16px;
-  color: white;
-  font-weight: 500;
-}
-
-.desk-lamp-box-icon:hover {
-  background-color: rgba(255, 255, 255, 0.3);
-  transform: translateX(-3px);
-}
-
-.left-icon {
-  font-size: 18px;
-}
-
-/* 右下角按钮组样式 */
-.button-group {
-  position: absolute;
-  bottom: 30px;
-  right: 30px;
-  display: flex;
-  gap: 15px;
-  z-index: 1000;
-}
-
-.control-button {
-  padding: 12px 24px;
-  border-radius: 8px;
-  font-size: 16px;
-  font-weight: 500;
-  transition: all 0.3s ease;
-  backdrop-filter: blur(5px);
-}
-
-.run-button {
-  background-color: rgba(64, 169, 255, 0.8);
-  color: white;
-  border: none;
-}
-
-.run-button:hover {
-  background-color: rgba(64, 169, 255, 1);
-  transform: translateY(-2px);
-}
-
-.code-button {
-  background-color: rgba(132, 94, 255, 0.8);
-  color: white;
-  border: none;
-}
-
-.code-button:hover {
-  background-color: rgba(132, 94, 255, 1);
-  transform: translateY(-2px);
-}
-
-// 页面主容器样式
-.page-container {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: linear-gradient(to bottom, #001169, #b4a8e1);
-  display: flex;
-  flex-direction: column;
-}
-
-// 标题样式
-.title-box {
-  height: rpx(30);
-}
-.box-icon {
-  width: 100%;
-  height: 100%;
-  display: flex;
-  align-items: center;
-  color: white;
-  padding-left: rpx(15);
-  font-size: rpx(10);
-  cursor: pointer;
-}
-.box-icon .left-icon {
-  margin-left: rpx(10);
-  margin-right: rpx(5);
-}
-
-.home-container {
-  flex: 1;
-  display: flex;
-  // padding-top: rpx(10);
-}
-
-.box-blockly {
-  width: 60%;
-  height: 90%;
-  padding: rpx(10) rpx(10);
-  float: left;
-}
-
-.box-code {
-  color: white;
-  width: 35%;
-  height: 90%;
-  padding: rpx(10) rpx(10);
-  display: flex;
-  flex-direction: column;
-
-  .button-container {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 8px;
-    align-items: flex-start;
-  }
-
-  .button-container :deep(.el-button) {
-    flex-shrink: 0;
-  }
-
-  .code-section {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    margin-bottom: 10px;
-
-    .box-code-textarea {
-      flex: 1;
-      resize: none;
-      background-color: #f8f9fa;
-      border: 1px solid #ddd;
-      border-radius: 4px;
-      padding: 10px;
-      overflow-y: auto;
-      font-family: monospace;
-      font-size: 14px;
-    }
-  }
-
-  //运行结果区域样式
-  .result-section {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-
-    .run-result-content {
-      color: black;
-      // 固定高度为300px,可根据需要调整
-      height: 180px;
-      background-color: #f8f9fa;
-      border: 1px solid #ddd;
-      border-radius: 4px;
-      padding: 10px;
-      overflow-y: auto;
-      font-family: monospace;
-      font-size: 14px;
-
-      // 优化滚动条样式 - 适用于Webkit浏览器(Chrome, Safari)
-      &::-webkit-scrollbar {
-        width: 8px;
-        height: 8px;
-      }
-
-      &::-webkit-scrollbar-track {
-        background: #f1f1f1;
-        border-radius: 4px;
-      }
-
-      &::-webkit-scrollbar-thumb {
-        background: #c1c1c1;
-        border-radius: 4px;
-      }
-
-      &::-webkit-scrollbar-thumb:hover {
-        background: #a8a8a8;
-      }
-
-      // Firefox滚动条样式
-      scrollbar-width: thin;
-      scrollbar-color: #c1c1c1 #f1f1f1;
-    }
-  }
-}
-
-// AI模块样式
-[categorystyle="ai_category"] > .blocklyTreeRow {
-  background-color: #9c27b0 !important;
-}
-
-// 运行结果样式
-.running-indicator {
-  color: #666;
-}
-.log-message {
-  color: #333;
-}
-.error-message {
-  color: #dc3545;
-}
-.warn-message {
-  color: #ffc107;
-}
-.success-message {
-  color: #28a745;
-}
-
-// 预览样式
-.preview-image-container,
-.preview-video-container {
-  display: flex;
-  justify-content: center;
-}
-
-.preview-image,
-.preview-video {
-  max-width: 100%;
-  max-height: 60vh;
-  border-radius: 4px;
-}
-
-.preview-text-container {
-  max-height: 60vh;
-  overflow-y: auto;
-  padding: 10px;
-  background-color: #f5f5f5;
-  border-radius: 4px;
-}
-
-//【文生图预览】
-.extra-image-preview {
-  color: black;
-  margin-top: 10px;
-  padding: 10px;
-  border: 1px solid #ddd;
-  border-radius: 5px;
-  background-color: #f9f9f9;
-}
-
-.extra-preview-image {
-  max-width: 100%;
-  max-height: 400px;
-  border-radius: 4px;
-}
-
-//台灯
-.lamp-preview-container {
-  margin-top: 20px;
-  padding: 10px;
-  background-color: rgba(255, 255, 255, 0.1);
-  border-radius: 8px;
-  text-align: center;
-}
-
-.lamp-display {
-  margin: 15px auto;
-  position: relative;
-  transition: all 0.3s ease;
-}
-
-.lamp-image {
-  width: 100px;
-  height: 150px;
-  margin: 0 auto;
-  background-color: #e0e0e0;
-  border-radius: 10px 10px 0 0;
-  position: relative;
-  box-shadow: 0 0 40px 20px #ffffff80;
-  transition: box-shadow 0.5s ease;
-
-  &::before {
-    content: "";
-    position: absolute;
-    top: 10px;
-    left: 10px;
-    right: 10px;
-    bottom: 40px;
-    background-color: #f5f5f5;
-    border-radius: 5px;
-  }
-
-  &::after {
-    content: "";
-    position: absolute;
-    bottom: 10px;
-    left: 40px;
-    width: 20px;
-    height: 30px;
-    background-color: #888888;
-  }
-}
-
-.lamp-info {
-  margin-top: 10px;
-  font-size: 14px;
-  color: #ffffff;
-}
-
-//音乐播放器
-.music-player-container {
-  margin-top: 20px;
-  padding: 15px;
-  background-color: #f9f9f9;
-  border-radius: 8px;
-  border: 1px solid #e0e0e0;
-}
-
-.music-player-container h5 {
-  margin-top: 0;
-  margin-bottom: 10px;
-  color: #333;
-  font-size: 16px;
-}
-
-.music-player-container audio {
-  width: 100%;
-  margin-bottom: 10px;
-}
-
-.music-status {
-  font-size: 14px;
-  color: #666;
-  padding: 5px 0;
-}
-.json-section {
-  margin-top: 15px;
-  padding: 15px;
-  background: #f1f8ff;
-  border-radius: 8px;
-  border: 1px solid #d1e7ff;
-}
-
-.json-section h3 {
-  margin-bottom: 10px;
-  color: #2c3e50;
-}
-
-textarea {
-  width: 100%;
-  min-height: 120px;
-  padding: 10px;
-  border: 1px solid #ddd;
-  border-radius: 5px;
-  font-family: 'Courier New', monospace;
-  resize: vertical;
-}
-</style>

+ 0 - 486
src/views/Blockly_bak.vue

@@ -1,486 +0,0 @@
-<template>
-  <!-- Blockly -->
-  <div class="home-container">
-
-    <div class="box-blockly">
-      <div id="blocklyDiv" style="height: 100%; width: 100%; background-color: #cccccc"></div>
-
-      <!-- blockly工具栏 -->
-      <!-- 使用 template 标签包裹 XML 内容 -->
-      <template >
-        <xml id="toolbox" style="display: none">
-          <category name="逻辑" categorystyle="logic_category">
-            <block type="controls_if"></block>
-            <block type="logic_compare"></block>
-            <block type="logic_operation"></block>
-            <block type="logic_negate"></block>
-            <block type="logic_boolean"></block>
-            <block type="logic_null" disabled="true"></block>
-            <block type="logic_ternary"></block>
-          </category>
-          <category name="循环" categorystyle="loop_category">
-            <block type="controls_repeat_ext">
-              <value name="TIMES">
-                <shadow type="math_number">
-                  <field name="NUM">10</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="controls_repeat" disabled="true"></block>
-            <block type="controls_whileUntil"></block>
-            <block type="controls_for">
-              <value name="FROM">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="TO">
-                <shadow type="math_number">
-                  <field name="NUM">10</field>
-                </shadow>
-              </value>
-              <value name="BY">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="controls_forEach"></block>
-            <block type="controls_flow_statements"></block>
-          </category>
-          <category name="数值" categorystyle="math_category">
-            <block type="math_number" gap="32">
-              <field name="NUM">123</field>
-            </block>
-            <block type="math_arithmetic">
-              <value name="A">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="B">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_single">
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM">9</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_trig">
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM">45</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_constant"></block>
-            <block type="math_number_property">
-              <value name="NUMBER_TO_CHECK">
-                <shadow type="math_number">
-                  <field name="NUM">0</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_round">
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM">3.1</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_on_list"></block>
-            <block type="math_modulo">
-              <value name="DIVIDEND">
-                <shadow type="math_number">
-                  <field name="NUM">64</field>
-                </shadow>
-              </value>
-              <value name="DIVISOR">
-                <shadow type="math_number">
-                  <field name="NUM">10</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_constrain">
-              <value name="VALUE">
-                <shadow type="math_number">
-                  <field name="NUM">50</field>
-                </shadow>
-              </value>
-              <value name="LOW">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="HIGH">
-                <shadow type="math_number">
-                  <field name="NUM">100</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_random_int">
-              <value name="FROM">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="TO">
-                <shadow type="math_number">
-                  <field name="NUM">100</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="math_random_float"></block>
-            <block type="math_atan2">
-              <value name="X">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="Y">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-            </block>
-          </category>
-          <category name="文本" categorystyle="text_category">
-            <block type="text"></block>
-            <block type="text_join"></block>
-            <block type="text_append">
-              <value name="TEXT">
-                <shadow type="text"></shadow>
-              </value>
-            </block>
-            <block type="text_length">
-              <value name="VALUE">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_isEmpty">
-              <value name="VALUE">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_indexOf">
-              <value name="VALUE">
-                <block type="variables_get">
-                  <field name="VAR">text</field>
-                </block>
-              </value>
-              <value name="FIND">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_charAt">
-              <value name="VALUE">
-                <block type="variables_get">
-                  <field name="VAR">text</field>
-                </block>
-              </value>
-            </block>
-            <block type="text_getSubstring">
-              <value name="STRING">
-                <block type="variables_get">
-                  <field name="VAR">text</field>
-                </block>
-              </value>
-            </block>
-            <block type="text_changeCase">
-              <value name="TEXT">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_trim">
-              <value name="TEXT">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_count">
-              <value name="SUB">
-                <shadow type="text"></shadow>
-              </value>
-              <value name="TEXT">
-                <shadow type="text"></shadow>
-              </value>
-            </block>
-            <block type="text_replace">
-              <value name="FROM">
-                <shadow type="text"></shadow>
-              </value>
-              <value name="TO">
-                <shadow type="text"></shadow>
-              </value>
-              <value name="TEXT">
-                <shadow type="text"></shadow>
-              </value>
-            </block>
-            <block type="text_reverse">
-              <value name="TEXT">
-                <shadow type="text"></shadow>
-              </value>
-            </block>
-            <label text="Input/Output:" web-class="ioLabel"></label>
-            <block type="text_print">
-              <value name="TEXT">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="text_prompt_ext">
-              <value name="TEXT">
-                <shadow type="text">
-                  <field name="TEXT">abc</field>
-                </shadow>
-              </value>
-            </block>
-          </category>
-          <category name="列表" categorystyle="list_category">
-            <block type="lists_create_with">
-              <mutation items="0"></mutation>
-            </block>
-            <block type="lists_create_with"></block>
-            <block type="lists_repeat">
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM">5</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="lists_length"></block>
-            <block type="lists_isEmpty"></block>
-            <block type="lists_indexOf">
-              <value name="VALUE">
-                <block type="variables_get">
-                  <field name="VAR">list</field>
-                </block>
-              </value>
-            </block>
-            <block type="lists_getIndex">
-              <value name="VALUE">
-                <block type="variables_get">
-                  <field name="VAR">list</field>
-                </block>
-              </value>
-            </block>
-            <block type="lists_setIndex">
-              <value name="LIST">
-                <block type="variables_get">
-                  <field name="VAR">list</field>
-                </block>
-              </value>
-            </block>
-            <block type="lists_getSublist">
-              <value name="LIST">
-                <block type="variables_get">
-                  <field name="VAR">list</field>
-                </block>
-              </value>
-            </block>
-            <block type="lists_split">
-              <value name="DELIM">
-                <shadow type="text">
-                  <field name="TEXT">,</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="lists_sort"></block>
-            <block type="lists_reverse"></block>
-          </category>
-          <sep></sep>
-          <category
-              name="Variables"
-              categorystyle="variable_category"
-              custom="VARIABLE"></category>
-          <category
-              name="Functions"
-              categorystyle="procedure_category"
-              custom="PROCEDURE"></category>
-        </xml>
-      </template>
-    </div>
-
-    <div class="box-code">
-      <el-button type="info" plain id="save-json" @click="saveJson()">保存 JSON</el-button>
-      <el-button type="info" plain id="save-xml" @click="saveXml()">保存 XML</el-button>
-      <el-button type="warning" plain id="import" @click="load()">重新加载</el-button>
-      <br/><br/>
-
-      <!-- 添加生成 JavaScript 和 Python 代码的按钮 -->
-      <el-button type="primary" plain id="to-code-py" @click="generateCode('javascript')">生成 JavaScript</el-button>
-      <el-button type="success" plain id="to-code-py" @click="generateCode('python')">生成 Python</el-button>
-      <br/><br/>
-      <!-- blockly代码区 -->
-      <textarea name="" id="textarea" class="box-code-textarea"></textarea>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted } from 'vue'
-import * as Blockly from 'blockly';
-import {javascriptGenerator} from 'blockly/javascript';
-// 引入 Python 代码生成器
-import {pythonGenerator} from 'blockly/python';
-// 引入想要转换的语言,语言有php python dart lua javascript
-// import 'blockly/javascript'
-// 引入语言包并使用
-import * as hans from 'blockly/msg/zh-hans'
-Blockly.setLocale(hans);
-
-// 使用 ref 存储 workspace
-const workspace = ref(null)
-
-onMounted(async () => {
-
-  workspace.value = Blockly.inject('blocklyDiv',
-      {
-        //工具栏
-        toolbox: document.getElementById('toolbox'),
-        //网格效果
-        grid:{spacing: 20,length: 3,colour: '#ccc',snap: true},
-        // 启用垃圾桶
-        trashcan: true
-      }
-  );
-  // 工作区监听代码生成器
-  // 修改为传递一个函数
-  workspace.value.addChangeListener(() => generateCode("python"));
-})
-
-//保存Json
-function saveJson() {
-  var output = document.getElementById('textarea');
-  var state = Blockly.serialization.workspaces.save(workspace.value);
-  output.value = JSON.stringify(state, null, 2);
-  output.focus();
-  output.select();
-  taChange();
-}
-
-//保存Xml
-function saveXml() {
-  var output = document.getElementById('textarea');
-  var xml = Blockly.Xml.workspaceToDom(workspace.value);
-  output.value = Blockly.Xml.domToPrettyText(xml);
-  output.focus();
-  output.select();
-  taChange();
-}
-
-//重新加载
-function load() {
-  var input = document.getElementById('textarea');
-  if (!input.value) {
-    return;
-  }
-  var valid = saveIsValid(input.value);
-  if (valid.json) {
-    var state = JSON.parse(input.value);
-    Blockly.serialization.workspaces.load(state, workspace.value);
-  } else if (valid.xml) {
-    var xml = Blockly.utils.xml.textToDom(input.value);
-    Blockly.Xml.domToWorkspace(xml, workspace.value);
-  }
-  taChange();
-}
-
-//
-function taChange() {
-  var textarea = document.getElementById('textarea');
-  if (sessionStorage) {
-    sessionStorage.setItem('textarea', textarea.value);
-  }
-  var valid = saveIsValid(textarea.value);
-  document.getElementById('import').disabled = !valid.json && !valid.xml;
-}
-
-function saveIsValid(save) {
-  var validJson = true;
-  try {
-    JSON.parse(save);
-  } catch (e) {
-    validJson = false;
-  }
-  var validXml = true;
-  try {
-    Blockly.utils.xml.textToDom(save);
-  } catch (e) {
-    validXml = false;
-  }
-  return {
-    json: validJson,
-    xml: validXml,
-  };
-}
-
-// 定义生成代码的函数
-const generateCode = (language) => {
-  if (!workspace.value) {
-    console.error('workspace 未正确初始化');
-    return;
-  }
-  let generator;
-  if (language === 'javascript') {
-    generator = javascriptGenerator;
-  } else if (language === 'python') {
-    generator = pythonGenerator;
-  } else {
-    console.error('不支持的语言类型');
-    return;
-  }
-  const code = generator.workspaceToCode(workspace.value);
-  document.getElementById('textarea').value = code;
-};
-</script>
-
-<style scoped lang="scss">
-@use 'sass:math';
-// 定义rpx转换函数
-@function rpx($px) {
-  @return math.div($px, 750) * 100vw;
-}
-
-.home-container {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: linear-gradient(to bottom, #001169, #b4a8e1);
-  display: flex;
-  padding-top: rpx(10) ;
-}
-.box-blockly {
-  width: 60%;
-  height: 90%;
-  padding: rpx(10) rpx(10);
-  float: left;
-}
-.box-code{
-  width: 35%;
-  padding: rpx(10) rpx(10);
-
-  .box-code-textarea{
-    height: 80%;
-    width: 100%;
-  }
-}
-
-</style>

+ 2162 - 0
src/views/block/Blockly.vue

@@ -0,0 +1,2162 @@
+<template>
+  <!-- 智能台灯 -->
+  <div v-if="showLampPreview" class="desk-lamp-container">
+    <!-- 标题框 -->
+    <div class="desk-lamp-title-box">
+      <div class="desk-lamp-box-icon" @click="goBackLab">
+        <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
+        返回虚拟实验室
+      </div>
+    </div>
+
+    <!-- 收音状态显示区域 - 添加这段代码 -->
+    <div v-if="isRecording" class="recording-status-container">
+      <div class="recording-text">正在收音...</div>
+      <div class="equalizer">
+        <div class="bar bar-1"></div>
+        <div class="bar bar-2"></div>
+        <div class="bar bar-3"></div>
+        <div class="bar bar-4"></div>
+        <div class="bar bar-5"></div>
+        <div class="bar bar-6"></div>
+        <div class="bar bar-7"></div>
+      </div>
+      <div v-if="recordingCountdown <= 5" class="recording-countdown">{{ recordingCountdown }}秒</div>
+    </div>
+
+    <img src="@/assets/images/desklamp.png" alt="智能台灯" class="full-screen-image" />
+    <!-- 使用动态样式设置灯光遮罩 -->
+    <div v-if="state.lamp.isLightOn"  :style="{ '--lamp-color': state.lamp.color,'--lamp-opacity': state.lamp.brightness / 100 }" class="lamp-light-mask"></div>
+
+    <!-- 右下角按钮组 -->
+    <div class="button-group">
+      <el-button class="control-button run-button" @click="toggleLight">运行</el-button>
+      <el-button class="control-button code-button" @click="handleViewCode">代码</el-button>
+    </div>
+
+    <!-- 显示当前灯光信息 -->
+    <div v-if="state.lamp.isLightOn" class="lamp-info">
+      <p>颜色: {{ state.lamp.colorLog }}色</p>
+      <p>亮度: {{ state.lamp.brightness }}%</p>
+
+      <!-- 音乐播放状态显示和控制按钮 -->
+      <div v-if="state.isMusicPlaying" class="music-info">
+        <p>正在播放: {{ state.currentMusicName }}</p>
+        <button class="stop-music-btn" @click="handleStopMusic">
+          停止播放
+        </button>
+      </div>
+    </div>
+  </div>
+
+
+  <!-- Blockly编程界面 -->
+  <div class="container">
+    <!-- 返回智能台灯 -->
+    <div class="title-box">
+      <div class="box-icon" @click="goBack">
+        <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
+        返回智能台灯
+      </div>
+    </div>
+
+    <!--    工具箱-->
+    <div class="content">
+      <div class="toolbox-section">
+        <h2>工具箱</h2>
+        <div id="toolbox" style="display: none;">
+
+          <!-- 添加AI模块分类 -->
+          <category name="AI模块" categorystyle="ai_category">
+            <block type="ai_voice_input"></block>
+            <block type="ai_text_to_image"></block>
+            <block type="ai_text_to_video"></block>
+            <block type="ai_text_to_text"></block>
+            <block type="ai_smart_lamp_single_param"></block>
+            <block type="ai_smart_lamp"></block>
+            <block type="ai_lamp_set_brightness"></block>
+            <block type="ai_lamp_set_color"></block>
+            <block type="ai_music_play"></block>
+          </category>
+
+          <category name="逻辑" colour="%{BKY_LOGIC_HUE}">
+            <block type="controls_if"></block>
+            <block type="logic_compare"></block>
+            <block type="logic_operation"></block>
+            <block type="logic_negate"></block>
+            <block type="logic_boolean"></block>
+          </category>
+          <category name="循环" colour="%{BKY_LOOPS_HUE}">
+            <block type="controls_repeat_ext">
+              <value name="TIMES">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="controls_whileUntil"></block>
+          </category>
+          <category name="数学" colour="%{BKY_MATH_HUE}">
+            <block type="math_number"></block>
+            <block type="math_arithmetic"></block>
+            <block type="math_single"></block>
+          </category>
+          <category name="文本" colour="%{BKY_TEXTS_HUE}">
+            <block type="text"></block>
+            <block type="text_length"></block>
+            <block type="text_print"></block>
+          </category>
+          <category name="变量" colour="%{BKY_VARIABLES_HUE}" custom="VARIABLE"></category>
+        </div>
+
+        <div class="json-section">
+          <h3> 数据</h3>
+          <textarea v-model="jsonDataString" rows="10" placeholder="在此输入JSON格式的积木块数据..."></textarea>
+          <div class="controls">
+            <button @click="loadWorkspaceFromJson">加载JSON到工作区</button>
+            <button @click="exportWorkspaceToJson">导出工作区为JSON</button>
+            <button id="generateCode" @click="generateCode('javascript')">生成JavaScript代码</button>
+            <button id="generateCode" @click="generateCode('python')">生成Python代码</button>
+          </div>
+          <div v-if="statusMessage" :class="['status', statusType]">
+            {{ statusMessage }}
+          </div>
+        </div>
+
+        <!-- 在template部分的适当位置音频播放器组件 -->
+        <div class="music-player-container" v-if="state.currentMusicUrl">
+          <h5>音乐播放</h5>
+          <audio
+              ref="musicPlayer"
+              :src="state.currentMusicUrl"
+              @ended="handleMusicEnded"
+              preload="metadata">
+            您的浏览器不支持音频元素
+          </audio>
+          <div class="music-status">
+            <p v-if="state.isMusicPlaying">正在播放: {{ state.currentMusicName }}</p>
+            <p v-else>准备就绪</p>
+          </div>
+          <!-- 停止播放按钮 - 修复点击事件 -->
+          <el-button
+              v-if="state.isMusicPlaying"
+              type="danger"
+              size="small"
+              @click="handleStopMusic"
+              style="margin-top: 10px;">
+            停止播放
+          </el-button>
+        </div>
+
+      </div>
+
+      <!--      工作区-->
+      <div class="workspace-section">
+        <h2>工作区</h2>
+        <div id="blocklyDiv"></div>
+        <div class="controls">
+          <button id="runCode" @click="runCode">运行代码</button>
+          <button @click="clearWorkspace">清空工作区</button>
+        </div>
+      </div>
+
+      <!--      输出-->
+      <div class="output-section">
+        <h2>输出</h2>
+        <div class="controls">
+          <button @click="clearOutput">清空输出</button>
+        </div>
+        <pre id="output">{{ output }}</pre>
+      </div>
+
+
+      <!-- AI结果预览模态框 -->
+      <div v-if="state.previewVisible" class="preview-modal" @click="handleClosePreview">
+        <div class="preview-content" @click.stop>
+          <button class="close-button" @click="handleClosePreview">&times;</button>
+          <div v-if="state.previewType === 'image'" class="preview-image-container">
+            <img :src="state.previewContent" alt="AI生成图片" class="preview-image">
+          </div>
+          <div v-else-if="state.previewType === 'video'" class="preview-video-container">
+            <video :src="state.previewContent" controls class="preview-video"></video>
+          </div>
+          <div v-else-if="state.previewType === 'text'" class="preview-text-container">
+            {{ state.previewContent }}
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+// 仅保留必要的导入和主组件逻辑
+import {ref, onMounted, onUnmounted, reactive, computed} from 'vue';
+import { useRouter } from 'vue-router';
+import { ArrowLeftBold } from '@element-plus/icons-vue';
+import * as Blockly from "blockly";
+import 'blockly/msg/zh-hans';
+import { javascriptGenerator } from "blockly/javascript";
+import { pythonGenerator } from "blockly/python";
+
+// 【文生图】文生图
+import {
+  AiImageStatusEnum,
+  CreatePainting,
+  PaintingGetMys,
+  CreateVideo,
+  VideoGetMys,
+  sendChatMessageStream,
+  CreateDialogue
+} from "@/api/questions.js";
+import { getModelIdByType, ModelPlatformEnum } from "@/api/teachers.js";
+import { ModelTypeEnum } from "@/api/teachers.js";
+import { globalState } from "@/utils/globalState.js";
+//音乐
+import { playMusic, stopMusic, onMusicEnded } from "@/api/blockly/music.js";
+import {ElButton} from "element-plus";
+
+const router = useRouter();
+
+// 设备信息
+const device = ref({
+  name: "",
+  image: "",
+  jsonData: {}
+});
+
+// 台灯预览显示状态
+const showLampPreview = ref(true);
+// 语音识别
+const isRecording = ref(false);
+const recordingCountdown = ref(10);
+let countdownInterval = null;
+
+// 返回虚拟实验室
+const goBackLab = () => {
+  router.push("/virtual-laboratory");
+};
+const goBack = () => {
+  showLampPreview.value = true;
+};
+// 切换灯光状态
+const toggleLight = () => {
+  state.lamp.isLightOn = true;
+  generateCode('javascript');
+  // 在运行前设置为正在录音状态
+  startRecordingStatus();
+  runCode();
+};
+
+// 添加开始录音状态函数
+const handleMusicEnded = () => {
+  onMusicEnded(state);
+};
+
+// 添加开始录音状态函数
+function startRecordingStatus() {
+  isRecording.value = true;
+  recordingCountdown.value = 10;
+
+  // 清除之前的定时器
+  if (countdownInterval) {
+    clearInterval(countdownInterval);
+  }
+
+  // 设置新的倒计时
+  countdownInterval = setInterval(() => {
+    recordingCountdown.value--;
+    if (recordingCountdown.value <= 0) {
+clearInterval(countdownInterval);
+      endRecordingStatus();
+    }
+  }, 1000);
+}
+
+// 添加结束录音状态函数
+function endRecordingStatus() {
+  isRecording.value = false;
+  if (countdownInterval) {
+    clearInterval(countdownInterval);
+    countdownInterval = null;
+  }
+}
+// 查看代码编程界面显示状态
+const handleViewCode = () => {
+  showLampPreview.value = false;
+};
+
+// 创建计算属性处理 JSON 字符串的序列化和反序列化
+const jsonDataString = computed({
+  get() {
+    // 获取时序列化对象为字符串
+    return JSON.stringify(device.value.jsonData, null, 2);
+  },
+  set(value) {
+    // 设置时解析字符串为对象
+    try {
+      device.value.jsonData = JSON.parse(value);
+    } catch (e) {
+      console.error("无效的JSON格式", e);
+      // 可以添加错误提示给用户
+    }
+  }
+});
+
+// 响应式变量
+// const jsonData = ref({
+//   "blocks": {
+//     "languageVersion": 0,
+//     "blocks": [
+//       {
+//         "type": "variables_set",
+//         "id": "kM:Fgf:wd4U3Z$j0x8oK",
+//         "x": 90,
+//         "y": 130,
+//         "fields": {
+//           "VAR": {
+//             "id": "MHW(ZbOKhL!/An`5N@6`"
+//           }
+//         },
+//         "inputs": {
+//           "VALUE": {
+//             "block": {
+//               "type": "ai_voice_input",
+//               "id": "l5E=g|1L+4hThQ8v})lQ",
+//               "fields": {
+//                 "LANGUAGE": "zh-CN"
+//               },
+//               "inputs": {
+//                 "PROMPT": {
+//                   "block": {
+//                     "type": "text",
+//                     "id": "Q*n.c_)@7j^E2=s5/X!n",
+//                     "fields": {
+//                       "TEXT": "请发言:"
+//                     }
+//                   }
+//                 }
+//               }
+//             }
+//           }
+//         },
+//         "next": {
+//           "block": {
+//             "type": "variables_set",
+//             "id": "]g.xbBe.i=a9B*Kfw@|`",
+//             "fields": {
+//               "VAR": {
+//                 "id": "zn.7{ZqbUaH1?P,R05hF"
+//               }
+//             },
+//             "inputs": {
+//               "VALUE": {
+//                 "block": {
+//                   "type": "ai_text_to_text",
+//                   "id": "R$h+R!6#@+4=+WX1*nvh",
+//                   "inputs": {
+//                     "PROMPT": {
+//                       "block": {
+//                         "type": "variables_get",
+//                         "id": "h$S$nt)3VU.=nX*W-mo~",
+//                         "fields": {
+//                           "VAR": {
+//                             "id": "MHW(ZbOKhL!/An`5N@6`"
+//                           }
+//                         }
+//                       }
+//                     },
+//                     "提示词": {
+//                       "block": {
+//                         "type": "text",
+//                         "id": "7k%sgLP?i]e[,m^49P++",
+//                         "fields": {
+//                           "TEXT": "请只回复我指定格式:白,100,热闹"
+//                         }
+//                       }
+//                     }
+//                   }
+//                 }
+//               }
+//             },
+//             "next": {
+//               "block": {
+//                 "type": "ai_smart_lamp_single_param",
+//                 "id": "!.0;Ktwm+Z?o8_9FRa}G",
+//                 "inputs": {
+//                   "PARAMS": {
+//                     "block": {
+//                       "type": "variables_get",
+//                       "id": "d{cIJ-kEFFQcn~%A,g@g",
+//                       "fields": {
+//                         "VAR": {
+//                           "id": "zn.7{ZqbUaH1?P,R05hF"
+//                         }
+//                       }
+//                     }
+//                   }
+//                 }
+//               }
+//             }
+//           }
+//         }
+//       }
+//     ]
+//   },
+//   "variables": [
+//     {
+//       "name": "inputText",
+//       "id": "MHW(ZbOKhL!/An`5N@6`"
+//     },
+//     {
+//       "name": "lampConfig",
+//       "id": "zn.7{ZqbUaH1?P,R05hF"
+//     }
+//   ]
+// });
+
+//输出结果
+const output = ref('');
+const statusMessage = ref('');
+const statusType = ref('');
+let workspace = null;
+
+// 创建音乐播放器引用
+const musicPlayer = ref(null);
+
+// 状态管理
+const state = reactive({
+  workspace: null,
+  generatedContent: {
+    imageUrl: "",
+    videoUrl: "",
+    text: "",
+  },
+  previewVisible: false,
+  previewType: "",
+  previewContent: "",
+  isProcessing: false,
+
+  //年级
+  gradeId: "",
+
+  //【文生图】文生图
+  inProgressImageMap: {},
+
+  //【文生视频】文生视频
+  inProgressVideoMap: {},
+
+  // 台灯状态
+  lamp: {
+    isLightOn: false,// 台灯是否亮着
+    brightness: 50, // 默认亮度50%
+    color: "#ffffff", // 默认颜色白色
+    colorLog: "白", // 默认颜色白色
+  },
+
+  // 【文本文】对话相关状态
+  activeConversationId: null,
+  conversationInAbortController: null,
+
+  // 独立的音乐播放状态
+  currentMusicUrl: '',
+  currentMusicName: '',
+  isMusicPlaying: false,
+});
+
+// 统一轮询管理器
+const pollingManager = {
+  timers: {},
+
+  // 启动轮询
+  startPolling(type, callback, interval = 3000) {
+    // 如果已有相同类型的轮询,先清除
+    this.stopPolling(type);
+
+    this.timers[type] = setInterval(async () => {
+      try {
+        await callback();
+      } catch (error) {
+        console.error(`${type}轮询失败:`, error);
+      }
+    }, interval);
+
+    return this.timers[type];
+  },
+
+  // 停止轮询
+  stopPolling(type) {
+    if (this.timers[type]) {
+      clearInterval(this.timers[type]);
+      this.timers[type] = null;
+    }
+  },
+
+  // 停止所有轮询
+  stopAll() {
+    Object.keys(this.timers).forEach(type => this.stopPolling(type));
+  }
+};
+
+// 统一的错误处理包装器
+function withErrorHandling(operationName, fn, errorMessage = null) {
+  return async function(...args) {
+    try {
+      state.isProcessing = true;
+      return await fn.apply(this, args);
+    } catch (error) {
+      console.error(`${operationName}失败:`, error);
+      showStatus(errorMessage || `${operationName}发生错误: ${error.message || '未知错误'}`);
+      return null;
+    } finally {
+      state.isProcessing = false;
+    }
+  };
+}
+
+// 任务状态轮询公共函数
+async function pollTaskStatus(taskType, taskIds, fetchApi, onSuccess, onFailure) {
+  if (taskIds.length === 0) {
+    pollingManager.stopPolling(taskType);
+    return {};
+  }
+
+  try {
+    const list = await fetchApi(taskIds);
+    const activeTasks = {};
+
+    list.data.forEach((task) => {
+      if (task.status === AiImageStatusEnum.IN_PROGRESS) {
+        activeTasks[task.id] = task;
+      } else if (task.status === AiImageStatusEnum.SUCCESS) {
+        // 任务成功完成
+        if (onSuccess) {
+          onSuccess(task);
+        }
+      } else if (task.status === AiImageStatusEnum.FAIL) {
+        // 任务失败
+        if (onFailure) {
+          onFailure(task);
+        }
+      }
+    });
+
+    return activeTasks;
+  } catch (error) {
+    console.error(`${taskType}状态轮询失败:`, error);
+    return {};
+  }
+}
+
+// AI服务模块 - 统一管理
+const aiService = {
+  // 语音识别
+  recognizeVoice: withErrorHandling('语音识别', async function(promptText = "", language = "zh-CN") {
+    console.log("语音识别开始");
+    // 前端语音采集
+    const recognitionResult = await this.captureVoice(language, promptText);
+    return recognitionResult || "";
+  }, '语音识别失败'),
+
+  // 前端语音采集
+  captureVoice(language, promptText) {
+    return new Promise((resolve) => {
+      if (
+          !"webkitSpeechRecognition" in window &&
+          !"SpeechRecognition" in window
+      ) {
+        showStatus("您的浏览器不支持语音识别功能");
+        resolve("");
+        return;
+      }
+
+      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+      const recognition = new SpeechRecognition();
+
+      recognition.lang = language;
+      recognition.interimResults = false;
+      recognition.maxAlternatives = 1;
+
+      let countdown = 10;
+
+      // 固定的消息提示框
+      const messageText = promptText ? `${promptText}\n请开始说话...(${countdown}秒)` : `请开始说话...(${countdown}秒)`;
+      showStatus(messageText);
+
+      // 倒计时
+      const timer = setInterval(() => {
+        countdown--;
+        if (countdown > 0) {
+          const newText = promptText ? `${promptText}\n请开始说话...(${countdown}秒)` : `请开始说话...(${countdown}秒)`;
+          showStatus(newText);
+        } else {
+          clearInterval(timer);
+          // 倒计时结束时也要调用endRecordingStatus
+          endRecordingStatus();
+        }
+      }, 1000);
+
+      recognition.onresult = (event) => {
+        const speechResult = event.results[0][0].transcript;
+        console.log("语音识别结果:", speechResult);
+        showStatus("语音识别完成");
+        endRecordingStatus(); // 添加这行,在识别成功时结束语音状态
+        resolve(speechResult);
+      };
+
+      recognition.onerror = (event) => {
+        console.error("语音识别错误:", event.error);
+        showStatus("语音识别发生错误: " + event.error, 'error');
+        endRecordingStatus(); // 添加这行,在识别错误时结束语音状态
+        resolve("");
+      };
+
+      // 添加onend事件处理程序,确保语音识别无论如何结束都会清除状态
+      recognition.onend = () => {
+        console.log("语音识别结束");
+        endRecordingStatus(); // 确保在识别结束时调用
+        clearInterval(timer); // 确保清除倒计时定时器
+      };
+
+      recognition.start();
+    });
+  },
+
+  // 文本生成图片
+  textToImage: withErrorHandling('AI图片生成', async function(prompt, waitForCompletion = true) {
+    console.log("AI图片生成中,提示词:", prompt);
+
+    //获取文生图-模型id
+    const modelRes = await getModelIdByType({
+      type: ModelTypeEnum.TEXT_TO_IMAGE,
+      platform: ModelPlatformEnum.DOUBAO,
+    });
+    if (!modelRes.data) {
+      showStatus("获取模型ID失败", 'error');
+      return null;
+    }
+
+    // 使用CreatePainting API创建图片任务
+    const createRes = await CreatePainting({
+      modelId: modelRes.data,
+      prompt: prompt,
+      width: 1024,
+      height: 1024,
+    });
+
+    // 记录任务ID到映射中
+    state.inProgressImageMap[createRes.data] = {
+      id: createRes.data,
+      status: AiImageStatusEnum.IN_PROGRESS,
+    };
+
+    // 开始轮询任务状态
+    this.startPollingTasks('image');
+
+    // 如果需要等待完成,等待图片生成完成
+    if (waitForCompletion) {
+      console.log("AI图片生成中,请等待。。。:");
+      return await this.waitForImageCompletion(createRes.data);
+    }
+
+    return createRes.data; // 返回任务ID
+  }, '生成图片失败'),
+
+  // 【文生图】等待图片生成完成
+  waitForImageCompletion(imageId) {
+    return new Promise((resolve, reject) => {
+      const checkInterval = setInterval(async () => {
+        try {
+          const list = await PaintingGetMys([imageId]);
+          if (list.data && list.data.length > 0) {
+            const image = list.data[0];
+            if (image.status === AiImageStatusEnum.SUCCESS) {
+              clearInterval(checkInterval);
+              resolve(image.picUrl);
+            } else if (image.status === AiImageStatusEnum.FAIL) {
+              clearInterval(checkInterval);
+              reject(new Error(image.error || "图片生成失败"));
+            }
+          }
+        } catch (error) {
+          clearInterval(checkInterval);
+          reject(error);
+        }
+      }, 3000);
+    });
+  },
+
+  // 文本生成视频
+  textToVideo: withErrorHandling('AI视频生成', async function(prompt, waitForCompletion = true) {
+    console.log("AI视频生成中,提示词:", prompt);
+
+    //获取视频生成模型id
+    const modelRes = await getModelIdByType({
+      type: ModelTypeEnum.IMAGE_TO_VIDEO,
+      platform: ModelPlatformEnum.DOUBAO,
+    });
+
+    if (!modelRes.data) {
+      showStatus("获取模型ID失败", 'error');
+      return null;
+    }
+
+    // 使用CreateVideo API创建视频任务
+    const createRes = await CreateVideo({
+      modelId: modelRes.data,
+      prompt: prompt,
+      duration: 4,
+      resolution: "1080P",
+    });
+
+    // 记录任务ID
+    state.inProgressVideoMap[createRes.data] = {
+      id: createRes.data,
+      status: AiImageStatusEnum.IN_PROGRESS,
+    };
+
+    console.log("AI视频生成中,请等待。。。");
+    // 启动统一的轮询机制
+    this.startPollingTasks('video');
+
+    // 如果需要等待完成,使用Promise封装结果
+    if (waitForCompletion) {
+      return new Promise((resolve, reject) => {
+        // 设置一次性的状态检查
+        const checkStatus = () => {
+          const videoInfo = state.generatedContent.videoUrl;
+          if (videoInfo && videoInfo.includes(createRes.data)) {
+            resolve(videoInfo);
+          } else if (state.inProgressVideoMap[createRes.data]?.status === AiImageStatusEnum.FAIL) {
+            reject(new Error("视频生成失败"));
+          } else if (!state.inProgressVideoMap[createRes.data]) {
+            reject(new Error("视频任务已不存在"));
+          } else {
+            // 继续检查
+            setTimeout(checkStatus, 1000);
+          }
+        };
+        checkStatus();
+      });
+    }
+
+    return createRes.data; // 返回任务ID
+  }, '生成视频失败'),
+
+  // 文本生成文本(如AI对话)
+  textToText: withErrorHandling('AI大模型调用', async function(prompt, model = "default") {
+    console.log("AI智能体请求,输入文本:", prompt);
+
+    // 如果没有活跃的对话ID,创建新对话
+    if (!state.activeConversationId) {
+      // 使用与TextToText.vue相同的方式创建对话
+      const res = await CreateDialogue({ roleId: 75 });
+      state.activeConversationId = res.data;
+      console.log("AI智能体创建成功,请等待。。。");
+    }
+
+    // 创建AbortController实例
+    state.conversationInAbortController = new AbortController();
+
+    // 使用流式API发送消息
+    let resultText = "";
+    let isFirstChunk = true;
+
+    await sendChatMessageStream(
+        state.activeConversationId,
+        prompt,
+        null,
+        state.conversationInAbortController,
+        true, // 启用上下文
+        async (res) => {
+          try {
+            const { code, data, msg } = JSON.parse(res.data);
+            if (code !== 0) {
+              console.log(`对话异常! ${msg}`);
+              return;
+            }
+            // 根据事件类型处理
+            if (data.eventType === "TEXT") {
+              // 如果内容为空,就不处理
+              if (data.receive?.content === "") {
+                return;
+              }
+
+              // 处理文本消息
+              resultText += data.receive.content;
+
+              // 首次返回时更新预览内容
+              if (isFirstChunk) {
+                isFirstChunk = false;
+                // 设置预览内容
+                state.generatedContent.text = resultText;
+                state.previewType = "text";
+                state.previewContent = resultText;
+              } else {
+                // 更新预览内容
+                state.generatedContent.text = resultText;
+                state.previewContent = resultText;
+              }
+            }
+          } catch (error) {
+            console.error("处理流式响应失败:", error);
+          }
+        },
+        (error) => {
+          console.log(`对话异常! ${error}`);
+          this.stopTextToTextStream();
+          throw error;
+        },
+        () => {
+          // console.log(`结束对话!`);
+          this.stopTextToTextStream();
+        }
+    );
+
+    // 确保最终结果被设置
+    if (resultText) {
+      console.log("AI大模型调用成功,返回结果:", resultText);
+      state.generatedContent.text = resultText;
+      state.previewType = "text";
+      state.previewContent = resultText;
+    }
+
+    return resultText;
+  }, 'AI大模型调用失败'),
+
+  // 停止文本生成流
+  stopTextToTextStream() {
+    if (state.conversationInAbortController) {
+      state.conversationInAbortController.abort();
+    }
+  },
+
+  // 设置台灯亮度
+  setLampBrightness: withErrorHandling('设置台灯亮度', async function(brightness) {
+    // 验证亮度值在0-100之间
+    const validBrightness = Math.max(0, Math.min(100, parseInt(brightness) || 0));
+
+    // 更新状态
+    state.lamp.brightness = validBrightness;
+
+    // 模拟API调用(实际项目中可替换为真实API)
+    console.log(`智能台灯亮度已设置为: ${validBrightness}%`);
+
+    return validBrightness;
+  }, '设置台灯亮度失败'),
+
+  // 设置台灯颜色
+  setLampColor: withErrorHandling('设置台灯颜色', async function(color) {
+    // 预定义的颜色映射
+    const colorMap = {
+      '紫': '#D886F0',
+      '橙': '#F89E35',
+      '黄': '#F9E67E',
+      '青': '#6BF5E6',
+      '白': '#ffffff',
+    };
+
+    // 获取有效的颜色值
+    let validColor = colorMap[color] || color;
+
+    // 检查是否是有效的颜色格式
+    if (!/^#[0-9A-F]{6}$/i.test(validColor)) {
+      validColor = "#ffffff"; // 默认白色
+    }
+
+    // 更新状态
+    state.lamp.color = validColor;
+    state.lamp.colorLog = color;
+
+    // 模拟API调用(实际项目中可替换为真实API)
+    console.log(`智能台灯颜色已设置为: ${color}`);
+
+    return validColor;
+  }, '设置台灯颜色失败'),
+
+  /**
+   * 播放音乐功能
+   * @param {string} musicType - 音乐类型
+   * @returns {Promise<void>} - 返回Promise
+   */
+  playMusic: withErrorHandling('播放音乐', async function(musicType) {
+    return playMusic(musicType, state, musicPlayer);
+  }, '播放音乐失败'),
+  /**
+   * 停止音乐播放
+   * @returns {Promise<void>} - 返回Promise
+   */
+  stopMusic: withErrorHandling('停止音乐', async function() {
+    return stopMusic(state, musicPlayer);
+  }, '停止音乐失败'),
+
+  // 综合控制台灯(参数格式:"颜色, 亮度, 音乐")
+  controlLampWithSingleParam: withErrorHandling('智能台灯综合控制', async function(params) {
+    // 解析参数字符串
+    let color = '白'; // 默认颜色
+    let brightness = 0; // 默认亮度
+    let music = ''; // 音乐信息
+
+    if (params && typeof params === 'string') {
+      // 根据逗号分割参数
+      const paramArray = params.split(',').map(p => p.trim());
+
+      // 提取颜色(第一个参数)
+      if (paramArray.length > 0 && paramArray[0]) {
+        color = paramArray[0];
+      }
+
+      // 提取亮度(第二个参数)
+      if (paramArray.length > 1 && paramArray[1]) {
+        brightness = paramArray[1];
+      }
+
+      // 提取音乐(第三个参数)
+      if (paramArray.length > 2 && paramArray[2]) {
+        music = paramArray[2];
+
+        // 调用音乐播放函数
+        await this.playMusic(music);
+      }
+    }
+    // 调用控制台灯方法
+    return await this.controlLamp(brightness, color);
+  }, '智能台灯综合控制失败'),
+
+  // 综合控制台灯(参数格式:"颜色, 亮度")
+  controlLamp: withErrorHandling('智能台灯控制', async function(brightness, color) {
+    // 先设置亮度
+    await this.setLampBrightness(brightness);
+
+    // 再设置颜色
+    await this.setLampColor(color);
+
+    return { brightness: state.lamp.brightness, color: state.lamp.color };
+  }, '智能台灯控制失败'),
+
+  // 启动任务轮询
+  startPollingTasks(type) {
+    if (type === 'image' || type === 'all') {
+      pollingManager.startPolling('image', async () => {
+        const imageIds = Object.keys(state.inProgressImageMap).map(Number);
+        state.inProgressImageMap = await pollTaskStatus(
+            'image',
+            imageIds,
+            PaintingGetMys,
+            (image) => {
+              state.generatedContent.imageUrl = image.picUrl;
+              state.previewType = "image";
+              state.previewContent = image.picUrl;
+              state.previewVisible = true;
+              console.log("AI图片生成完成:", image.picUrl);
+            },
+            (image) => {
+              showStatus("图片生成失败: " + (image.error || "未知错误"), 'error');
+              console.error("图片生成失败:", image.id, image.error);
+            }
+        );
+      });
+    }
+
+    if (type === 'video' || type === 'all') {
+      pollingManager.startPolling('video', async () => {
+        const videoIds = Object.keys(state.inProgressVideoMap).map(Number);
+        state.inProgressVideoMap = await pollTaskStatus(
+            'video',
+            videoIds,
+            VideoGetMys,
+            (video) => {
+              state.generatedContent.videoUrl = video.videoUrl;
+              state.previewType = "video";
+              state.previewContent = video.videoUrl;
+              state.previewVisible = true;
+              console.log("AI视频生成完成:", video.videoUrl);
+            },
+            (video) => {
+              showStatus("视频生成失败: " + (video.error || "未知错误"), 'error');
+              console.error("视频生成失败:", video.id, video.error);
+            }
+        );
+      });
+    }
+  }
+};
+
+// 专门的停止音乐处理函数
+const handleStopMusic = () => {
+  // 直接调用导入的stopMusic函数并传递正确的参数
+  stopMusic(state, musicPlayer);
+  // 提示信息
+  showStatus('音乐已停止播放');
+};
+
+// 关闭预览
+const handleClosePreview = () => {
+  state.previewVisible = false;
+  state.previewContent = "";
+  state.previewType = "";
+  state.generatedContent.text = null;
+  state.generatedContent.imageUrl = null;
+  state.generatedContent.videoUrl = null;
+};
+
+// 组件挂载后初始化Blockly
+onMounted(() => {
+  // 从全局状态初始化年级ID
+  state.gradeId = globalState.initGradeId();
+
+  device.value.name = router.currentRoute.value.query.name || "";
+  device.value.image = router.currentRoute.value.query.image || "";
+  device.value.jsonData = JSON.parse(router.currentRoute.value.query.jsonData || {});
+
+    // 注册AI语音识别积木
+  Blockly.Blocks["ai_voice_input"] = {
+    init: function () {
+      this.appendDummyInput().appendField("语音识别");
+      this.appendValueInput("PROMPT")
+          .setCheck("String")
+          .appendField("提示文字:");
+      this.appendDummyInput()
+          .appendField("语言:")
+          .appendField(
+              new Blockly.FieldDropdown([
+                ["中文", "zh-CN"],
+                // ["英文", "en-US"],
+              ]),
+              "LANGUAGE"
+          );
+      this.setOutput(true, "String");
+      this.setColour(310);
+      this.setTooltip("使用语音识别获取文本输入");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册AI文本生成图片积木
+  Blockly.Blocks["ai_text_to_image"] = {
+    init: function () {
+      this.appendDummyInput().appendField("AI生成图片");
+      this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
+      this.appendDummyInput()
+          .appendField("等待完成:", "WAIT_LABEL")
+          .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(340);
+      this.setTooltip("使用AI将文本描述转换为图片");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册AI文本生成视频积木
+  Blockly.Blocks["ai_text_to_video"] = {
+    init: function () {
+      this.appendDummyInput().appendField("AI生成视频");
+      this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
+      this.appendDummyInput()
+          .appendField("等待完成:", "WAIT_LABEL")
+          .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(340);
+      this.setTooltip("使用AI将文本描述转换为视频");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册AI文本生成文本积木
+  Blockly.Blocks["ai_text_to_text"] = {
+    init: function () {
+      this.appendDummyInput().appendField("AI大模型调用");
+      this.appendValueInput("PROMPT")
+          .setCheck("String")
+          .appendField("输入文本:");
+      this.appendValueInput("提示词")
+          .setCheck("String")
+          .appendField("提示词:");
+      this.setOutput(true, "String");
+      this.setColour(300);
+      this.setTooltip("使用AI大模型调用并返回结果");
+      this.setHelpUrl("");
+    },
+  };
+
+  //AI智能台灯单参数积木
+  Blockly.Blocks['ai_smart_lamp_single_param'] = {
+    init: function() {
+      this.appendDummyInput()
+          .appendField('智能台灯控制(单参数)');
+      this.appendValueInput('PARAMS')
+          .setCheck('String')
+          .appendField('参数(格式: 颜色,亮度,音乐):');
+      this.appendDummyInput()
+          .appendField('例如: 蓝,50,平静');
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(280);
+      this.setTooltip('通过一个参数字符串控制智能台灯的亮度、颜色和音乐\n格式: 颜色,亮度,音乐\n例如: 蓝,50,平静');
+      this.setHelpUrl('');
+    }
+  };
+
+  // 注册AI智能台灯积木
+  Blockly.Blocks["ai_smart_lamp"] = {
+    init: function () {
+      this.appendDummyInput().appendField("智能台灯控制");
+      this.appendValueInput("BRIGHTNESS")
+          .setCheck(["Number", "String"])
+          .appendField("亮度 (0-100):");
+      this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(280);
+      this.setTooltip("控制智能台灯的亮度和颜色");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册AI台灯设置亮度积木
+  Blockly.Blocks["ai_lamp_set_brightness"] = {
+    init: function () {
+      this.appendDummyInput().appendField("设置台灯亮度");
+      this.appendValueInput("BRIGHTNESS")
+          .setCheck(["Number", "String"])
+          .appendField("亮度 (0-100):");
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(270);
+      this.setTooltip("设置智能台灯的亮度");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册AI台灯设置颜色积木
+  Blockly.Blocks["ai_lamp_set_color"] = {
+    init: function () {
+      this.appendDummyInput().appendField("设置台灯颜色");
+      this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
+      this.appendDummyInput().appendField("可选颜色: 白,黄,紫,橙,青");
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(275);
+      this.setTooltip("设置智能台灯的颜色");
+      this.setHelpUrl("");
+    },
+  };
+
+  // 注册音乐播放积木
+  Blockly.Blocks['ai_music_play'] = {
+    init: function() {
+      this.appendDummyInput()
+          .appendField('播放音乐');
+      this.appendDummyInput()
+          .appendField('音乐类型:')
+          .appendField(
+              new Blockly.FieldDropdown([
+                ['热闹', '热闹'],
+                ['舒缓', '舒缓'],
+              ]),
+              'MUSIC_TYPE'
+          );
+      this.setInputsInline(false);
+      this.setPreviousStatement(true, null);
+      this.setNextStatement(true, null);
+      this.setColour(290);
+      this.setTooltip('播放指定类型的音乐');
+      this.setHelpUrl('');
+    }
+  };
+
+  // 注册JavaScript代码生成器
+  registerJavaScriptGenerators();
+
+  // 注册Python代码生成器
+  registerPythonGenerators();
+
+  // 初始化Blockly工作区
+  const blocklyDiv = document.getElementById('blocklyDiv');
+  const toolbox = document.getElementById('toolbox');
+
+  workspace = Blockly.inject(blocklyDiv, {
+    toolbox: toolbox,
+    collapse: true,
+    comments: true,
+    disable: false, // 设为false以允许编辑
+    maxBlocks: Infinity,
+    trashcan: true,
+    horizontalLayout: false,
+    toolboxPosition: 'start',
+    css: true,
+    media: 'https://unpkg.com/blockly/media/',
+    rtl: false,
+    scrollbars: true,
+    sounds: false, // 禁用声音以提高性能
+    oneBasedIndex: true,
+    grid: {
+      spacing: 20,
+      length: 3,
+      colour: "#ccc",
+      snap: true
+    },
+    zoom: {
+      controls: true,
+      wheel: true,
+      startScale: 1.0,
+      maxScale: 3,
+      minScale: 0.3,
+      scaleSpeed: 1.2
+    }
+  });
+
+  // 使用state.workspace替代workspace
+  state.workspace = workspace;
+
+  // 加载初始JSON数据
+  loadWorkspaceFromJson();
+
+  // 修改工作区变化监听器,使其包含拖拽修复逻辑
+  workspace.addChangeListener((event) => {
+    // 生成代码
+    generateCode("javascript");
+
+    // 拖拽修复逻辑
+    if (event.type === Blockly.Events.BLOCK_CREATE) {
+      const block = workspace.getBlockById(event.blockId);
+      if (block) {
+        block.setEditable(true);
+      }
+    }
+  });
+
+  // 将aiService挂载到window,以便执行生成的代码时可以访问
+  window.aiService = aiService;
+});
+
+// 组件卸载时清除所有资源
+onUnmounted(() => {
+  // 清除所有定时器
+  if (countdownInterval) {
+    clearInterval(countdownInterval);
+  }
+
+  // 停止所有轮询
+  pollingManager.stopAll();
+
+  // 关闭语音识别(如果正在进行)
+  if (recognition) {
+    recognition.stop();
+  }
+
+  // 清理工作区
+  if (workspace) {
+    workspace.dispose();
+  }
+
+  // 清理音频资源
+  if (musicPlayer.value) {
+    musicPlayer.value.pause();
+  }
+});
+
+// 注册JavaScript代码生成器
+function registerJavaScriptGenerators() {
+  // 语音识别
+  javascriptGenerator.forBlock['ai_voice_input'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
+    const language = block.getFieldValue('LANGUAGE');
+    const code = `await aiService.recognizeVoice(${prompt || "''"}, '${language}')`;
+    return [code, javascriptGenerator.ORDER_ATOMIC];
+  };
+
+  // 文本生成图片
+  javascriptGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
+    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
+    const code = `await aiService.textToImage(${prompt}, ${waitForCompletion});`;
+    return code;
+  };
+
+  // 文本生成视频
+  javascriptGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
+    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
+    const code = `await aiService.textToVideo(${prompt}, ${waitForCompletion});`;
+    return code;
+  };
+
+  // 文本生成文本
+  javascriptGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
+    const model = block.getFieldValue('MODEL');
+    const code = `await aiService.textToText(${prompt}, '${model}')`;
+    return [code, javascriptGenerator.ORDER_ATOMIC];
+  };
+
+  // 智能台灯控制(单参数)
+  javascriptGenerator.forBlock['ai_smart_lamp_single_param'] = function(block, generator) {
+    const params = generator.valueToCode(block, 'PARAMS', javascriptGenerator.ORDER_ATOMIC);
+    const code = `await aiService.controlLampWithSingleParam(${params || "'白,0,平静'"});`;
+    return code;
+  };
+
+  // 智能台灯控制(多参数)
+  javascriptGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
+    const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
+    const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
+    const code = `await aiService.controlLamp(${brightness || '0'}, ${color || "'白'"});`;
+    return code;
+  };
+
+  // 设置台灯亮度
+  javascriptGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
+    const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
+    const code = `await aiService.setLampBrightness(${brightness || '0'});`;
+    return code;
+  };
+
+  // 设置台灯颜色
+  javascriptGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
+    const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
+    const code = `await aiService.setLampColor(${color || "'白'"});`;
+    return code;
+  };
+
+  // 音乐播放
+  javascriptGenerator.forBlock['ai_music_play'] = function(block, generator) {
+    const musicType = block.getFieldValue('MUSIC_TYPE');
+    const code = `await aiService.playMusic('${musicType}');`;
+    return code;
+  };
+}
+
+// 注册Python代码生成器
+function registerPythonGenerators() {
+  // 语音识别
+  pythonGenerator.forBlock['ai_voice_input'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
+    const language = block.getFieldValue('LANGUAGE');
+    const code = `ai_service.recognize_voice(${prompt || "''"}, '${language}')`;
+    return [code, pythonGenerator.ORDER_ATOMIC];
+  };
+
+  // 文本生成图片
+  pythonGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
+    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
+    const code = `ai_service.text_to_image(${prompt}, ${waitForCompletion})\n`;
+    return code;
+  };
+
+  // 文本生成视频
+  pythonGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
+    const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
+    const code = `ai_service.text_to_video(${prompt}, ${waitForCompletion})\n`;
+    return code;
+  };
+
+  // 文本生成文本
+  pythonGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
+    const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
+    const model = block.getFieldValue('MODEL');
+    const code = `ai_service.text_to_text(${prompt}, '${model}')`;
+    return [code, pythonGenerator.ORDER_ATOMIC];
+  };
+
+  // 智能台灯控制(单参数)
+  pythonGenerator.forBlock['ai_smart_lamp_single_param'] = function(block, generator) {
+    const params = generator.valueToCode(block, 'PARAMS', pythonGenerator.ORDER_ATOMIC);
+    const code = `ai_service.control_lamp_with_single_param(${params || "'白,0,平静'"})\n`;
+    return code;
+  };
+
+  // 智能台灯控制
+  pythonGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
+    const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
+    const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
+    const code = `ai_service.control_lamp(${brightness || '0'}, ${color || "'白'"})\n`;
+    return code;
+  };
+
+  // 设置台灯亮度
+  pythonGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
+    const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
+    const code = `ai_service.set_lamp_brightness(${brightness || '0'})\n`;
+    return code;
+  };
+
+  // 设置台灯颜色
+  pythonGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
+    const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
+    const code = `ai_service.set_lamp_color(${color || "'白'"})\n`;
+    return code;
+  };
+
+  // 音乐播放
+  pythonGenerator.forBlock['ai_music_play'] = function(block, generator) {
+    const musicType = block.getFieldValue('MUSIC_TYPE');
+    const code = `ai_service.play_music('${musicType}')\n`;
+    return code;
+  };
+}
+
+// 从JSON加载工作区
+const loadWorkspaceFromJson = () => {
+  try {
+    // const json = JSON.parse(jsonData.value);
+    const json = device.value.jsonData;
+    console.log(typeof json);
+    Blockly.serialization.workspaces.load(json, workspace);
+    showStatus('工作区已成功从JSON加载!');
+  } catch (error) {
+    showStatus('JSON解析错误: ' + error.message, 'error');
+    console.error('JSON解析错误:', error);
+  }
+};
+
+// 导出工作区为JSON
+const exportWorkspaceToJson = () => {
+  try {
+    const state = Blockly.serialization.workspaces.save(workspace);
+    // jsonData.value = JSON.stringify(state, null, 2);
+    device.value.jsonData = state;
+    showStatus('工作区已成功导出为JSON!');
+  } catch (error) {
+    showStatus('导出错误: ' + error.message, 'error');
+    console.error('导出错误:', error);
+  }
+};
+
+// 生成代码
+const generateCode = (language = 'javascript') => {
+  try {
+    let generator;
+    if (language == "javascript") {
+      generator = javascriptGenerator;
+    } else if (language == "python") {
+      generator = pythonGenerator;
+    } else {
+      console.error("不支持的语言类型");
+      return;
+    }
+
+    const code = generator.workspaceToCode(workspace);
+    output.value = code;
+
+    // 将生成的代码也输出到JSON框中
+    // 创建一个包含代码的JSON对象
+    const codeJson = {
+      "generated_code": {
+        "language": language,
+        "code": code
+      }
+    };
+    // 将JSON对象格式化为字符串并设置到JSON框
+    // jsonData.value = JSON.stringify(codeJson, null, 2);
+    device.value.jsonData = codeJson;
+
+    showStatus('代码生成成功!已同时输出到JSON框');
+  } catch (error) {
+    output.value = '// 代码生成错误: ' + error.message;
+    showStatus('代码生成错误: ' + error.message, 'error');
+    console.error('代码生成错误:', error);
+  }
+};
+
+// 运行代码
+const runCode = async () => {
+  try {
+    const code = javascriptGenerator.workspaceToCode(workspace);
+    // 初始化输出区域,显示生成的代码和执行结果标题
+    output.value = code + '\n\n// 执行结果:\n';
+
+    // 保存原始console方法
+    const originalConsoleLog = console.log;
+    const originalConsoleError = console.error;
+    const originalConsoleWarn = console.warn;
+
+    // 创建输出缓冲区
+    let outputBuffer = "";
+
+    // 重定义console.log方法,确保所有日志都输出到output变量
+    console.log = (...args) => {
+      // 将参数转换为字符串
+      const message = args.map(arg => {
+        if (typeof arg === 'object') {
+          try {
+            return JSON.stringify(arg, null, 2);
+          } catch (e) {
+            return String(arg);
+          }
+        }
+        return arg;
+      }).join(' ');
+
+      // 过滤掉Vue警告信息
+      if (!message.includes('[Vue warn]')) {
+        outputBuffer += message + '\n';
+        // 实时更新输出区域内容
+        output.value = code + '\n\n// 执行结果:\n' + outputBuffer;
+      }
+      // 保留原始console.log功能
+      originalConsoleLog.apply(console, args);
+    };
+
+    // 重定义console.error方法
+    console.error = (...args) => {
+      const message = args.map(arg => {
+        if (typeof arg === 'object') {
+          try {
+            return JSON.stringify(arg, null, 2);
+          } catch (e) {
+            return String(arg);
+          }
+        }
+        return arg;
+      }).join(' ');
+
+      if (!message.includes('[Vue warn]')) {
+        outputBuffer += '// 错误: ' + message + '\n';
+        output.value = code + '\n\n// 执行结果:\n' + outputBuffer;
+      }
+      originalConsoleError.apply(console, args);
+    };
+
+    // 重定义console.warn方法
+    console.warn = (...args) => {
+      const message = args.map(arg => {
+        if (typeof arg === 'object') {
+          try {
+            return JSON.stringify(arg, null, 2);
+          } catch (e) {
+            return String(arg);
+          }
+        }
+        return arg;
+      }).join(' ');
+
+      if (!message.includes('[Vue warn]')) {
+        outputBuffer += '// 警告: ' + message + '\n';
+        output.value = code + '\n\n// 执行结果:\n' + outputBuffer;
+      }
+      originalConsoleWarn.apply(console, args);
+    };
+
+    try {
+      // 安全检查
+      if (code.includes('eval(') || code.includes('Function(') ||
+          code.includes('document.write') || code.includes('window.location')) {
+        throw new Error('代码包含不安全的操作');
+      }
+      // 包装代码为异步函数执行,支持await
+      const wrappedCode = `(async () => { ${code} })()`;
+      await new Function(wrappedCode)();
+
+      // 确保最终结果被正确显示
+      output.value = code + '\n\n// 执行结果:\n' + outputBuffer;
+    } catch (error) {
+      // 捕获并显示执行错误
+      outputBuffer += '\n// 执行错误: ' + error.message;
+      output.value = code + '\n\n// 执行结果:\n' + outputBuffer;
+    } finally {
+      // 恢复原始console方法
+      // console.log = originalConsoleLog;
+      // console.error = originalConsoleError;
+      // console.warn = originalConsoleWarn;
+    }
+
+    // 显示状态消息
+    showStatus('代码执行成功!');
+  } catch (error) {
+    // 处理runCode函数本身的错误
+    output.value += '\n// 执行错误: ' + error.message;
+    showStatus('代码执行错误: ' + error.message, 'error');
+    console.error('代码执行错误:', error);
+  }
+};
+
+// 清空工作区
+const clearWorkspace = () => {
+  workspace.clear();
+  showStatus('工作区已清空!');
+};
+
+// 清空输出
+const clearOutput = () => {
+  output.value = '// 输出已清空\n';
+};
+
+// 显示状态消息
+const showStatus = (message, type = 'success') => {
+  statusMessage.value = message;
+  statusType.value = type;
+
+  // 3秒后自动清除状态消息
+  // setTimeout(() => {
+  //   statusMessage.value = '';
+  // }, 3000);
+};
+</script>
+
+<style scoped lang="scss">
+@use "sass:math";
+
+@function rpx($px) {
+  @return math.div($px, 750) * 100vw;
+}
+
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-image: url('@/assets/images/desklamp.png');
+  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;
+}
+/* 自定义滚动条样式 */
+.container::-webkit-scrollbar {
+  width: rpx(2); /* 滚动条宽度 */
+}
+.container::-webkit-scrollbar-track {
+  background: #f1effd; /* 滚动条轨道背景色 */
+  border-radius: rpx(4);
+}
+.container::-webkit-scrollbar-thumb {
+  background: #e2ddfc; /* 滚动条滑块颜色 */
+  border-radius: rpx(4);
+}
+.container::-webkit-scrollbar-thumb:hover {
+  background: #e2ddfc; /* 滚动条滑块 hover 状态颜色 */
+}
+
+.content {
+  display: flex;
+  flex-wrap: wrap;
+  min-height: 600px;
+}
+
+//工具箱
+.toolbox-section {
+  flex: 1;
+  //min-width: 250px;
+  background: rgba(248, 249, 250, 0.82);
+  padding: 15px;
+  border-radius: 15px;
+  display: flex;
+  flex-direction: column;
+  width: 30%;
+  margin-left: 10px;
+  margin-right: 10px;
+}
+
+//工作区
+.workspace-section {
+  flex: 3;
+  //min-width: 400px;
+  padding: 15px;
+  position: relative;
+  width: 65%;
+  height: 70%;
+  background: rgba(248, 249, 250, 0.82);
+  border-radius: 10px;
+  margin-left: 10px;
+  margin-right: 10px;
+}
+
+.output-section {
+  background: rgba(241, 248, 255, 84%);
+  color: black;
+  padding: 15px;
+  border-radius: 8px;
+  border: 1px solid #d1e7ff;
+  width: 100%;
+  margin: 15px 10px;
+}
+
+/* AI模块样式 */
+[categorystyle="ai_category"] > .blocklyTreeRow {
+  background-color: #9c27b0 !important;
+}
+
+/* 预览样式 */
+.preview-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.preview-content {
+  background-color: white;
+  padding: 20px;
+  border-radius: 8px;
+  max-width: 80%;
+  max-height: 80%;
+  overflow: auto;
+  position: relative;
+}
+
+.close-button {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  font-size: 24px;
+  background: none;
+  border: none;
+  cursor: pointer;
+  color: #333;
+}
+
+.preview-image-container,
+.preview-video-container {
+  display: flex;
+  justify-content: center;
+}
+
+.preview-image,
+.preview-video {
+  max-width: 100%;
+  max-height: 60vh;
+  border-radius: 4px;
+}
+
+.preview-text-container {
+  max-height: 60vh;
+  overflow-y: auto;
+  padding: 10px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  color: #333;
+}
+
+/* 文生图预览 */
+.extra-image-preview {
+  color: black;
+  margin-top: 10px;
+  padding: 10px;
+  border: 1px solid #ddd;
+  border-radius: 5px;
+  background-color: #f9f9f9;
+}
+
+.extra-preview-image {
+  max-width: 100%;
+  max-height: 400px;
+  border-radius: 4px;
+}
+
+/* 音乐播放器 */
+.music-player-container {
+  margin-top: 20px;
+  padding: 15px;
+  background-color: #f9f9f9;
+  border-radius: 8px;
+  border: 1px solid #e0e0e0;
+  color: #333;
+}
+
+.music-player-container h5 {
+  margin-top: 0;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 16px;
+}
+
+.music-player-container audio {
+  width: 100%;
+  margin-bottom: 10px;
+}
+
+.music-status {
+  font-size: 14px;
+  color: #666;
+  padding: 5px 0;
+}
+
+h2 {
+  margin-bottom: 15px;
+  color: #2c3e50;
+  border-bottom: 2px solid #3498db;
+  padding-bottom: 8px;
+}
+
+.output-section h2 {
+  color: black;
+  border-bottom: 2px solid #3498db;
+}
+
+#blocklyDiv {
+  height: 500px;
+  width: 100%;
+  background: white;
+  border: 1px solid #ddd;
+  border-radius: 8px;
+}
+
+.controls {
+  display: flex;
+  gap: 10px;
+  margin-top: 15px;
+  flex-wrap: wrap;
+}
+
+button {
+  padding: 10px 20px;
+  border: none;
+  border-radius: 5px;
+  background: #3498db;
+  color: white;
+  font-weight: bold;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+button:hover {
+  background: #2980b9;
+  transform: translateY(-2px);
+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
+}
+
+#generateCode {
+  background: #2ecc71;
+}
+
+#generateCode:hover {
+  background: #27ae60;
+}
+
+#runCode {
+  background: #e74c3c;
+}
+
+#runCode:hover {
+  background: #c0392b;
+}
+
+#output {
+  // background: #1a2530;
+  background: rgba($color: #ffffff, $alpha: 0.5);
+  padding: 10px;
+  border-radius: 5px;
+  min-height: 100px;
+  max-height: 300px;
+  margin-top: 10px;
+  font-family: 'Courier New', monospace;
+  white-space: pre-wrap;
+  overflow-y: auto;
+  font-size: 12px;
+}
+
+.json-section {
+  margin-top: 15px;
+  padding: 15px;
+  background: #f1f8ff;
+  border-radius: 8px;
+  border: 1px solid #d1e7ff;
+}
+
+.json-section h3 {
+  margin-bottom: 10px;
+  color: #2c3e50;
+}
+
+textarea {
+  width: 100%;
+  min-height: 120px;
+  padding: 10px;
+  border: 1px solid #ddd;
+  border-radius: 5px;
+  font-family: 'Courier New', monospace;
+  resize: vertical;
+}
+
+.status {
+  margin-top: 10px;
+  padding: 8px 12px;
+  border-radius: 4px;
+  font-weight: bold;
+}
+
+.success {
+  background: #d4edda;
+  color: #155724;
+  border: 1px solid #c3e6cb;
+}
+
+.error {
+  background: #f8d7da;
+  color: #721c24;
+  border: 1px solid #f5c6cb;
+}
+
+/* 智能台灯 */
+.desk-lamp-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.full-screen-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+/* 台灯灯光样式 - 使用动态颜色 */
+.lamp-light-mask {
+  position: absolute;
+  width: rpx(78);
+  height: rpx(250);
+  margin-top: rpx(49);
+  margin-left: rpx(192);
+  background: linear-gradient(to bottom, var(--lamp-color) 0%, var(--lamp-color) 10%, rgba(255, 255, 255, 0) 90%);
+  filter: blur(60px);
+  transform: rotate(9deg);
+  transform-origin: top center;
+  opacity: var(--lamp-opacity, 0.6);
+  /* 创建扇形效果 */
+  clip-path: polygon(0% 0%, 100% 0%, 250% 100%, -150% 100%);
+}
+
+/* 灯光信息显示 */
+.lamp-info {
+  position: absolute;
+  bottom: 100px;
+  right: 30px;
+  background-color: rgba(255, 255, 255, 0.2);
+  padding: 10px 20px;
+  border-radius: 8px;
+  backdrop-filter: blur(10px);
+  color: white;
+  font-size: 14px;
+  z-index: 1000;
+}
+
+/* 标题框样式 */
+.desk-lamp-title-box {
+  position: absolute;
+  top: 20px;
+  left: 20px;
+  z-index: 1000;
+}
+
+.desk-lamp-box-icon {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 10px 20px;
+  background-color: rgba(255, 255, 255, 0.2);
+  border-radius: 30px;
+  backdrop-filter: blur(10px);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-size: 16px;
+  color: white;
+  font-weight: 500;
+}
+
+.desk-lamp-box-icon:hover {
+  background-color: rgba(255, 255, 255, 0.3);
+  transform: translateX(-3px);
+}
+
+.left-icon {
+  font-size: 18px;
+}
+
+/* 右下角按钮组样式 */
+.button-group {
+  position: absolute;
+  bottom: 30px;
+  right: 30px;
+  display: flex;
+  gap: 15px;
+  z-index: 1000;
+}
+
+.control-button {
+  padding: 12px 24px;
+  border-radius: 8px;
+  font-size: 16px;
+  font-weight: 500;
+  transition: all 0.3s ease;
+  backdrop-filter: blur(5px);
+}
+
+.run-button {
+  background-color: rgba(64, 169, 255, 0.8);
+  color: white;
+  border: none;
+}
+
+.run-button:hover {
+  background-color: rgba(64, 169, 255, 1);
+  transform: translateY(-2px);
+}
+
+.code-button {
+  background-color: rgba(132, 94, 255, 0.8);
+  color: white;
+  border: none;
+}
+
+.code-button:hover {
+  background-color: rgba(132, 94, 255, 1);
+  transform: translateY(-2px);
+}
+/* 返回按钮样式 */
+.title-box {
+  position: relative;
+  top: 10px;
+  padding-left: 15px;
+  margin-bottom: 20px;
+  z-index: 10;
+}
+
+.box-icon {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 10px 20px;
+  background-color: rgba(255, 255, 255, 80%);
+  border-radius: 30px;
+  backdrop-filter: blur(10px);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-size: 16px;
+  color: #333;
+  font-weight: 500;
+  width: fit-content;
+}
+
+.box-icon:hover {
+  background-color: rgba(255, 255, 255, 90%);
+  transform: translateX(-3px);
+}
+
+.left-icon {
+  font-size: 18px;
+}
+
+
+/* 语音识别-录音状态容器 */
+.recording-status-container {
+  position: absolute;
+  top: 20%;
+  left: 50%;
+  transform: translateX(-50%);
+  background: rgba(0, 0, 0, 0.7);
+  color: white;
+  padding: 20px 40px;
+  border-radius: 10px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+  text-align: center;
+  z-index: 1000;
+  backdrop-filter: blur(5px);
+}
+
+.recording-text {
+  font-size: 18px;
+  margin-bottom: 15px;
+  font-weight: 500;
+}
+
+.recording-countdown {
+  font-size: 16px;
+  color: #4CAF50;
+  margin-top: 15px;
+  font-weight: bold;
+}
+
+.equalizer {
+  display: flex;
+  justify-content: center;
+  align-items: flex-end;
+  height: 60px;
+  gap: 4px;
+}
+
+.bar {
+  width: 8px;
+  background: linear-gradient(180deg, #4CAF50 0%, #8BC34A 100%);
+  border-radius: 4px;
+  animation: equalize 1s infinite ease-in-out;
+}
+
+.bar-1 { animation-delay: 0s; }
+.bar-2 { animation-delay: 0.1s; }
+.bar-3 { animation-delay: 0.2s; }
+.bar-4 { animation-delay: 0.3s; }
+.bar-5 { animation-delay: 0.4s; }
+.bar-6 { animation-delay: 0.5s; }
+.bar-7 { animation-delay: 0.6s; }
+
+@keyframes equalize {
+  0%, 100% { height: 10px; }
+  25% { height: 40px; }
+  50% { height: 60px; }
+  75% { height: 25px; }
+}
+
+//台灯播放音乐
+.music-info {
+  margin-top: 10px;
+  padding: 8px;
+  background-color: rgba(255, 255, 255, 0.1);
+  border-radius: 6px;
+}
+
+.music-info p {
+  margin: 0 0 8px 0;
+  color: #ffffff;
+  font-size: 14px;
+}
+
+.stop-music-btn {
+  background-color: #ff4d4f;
+  color: white;
+  border: none;
+  padding: 6px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 12px;
+  transition: background-color 0.3s;
+}
+
+.stop-music-btn:hover {
+  background-color: #ff7875;
+}
+</style>

+ 56 - 18
src/views/virtuallaboratory/index.vue

@@ -52,11 +52,13 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 import { ArrowLeftBold } from '@element-plus/icons-vue'
 import LeftPanel from '@/components/LeftPanel.vue'
 import desklamp from '@/assets/images/desklamp.png'
+// 导入虚拟设备API
+import { getVirtualDeviceList } from '@/api/ai/virtualDevice';
 
 const leftPanelRef = ref(null)
 
@@ -82,25 +84,61 @@ const goBack = () => {
     window.location.reload();
   }
 }
-const laboratoryList = ref([
-  {
-    name: '智能台灯',
-    image: desklamp
-  },
-])
 
-const handleLabClick = (item) => {
-if (item.name === '智能台灯') {
-    try {
-      // 立即修改URL,不经过Vue Router的队列处理
-      window.location.href = '/blockly2';
-    } catch (error) {
-      console.error('直接跳转失败:', error);
-      // 备用方案:强制Vue Router进行跳转
-      router.replace('/blockly2');
-      // 强制刷新页面以确保视图更新
-      window.location.reload();
+const laboratoryList = ref([]);
+
+// 从API获取实验室设备列表
+const fetchLaboratoryList = async () => {
+  try {
+    const response = await getVirtualDeviceList();
+    console.log('获取实验室设备列表成功:', response);
+    // 根据实际API返回格式调整下面的代码
+    if (response.data && response.data.list) {
+      laboratoryList.value = response.data.list;
+    } else {
+      // API返回格式不符合预期时使用默认数据
+      laboratoryList.value = [
+        {
+          name: '智能台灯2',
+          image: desklamp
+        },
+      ];
     }
+  } catch (error) {
+    console.error('获取实验室设备列表失败:', error);
+    // 请求失败时使用默认数据
+    laboratoryList.value = [
+      {
+        name: '智能台灯',
+        image: desklamp
+      },
+    ];
+  }
+};
+
+// 组件挂载时获取数据
+onMounted(() => {
+  fetchLaboratoryList();
+});
+
+const handleLabClick = (item) => {
+  try {
+    // 立即修改URL,不经过Vue Router的队列处理
+    // window.location.href = item.routePath;
+    router.replace({
+      path: item.routePath,
+      query: {
+        name: item.name,
+        image: item.image,
+        jsonData: item.jsonData
+      }
+    });
+  } catch (error) {
+    console.error('直接跳转失败:', error);
+    // 备用方案:强制Vue Router进行跳转
+    router.replace('/blockly2');
+    // 强制刷新页面以确保视图更新
+    window.location.reload();
   }
 }
 </script>