Răsfoiți Sursa

1、blockly进度提交,能够实现文生图

liyanbo 7 luni în urmă
părinte
comite
804623f68e
5 a modificat fișierele cu 2011 adăugiri și 1689 ștergeri
  1. 480 1
      package-lock.json
  2. 1 0
      package.json
  3. 2 0
      src/router/index.js
  4. 1072 1417
      src/views/Blockly.vue
  5. 456 271
      src/views/Blockly_bak.vue

+ 480 - 1
package-lock.json

@@ -15,6 +15,7 @@
         "@vue-office/excel": "^1.7.14",
         "@vue-office/pptx": "^1.0.1",
         "axios": "^1.10.0",
+        "blockly": "^12.3.1",
         "element-plus": "^2.10.2",
         "highlight.js": "^11.11.1",
         "hls.js": "^1.6.7",
@@ -56,6 +57,25 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@asamuzakjp/css-color": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+      "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/css-calc": "^2.1.3",
+        "@csstools/css-color-parser": "^3.0.9",
+        "@csstools/css-parser-algorithms": "^3.0.4",
+        "@csstools/css-tokenizer": "^3.0.3",
+        "lru-cache": "^10.4.3"
+      }
+    },
+    "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "license": "ISC"
+    },
     "node_modules/@babel/code-frame": {
       "version": "7.27.1",
       "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -1490,6 +1510,116 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@csstools/color-helpers": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+      "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@csstools/css-calc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+      "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-color-parser": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+      "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/color-helpers": "^5.1.0",
+        "@csstools/css-calc": "^2.1.4"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-parser-algorithms": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+      "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-tokenizer": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+      "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/@ctrl/tinycolor": {
       "version": "3.6.1",
       "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@@ -3183,6 +3313,15 @@
         "pkcs7": "^1.0.4"
       }
     },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/ajv": {
       "version": "6.12.6",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -3315,6 +3454,18 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/blockly": {
+      "version": "12.3.1",
+      "resolved": "https://registry.npmjs.org/blockly/-/blockly-12.3.1.tgz",
+      "integrity": "sha512-BbWUcpqroY241XgSxTuAiEMHeIZ6u3+oD2zOATf3Fi+0NMWJ/MdMtuSkOcDCSk6Nc7WR3z5n9GHKqz2L+3kQOQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "jsdom": "26.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/boolbase": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -3583,12 +3734,38 @@
         "node": ">=4"
       }
     },
+    "node_modules/cssstyle": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+      "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+      "license": "MIT",
+      "dependencies": {
+        "@asamuzakjp/css-color": "^3.2.0",
+        "rrweb-cssom": "^0.8.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/csstype": {
       "version": "3.1.3",
       "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
       "license": "MIT"
     },
+    "node_modules/data-urls": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/dayjs": {
       "version": "1.11.13",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
@@ -3612,6 +3789,12 @@
         }
       }
     },
+    "node_modules/decimal.js": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+      "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+      "license": "MIT"
+    },
     "node_modules/deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -4352,6 +4535,56 @@
       "integrity": "sha512-QW2fnwDGKGc9DwQUGLbmMOz8G48UZK7PVNJPcOUql1b8jubKx4/eMHNP5mGqr6tYlJNDG1g10Lx2U/qPzL6zwQ==",
       "license": "Apache-2.0"
     },
+    "node_modules/html-encoding-sniffer": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-encoding": "^3.1.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/ignore": {
       "version": "5.3.2",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4462,6 +4695,12 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/is-potential-custom-element-name": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+      "license": "MIT"
+    },
     "node_modules/is-promise": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
@@ -4503,6 +4742,54 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/jsdom": {
+      "version": "26.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
+      "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+      "license": "MIT",
+      "dependencies": {
+        "cssstyle": "^4.2.1",
+        "data-urls": "^5.0.0",
+        "decimal.js": "^10.5.0",
+        "html-encoding-sniffer": "^4.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.6",
+        "is-potential-custom-element-name": "^1.0.1",
+        "nwsapi": "^2.2.16",
+        "parse5": "^7.2.1",
+        "rrweb-cssom": "^0.8.0",
+        "saxes": "^6.0.0",
+        "symbol-tree": "^3.2.4",
+        "tough-cookie": "^5.1.1",
+        "w3c-xmlserializer": "^5.0.0",
+        "webidl-conversions": "^7.0.0",
+        "whatwg-encoding": "^3.1.1",
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.1.1",
+        "ws": "^8.18.0",
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "canvas": "^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jsdom/node_modules/xml-name-validator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/jsencrypt": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.3.2.tgz",
@@ -4888,6 +5175,12 @@
         "url": "https://github.com/fb55/nth-check?sponsor=1"
       }
     },
+    "node_modules/nwsapi": {
+      "version": "2.2.22",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz",
+      "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==",
+      "license": "MIT"
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -4966,6 +5259,30 @@
       "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==",
       "license": "MIT"
     },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5/node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/parseurl": {
       "version": "1.3.3",
       "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
@@ -5122,7 +5439,6 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
       "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -5307,6 +5623,12 @@
         "node": ">= 18"
       }
     },
+    "node_modules/rrweb-cssom": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+      "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+      "license": "MIT"
+    },
     "node_modules/rust-result": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz",
@@ -5324,6 +5646,12 @@
         "rust-result": "^1.0.0"
       }
     },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
     "node_modules/sass": {
       "version": "1.89.2",
       "resolved": "https://registry.npmmirror.com/sass/-/sass-1.89.2.tgz",
@@ -5345,6 +5673,18 @@
         "@parcel/watcher": "^2.4.1"
       }
     },
+    "node_modules/saxes": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+      "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+      "license": "ISC",
+      "dependencies": {
+        "xmlchars": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=v12.22.7"
+      }
+    },
     "node_modules/semver": {
       "version": "6.3.1",
       "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
@@ -5445,6 +5785,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/symbol-tree": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+      "license": "MIT"
+    },
     "node_modules/systemjs": {
       "version": "6.15.1",
       "resolved": "https://registry.npmmirror.com/systemjs/-/systemjs-6.15.1.tgz",
@@ -5495,6 +5841,24 @@
         "url": "https://github.com/sponsors/SuperchupuDev"
       }
     },
+    "node_modules/tldts": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+      "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tldts-core": "^6.1.86"
+      },
+      "bin": {
+        "tldts": "bin/cli.js"
+      }
+    },
+    "node_modules/tldts-core": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+      "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+      "license": "MIT"
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5508,6 +5872,30 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/tough-cookie": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+      "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tldts": "^6.1.32"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+      "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/tsml": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/tsml/-/tsml-1.0.1.tgz",
@@ -6112,18 +6500,82 @@
         "vue": "^3.0.2"
       }
     },
+    "node_modules/w3c-xmlserializer": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+      "license": "MIT",
+      "dependencies": {
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/web-storage-cache": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/web-storage-cache/-/web-storage-cache-1.1.1.tgz",
       "integrity": "sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==",
       "license": "MIT"
     },
+    "node_modules/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/webwackify": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/webwackify/-/webwackify-0.1.6.tgz",
       "integrity": "sha512-pGcw1T3HpNnM/UTRQqqRkkkzythSLts05mB+7Gr00B+0VbL0m39dFL5g20rSIEUt9Wrpw+/8k+snxRlUFHhcqA==",
       "license": "MIT"
     },
+    "node_modules/whatwg-encoding": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+      "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "^5.1.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6150,6 +6602,27 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ws": {
+      "version": "8.18.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+      "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/xhr": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.0.tgz",
@@ -6190,6 +6663,12 @@
         "node": ">=12"
       }
     },
+    "node_modules/xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+      "license": "MIT"
+    },
     "node_modules/xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "@vue-office/excel": "^1.7.14",
     "@vue-office/pptx": "^1.0.1",
     "axios": "^1.10.0",
+    "blockly": "^12.3.1",
     "element-plus": "^2.10.2",
     "highlight.js": "^11.11.1",
     "hls.js": "^1.6.7",

+ 2 - 0
src/router/index.js

@@ -8,6 +8,8 @@ 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: '/home',

+ 1072 - 1417
src/views/Blockly.vue

@@ -1,1550 +1,1205 @@
 <template>
-  <!-- AI发展历程 -->
+  <!-- Blockly -->
   <div class="home-container">
-    <!-- 展开收起侧边栏 -->
-    <div class="icon-expand">
-      <el-icon
-          @click="toggleDrawer"
-          :style="{ color: drawerVisible ? 'white' : '#B6B0D8' }"
-      >
-        <component :is="drawerVisible ? Fold : Expand" />
-        <!-- <Tickets /> -->
-      </el-icon>
-    </div>
-
-    <transition name="drawer-slide">
-      <div class="main-content" v-if="drawerVisible">
-        <!-- 添加抽屉 -->
-        <div class="drawer-box">
-          <el-row class="tac">
-            <el-col :span="12">
-              <h3 class="mb-2">
-                <el-icon><Memo /></el-icon>
-                课程小节
-              </h3>
-              <el-menu
-                  :default-active="currentIndex"
-                  @open="handleOpen"
-                  @close="handleClose"
-                  @select="handleSelect"
-                  :default-openeds="['1']"
-              >
-                <template v-for="item in menuItems" :key="item.index">
-                  <el-menu-item v-if="!item.children" :index="item.index">{{
-                      item.title
-                    }}</el-menu-item>
-                  <el-sub-menu v-else :index="item.index">
-                    <template #title>
-                      <span>{{ item.title }}</span>
-                    </template>
-                    <el-menu-item-group v-if="item.children">
-                      <template
-                          v-for="child in item.children"
-                          :key="child.index"
-                      >
-                        <el-menu-item :index="child.index">{{
-                            child.title
-                          }}</el-menu-item>
-                      </template>
-                    </el-menu-item-group>
-                  </el-sub-menu>
-                </template>
-              </el-menu>
-            </el-col>
-          </el-row>
-        </div>
-      </div>
-    </transition>
-
-    <div class="content-box">
-      <div class="box-1">
-        <div class="inner-box left-box">
-          <div class="box-icon" @click="goBack">
-            <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
-            {{ boxIconTitle }}
-          </div>
-        </div>
-        <div class="inner-box right-box">
-          <div class="top-right-box">
-            <el-input
-                v-model="SearchInput"
-                class="search-input"
-                placeholder="搜索"
-            >
-              <template #prefix>
-                <el-icon class="el-input__icon"><search /></el-icon>
-              </template>
-            </el-input>
-          </div>
-        </div>
-      </div>
-      <div class="box-2">
-        <!-- 课程标题 autoplay自动播放   @ended="playNextVideo"-->
-        <div class="small-title">
-          <span>{{ smallTitle }}</span>
-        </div>
-        <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="逻辑" 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>
-              <sep></sep>
-              <category name="数学" colour="%{BKY_MATH_HUE}">
-                <block type="math_number">
-                  <field name="NUM">123</field>
-                </block>
-                <block type="math_number">
-                  <field name="NUM">213312</field>
-                </block>
-                <block type="math_arithmetic"></block>
-                <block type="math_single"></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>
+    <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>
+          </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>
-                <block type="text_charAt">
-                  <value name="VALUE">
-                    <block type="variables_get">
-                      <field name="VAR">text</field>
-                    </block>
-                  </value>
+              </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>
-                <block type="text_getSubstring">
-                  <value name="STRING">
-                    <block type="variables_get">
-                      <field name="VAR">text</field>
-                    </block>
-                  </value>
+              </value>
+            </block>
+            <block type="text_getSubstring">
+              <value name="STRING">
+                <block type="variables_get">
+                  <field name="VAR">text</field>
                 </block>
-                <block type="text_changeCase">
-                  <value name="TEXT">
-                    <shadow type="text">
-                      <field name="TEXT">abc</field>
-                    </shadow>
-                  </value>
+              </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>
-                <block type="text_trim">
-                  <value name="TEXT">
-                    <shadow type="text">
-                      <field name="TEXT">abc</field>
-                    </shadow>
-                  </value>
+              </value>
+            </block>
+            <block type="lists_getIndex">
+              <value name="VALUE">
+                <block type="variables_get">
+                  <field name="VAR">list</field>
                 </block>
-                <block type="text_count">
-                  <value name="SUB">
-                    <shadow type="text"></shadow>
-                  </value>
-                  <value name="TEXT">
-                    <shadow type="text"></shadow>
-                  </value>
+              </value>
+            </block>
+            <block type="lists_setIndex">
+              <value name="LIST">
+                <block type="variables_get">
+                  <field name="VAR">list</field>
                 </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>
+              </value>
+            </block>
+            <block type="lists_getSublist">
+              <value name="LIST">
+                <block type="variables_get">
+                  <field name="VAR">list</field>
                 </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>
+              </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 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="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>
     </div>
+
+    <!-- AI结果预览模态框 -->
+    <el-dialog
+        title="AI生成结果"
+        :visible.sync="state.previewVisible"
+        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>
 </template>
 
 <script setup>
-import { ref, onMounted } from 'vue'
-import { useRouter } from 'vue-router'
-import {
-  Expand,
-  Fold,
-  Memo,
-  Delete
-} from '@element-plus/icons-vue'
-import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
-import {Message} from "@/utils/message/Message.js";
-import Blockly from 'blockly';
-import {javascriptGenerator} from 'blockly/javascript';
-// 引入 Python 代码生成器
-import {pythonGenerator} from 'blockly/python';
-// 引入想要转换的语言,语言有php python dart lua javascript
-// import 'blockly/javascript'
-// 引入语言包并使用
+import { ref, onMounted, onUnmounted, reactive } from 'vue'
+import * as Blockly from 'blockly';
+import { javascriptGenerator } from 'blockly/javascript';
+import { pythonGenerator } from 'blockly/python';
 import * as hans from 'blockly/msg/zh-hans'
-Blockly.setLocale(hans);
+import { ElDialog, ElButton, ElMessage } from 'element-plus';
 
-const router = useRouter() // 获取当前路由对象
-// 搜索框
-const SearchInput = ref('')
-// 添加抽屉显示状态
-const drawerVisible = ref(true)
-// 添加切换抽屉显示状态的函数
-const toggleDrawer = () => {
-  drawerVisible.value = !drawerVisible.value
-}
+//【文生图】文生图
+import {AiImageStatusEnum, CreatePainting, PaintingGetMys} from '@/api/questions.js'
+import { getModelIdByType } from '@/api/teachers.js'
+import { ModelTypeEnum } from '@/api/teachers.js'
+import { globalState } from '@/utils/globalState.js'
 
-// 返回上一页
-const goBack = () => {
-  router.go(-1)
-}
-// 渲染页面标题
-const boxIconTitle = ref('')
-// 使用 ref 存储 workspace
-const workspace = ref(null)
+Blockly.setLocale(hans);
 
-onMounted(async () => {
-  const typeId = router.currentRoute.value.query.typeId
-  console.log(typeId,"----")
-  if (typeId) {
+// 状态管理
+const state = reactive({
+  workspace: null,
+  generatedContent: {
+    imageUrl: '',
+    videoUrl: '',
+    text: ''
+  },
+  previewVisible: false,
+  previewType: '',
+  previewContent: '',
+  isProcessing: false,
 
-  }
-  const title = router.currentRoute.value.query.typeName
-  if (title) {
-    boxIconTitle.value = String(title)
-  }
+//【文生图】文生图
+  modelId: 0,
+  gradeId: '',
+  inProgressImageMap: {}
+});
 
-  workspace.value = Blockly.inject('blocklyDiv',
-      {
-        //工具栏
-        toolbox: document.getElementById('toolbox'),
-        //网格效果
-        grid:{spacing: 20,length: 3,colour: '#ccc',snap: true}
-      }
-  );
-  // 工作区监听代码生成器
-  // 修改为传递一个函数
-  workspace.value.addChangeListener(() => generateCode("python"));
-})
+// 【文生图】自动刷新image列表的定时器
+const inProgressTimer = ref();
 
-//保存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);
+// 初始化Blockly工作区和自定义积木
+onMounted(async () => {
+  // 从全局状态初始化年级ID
+  state.gradeId = globalState.initGradeId()
+  try{
+    // 获取modelId
+    const modelRes = await getModelIdByType({ type: ModelTypeEnum.TEXT_TO_IMAGE, platform: "DouBao" })
+    state.modelId = modelRes.data
+  }catch(error){
+    console.error('获取modelId失败:', error);
   }
-  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;
-}
+  // 注册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('');
+    }
+  };
 
-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,
+  // 注册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('');
+    }
   };
-}
 
-// 定义生成代码的函数
-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;
-};
-// 代码生成器
-const myUpdateFunction = () => {
-  // 修改此处,传入 workspace.value
-  const code = javascriptGenerator.workspaceToCode(workspace.value);
-  document.getElementById('textarea').value = code;
-};
+  // 注册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('');
+    }
+  };
 
-// 获取 blockly 工作区中的 code 和 xml 结构
-const getBlockData = () => {
-  if (workspace.value) {
-    const code = Blockly.JavaScript.workspaceToCode(workspace.value);
-    const xml = Blockly.Xml.workspaceToDom(workspace.value)
-    const xmlText = Blockly.Xml.domToText(xml);
-    // 可以在这里返回或处理 code 和 xmlText
-    return { code, xmlText };
-  } else {
-    console.error('workspace 或 Blockly.JavaScript 未正确初始化');
-  }
-};
+  // 注册AI文本生成文本积木
+  Blockly.Blocks['ai_text_to_text'] = {
+    init: function() {
+      this.appendDummyInput()
+          .appendField('AI文本处理');
+      this.appendValueInput('PROMPT')
+          .setCheck('String')
+          .appendField('输入文本:');
+      this.appendDummyInput()
+          .appendField('模型:')
+          .appendField(new Blockly.FieldDropdown([
+            ['默认', 'default'],
+            ['对话', 'chat'],
+            ['分析', 'analysis']
+          ]), 'MODEL');
+      this.setOutput(true, 'String');
+      this.setColour(300);
+      this.setTooltip('使用AI处理文本并返回结果');
+      this.setHelpUrl('');
+    }
+  };
 
-// 回显工作区中的 xml 结构
-const setBlockData = (xmlText) => {
-  if (workspace.value) {
-    clearBlockData();
-    const xml = Blockly.Xml.textToDom(xmlText);
-    Blockly.Xml.domToWorkspace(xml, workspace.value);
-  } else {
-    console.error('workspace 未正确初始化');
+  // 注册JavaScript代码生成器
+  registerJavaScriptGenerators();
+
+  // 注册Python代码生成器
+  registerPythonGenerators();
+
+  // 初始化工作区
+  state.workspace = Blockly.inject('blocklyDiv', {
+    toolbox: document.getElementById('toolbox'),
+    grid: { spacing: 20, length: 3, colour: '#ccc', snap: true },
+    trashcan: true
+  });
+
+  // 工作区变化时自动生成JavaScript代码
+  state.workspace.addChangeListener(() => generateCode("javascript"));
+});
+// 组件卸载时清除定时器
+onUnmounted(() => {
+  if (inProgressTimer.value) {
+    clearInterval(inProgressTimer.value);
   }
-};
+});
 
-// 清空工作区
-const clearBlockData = () => {
-  if (workspace.value) {
-    workspace.value.clear();
-  } else {
-    console.error('workspace 未正确初始化');
+
+// AI服务配置
+const aiServiceConfig = {
+  baseUrl: import.meta.env.VITE_BASE_URL + '/bjdxWeb/ai',
+  endpoints: {
+    voiceRecognition: '/voice-recognition',
+    textToImage: '/create-painting',
+    textToVideo: '/text-to-video',
+    textToText: '/text-to-text'
   }
 };
 
-
-// 菜单打开和关闭的处理函数
-const handleOpen = () => {}
-const handleClose = () => {}
-
-// 动态渲染树型结构
-const menuItems = ref([
-  { index: '1-1', title: '课前回顾' },
-  { index: '1-2', title: '课程引入' },
-  {
-    index: '1',
-    title: '知识讲解',
-    children: [
-      { index: '1-3', title: '图灵测试' },
-      { index: '1-4', title: '课堂提问' },
-      { index: '1-5', title: '达特茅斯会议' },
-      { index: '1-6', title: '课堂选择' },
-      { index: '1-7', title: '聊天机器人' },
-      { index: '1-8', title: '专家系统' },
-      { index: '1-9', title: '课堂互动' },
-      { index: '1-10', title: '互联网快速发展' },
-      { index: '1-11', title: '黄金时代' },
-      { index: '1-12', title: '课堂提问' },
-      { index: '1-13', title: '大模型时代' }
-    ]
-  },
-  { index: '1-14', title: '趣味实操' },
-  { index: '1-15', title: '课程总结' }
-])
-
-// 当前播放的索引
-const currentIndex = ref('1-1')
-
-// 渲染课程标题到视频上方
-const smallTitle = ref('课前回顾')
-const handleSelect = index => {
-  //测试账号禁用视频
-  if (disableVideo(index))return;
-
-  const findTitle = items => {
-    for (const item of items) {
-      if (item.index === index) {
-        return item.title
-      }
-      if (item.children) {
-        const result = findTitle(item.children)
-        if (result) {
-          return result
-        }
+// AI服务模块 - 统一管理API调用
+const aiService = {
+  // 语音识别
+  async recognizeVoice(promptText = '', language = 'zh-CN') {
+    console.log('recognizeVoice', promptText, language);
+    // 前端语音采集
+    const recognitionResult = await this.captureVoice(language, promptText);
+    if (!recognitionResult) return '';
+
+    // 可选:将语音识别结果发送到后端进行优化处理
+    try {
+      const response = await fetch(`${aiServiceConfig.baseUrl}${aiServiceConfig.endpoints.voiceRecognition}`, {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({
+          text: recognitionResult,
+          language
+        })
+      });
+
+      if (response.ok) {
+        const data = await response.json();
+        return data.optimizedText || recognitionResult;
       }
+    } catch (error) {
+      console.warn('语音识别优化失败,使用原始结果', error);
     }
-    return null
-  }
-  const title = findTitle(menuItems.value)
-  if (title) {
-    smallTitle.value = title
-  }
-  // 根据索引切换视频
-  if (videoMap[index]) {
-    videoSrc.value = videoMap[index]
-    currentIndex.value = index
-  }
 
-}
+    return recognitionResult;
+  },
 
-// 展平所有菜单项索引
-const flattenMenuItems = () => {
-  const indices = []
-  const traverse = items => {
-    for (const item of items) {
-      if (!item.children) {
-        indices.push(item.index)
-      } else {
-        traverse(item.children)
+  // 前端语音采集
+  captureVoice(language, promptText) {
+    console.log('captureVoice', language, promptText);
+    return new Promise((resolve) => {
+      if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) {
+        ElMessage.warning('您的浏览器不支持语音识别功能');
+        resolve('');
+        return;
       }
-    }
-  }
-  traverse(menuItems.value)
-  return indices
-}
 
-//禁用视频
-const disableVideo = (index = currentIndex.value) => {
-  let dis = ["1-6","1-7","1-8","1-9","1-10","1-11","1-12","1-13","1-14","1-15"]
+      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+      const recognition = new SpeechRecognition();
 
-  if (localStorage.getItem('userName') === "aiTest" &&
-      dis.indexOf(index) !== -1) {
+      recognition.lang = language;
+      recognition.interimResults = false;
+      recognition.maxAlternatives = 1;
 
-    if (videoRef.value) {
-      // 记录当前播放时间
-      videoRef.value.pause()
-      // 阻止用户跳转到新的时间点,将播放时间重置为之前的时间
-      videoRef.value.currentTime = 0;
-    }
-    //提示禁用// 显示消息框
-    Message().notifyWarning('您的账号并未开放此课程!', true);
-    return true;
-  }
-  return false;
-}
-</script>
+      if (promptText) {
+        ElMessage.info(promptText + '\n请开始说话...');
+      } else {
+        ElMessage.info('请开始说话...');
+      }
 
-<style scoped lang="scss">
-@use 'sass:math';
-@use 'sass:color'; // 引入 color 模块
-// 定义rpx转换函数
-@function rpx($px) {
-  @return math.div($px, 750) * 100vw;
-}
+      recognition.onresult = (event) => {
+        const speechResult = event.results[0][0].transcript;
+        console.log('语音识别结果:', speechResult);
+        resolve(speechResult);
+      };
 
-// 定义儿童风格的蓝紫色调
-$primary-color: rgba(106, 90, 205, 0.52); // 主色调:蓝紫色
-$secondary-color: rgba(147, 112, 219, 0.66); // 辅助色:亮蓝紫色
-$accent-color: rgb(133, 89, 220); // 强调色:暗蓝紫色
-$light-color: #e6e6fa; // 浅色背景:淡紫色
-$text-color: #483d8b; // 文本颜色:靛蓝色
-
-/* 添加过渡样式 */
-.drawer-slide-enter-active,
-.drawer-slide-leave-active {
-  transition: all 0.3s ease;
-}
-.drawer-slide-enter-from,
-.drawer-slide-leave-to {
-  transform: translateX(-100%);
-  opacity: 0;
-}
-.main-content {
-  width: rpx(135);
-  height: 100%;
-  position: relative;
-  background: linear-gradient(to bottom, #001169, #8a78d0);
+      recognition.onerror = (event) => {
+        console.error('语音识别错误:', event.error);
+        ElMessage.error('语音识别发生错误: ' + event.error);
+        resolve('');
+      };
 
-}
-.content-box {
-  flex: 1;
-  height: 100%;
-  display: flex;
-  flex-direction: column; /* 子元素上下排列 */
-  background: linear-gradient(
-          to bottom,
-          #e2ddfc,
-          #f1effd
-  ); /* 设置悬停、聚焦、点击状态下的背景色 */
-}
-.icon-expand {
-  width: rpx(30);
-  height: rpx(30);
-  z-index: 9999;
-  position: absolute;
-  cursor: pointer; // 添加鼠标指针样式
-}
-.icon-expand .el-icon {
-  font-size: rpx(15);
-  position: absolute;
-  color: white;
-  left: rpx(9);
-  margin-top: rpx(17);
-}
-.icon-expand .el-icon:active {
-  color: black;
-}
-.home-container {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: linear-gradient(to bottom, #001169, #b4a8e1);
-  display: flex;
-  flex-direction: row;
-  gap: rpx(0);
-}
-.el-row {
-  margin: auto;
-  margin-top: rpx(40);
-}
-.tac ::v-deep(.el-menu) {
-  background-color: transparent;
-  border: none;
-  width: 100%;
-}
+      recognition.onend = () => {
+        console.log('语音识别已结束');
+      };
 
-/* 取消点击后的蓝色字体 el-sub-menu__title*/
-.tac ::v-deep(.el-menu-item.is-active),
-.tac ::v-deep(.el-sub-menu__title.is-active) {
-  color: white;
-}
-// 取消鼠标悬浮颜色
-.tac ::v-deep(.el-sub-menu__title) {
-  width: rpx(130);
-  height: rpx(20);
-  margin-bottom: rpx(5);
-  border-radius: rpx(6);
-}
-.el-menu ::v-deep(.el-sub-menu__title:hover),
-.el-menu ::v-deep(.el-sub-menu__title:focus),
-.el-menu ::v-deep(.el-sub-menu__title:active) {
-  background: linear-gradient(
-          to bottom,
-          #fee78a,
-          #ffce1b
-  );
-  background-color: transparent;
-}
-.mb-2 {
-  color: white;
-  margin-top: rpx(1);
-}
-.mb-2 .el-icon {
-  font-size: rpx(10);
-  margin-left: rpx(5);
-}
-.el-menu-item {
-  width: rpx(115);
-  height: rpx(20);
-  margin-bottom: rpx(5);
-  border-radius: rpx(6);
-  color: white;
-  font-size: rpx(7);
+      recognition.start();
+    });
+  },
 
-}
-.el-menu ::v-deep(.el-sub-menu__title){
-  color: white;
-  width: rpx(115);
-  height: rpx(20);
-  margin-bottom: rpx(5);
-  font-size: rpx(7);
-}
-.el-menu ::v-deep(.el-sub-menu__title:hover),
-.el-menu ::v-deep(.el-sub-menu__title:focus),
-.el-menu ::v-deep(.el-sub-menu__title:active) {
-  background: linear-gradient(
-          to bottom,
-          #fee78a,
-          #ffce1b
-  );
-  color: black;
-  font-size: rpx(8);
-}
-.el-menu ::v-deep(.el-menu-item:hover),
-.el-menu ::v-deep(.el-menu-item:focus),
-.el-menu ::v-deep(.el-menu-item:active) {
-  background: linear-gradient(
-          to bottom,
-          #fee78a,
-          #ffce1b
-  );
-  color: black;
-  font-size: rpx(8);
-}
-.drawer-box {
-  position: absolute;
-  display: flex;
-  align-items: center;
-  height: 100%;
-  width: 100%;
-  overflow-y: auto; /* 添加垂直滚动条 */
-  max-height: 100%; /* 设置最大高度 */
-  /* 添加滚动条样式 */
-  &::-webkit-scrollbar {
-    width: rpx(1);
-  }
-  &::-webkit-scrollbar-thumb {
-    background-color:#b4a8e1;
-    border-radius: rpx(5);
-  }
-  &::-webkit-scrollbar-track {
-    background-color: rgba(255, 255, 255, 0.2);
-    border-radius: rpx(5);
-  }
-}
-.drawer-box .toggle-button {
-  width: rpx(10);
-  height: rpx(50);
-  font-size: rpx(7);
-  background-color: rgba(17, 23, 29, 0.2);
-  border: none;
-  position: relative;
-  writing-mode: vertical-lr; // 文字垂直排列,从左到右
-  text-orientation: upright; // 文字保持正立
-}
-.toggle-button:hover {
-  left: 0;
-}
+  // 文本生成图片
+  async textToImage(prompt, waitForCompletion = true) {
+    console.log('textToImage', prompt, waitForCompletion);
+
+    state.isProcessing = true;
+    try {
+      // 使用CreatePainting API创建图片任务
+      const createRes = await CreatePainting({
+        "modelId": state.modelId,
+        "prompt": prompt,
+        "width": 1024,
+        "height": 1024
+      });
+
+      // 记录任务ID到映射中
+      state.inProgressImageMap[createRes.data] = {id: createRes.data, status: AiImageStatusEnum.IN_PROGRESS};
+
+      // 开始轮询任务状态
+      if (!inProgressTimer.value) {
+        this.startImagePolling();
+      }
 
+      // 如果需要等待完成,等待图片生成完成
+      if (waitForCompletion) {
+        return await this.waitForImageCompletion(createRes.data);
+      }
 
-.toggle-button.is-active,
-.toggle-button:active,
-.toggle-button:focus {
-  background-color: rgba(165, 209, 247, 0.8);
-  color: white;
-  border: none; // 移除点击时的边框
-  outline: none; // 移除点击时的外边框
-}
+      return createRes.data; // 返回任务ID
+    } catch (error) {
+      console.error('生成图片失败:', error);
+      ElMessage.error('生成图片失败: ' + error.message);
+      return null;
+    } finally {
+      state.isProcessing = false;
+    }
+  },
 
-.box-1 {
-  width: 100%;
-  height: rpx(50);
-  margin-top: rpx(10);
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  box-sizing: border-box;
-  font-size: rpx(15); // 默认字体大小
-}
+  // 【文生图】开始轮询图片生成状态
+  startImagePolling() {
+    if (inProgressTimer.value) {
+      clearInterval(inProgressTimer.value);
+    }
 
-.inner-box {
-  height: 100%;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: rpx(16); // 默认字体大小
-}
+    inProgressTimer.value = setInterval(async () => {
+      await this.refreshWatchImages();
+    }, 3000); // 每3秒轮询一次
+  },
 
-.left-box {
-  position: relative;
-  justify-content: flex-start;
-  align-items: flex-start;
-  flex: 1; // 设置左侧盒子占比为 2
-  cursor: pointer; // 添加鼠标指针样式
-}
-.box-icon {
-  width: 100%;
-  height: 100%;
-  flex: 1;
-  display: flex; // 添加 flex 布局
-  align-items: center; // 垂直居中
-  color: black; // 设置图标颜色为白色
-  padding-left: rpx(15);
-  font-size: rpx(10); // 设置图标大小,可按需调整
-}
-.box-icon .left-icon {
-  margin-left: rpx(10);
-  margin-right: rpx(5); // 设置图标和文字之间的间距 ;
-}
+  // 【文生图】轮询生成中的image列表
+  async refreshWatchImages() {
+    const imageIds = Object.keys(state.inProgressImageMap).map(Number);
+    if (imageIds.length === 0) {
+      if (inProgressTimer.value) {
+        clearInterval(inProgressTimer.value);
+        inProgressTimer.value = null;
+      }
+      return;
+    }
 
-.left-box span {
-  position: absolute;
-  font-size: rpx(11); // 默认字体大小
-  color: black;
-}
+    try {
+      const list = await PaintingGetMys(imageIds);
+      const newWatchImages = {};
+
+      list.data.forEach((image) => {
+        if (image.status === AiImageStatusEnum.IN_PROGRESS) {
+          newWatchImages[image.id] = image;
+        } else if (image.status === AiImageStatusEnum.SUCCESS) {
+          // 图片生成完成,更新状态并显示预览
+          state.generatedContent.imageUrl = image.picUrl;
+          state.previewType = 'image';
+          state.previewContent = image.picUrl;
+          state.previewVisible = true;
+
+          // 打印到控制台,便于在结果区域显示
+          console.log('图片生成完成:', image.picUrl);
+        } else if (image.status === AiImageStatusEnum.FAIL) {
+          // 图片生成失败
+          ElMessage.error('图片生成失败: ' + (image.error || '未知错误'));
+          console.error('图片生成失败:', image.id, image.error);
+        }
+      });
 
-.right-box {
-  flex: 1;
-  position: relative; // 添加相对定位;
-}
-.top-right-box {
-  position: absolute; // 添加绝对定位
-  margin-top: rpx(20); // 调整上边距离
-  margin-right: rpx(50); // 调整右边距离
-  width: 100%; // 设置盒子宽度,可按需调整
-  height: 60px; // 设置盒子高度,可按需调整
-  display: flex;
-  justify-content: flex-end;
-}
-.top-right-box {
-  ::v-deep(.el-input__wrapper) {
-    background-color: rgb(255, 255, 255, 0.5);
-    border-radius: rpx(12);
-    border: white 1px solid;
-    color: #aaa5c5;
-  }
-  ::v-deep(.el-input__icon) {
-    color: #aaa5c5; // 设置输入框图标颜色为白色
-  }
-  // 添加占位符样式
-  ::v-deep(.el-input__inner::placeholder) {
-    color: #aaa5c5;
-  }
-  // 添加输入框文字颜色样式
-  ::v-deep(.el-input__inner) {
-    color: black;
-  }
-}
-// 搜索框
-.search-input {
-  width: rpx(100);
-  height: rpx(15);
-  font-size: rpx(7);
-}
-.box-2 {
-  width: 100%;
-  flex: 1;
-  box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
-  box-sizing: border-box;
-  display: flex; // 确保子元素水平排列
-  flex-wrap: wrap; // 允许子元素换行;
-  align-content: center; // 顶部对齐;
-  cursor: pointer; // 添加鼠标指针样式
-}
-.box-blockly {
-  width: 60%;
-  height: rpx(300);
-  padding: 0px 20px;
-  float: left;
-}
-.box-code{
-  width: 35%;
-  height: rpx(300);
+      state.inProgressImageMap = newWatchImages;
 
-  .box-code-textarea{
-    height: 80%;
-    width: 100%;
-  }
-}
+      // 如果没有正在处理的图片,清除定时器
+      if (Object.keys(newWatchImages).length === 0 && inProgressTimer.value) {
+        clearInterval(inProgressTimer.value);
+        inProgressTimer.value = null;
+      }
+    } catch (error) {
+      console.error('轮询图片状态失败:', error);
+    }
+  },
 
-/* 隐藏 Chrome 视频控件的渐变背景等默认样式 */
-video::-webkit-media-controls-panel {
-  background: transparent !important; /* 去掉背景渐变,设为透明 */
-}
+  // 【文生图】等待图片生成完成
+  async 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.COMPLETED) {
+              clearInterval(checkInterval);
+              resolve(image.picUrl);
+            } else if (image.status === AiImageStatusEnum.FAILED) {
+              clearInterval(checkInterval);
+              reject(new Error(image.error || '图片生成失败'));
+            }
+          }
+        } catch (error) {
+          clearInterval(checkInterval);
+          reject(error);
+        }
+      }, 3000);
+    });
+  },
 
-.small-title {
-  width: 100%;
-  margin-top: rpx(-20);
-  height: rpx(20);
-  color: black;
-  font-size: rpx(10);
-  justify-content: center; //使子元素水平居中
-}
-.video-switch {
-  width: 100%;
-  // height: rpx(50);
-  display: flex;
-  margin-top: rpx(-20);
-  // background-color: saddlebrown;
-}
-.caret-right,
-.caret-left {
-  width: rpx(50);
-  margin: auto;
-  display: flex;
-  margin-top: rpx(5);
-  margin-bottom: rpx(15);
-}
-.caret-left ::v-deep(.el-button.is-round),
-.caret-right ::v-deep(.el-button.is-round) {
-  width: rpx(50);
-  height: rpx(15);
-  color: #aaa5c5;
-  border-radius: none;
-  border: 1px white solid;
-  background-color: rgb(255, 255, 255, 0.5); // 进度条背景颜色,调浅为半透明白色
-  box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
-}
+  // 文本生成视频
+  async textToVideo(prompt, waitForCompletion = true) {
+    console.log('textToVideo', prompt, waitForCompletion);
+
+    state.isProcessing = true;
+    try {
+      // 视频生成通常耗时较长,这里使用轮询或WebSocket会更好
+      // 简单实现:先获取任务ID,再轮询结果
+      const initResponse = await fetch(`${aiServiceConfig.baseUrl}${aiServiceConfig.endpoints.textToVideo}`, {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({ prompt })
+      });
+
+      if (!initResponse.ok) throw new Error('初始化视频生成失败');
+
+      const initData = await initResponse.json();
+      const taskId = initData.taskId;
+
+      // 轮询结果
+      const checkResult = async () => {
+        const resultResponse = await fetch(`${aiServiceConfig.baseUrl}${aiServiceConfig.endpoints.textToVideo}/${taskId}`);
+        const resultData = await resultResponse.json();
+
+        if (resultData.status === 'completed') {
+          state.generatedContent.videoUrl = resultData.videoUrl;
+
+          // 显示预览
+          state.previewType = 'video';
+          state.previewContent = resultData.videoUrl;
+          state.previewVisible = true;
+
+          return resultData.videoUrl;
+        } else if (resultData.status === 'failed') {
+          throw new Error(resultData.error || '视频生成失败');
+        } else {
+          // 继续轮询
+          await new Promise(resolve => setTimeout(resolve, 3000));
+          return checkResult();
+        }
+      };
+
+      return checkResult();
+    } catch (error) {
+      console.error('生成视频失败:', error);
+      ElMessage.error('生成视频失败: ' + error.message);
+      return null;
+    } finally {
+      state.isProcessing = false;
+    }
+  },
 
-// 儿童风格试题弹框样式
-.child-dialog {
-  .el-dialog__header {
-    display: none; // 隐藏原有的标题栏
+  // 文本生成文本(如AI对话)
+  async textToText(prompt, model = 'default') {
+
+    console.log('textToText', prompt, model);
+    state.isProcessing = true;
+    try {
+      const response = await fetch(`${aiServiceConfig.baseUrl}${aiServiceConfig.endpoints.textToText}`, {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({
+          prompt,
+          model
+        })
+      });
+
+      if (!response.ok) throw new Error('AI处理失败');
+
+      const data = await response.json();
+      state.generatedContent.text = data.result;
+
+      // 显示预览
+      state.previewType = 'text';
+      state.previewContent = data.result;
+      state.previewVisible = true;
+
+      return data.result;
+    } catch (error) {
+      console.error('AI文本处理失败:', error);
+      ElMessage.error('AI文本处理失败: ' + error.message);
+      return null;
+    } finally {
+      state.isProcessing = false;
+    }
   }
+};
 
-  .el-dialog__body {
-    padding: rpx(20);
-    position: relative;
-  }
+// 注册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}')`;
+    console.log('ai_voice_input', prompt, language);
+    return code;
+  };
 
-  .el-dialog__footer {
-    border-top: none;
-    padding: rpx(10) rpx(20);
-    text-align: center;
-  }
+  // 文本生成图片
+  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});`;
+    console.log('ai_text_to_image', prompt, waitForCompletion);
+    return code;
+  };
 
-  .el-dialog__wrapper {
-    // 修改半透明背景色
-    background-color: rgba(0, 0, 0, 0.6);
-  }
+  // 文本生成视频
+  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});`;
+    console.log('ai_text_to_video', prompt, waitForCompletion);
+    return code;
+  };
 
-  .el-dialog {
-    border: none;
-    border-radius: rpx(20);
-    background: linear-gradient(
-            135deg,
-            $light-color,
-            #d8bfd8
-    ); // 柔和的蓝紫色渐变
-    // 添加边框边晕效果
-    box-shadow: 0 0 20px 5px rgba($primary-color, 0.6),
-    0 0 40px 10px rgba($primary-color, 0.4),
-    0 0 60px 15px rgba($primary-color, 0.2);
-    overflow: hidden;
-    // 添加装饰元素
-    &::before {
-      content: '';
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: rpx(10);
-      background: linear-gradient(
-              90deg,
-              $primary-color,
-              $secondary-color,
-              $accent-color
-      );
-    }
-  }
+  // 文本生成文本
+  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}')`;
+    console.log('ai_text_to_text', prompt, model);
+    return code;
+  };
 }
 
-// 问题标题样式
-.question-title {
-  background: linear-gradient(
-          90deg,
-          rgba($primary-color, 0.2),
-          rgba($secondary-color, 0.2)
-  );
-  padding: rpx(15);
-  border-radius: rpx(12);
-  margin-bottom: rpx(20);
-  color: $accent-color;
-  font-weight: bold;
-  font-size: rpx(14);
-  box-shadow: 0 rpx(3) rpx(10) rgba($primary-color, 0.1);
-  position: relative;
-  display: flex;
-  align-items: center;
-
-  .question-icon {
-    background-color: $accent-color;
-    color: white;
-    width: rpx(24);
-    height: rpx(24);
-    border-radius: 50%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    margin-right: rpx(10);
-    font-weight: bold;
-    box-shadow: 0 rpx(2) rpx(5) rgba($accent-color, 0.3);
-  }
-}
+// 注册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;
+  };
 
-// 选项容器样式
-.options-container {
-  margin-bottom: rpx(20);
-}
+  // 文本生成图片
+  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;
+  };
 
-// 问题选项样式
-.question-option {
-  margin: rpx(8) 0;
-  padding: rpx(10) rpx(15);
-  border-radius: rpx(12);
-  border: rpx(1) solid rgba($primary-color, 0.3);
-  transition: all 0.3s ease;
-  display: flex;
-  align-items: center;
-  background-color: white;
-  box-shadow: 0 rpx(2) rpx(5) rgba($primary-color, 0.05);
+  // 文本生成视频
+  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;
+  };
 
-  ::v-deep(.el-radio__label) {
-    color: $text-color;
-    margin-left: rpx(8);
-    flex: 1;
-    text-align: left;
-    // 增大字体大小
-    font-size: rpx(12);
-  }
+  // 文本生成文本
+  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;
+  };
+}
 
-  // 选中时的样式变化
-  .el-radio__input.is-checked + .el-radio__label {
-    font-weight: bold;
-    color: $accent-color;
+// 生成代码
+const generateCode = (language) => {
+  if (!state.workspace) {
+    console.error('workspace 未正确初始化');
+    return;
   }
 
-  &:hover {
-    background-color: rgba($primary-color, 0.05);
-    border-color: rgba($primary-color, 0.5);
-    transform: translateY(-rpx(1));
+  let generator;
+  if (language === 'javascript') {
+    generator = javascriptGenerator;
+  } else if (language === 'python') {
+    generator = pythonGenerator;
+  } else {
+    console.error('不支持的语言类型');
+    return;
   }
-}
 
-// 暂无选项样式
-.no-options {
-  color: rgba($text-color, 0.7);
-  text-align: center;
-  padding: rpx(20);
-  font-size: rpx(12);
-}
+  const code = generator.workspaceToCode(state.workspace);
+  document.getElementById('textarea').value = code;
+};
 
-// 底部按钮样式
-.child-button {
-  min-width: rpx(80);
-  height: rpx(30);
-  border-radius: rpx(15);
-  font-size: rpx(12);
-  font-weight: 500;
-  transition: all 0.3s ease;
-  box-shadow: 0 rpx(2) rpx(8) rgba(0, 0, 0, 0.1);
-
-  &.confirm {
-    background: linear-gradient(to right, $primary-color, $secondary-color);
-    border: none;
-    border-right: 15px;
-    color: white;
-
-    &:hover {
-      background: linear-gradient(
-              to right,
-              color.adjust($primary-color, $lightness: -5%),
-              color.adjust($secondary-color, $lightness: -5%)
-      );
-      box-shadow: 0 rpx(3) rpx(10) rgba($primary-color, 0.4);
-      transform: translateY(-rpx(1));
-      color: white;
-    }
-  }
+// 运行代码
+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 = (...args) => {
+      outputBuffer += '<div class="log-message">' + args.map(arg =>
+          typeof arg === 'object' ? JSON.stringify(arg) : arg
+      ).join(' ') + '</div>';
+      resultElement.innerHTML = outputBuffer;
+      originalConsoleLog.apply(console, args);
+    };
+
+    console.error = (...args) => {
+      outputBuffer += '<div class="error-message">' + args.map(arg =>
+          typeof arg === 'object' ? JSON.stringify(arg) : arg
+      ).join(' ') + '</div>';
+      resultElement.innerHTML = outputBuffer;
+      originalConsoleError.apply(console, args);
+    };
+
+    console.warn = (...args) => {
+      outputBuffer += '<div class="warn-message">' + args.map(arg =>
+          typeof arg === 'object' ? JSON.stringify(arg) : arg
+      ).join(' ') + '</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('代码包含不安全的操作');
+      }
 
-  &.cancel {
-    background: white;
-    border: rpx(1) solid rgba($primary-color, 0.3);
-    color: $text-color;
+      // 包装代码为异步函数执行,支持await
+      const wrappedCode = `(async () => { ${code} })()`;
+      await new Function(wrappedCode)();
 
-    &:hover {
-      background: rgba($primary-color, 0.05);
-      border-color: rgba($primary-color, 0.5);
-      transform: translateY(-rpx(1));
+      if (outputBuffer === '') {
+        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>`;
   }
 }
 
-// AI对话图标样式
-.ai-icon-container {
-  position: absolute;
-  bottom: rpx(20);
-  right: rpx(20);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  cursor: pointer;
-  transition: all 0.3s ease;
+// 保存JSON
+function saveJson() {
+  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();
+}
 
-  &:hover {
-    transform: translateY(-rpx(2));
-  }
+// 保存XML
+function saveXml() {
+  const output = document.getElementById('textarea');
+  const xml = Blockly.Xml.workspaceToDom(state.workspace);
+  output.value = Blockly.Xml.domToPrettyText(xml);
+  output.focus();
+  output.select();
+  taChange();
+}
 
-  .ai-icon {
-    width: rpx(30);
-    height: rpx(30);
-    margin-bottom: rpx(0);
-    filter: drop-shadow(0 rpx(2) rpx(4) rgba($primary-color, 0.3));
-    // 添加过渡动画
-    transition: transform 0.3s ease;
-  }
+// 重新加载
+function load() {
+  const input = document.getElementById('textarea');
+  if (!input.value) return;
 
-  // 悬浮时放大效果
-  .ai-icon:hover {
-    transform: scale(1.5);
+  try {
+    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);
   }
+}
 
-  .ai-text {
-    color: $text-color;
-    font-size: rpx(8);
-    background-color: rgba(255, 255, 255, 0.7);
-    padding: rpx(2) rpx(5);
-    border-radius: rpx(5);
+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;
 }
 
-// AI消息样式
-.ai-message {
-  display: flex;
-  align-items: flex-start;
-  margin-bottom: rpx(15);
-
-  .ai-avatar {
-    width: rpx(30);
-    height: rpx(30);
-    border-radius: 50%;
-    margin-right: rpx(10);
-    background-color: $primary-color;
-    padding: rpx(5);
-    box-shadow: 0 rpx(2) rpx(5) rgba($primary-color, 0.2);
+function saveIsValid(save) {
+  let validJson = true;
+  try {
+    JSON.parse(save);
+  } catch (e) {
+    validJson = false;
   }
 
-  .ai-text-content {
-    background-color: $light-color;
-    padding: rpx(8) rpx(12);
-    border-radius: rpx(10);
-    font-size: rpx(10);
-    color: $text-color;
-    max-width: 80%;
-    box-shadow: 0 rpx(1) rpx(3) rgba(0, 0, 0, 0.05);
+  let validXml = true;
+  try {
+    Blockly.utils.xml.textToDom(save);
+  } catch (e) {
+    validXml = false;
   }
+
+  return { json: validJson, xml: validXml };
 }
 
-// 用户输入框样式
-.user-input {
-  ::v-deep(.el-input__wrapper) {
-    border-radius: rpx(15);
-    border-color: rgba($primary-color, 0.3);
-    margin-right: 10px;
+// 关闭预览
+function handleClosePreview() {
+  state.previewVisible = false;
+  state.previewContent = '';
+  state.previewType = '';
+}
 
-    &:focus-within {
-      box-shadow: 0 0 0 rpx(1) rgba($primary-color, 0.5);
-    }
-  }
+// 将aiService挂载到window,以便执行生成的代码时可以访问
+window.aiService = aiService;
+</script>
 
-  ::v-deep(.el-input__inner) {
-    font-size: rpx(10);
-    color: $text-color;
-    text-indent: 1em;
-  }
-}
-/* 定义淡入和缩放动画 */
-.fade-scale-enter-active,
-.fade-scale-leave-active {
-  transition: all 0.5s ease;
-}
+<style scoped lang="scss">
+/* 原有样式保持不变 */
+@use 'sass:math';
 
-.fade-scale-enter-from,
-.fade-scale-leave-to {
-  opacity: 0.1;
-  transform: scale(0.9);
+@function rpx($px) {
+  @return math.div($px, 750) * 100vw;
 }
 
-// 自定义试题弹框背景
-.child-dialog-wrapper {
+.home-container {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
-  background-color: rgba(0, 0, 0, 0.6); // 半透明背景
+  background: linear-gradient(to bottom, #001169, #b4a8e1);
   display: flex;
-  justify-content: center;
-  align-items: center;
-  z-index: 1000;
+  padding-top: rpx(10);
 }
 
-.child-dialog {
-  border: none;
-  border-radius: rpx(20);
-  background: linear-gradient(
-          135deg,
-          $light-color,
-          #d8bfd8
-  ); // 柔和的蓝紫色渐变
-  box-shadow: 0 0 20px 5px rgba($primary-color, 0.6),
-  0 0 40px 10px rgba($primary-color, 0.4),
-  0 0 60px 15px rgba($primary-color, 0.2);
-  overflow: hidden;
-  padding: rpx(20);
+.box-blockly {
   width: 60%;
-  position: relative;
-}
-
-// AI对话弹框样式
-.ai-dialog-wrapper {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  z-index: 1001;
-  pointer-events: none;
+  height: 90%;
+  padding: rpx(10) rpx(10);
+  float: left;
 }
 
-.ai-dialog {
-  border: none;
-  border-radius: rpx(20);
-  background: linear-gradient(135deg, $light-color, #d8bfd8);
-  box-shadow: 0 0 20px 5px rgba($primary-color, 0.6),
-  0 0 40px 10px rgba($primary-color, 0.4),
-  0 0 60px 15px rgba($primary-color, 0.2);
-  overflow: hidden;
-  padding: rpx(20);
-  width: 30%;
-  // 增加高度
-  height: 80%;
-  margin-right: rpx(50);
-  pointer-events: auto;
-  position: relative;
+.box-code {
+  color: white;
+  width: 35%;
+  height: 90%;
+  padding: rpx(10) rpx(10);
   display: flex;
   flex-direction: column;
 
-  &::before {
-    content: '';
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: rpx(10);
-    background: linear-gradient(
-            90deg,
-            $primary-color,
-            $secondary-color,
-            $accent-color
-    );
-  }
-}
-
-.ai-dialog-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: rpx(15);
-
-  h3 {
-    color: $text-color;
-    font-size: rpx(12);
+  .button-container {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    align-items: flex-start;
   }
 
-  .close-btn {
-    padding: 0;
-    width: rpx(20);
-    height: rpx(20);
-    font-size: rpx(16);
-    line-height: 1;
+  .button-container >>> .el-button {
+    flex-shrink: 0;
   }
-}
 
-.ai-dialog-content {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-}
-
-.ai-message-history {
-  flex: 1;
-  // 当内容超出容器高度时,显示垂直滚动条
-  overflow-y: auto;
-  margin-bottom: rpx(15);
-  // 可以根据实际情况调整最大高度
-  max-height: 50vh;
-  padding: 5px 10px;
-
-  .message {
+  .code-section {
+    flex: 1;
     display: flex;
-    align-items: flex-start;
-    margin-bottom: rpx(10);
-
-    &.user {
-      flex-direction: row-reverse;
-    }
-
-    .avatar {
-      width: rpx(30);
-      height: rpx(30);
-      border-radius: 50%;
-      margin: 0 rpx(10);
-    }
-
-    .user {
-      width: 15px;
-      height: 15px;
+    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;
     }
-
-    .message-content {
-      background-color: $light-color;
-      padding: rpx(8) rpx(12);
-      border-radius: rpx(10);
-      font-size: rpx(10);
-      color: $text-color;
-      max-width: 80%;
-      box-shadow: 0 rpx(1) rpx(3) rgba(0, 0, 0, 0.05);
-    }
-  }
-  // 滚动条整体样式
-  &::-webkit-scrollbar {
-    width: rpx(4); // 滚动条宽度
   }
 
-  // 滚动条滑块样式
-  &::-webkit-scrollbar-thumb {
-    background-color: $primary-color; // 滑块颜色
-    border-radius: rpx(4); // 滑块圆角
-    transition: background-color 0.3s ease; // 颜色过渡效果
-
-    &:hover {
-      //background-color: darken($primary-color, 10%); // 悬停时滑块颜色加深
+  .result-section {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+
+    .run-result-content {
+      flex: 1;
+      background-color: #f8f9fa;
+      border: 1px solid #ddd;
+      border-radius: 4px;
+      padding: 10px;
+      overflow-y: auto;
+      font-family: monospace;
+      font-size: 14px;
     }
   }
+}
 
-  // 滚动条轨道样式
-  &::-webkit-scrollbar-track {
-    background-color: rgba($primary-color, 0.2); // 轨道颜色
-    border-radius: rpx(4); // 轨道圆角
-  }
-
-  .message {
-    display: flex;
-    align-items: flex-start;
-    margin-bottom: rpx(10);
+// AI模块样式
+[categorystyle="ai_category"] > .blocklyTreeRow {
+  background-color: #9c27b0 !important;
+}
 
-    &.user {
-      flex-direction: row-reverse;
-    }
+// 运行结果样式
+.running-indicator { color: #666; }
+.log-message { color: #333; }
+.error-message { color: #dc3545; }
+.warn-message { color: #ffc107; }
+.success-message { color: #28a745; }
 
-    .avatar {
-      width: rpx(30);
-      height: rpx(30);
-      border-radius: 50%;
-      margin: 0 rpx(10);
-    }
+// 预览样式
+.preview-image-container, .preview-video-container {
+  display: flex;
+  justify-content: center;
+}
 
-    .message-content {
-      background-color: $light-color;
-      padding: rpx(8) rpx(12);
-      border-radius: rpx(10);
-      font-size: rpx(10);
-      color: $text-color;
-      max-width: 80%;
-      box-shadow: 0 rpx(1) rpx(3) rgba(0, 0, 0, 0.05);
-    }
-  }
+.preview-image, .preview-video {
+  max-width: 100%;
+  max-height: 60vh;
+  border-radius: 4px;
 }
 
-// 优化发送按钮样式
-.send-button {
-  //background: linear-gradient(90deg, $primary-color, $secondary-color);
-  border: none;
-  color: white;
+.preview-text-container {
+  max-height: 60vh;
+  overflow-y: auto;
+  padding: 10px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
 
-  &:hover {
-    //background: linear-gradient(90deg, darken($primary-color, 5%), darken($secondary-color, 5%));
-  }
+//【文生图预览】
+.extra-image-preview {
+  margin-top: 10px;
+  padding: 10px;
+  border: 1px solid #ddd;
+  border-radius: 5px;
+  background-color: #f9f9f9;
 }
-.el-menu .el-menu-item.is-active {
-  background: linear-gradient(to bottom, #fee78a, #ffce1b);
-  color: black;
-  font-size: rpx(8);
-  box-shadow: 0 4px 8px rgba(3, 3, 3, 0.3);
+
+.extra-preview-image {
+  max-width: 100%;
+  max-height: 400px;
+  border-radius: 4px;
 }
-</style>
+</style>

+ 456 - 271
src/views/Blockly_bak.vue

@@ -1,301 +1,486 @@
 <template>
-  <div class="min-h-screen p-6 bg-gray-50">
-    <!-- 标题与操作 -->
-    <div class="flex items-center justify-between mb-6">
-      <h1 class="text-2xl font-bold text-gray-800">迷宫题目管理</h1>
-      <button
-          class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
-          @click="openCreateModal"
-      >
-        新建题目
-      </button>
-    </div>
-
-    <!-- 题目列表 -->
-    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
-      <div
-          v-for="(problem, index) in problems"
-          :key="problem.id"
-          class="bg-white rounded-lg shadow p-4 transition hover:shadow-lg"
-      >
-        <h2 class="text-xl font-semibold text-gray-800 mb-2">{{ problem.title }}</h2>
-        <p class="text-gray-600 mb-4">{{ problem.description }}</p>
-        <div class="flex items-center justify-between">
-          <button
-              class="px-3 py-2 bg-green-500 text-white rounded hover:bg-green-600"
-              @click="openEditModal(problem, index)"
-          >
-            编辑
-          </button>
-          <button
-              class="px-3 py-2 bg-red-500 text-white rounded hover:bg-red-600"
-              @click="deleteProblem(index)"
-          >
-            删除
-          </button>
-        </div>
-      </div>
-    </div>
-
-    <!-- 新建/编辑弹窗 -->
-    <Teleport to="body">
-      <div
-          v-if="showModal"
-          class="fixed inset-0 flex items-center justify-center bg-black/50"
-      >
-        <div class="w-full max-w-xl bg-white rounded-lg shadow p-6">
-          <h2 class="text-xl font-bold text-gray-800 mb-4">
-            {{ isEdit ? "编辑题目" : "新建题目" }}
-          </h2>
-          <!-- 基础信息 -->
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">题目标题</label>
-            <input
-                v-model="form.title"
-                class="w-full border border-gray-300 rounded p-2"
-                placeholder="输入题目标题"
-            />
-          </div>
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">题目描述</label>
-            <textarea
-                v-model="form.description"
-                class="w-full border border-gray-300 rounded p-2"
-                rows="3"
-                placeholder="输入题目描述"
-            />
-          </div>
+  <!-- Blockly -->
+  <div class="home-container">
 
-          <!-- 迷宫地图配置 -->
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">迷宫尺寸</label>
-            <div class="flex gap-2">
-              <input
-                  v-model.number="form.mazeConfig.width"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  min="5"
-                  max="20"
-                  placeholder="宽度"
-              />
-              <input
-                  v-model.number="form.mazeConfig.height"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  min="5"
-                  max="20"
-                  placeholder="高度"
-              />
-            </div>
-          </div>
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">墙坐标(JSON 数组,如 [[0,1]])</label>
-            <textarea
-                v-model="form.mazeConfig.walls"
-                class="w-full border border-gray-300 rounded p-2"
-                rows="3"
-                placeholder="输入墙坐标"
-            />
-          </div>
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">起点坐标</label>
-            <div class="flex gap-2">
-              <input
-                  v-model.number="form.mazeConfig.start[0]"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  placeholder="X"
-              />
-              <input
-                  v-model.number="form.mazeConfig.start[1]"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  placeholder="Y"
-              />
-            </div>
-          </div>
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">终点坐标</label>
-            <div class="flex gap-2">
-              <input
-                  v-model.number="form.mazeConfig.end[0]"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  placeholder="X"
-              />
-              <input
-                  v-model.number="form.mazeConfig.end[1]"
-                  class="w-1/2 border border-gray-300 rounded p-2"
-                  type="number"
-                  placeholder="Y"
-              />
-            </div>
-          </div>
+    <div class="box-blockly">
+      <div id="blocklyDiv" style="height: 100%; width: 100%; background-color: #cccccc"></div>
 
-          <!-- 初始积木配置 -->
-          <div class="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">初始积木(JSON 数组,如 {"type":"move_forward","count":3} )</label>
-            <textarea
-                v-model="form.initialBlocks"
-                class="w-full border border-gray-300 rounded p-2"
-                rows="3"
-                placeholder="输入初始积木配置"
-            />
-          </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="mb-4">
-            <label class="block text-gray-700 font-medium mb-1">参考解法代码</label>
-            <textarea
-                v-model="form.solutionCode"
-                class="w-full border border-gray-300 rounded p-2"
-                rows="3"
-                placeholder="输入参考代码(如 moveForward(3); turnRight(); )"
-            />
-          </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/>
 
-          <!-- 操作按钮 -->
-          <div class="flex justify-end gap-2">
-            <button
-                class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
-                @click="closeModal"
-            >
-              取消
-            </button>
-            <button
-                class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
-                @click="saveProblem"
-            >
-              保存
-            </button>
-          </div>
-        </div>
-      </div>
-    </Teleport>
+      <!-- 添加生成 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, reactive } from 'vue';
+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);
 
-// 模拟已有题目列表(实际可从接口获取)
-const problems = ref([
-  {
-    id: 1,
-    title: "入门迷宫",
-    description: "帮助角色找到出口",
-    mazeConfig: {
-      width: 8,
-      height: 8,
-      walls: [[1,0], [1,1], [1,2], [1,3], [2,3], [3,3], [4,3], [5,3], [5,4], [5,5], [5,6]],
-      start: [0, 0],
-      end: [7, 7]
-    },
-    initialBlocks: '[{"type":"move_forward","count":5},{"type":"turn_right","count":3},{"type":"turn_left","count":2}]',
-    solutionCode: "moveForward(3); turnRight(); moveForward(5); turnRight(); moveForward(3);"
-  }
-]);
+// 使用 ref 存储 workspace
+const workspace = ref(null)
+
+onMounted(async () => {
 
-const showModal = ref(false);
-const isEdit = ref(false);
-const form = reactive({
-  title: "",
-  description: "",
-  mazeConfig: {
-    width: 8,
-    height: 8,
-    walls: '[]',
-    start: [0, 0],
-    end: [7, 7]
-  },
-  initialBlocks: '[]',
-  solutionCode: ""
-});
+  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"));
+})
 
-// 打开新建弹窗
-function openCreateModal() {
-  showModal.value = true;
-  isEdit.value = false;
-  resetForm();
+//保存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();
 }
 
-// 打开编辑弹窗
-function openEditModal(problem, index) {
-  showModal.value = true;
-  isEdit.value = true;
-  form.title = problem.title;
-  form.description = problem.description;
-  form.mazeConfig = { ...problem.mazeConfig };
-  // 转成字符串方便展示编辑
-  form.mazeConfig.walls = JSON.stringify(problem.mazeConfig.walls);
-  form.initialBlocks = JSON.stringify(problem.initialBlocks);
-  form.solutionCode = problem.solutionCode;
+//保存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 resetForm() {
-  form.title = "";
-  form.description = "";
-  form.mazeConfig = {
-    width: 8,
-    height: 8,
-    walls: '[]',
-    start: [0, 0],
-    end: [7, 7]
-  };
-  form.initialBlocks = '[]';
-  form.solutionCode = "";
+//重新加载
+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 closeModal() {
-  showModal.value = false;
-  resetForm();
+//
+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 saveProblem() {
-  // 处理配置格式(转成 JSON )
+function saveIsValid(save) {
+  var validJson = true;
   try {
-    form.mazeConfig.walls = JSON.parse(form.mazeConfig.walls);
-    form.initialBlocks = JSON.parse(form.initialBlocks);
-  } catch (err) {
-    alert("配置格式错误,请检查 JSON 语法!");
-    return;
+    JSON.parse(save);
+  } catch (e) {
+    validJson = false;
   }
-
-  if (isEdit.value) {
-    // 模拟更新,实际调接口
-    const index = problems.value.findIndex(item => item.id === form.id);
-    if (index !== -1) {
-      problems.value[index] = { ...form };
-    }
-  } else {
-    // 模拟新增,实际调接口,这里简单赋 ID
-    form.id = Date.now();
-    problems.value.push({ ...form });
+  var validXml = true;
+  try {
+    Blockly.utils.xml.textToDom(save);
+  } catch (e) {
+    validXml = false;
   }
-  closeModal();
-  alert("保存成功!");
+  return {
+    json: validJson,
+    xml: validXml,
+  };
 }
 
-// 删除题目
-function deleteProblem(index) {
-  if (confirm("确定删除该题目吗?")) {
-    problems.value.splice(index, 1);
-    alert("删除成功!");
+// 定义生成代码的函数
+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>
-/* 可自定义额外样式覆盖,Tailwind 已能满足基础布局,这里简单加个过渡 */
-.fade-enter-active,
-.fade-leave-active {
-  transition: opacity 0.3s ease;
+<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) ;
 }
-.fade-enter-from,
-.fade-leave-to {
-  opacity: 0;
+.box-blockly {
+  width: 60%;
+  height: 90%;
+  padding: rpx(10) rpx(10);
+  float: left;
 }
-</style>
+.box-code{
+  width: 35%;
+  padding: rpx(10) rpx(10);
+
+  .box-code-textarea{
+    height: 80%;
+    width: 100%;
+  }
+}
+
+</style>