| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073 |
- <template>
- <!-- 智能台灯 -->
- <div v-if="showLampPreview" class="desk-lamp-container">
- <!-- 标题框 -->
- <div class="desk-lamp-title-box">
- <div class="desk-lamp-box-icon" @click="goBackLab">
- <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
- 智能台灯
- </div>
- </div>
- <img src="@/assets/images/desklamp.png" alt="智能台灯" class="full-screen-image" />
- <!-- 使用动态样式设置灯光遮罩 -->
- <div v-if="isLightOn" :style="{ '--lamp-color': state.lamp.color,'--lamp-opacity': state.lamp.brightness / 100 }" class="lamp-light-mask"></div>
- <!-- 右下角按钮组 -->
- <div class="button-group">
- <el-button class="control-button run-button" @click="toggleLight">运行</el-button>
- <el-button class="control-button code-button" @click="handleViewCode">查看代码</el-button>
- </div>
- <!-- 显示当前灯光信息 -->
- <div v-if="isLightOn" class="lamp-info">
- <p>颜色: {{ state.lamp.color }}</p>
- <p>亮度: {{ state.lamp.brightness }}%</p>
- </div>
- </div>
- <!-- Blockly -->
- <div class="page-container">
- <!-- 返回 -->
- <div class="title-box">
- <div class="box-icon" @click="goBack">
- <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
- Blockly编程
- </div>
- </div>
- <div class="home-container">
- <div class="box-blockly">
- <div
- id="blocklyDiv"
- style="height: 100%; width: 100%; background-color: #cccccc"
- ></div>
- <!-- blockly工具栏 -->
- <template>
- <xml id="toolbox" style="display: none">
- <!-- AI模块分类 - 扩展更多功能 -->
- <category name="AI模块" categorystyle="ai_category">
- <block type="ai_voice_input"></block>
- <block type="ai_text_to_image"></block>
- <block type="ai_text_to_video"></block>
- <block type="ai_text_to_text"></block>
- <block type="ai_smart_lamp_single_param"></block>
- <block type="ai_smart_lamp"></block>
- <block type="ai_lamp_set_brightness"></block>
- <block type="ai_lamp_set_color"></block>
- </category>
- <!-- 其他原有分类保持不变 -->
- <category name="逻辑" categorystyle="logic_category">
- <block type="controls_if"></block>
- <block type="logic_compare"></block>
- <block type="logic_operation"></block>
- <block type="logic_negate"></block>
- <block type="logic_boolean"></block>
- <block type="logic_null" disabled="true"></block>
- <block type="logic_ternary"></block>
- </category>
- <category name="循环" categorystyle="loop_category">
- <block type="controls_repeat_ext">
- <value name="TIMES">
- <shadow type="math_number">
- <field name="NUM">10</field>
- </shadow>
- </value>
- </block>
- <block type="controls_repeat" disabled="true"></block>
- <block type="controls_whileUntil"></block>
- <block type="controls_for">
- <value name="FROM">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- <value name="TO">
- <shadow type="math_number">
- <field name="NUM">10</field>
- </shadow>
- </value>
- <value name="BY">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- </block>
- <block type="controls_forEach"></block>
- <block type="controls_flow_statements"></block>
- </category>
- <category name="数值" categorystyle="math_category">
- <block type="math_number" gap="32">
- <field name="NUM">123</field>
- </block>
- <block type="math_arithmetic">
- <value name="A">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- <value name="B">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- </block>
- <block type="math_single">
- <value name="NUM">
- <shadow type="math_number">
- <field name="NUM">9</field>
- </shadow>
- </value>
- </block>
- <block type="math_trig">
- <value name="NUM">
- <shadow type="math_number">
- <field name="NUM">45</field>
- </shadow>
- </value>
- </block>
- <block type="math_constant"></block>
- <block type="math_number_property">
- <value name="NUMBER_TO_CHECK">
- <shadow type="math_number">
- <field name="NUM">0</field>
- </shadow>
- </value>
- </block>
- <block type="math_round">
- <value name="NUM">
- <shadow type="math_number">
- <field name="NUM">3.1</field>
- </shadow>
- </value>
- </block>
- <block type="math_on_list"></block>
- <block type="math_modulo">
- <value name="DIVIDEND">
- <shadow type="math_number">
- <field name="NUM">64</field>
- </shadow>
- </value>
- <value name="DIVISOR">
- <shadow type="math_number">
- <field name="NUM">10</field>
- </shadow>
- </value>
- </block>
- <block type="math_constrain">
- <value name="VALUE">
- <shadow type="math_number">
- <field name="NUM">50</field>
- </shadow>
- </value>
- <value name="LOW">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- <value name="HIGH">
- <shadow type="math_number">
- <field name="NUM">100</field>
- </shadow>
- </value>
- </block>
- <block type="math_random_int">
- <value name="FROM">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- <value name="TO">
- <shadow type="math_number">
- <field name="NUM">100</field>
- </shadow>
- </value>
- </block>
- <block type="math_random_float"></block>
- <block type="math_atan2">
- <value name="X">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- <value name="Y">
- <shadow type="math_number">
- <field name="NUM">1</field>
- </shadow>
- </value>
- </block>
- </category>
- <category name="文本" categorystyle="text_category">
- <block type="text"></block>
- <block type="text_join"></block>
- <block type="text_append">
- <value name="TEXT">
- <shadow type="text"></shadow>
- </value>
- </block>
- <block type="text_length">
- <value name="VALUE">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- <block type="text_isEmpty">
- <value name="VALUE">
- <shadow type="text">
- <field name="TEXT"></field>
- </shadow>
- </value>
- </block>
- <block type="text_indexOf">
- <value name="VALUE">
- <block type="variables_get">
- <field name="VAR">text</field>
- </block>
- </value>
- <value name="FIND">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- <block type="text_charAt">
- <value name="VALUE">
- <block type="variables_get">
- <field name="VAR">text</field>
- </block>
- </value>
- </block>
- <block type="text_getSubstring">
- <value name="STRING">
- <block type="variables_get">
- <field name="VAR">text</field>
- </block>
- </value>
- </block>
- <block type="text_changeCase">
- <value name="TEXT">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- <block type="text_trim">
- <value name="TEXT">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- <block type="text_count">
- <value name="SUB">
- <shadow type="text"></shadow>
- </value>
- <value name="TEXT">
- <shadow type="text"></shadow>
- </value>
- </block>
- <block type="text_replace">
- <value name="FROM">
- <shadow type="text"></shadow>
- </value>
- <value name="TO">
- <shadow type="text"></shadow>
- </value>
- <value name="TEXT">
- <shadow type="text"></shadow>
- </value>
- </block>
- <block type="text_reverse">
- <value name="TEXT">
- <shadow type="text"></shadow>
- </value>
- </block>
- <label text="Input/Output:" web-class="ioLabel"></label>
- <block type="text_print">
- <value name="TEXT">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- <block type="text_prompt_ext">
- <value name="TEXT">
- <shadow type="text">
- <field name="TEXT">abc</field>
- </shadow>
- </value>
- </block>
- </category>
- <category name="列表" categorystyle="list_category">
- <block type="lists_create_with">
- <mutation items="0"></mutation>
- </block>
- <block type="lists_create_with"></block>
- <block type="lists_repeat">
- <value name="NUM">
- <shadow type="math_number">
- <field name="NUM">5</field>
- </shadow>
- </value>
- </block>
- <block type="lists_length"></block>
- <block type="lists_isEmpty"></block>
- <block type="lists_indexOf">
- <value name="VALUE">
- <block type="variables_get">
- <field name="VAR">list</field>
- </block>
- </value>
- </block>
- <block type="lists_getIndex">
- <value name="VALUE">
- <block type="variables_get">
- <field name="VAR">list</field>
- </block>
- </value>
- </block>
- <block type="lists_setIndex">
- <value name="LIST">
- <block type="variables_get">
- <field name="VAR">list</field>
- </block>
- </value>
- </block>
- <block type="lists_getSublist">
- <value name="LIST">
- <block type="variables_get">
- <field name="VAR">list</field>
- </block>
- </value>
- </block>
- <block type="lists_split">
- <value name="DELIM">
- <shadow type="text">
- <field name="TEXT">,</field>
- </shadow>
- </value>
- </block>
- <block type="lists_sort"></block>
- <block type="lists_reverse"></block>
- </category>
- <sep></sep>
- <category
- name="Variables"
- categorystyle="variable_category"
- custom="VARIABLE"
- ></category>
- <category
- name="Functions"
- categorystyle="procedure_category"
- custom="PROCEDURE"
- ></category>
- </xml>
- </template>
- </div>
- <div class="box-code">
- <div class="button-container">
- <el-button type="danger" plain id="run-code" @click="runCode()"
- >运行代码</el-button
- >
- <el-button type="warning" plain id="import" @click="load()"
- >重新加载</el-button
- >
- </div>
- <br />
- <div class="button-container">
- <el-button
- type="primary"
- plain
- id="to-code-js"
- @click="generateCode('javascript')"
- >生成 JavaScript</el-button
- >
- <el-button
- type="success"
- plain
- id="to-code-py"
- @click="generateCode('python')"
- >生成 Python</el-button
- >
- <el-button type="info" plain id="save-json" @click="saveJson()"
- >保存 JSON</el-button
- >
- <el-button type="info" plain id="save-xml" @click="saveXml()"
- >保存 XML</el-button
- >
- </div>
- <!-- 代码显示区 -->
- <div class="code-section">
- <h4>生成的代码</h4>
- <textarea name="" id="textarea" class="box-code-textarea"></textarea>
- </div>
- <!-- 运行结果区 -->
- <div class="result-section">
- <h4>运行结果</h4>
- <div id="run-result" class="run-result-content"></div>
- <!-- 额外的图片预览区域 -->
- <div
- v-if="state.generatedContent.imageUrl"
- class="extra-image-preview"
- >
- <h5>生成的图片:</h5>
- <img
- :src="state.generatedContent.imageUrl"
- class="extra-preview-image"
- alt="AI生成图片"
- />
- </div>
- <div
- v-if="state.generatedContent.videoUrl"
- class="extra-image-preview"
- >
- <h5>生成的视频:</h5>
- <video
- :src="state.generatedContent.videoUrl"
- controls
- class="preview-video"
- alt="AI生成视频"
- ></video>
- </div>
- <!-- 添加台灯显示区域 -->
- <!-- <div class="lamp-preview-container">
- <h5>AI智能台灯</h5>
- <div
- class="lamp-display"
- :style="{
- filter: `brightness(${state.lamp.brightness / 100})`,
- }"
- >
- <div
- class="lamp-image"
- :style="{ boxShadow: `0 0 40px 20px ${state.lamp.color}80` }"
- ></div>
- </div>
- <div class="lamp-info">
- <p>亮度: {{ state.lamp.brightness }}%</p>
- <p>颜色: {{ state.lamp.color }}</p>
- </div>
- </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>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onUnmounted, reactive } from "vue";
- import { useRouter } from "vue-router";
- import { ArrowLeftBold } from "@element-plus/icons-vue";
- import * as Blockly from "blockly";
- import { javascriptGenerator } from "blockly/javascript";
- import { pythonGenerator } from "blockly/python";
- import * as hans from "blockly/msg/zh-hans";
- import { ElDialog, ElButton, ElMessage } from "element-plus";
- // 引入DeskLampView组件
- // import DeskLampView from "./virtuallaboratory/DeskLampView.vue";
- const router = useRouter();
- const isLightOn = ref(false)
- const lampColor = ref('#fff') // 默认白色
- const lampBrightness = ref(50) // 默认亮度50%
- const showLampPreview = ref(true)
- // 返回虚拟实验室
- const goBackLab = () => {
- router.push("/virtual-laboratory");
- };
- const goBack = () => {
- showLampPreview.value = true
- };
- // 切换灯光状态
- const toggleLight = () => {
- isLightOn.value = true;
- generateCode('javascript');
- runCode()
- }
- // 查看代码编程界面显示状态
- const handleViewCode = () =>{
- showLampPreview.value = false
- }
- //【文生图】文生图
- import {
- AiImageStatusEnum,
- CreatePainting,
- PaintingGetMys,
- CreateVideo,
- VideoGetMys,
- sendChatMessageStream,
- CreateDialogue,
- } from "@/api/questions.js";
- import { getModelIdByType, ModelPlatformEnum } from "@/api/teachers.js";
- import { ModelTypeEnum } from "@/api/teachers.js";
- import { globalState } from "@/utils/globalState.js";
- Blockly.setLocale(hans);
- // 状态管理
- const state = reactive({
- workspace: null,
- generatedContent: {
- imageUrl: "",
- videoUrl: "",
- text: "",
- },
- previewVisible: false,
- previewType: "",
- previewContent: "",
- isProcessing: false,
- //年级
- gradeId: "",
- //【文生图】文生图
- inProgressImageMap: {},
- //【文生视频】文生视频
- inProgressVideoMap: {},
- // 台灯状态
- lamp: {
- brightness: 0, // 默认亮度50%
- color: "#ffffff", // 默认颜色白色
- },
- // 【文本文】对话相关状态
- activeConversationId: null,
- conversationInProgress: false,
- conversationInAbortController: null,
- });
- // 【文生图】自动刷新image列表的定时器
- const inProgressTimer = ref();
- // 【文生视频】自动刷新video列表的定时器
- const inProgressVideoTimer = ref();
- // 初始化Blockly工作区和自定义积木
- onMounted(async () => {
- // 从全局状态初始化年级ID
- state.gradeId = globalState.initGradeId();
- // 注册AI语音输入积木
- Blockly.Blocks["ai_voice_input"] = {
- init: function () {
- this.appendDummyInput().appendField("语音输入");
- this.appendValueInput("PROMPT")
- .setCheck("String")
- .appendField("提示文字:");
- this.appendDummyInput()
- .appendField("语言:")
- .appendField(
- new Blockly.FieldDropdown([
- ["中文", "zh-CN"],
- ["英文", "en-US"],
- ]),
- "LANGUAGE"
- );
- this.setOutput(true, "String");
- this.setColour(310);
- this.setTooltip("使用语音识别获取文本输入");
- this.setHelpUrl("");
- },
- };
- // 注册AI文本生成图片积木
- Blockly.Blocks["ai_text_to_image"] = {
- init: function () {
- this.appendDummyInput().appendField("AI生成图片");
- this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
- this.appendDummyInput()
- .appendField("等待完成:", "WAIT_LABEL")
- .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(340);
- this.setTooltip("使用AI将文本描述转换为图片");
- this.setHelpUrl("");
- },
- };
- // 注册AI文本生成视频积木
- Blockly.Blocks["ai_text_to_video"] = {
- init: function () {
- this.appendDummyInput().appendField("AI生成视频");
- this.appendValueInput("PROMPT").setCheck("String").appendField("提示词:");
- this.appendDummyInput()
- .appendField("等待完成:", "WAIT_LABEL")
- .appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT_FOR_COMPLETION");
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(340);
- this.setTooltip("使用AI将文本描述转换为视频");
- this.setHelpUrl("");
- },
- };
- // 注册AI文本生成文本积木
- Blockly.Blocks["ai_text_to_text"] = {
- init: function () {
- this.appendDummyInput().appendField("AI文本处理");
- this.appendValueInput("PROMPT")
- .setCheck("String")
- .appendField("输入文本:");
- this.appendDummyInput()
- .appendField("模型:")
- .appendField(
- new Blockly.FieldDropdown([
- ["默认", "default"],
- ["对话", "chat"],
- ["分析", "analysis"],
- ]),
- "MODEL"
- );
- this.setOutput(true, "String");
- this.setColour(300);
- this.setTooltip("使用AI处理文本并返回结果");
- this.setHelpUrl("");
- },
- };
- //AI智能台灯单参数积木
- Blockly.Blocks['ai_smart_lamp_single_param'] = {
- init: function() {
- this.appendDummyInput()
- .appendField('智能台灯控制(单参数)');
- this.appendValueInput('PARAMS')
- .setCheck('String')
- .appendField('参数(格式: 颜色,亮度,音乐):');
- this.appendDummyInput()
- .appendField('例如: 蓝,50,平静');
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(280);
- this.setTooltip('通过一个参数字符串控制智能台灯的亮度、颜色和音乐\n格式: 颜色,亮度,音乐\n例如: 蓝,50,平静');
- this.setHelpUrl('');
- }
- };
- // 注册AI智能台灯积木
- Blockly.Blocks["ai_smart_lamp"] = {
- init: function () {
- this.appendDummyInput().appendField("智能台灯控制");
- this.appendValueInput("BRIGHTNESS")
- .setCheck(["Number", "String"])
- .appendField("亮度 (0-100):");
- this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(280);
- this.setTooltip("控制智能台灯的亮度和颜色");
- this.setHelpUrl("");
- },
- };
- // 注册AI台灯设置亮度积木
- Blockly.Blocks["ai_lamp_set_brightness"] = {
- init: function () {
- this.appendDummyInput().appendField("设置台灯亮度");
- this.appendValueInput("BRIGHTNESS")
- .setCheck(["Number", "String"])
- .appendField("亮度 (0-100):");
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(270);
- this.setTooltip("设置智能台灯的亮度");
- this.setHelpUrl("");
- },
- };
- // 注册AI台灯设置颜色积木
- Blockly.Blocks["ai_lamp_set_color"] = {
- init: function () {
- this.appendDummyInput().appendField("设置台灯颜色");
- this.appendValueInput("COLOR").setCheck("String").appendField("颜色:");
- this.appendDummyInput().appendField("可选颜色: 红,蓝,绿,黄,青,靛,紫");
- this.setInputsInline(false);
- this.setPreviousStatement(true, null);
- this.setNextStatement(true, null);
- this.setColour(275);
- this.setTooltip("设置智能台灯的颜色");
- this.setHelpUrl("");
- },
- };
- // 注册JavaScript代码生成器
- registerJavaScriptGenerators();
- // 注册Python代码生成器
- registerPythonGenerators();
- // 初始化工作区
- state.workspace = Blockly.inject("blocklyDiv", {
- toolbox: document.getElementById("toolbox"),
- grid: { spacing: 20, length: 3, colour: "#ccc", snap: true },
- trashcan: true,
- });
- // 加载初始XML内容
- try {
- // 使用字符串拼接代替模板字符串,避免特殊字符问题
- const initialXml = '<xml xmlns="https://developers.google.com/blockly/xml">\n' +
- ' <block type="ai_smart_lamp_single_param" id="]t3dfZuv34^1VI%c+Vww" x="50" y="90">\n' +
- ' <value name="PARAMS">\n' +
- ' <block type="ai_text_to_text" id="iIw6Y|#j@Kh_}]!6A;Ef">\n' +
- ' <field name="MODEL">default</field>\n' +
- ' <value name="PROMPT">\n' +
- ' <block type="ai_voice_input" id="qo`+VHZpJ0^Y_;J2T!VT">\n' +
- ' <field name="LANGUAGE">zh-CN</field>\n' +
- ' <value name="PROMPT">\n' +
- ' <block type="text" id="uoH(8P)Q{|IMHBw=Gv">\n' +
- ' <field name="TEXT">请说话:</field>\n' +
- ' </block>\n' +
- ' </value>\n' +
- ' </block>\n' +
- ' </value>\n' +
- ' </block>\n' +
- ' </value>\n' +
- ' </block>\n' +
- '</xml>';
- const xml = Blockly.utils.xml.textToDom(initialXml);
- Blockly.Xml.domToWorkspace(xml, state.workspace);
- // 强制重新渲染工作区
- state.workspace.render();
- // 添加更长的延迟确保DOM完全更新后再设置属性
- setTimeout(() => {
- // 确保所有块都可移动和可删除
- const blocks = state.workspace.getAllBlocks();
- blocks.forEach(block => {
- // 直接设置块的可移动和可删除属性为true
- block.setMovable(true);
- block.setDeletable(true);
- // 设置嵌套连接点也可编辑
- if (block.inputList) {
- block.inputList.forEach(input => {
- if (input.connection) {
- input.connection.setCheck(null); // 允许任何类型的连接
- input.connection.setHangingPriority(10); // 设置较高的连接优先级
- }
- });
- }
- });
- // 再次渲染以应用属性更改
- state.workspace.render();
- // 初始化完成后手动生成一次代码
- generateCode("javascript");
- // 初始化完成后添加工作区变化监听器
- setTimeout(() => {
- state.workspace.addChangeListener(() => generateCode("javascript"));
- }, 500);
- }, 1500);
- } catch (error) {
- console.error('加载初始XML内容失败:', error);
- }
- // 工作区变化时自动生成JavaScript代码
- // 注意:这里设置监听器的位置移到了初始化完成后
- setTimeout(() => {
- state.workspace.addChangeListener(() => generateCode("javascript"));
- }, 1500);
- });
- // 组件卸载时清除定时器
- onUnmounted(() => {
- if (inProgressTimer.value) {
- clearInterval(inProgressTimer.value);
- }
- if (inProgressVideoTimer.value) {
- clearInterval(inProgressVideoTimer.value);
- }
- });
- // 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",
- semanticAnalysis: "/semantic-analysis",
- },
- };
- // 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 recognitionResult;
- },
- // 前端语音采集
- captureVoice(language, promptText) {
- console.log("captureVoice", language, promptText);
- return new Promise((resolve) => {
- if (
- !("webkitSpeechRecognition" in window) &&
- !("SpeechRecognition" in window)
- ) {
- ElMessage.warning("您的浏览器不支持语音识别功能");
- resolve("");
- return;
- }
- const SpeechRecognition =
- window.SpeechRecognition || window.webkitSpeechRecognition;
- const recognition = new SpeechRecognition();
- recognition.lang = language;
- recognition.interimResults = false;
- recognition.maxAlternatives = 1;
- if (promptText) {
- ElMessage.info(promptText + "\n请开始说话...");
- } else {
- ElMessage.info("请开始说话...");
- }
- recognition.onresult = (event) => {
- const speechResult = event.results[0][0].transcript;
- console.log("语音识别结果:", speechResult);
- resolve(speechResult);
- };
- recognition.onerror = (event) => {
- console.error("语音识别错误:", event.error);
- ElMessage.error("语音识别发生错误: " + event.error);
- resolve("");
- };
- recognition.onend = () => {
- console.log("语音识别已结束");
- };
- recognition.start();
- });
- },
- // 文本生成图片
- async textToImage(prompt, waitForCompletion = true) {
- console.log("textToImage", prompt, waitForCompletion);
- state.isProcessing = true;
- try {
- //获取文生图-模型id
- const modelRes = await getModelIdByType({
- type: ModelTypeEnum.TEXT_TO_IMAGE,
- platform: ModelPlatformEnum.DOUBAO,
- });
- if (!modelRes.data) {
- ElMessage.error("获取模型ID失败");
- return null;
- }
- // 使用CreatePainting API创建图片任务
- const createRes = await CreatePainting({
- modelId: modelRes.data,
- prompt: prompt,
- width: 1024,
- height: 1024,
- });
- // 记录任务ID到映射中
- state.inProgressImageMap[createRes.data] = {
- id: createRes.data,
- status: AiImageStatusEnum.IN_PROGRESS,
- };
- // 开始轮询任务状态
- if (!inProgressTimer.value) {
- this.startImagePolling();
- }
- // 如果需要等待完成,等待图片生成完成
- if (waitForCompletion) {
- return await this.waitForImageCompletion(createRes.data);
- }
- return createRes.data; // 返回任务ID
- } catch (error) {
- console.error("生成图片失败:", error);
- ElMessage.error("生成图片失败: " + error.message);
- return null;
- } finally {
- state.isProcessing = false;
- }
- },
- // 【文生图】开始轮询图片生成状态
- startImagePolling() {
- if (inProgressTimer.value) {
- clearInterval(inProgressTimer.value);
- }
- inProgressTimer.value = setInterval(async () => {
- await this.refreshWatchImages();
- }, 3000); // 每3秒轮询一次
- },
- // 【文生图】轮询生成中的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;
- }
- 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);
- }
- });
- state.inProgressImageMap = newWatchImages;
- // 如果没有正在处理的图片,清除定时器
- if (Object.keys(newWatchImages).length === 0 && inProgressTimer.value) {
- clearInterval(inProgressTimer.value);
- inProgressTimer.value = null;
- }
- } catch (error) {
- console.error("轮询图片状态失败:", error);
- }
- },
- // 【文生图】等待图片生成完成
- 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];
- console.log(
- "图片状态:",
- image.status,
- image.picUrl,
- AiImageStatusEnum
- );
- if (image.status === AiImageStatusEnum.SUCCESS) {
- clearInterval(checkInterval);
- resolve(image.picUrl);
- } else if (image.status === AiImageStatusEnum.FAIL) {
- clearInterval(checkInterval);
- reject(new Error(image.error || "图片生成失败"));
- }
- }
- } catch (error) {
- clearInterval(checkInterval);
- reject(error);
- }
- }, 3000);
- });
- },
- // 文本生成视频
- async textToVideo(prompt, waitForCompletion = true) {
- console.log("textToVideo", prompt, waitForCompletion);
- state.isProcessing = true;
- try {
- //获取视频生成模型id
- const modelRes = await getModelIdByType({
- type: ModelTypeEnum.IMAGE_TO_VIDEO,
- platform: ModelPlatformEnum.DOUBAO,
- });
- if (!modelRes.data) {
- ElMessage.error("获取模型ID失败");
- return null;
- }
- // 使用CreateVideo API创建视频任务
- const createRes = await CreateVideo({
- modelId: modelRes.data,
- prompt: prompt,
- duration: 4,
- resolution: "1080P",
- });
- // 记录任务ID到视频专用映射中
- state.inProgressVideoMap[createRes.data] = {
- id: createRes.data,
- status: AiImageStatusEnum.IN_PROGRESS,
- };
- // 开始视频专用的轮询任务状态
- if (!inProgressVideoTimer.value) {
- this.startVideoPolling();
- }
- // 如果需要等待完成,等待视频生成完成
- if (waitForCompletion) {
- return await this.waitForVideoCompletion(createRes.data);
- }
- return createRes.data; // 返回任务ID
- } catch (error) {
- console.error("生成视频失败:", error);
- ElMessage.error("生成视频失败: " + error.message);
- return null;
- } finally {
- state.isProcessing = false;
- }
- },
- // 【视频生成】开始轮询视频生成状态
- startVideoPolling() {
- if (inProgressVideoTimer.value) {
- clearInterval(inProgressVideoTimer.value);
- }
- inProgressVideoTimer.value = setInterval(async () => {
- await this.refreshWatchVideos();
- }, 3000); // 每3秒轮询一次
- },
- // 【视频生成】轮询生成中的视频列表
- async refreshWatchVideos() {
- const videoIds = Object.keys(state.inProgressVideoMap).map(Number);
- if (videoIds.length === 0) {
- if (inProgressVideoTimer.value) {
- clearInterval(inProgressVideoTimer.value);
- inProgressVideoTimer.value = null;
- }
- return;
- }
- try {
- const list = await VideoGetMys(videoIds);
- const newWatchVideos = {};
- list.data.forEach((video) => {
- if (video.status === AiImageStatusEnum.IN_PROGRESS) {
- newWatchVideos[video.id] = video;
- } else if (video.status === AiImageStatusEnum.SUCCESS) {
- // 视频生成完成,更新状态并显示预览
- state.generatedContent.videoUrl = video.videoUrl;
- state.previewType = "video";
- state.previewContent = video.videoUrl;
- state.previewVisible = true;
- // 打印到控制台,便于在结果区域显示
- console.log("视频生成完成:", video.videoUrl);
- } else if (video.status === AiImageStatusEnum.FAIL) {
- // 视频生成失败
- ElMessage.error("视频生成失败: " + (video.error || "未知错误"));
- console.error("视频生成失败:", video.id, video.error);
- }
- });
- state.inProgressVideoMap = newWatchVideos;
- // 如果没有正在处理的视频,清除定时器
- if (
- Object.keys(newWatchVideos).length === 0 &&
- inProgressVideoTimer.value
- ) {
- clearInterval(inProgressVideoTimer.value);
- inProgressVideoTimer.value = null;
- }
- } catch (error) {
- console.error("轮询视频状态失败:", error);
- }
- },
- // 【视频生成】等待视频生成完成
- async waitForVideoCompletion(videoId) {
- return new Promise((resolve, reject) => {
- const checkInterval = setInterval(async () => {
- try {
- const list = await VideoGetMys([videoId]);
- if (list.data && list.data.length > 0) {
- const video = list.data[0];
- console.log("视频状态:", video.status, video.videoUrl);
- if (video.status === AiImageStatusEnum.SUCCESS) {
- clearInterval(checkInterval);
- // 视频生成完成,设置预览状态
- state.generatedContent.videoUrl = video.videoUrl;
- state.previewType = "video";
- state.previewContent = video.videoUrl;
- state.previewVisible = true;
- resolve(video.videoUrl);
- } else if (video.status === AiImageStatusEnum.FAIL) {
- clearInterval(checkInterval);
- reject(new Error(video.error || "视频生成失败"));
- }
- }
- } catch (error) {
- clearInterval(checkInterval);
- reject(error);
- }
- }, 3000);
- });
- },
- // 文本生成文本(如AI对话)
- async textToText(prompt, model = "default") {
- console.log("textToText", prompt, model);
- state.isProcessing = true;
- try {
- // 如果没有活跃的对话ID,创建新对话
- if (!state.activeConversationId) {
- // 使用与TextToText.vue相同的方式创建对话
- const res = await CreateDialogue({ roleId: 75 });
- state.activeConversationId = res.data;
- console.log("创建会话:", res.data);
- }
- // 创建AbortController实例
- state.conversationInAbortController = new AbortController();
- state.conversationInProgress = true;
- // 使用流式API发送消息
- let resultText = "";
- let isFirstChunk = true;
- await sendChatMessageStream(
- state.activeConversationId,
- prompt,
- null,
- state.conversationInAbortController,
- true, // 启用上下文
- async (res) => {
- try {
- const { code, data, msg } = JSON.parse(res.data);
- if (code !== 0) {
- console.log(`对话异常! ${msg}`);
- return;
- }
- // 根据事件类型处理
- if (data.eventType === "TEXT") {
- // 如果内容为空,就不处理
- if (data.receive?.content === "") {
- return;
- }
- // 处理文本消息
- resultText += data.receive.content;
- // 首次返回时更新预览内容
- if (isFirstChunk) {
- isFirstChunk = false;
- // 设置预览内容
- state.generatedContent.text = resultText;
- state.previewType = "text";
- state.previewContent = resultText;
- if (!state.previewVisible) {
- state.previewVisible = true;
- }
- } else {
- // 更新预览内容
- state.generatedContent.text = resultText;
- state.previewContent = resultText;
- }
- }
- } catch (error) {
- console.error("处理流式响应失败:", error);
- }
- },
- (error) => {
- console.log(`对话异常! ${error}`);
- this.stopTextToTextStream();
- throw error;
- },
- () => {
- console.log(`结束对话!`);
- this.stopTextToTextStream();
- }
- );
- // 确保最终结果被设置
- if (resultText) {
- state.generatedContent.text = resultText;
- state.previewType = "text";
- state.previewContent = resultText;
- if (!state.previewVisible) {
- state.previewVisible = true;
- }
- }
- return resultText;
- } catch (error) {
- console.error("AI文本处理失败:", error);
- ElMessage.error("AI文本处理失败: " + error.message);
- return null;
- } finally {
- state.isProcessing = false;
- }
- },
- // 停止文本生成流
- stopTextToTextStream() {
- if (state.conversationInAbortController) {
- state.conversationInAbortController.abort();
- }
- state.conversationInProgress = false;
- },
- // 设置台灯亮度
- async setLampBrightness(brightness) {
- // console.log('setLampBrightness', brightness);
- // 验证亮度值在0-100之间
- const validBrightness = Math.max(0, Math.min(100, parseInt(brightness) || 0));
- // 更新状态
- state.lamp.brightness = validBrightness;
- // 模拟API调用(实际项目中可替换为真实API)
- console.log(`台灯亮度已设置为: ${validBrightness}%`);
- return validBrightness;
- },
- // 设置台灯颜色
- async setLampColor(color) {
- console.log("setLampColor", color);
- // 预定义的颜色映射
- const colorMap = {
- '红': '#ff0000',
- '蓝': '#0000ff',
- '绿': '#00ff00',
- '黄': '#ffff00',
- '青': '#00ffff',
- '靛': '#4b0082',
- '紫': '#800080',
- '白': '#ffffff',
- '黑': '#000000'
- };
- // 获取有效的颜色值
- let validColor = colorMap[color] || color;
- // 检查是否是有效的颜色格式
- if (!/^#[0-9A-F]{6}$/i.test(validColor)) {
- validColor = "#ffffff"; // 默认白色
- }
- // 更新状态
- state.lamp.color = validColor;
- // 模拟API调用(实际项目中可替换为真实API)
- console.log(`台灯颜色已设置为: ${validColor}`);
- return validColor;
- },
- // 综合控制台灯(参数格式:"颜色, 亮度, 音乐")
- async controlLampWithSingleParam(params) {
- // 解析参数字符串
- let color = '白'; // 默认颜色
- let brightness = 0; // 默认亮度
- let music = ''; // 音乐信息(暂不处理)
- if (params && typeof params === 'string') {
- // 根据逗号分割参数
- const paramArray = params.split(',').map(p => p.trim());
- // 提取颜色(第一个参数)
- if (paramArray.length > 0 && paramArray[0]) {
- color = paramArray[0];
- }
- // 提取亮度(第二个参数)
- if (paramArray.length > 1 && paramArray[1]) {
- brightness = paramArray[1];
- }
- // 提取音乐(第三个参数,暂不处理)
- if (paramArray.length > 2 && paramArray[2]) {
- music = paramArray[2];
- console.log('音乐信息(暂不处理):', music);
- }
- }
- // 调用控制台灯方法
- return await this.controlLamp(brightness, color);
- },
- // 综合控制台灯(参数格式:"颜色, 亮度, 音乐")
- async controlLamp(brightness, color) {
- console.log("controlLamp", brightness, color);
- // 先设置亮度
- await this.setLampBrightness(brightness);
- // 再设置颜色
- await this.setLampColor(color);
- return { brightness: state.lamp.brightness, color: state.lamp.color };
- },
- };
- // 注册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, javascriptGenerator.ORDER_ATOMIC];
- };
- // 文本生成图片
- javascriptGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
- const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
- const code = `await aiService.textToImage(${prompt}, ${waitForCompletion});`;
- console.log('ai_text_to_image', prompt, waitForCompletion);
- return code;
- };
- // 文本生成视频
- javascriptGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
- const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
- const code = `await aiService.textToVideo(${prompt}, ${waitForCompletion});`;
- console.log('ai_text_to_video', prompt, waitForCompletion);
- return code;
- };
- // 文本生成文本
- javascriptGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', javascriptGenerator.ORDER_ATOMIC);
- const model = block.getFieldValue('MODEL');
- const code = `await aiService.textToText(${prompt}, '${model}')`;
- console.log('ai_text_to_text', prompt, model);
- return [code, javascriptGenerator.ORDER_ATOMIC];
- };
- // 智能台灯控制(单参数)
- javascriptGenerator.forBlock['ai_smart_lamp_single_param'] = function(block, generator) {
- const params = generator.valueToCode(block, 'PARAMS', javascriptGenerator.ORDER_ATOMIC);
- const code = `await aiService.controlLampWithSingleParam(${params || "'白,0,平静'"});`;
- return code;
- };
- // 智能台灯控制(多参数)
- javascriptGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
- const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
- const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
- const code = `await aiService.controlLamp(${brightness || '0'}, ${color || "'白'"});`;
- return code;
- };
- // 设置台灯亮度
- javascriptGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
- const brightness = generator.valueToCode(block, 'BRIGHTNESS', javascriptGenerator.ORDER_ATOMIC);
- const code = `await aiService.setLampBrightness(${brightness || '0'});`;
- return code;
- };
- // 设置台灯颜色
- javascriptGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
- const color = generator.valueToCode(block, 'COLOR', javascriptGenerator.ORDER_ATOMIC);
- const code = `await aiService.setLampColor(${color || "'白'"});`;
- return code;
- };
- }
- // 注册Python代码生成器
- function registerPythonGenerators() {
- // 语音输入
- pythonGenerator.forBlock['ai_voice_input'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
- const language = block.getFieldValue('LANGUAGE');
- const code = `ai_service.recognize_voice(${prompt || "''"}, '${language}')`;
- return [code, pythonGenerator.ORDER_ATOMIC];
- };
- // 文本生成图片
- pythonGenerator.forBlock['ai_text_to_image'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
- const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
- const code = `ai_service.text_to_image(${prompt}, ${waitForCompletion})\n`;
- return code;
- };
- // 文本生成视频
- pythonGenerator.forBlock['ai_text_to_video'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
- const waitForCompletion = block.getFieldValue('WAIT_FOR_COMPLETION') === 'TRUE';
- const code = `ai_service.text_to_video(${prompt}, ${waitForCompletion})\n`;
- return code;
- };
- // 文本生成文本
- pythonGenerator.forBlock['ai_text_to_text'] = function(block, generator) {
- const prompt = generator.valueToCode(block, 'PROMPT', pythonGenerator.ORDER_ATOMIC);
- const model = block.getFieldValue('MODEL');
- const code = `ai_service.text_to_text(${prompt}, '${model}')`;
- return [code, pythonGenerator.ORDER_ATOMIC];
- };
- // 智能台灯控制
- pythonGenerator.forBlock['ai_smart_lamp'] = function(block, generator) {
- const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
- const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
- const code = `ai_service.control_lamp(${brightness || '0'}, ${color || "'白'"})`;
- return code;
- };
- // 设置台灯亮度
- pythonGenerator.forBlock['ai_lamp_set_brightness'] = function(block, generator) {
- const brightness = generator.valueToCode(block, 'BRIGHTNESS', pythonGenerator.ORDER_ATOMIC);
- const code = `ai_service.set_lamp_brightness(${brightness || '0'})`;
- return code;
- };
- // 设置台灯颜色
- pythonGenerator.forBlock['ai_lamp_set_color'] = function(block, generator) {
- const color = generator.valueToCode(block, 'COLOR', pythonGenerator.ORDER_ATOMIC);
- const code = `ai_service.set_lamp_color(${color || "'白'"})`;
- return code;
- };
- }
- // 生成代码
- const generateCode = (language) => {
- if (!state.workspace) {
- console.error("workspace 未正确初始化");
- return;
- }
- let generator;
- if (language === "javascript") {
- generator = javascriptGenerator;
- } else if (language === "python") {
- generator = pythonGenerator;
- } else {
- console.error("不支持的语言类型");
- return;
- }
- const code = generator.workspaceToCode(state.workspace);
- document.getElementById("textarea").value = code;
- };
- // 运行代码
- async function runCode() {
- try {
- const codeTextarea = document.getElementById("textarea");
- const code = codeTextarea.value;
- const resultElement = document.getElementById("run-result");
- // 清空之前的结果
- resultElement.innerHTML = '<div class="running-indicator">代码运行中...</div>';
- // 保存原始console方法
- const originalConsoleLog = console.log;
- const originalConsoleError = console.error;
- const originalConsoleWarn = console.warn;
- // 创建输出缓冲区
- let outputBuffer = "";
- // 重定义console方法
- console.log = (...args) => {
- // 过滤掉包含Vue警告的日志
- const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
- // 只显示不包含Vue warn的日志
- if (!message.includes('[Vue warn]')) {
- outputBuffer += '<div class="log-message">' + message + '</div>';
- resultElement.innerHTML = outputBuffer;
- }
- originalConsoleLog.apply(console, args);
- };
- console.error = (...args) => {
- // 过滤掉包含Vue警告的日志
- const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
- // 只显示不包含Vue warn的日志
- if (!message.includes('[Vue warn]')) {
- outputBuffer += '<div class="error-message">' + message + '</div>';
- resultElement.innerHTML = outputBuffer;
- }
- originalConsoleError.apply(console, args);
- };
- console.warn = (...args) => {
- // 过滤掉包含Vue警告的日志
- const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
- // 只显示不包含Vue warn的日志
- if (!message.includes('[Vue warn]')) {
- outputBuffer += '<div class="warn-message">' + message + '</div>';
- resultElement.innerHTML = outputBuffer;
- }
- originalConsoleWarn.apply(console, args);
- };
- try {
- // 添加安全检查
- if (code.includes('eval(') || code.includes('Function(') ||
- code.includes('document.write') || code.includes('window.location')) {
- throw new Error('代码包含不安全的操作');
- }
- // 包装代码为异步函数执行,支持await
- const wrappedCode = `(async () => { ${code} })()`;
- await new Function(wrappedCode)();
- 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>`;
- }
- }
- // 保存JSON
- function saveJson() {
- try {
- const output = document.getElementById('textarea');
- const json = Blockly.serialization.workspaces.save(state.workspace);
- output.value = JSON.stringify(json, null, 2);
- output.focus();
- output.select();
- taChange();
- ElMessage.success('JSON保存成功');
- } catch (error) {
- console.error('保存JSON失败:', error);
- ElMessage.error('保存JSON失败: ' + error.message);
- }
- }
- // 保存XML
- function saveXml() {
- const output = document.getElementById('textarea');
- const xml = Blockly.Xml.workspaceToDom(state.workspace);
- output.value = Blockly.Xml.domToPrettyText(xml);
- output.focus();
- output.select();
- taChange();
- }
- // 重新加载
- function load() {
- const input = document.getElementById('textarea');
- if (!input.value) return;
- try {
- //关闭预览
- handleClosePreview();
- const valid = saveIsValid(input.value);
- if (valid.json) {
- const parsedState = JSON.parse(input.value);
- Blockly.serialization.workspaces.load(parsedState, state.workspace);
- } else if (valid.xml) {
- const xml = Blockly.utils.xml.textToDom(input.value);
- Blockly.Xml.domToWorkspace(xml, state.workspace);
- }
- taChange();
- } catch (error) {
- console.error("加载失败:", error);
- ElMessage.error("加载失败: " + error.message);
- }
- }
- function taChange() {
- const textarea = document.getElementById("textarea");
- if (sessionStorage) {
- sessionStorage.setItem("textarea", textarea.value);
- }
- const valid = saveIsValid(textarea.value);
- document.getElementById("import").disabled = !valid.json && !valid.xml;
- }
- function saveIsValid(save) {
- let validJson = true;
- try {
- JSON.parse(save);
- } catch (e) {
- validJson = false;
- }
- let validXml = true;
- try {
- Blockly.utils.xml.textToDom(save);
- } catch (e) {
- validXml = false;
- }
- return { json: validJson, xml: validXml };
- }
- // 关闭预览
- function handleClosePreview() {
- state.previewVisible = false;
- state.previewContent = "";
- state.previewType = "";
- state.generatedContent.text = null;
- state.generatedContent.imageUrl = null;
- state.generatedContent.videoUrl = null;
- }
- // 将aiService挂载到window,以便执行生成的代码时可以访问
- window.aiService = aiService;
- </script>
- <style scoped lang="scss">
- /* 原有样式保持不变 */
- @use "sass:math";
- @function rpx($px) {
- @return math.div($px, 750) * 100vw;
- }
- // 智能台灯
- .desk-lamp-container {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- overflow: hidden;
- display: flex;
- justify-content: center;
- align-items: center;
- z-index: 1000;
- }
- .full-screen-image {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- /* 台灯灯光样式 - 使用动态颜色 */
- .lamp-light-mask {
- position: absolute;
- width: rpx(78);
- height: rpx(350);
- margin-top: rpx(148.5);
- margin-left: rpx(192);
- background: linear-gradient(to bottom, var(--lamp-color) 0%, var(--lamp-color) 20%, rgba(255, 255, 255, 0) 80%);
- filter: blur(40px);
- transform: rotate(8.5deg);
- transform-origin: top center;
- opacity: var(--lamp-opacity, 0.6);
- /* 创建扇形效果 */
- clip-path: polygon(0% 0%, 100% 0%, 200% 100%, -100% 100%);
- }
- /* 灯光信息显示 */
- .lamp-info {
- position: absolute;
- bottom: 100px;
- right: 30px;
- background-color: rgba(255, 255, 255, 0.2);
- padding: 10px 20px;
- border-radius: 8px;
- backdrop-filter: blur(10px);
- color: white;
- font-size: 14px;
- z-index: 1000;
- }
- /* 标题框样式 */
- .desk-lamp-title-box {
- position: absolute;
- top: 20px;
- left: 20px;
- z-index: 1000;
- }
- .desk-lamp-box-icon {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 20px;
- background-color: rgba(255, 255, 255, 0.2);
- border-radius: 30px;
- backdrop-filter: blur(10px);
- cursor: pointer;
- transition: all 0.3s ease;
- font-size: 16px;
- color: white;
- font-weight: 500;
- }
- .desk-lamp-box-icon:hover {
- background-color: rgba(255, 255, 255, 0.3);
- transform: translateX(-3px);
- }
- .left-icon {
- font-size: 18px;
- }
- /* 右下角按钮组样式 */
- .button-group {
- position: absolute;
- bottom: 30px;
- right: 30px;
- display: flex;
- gap: 15px;
- z-index: 1000;
- }
- .control-button {
- padding: 12px 24px;
- border-radius: 8px;
- font-size: 16px;
- font-weight: 500;
- transition: all 0.3s ease;
- backdrop-filter: blur(5px);
- }
- .run-button {
- background-color: rgba(64, 169, 255, 0.8);
- color: white;
- border: none;
- }
- .run-button:hover {
- background-color: rgba(64, 169, 255, 1);
- transform: translateY(-2px);
- }
- .code-button {
- background-color: rgba(132, 94, 255, 0.8);
- color: white;
- border: none;
- }
- .code-button:hover {
- background-color: rgba(132, 94, 255, 1);
- transform: translateY(-2px);
- }
- // 页面主容器样式
- .page-container {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(to bottom, #001169, #b4a8e1);
- display: flex;
- flex-direction: column;
- }
- // 标题样式
- .title-box {
- height: rpx(30);
- }
- .box-icon {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- color: white;
- padding-left: rpx(15);
- font-size: rpx(10);
- cursor: pointer;
- }
- .box-icon .left-icon {
- margin-left: rpx(10);
- margin-right: rpx(5);
- }
- .home-container {
- flex: 1;
- display: flex;
- // padding-top: rpx(10);
- }
- .box-blockly {
- width: 60%;
- height: 90%;
- padding: rpx(10) rpx(10);
- float: left;
- }
- .box-code {
- color: white;
- width: 35%;
- height: 90%;
- padding: rpx(10) rpx(10);
- display: flex;
- flex-direction: column;
- .button-container {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- align-items: flex-start;
- }
- .button-container :deep(.el-button) {
- flex-shrink: 0;
- }
- .code-section {
- flex: 1;
- display: flex;
- flex-direction: column;
- margin-bottom: 10px;
- .box-code-textarea {
- flex: 1;
- resize: none;
- background-color: #f8f9fa;
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 10px;
- overflow-y: auto;
- font-family: monospace;
- font-size: 14px;
- }
- }
- .result-section {
- flex: 1;
- display: flex;
- flex-direction: column;
- .run-result-content {
- color: black;
- flex: 1;
- background-color: #f8f9fa;
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 10px;
- overflow-y: auto;
- font-family: monospace;
- font-size: 14px;
- }
- }
- }
- // AI模块样式
- [categorystyle="ai_category"] > .blocklyTreeRow {
- background-color: #9c27b0 !important;
- }
- // 运行结果样式
- .running-indicator {
- color: #666;
- }
- .log-message {
- color: #333;
- }
- .error-message {
- color: #dc3545;
- }
- .warn-message {
- color: #ffc107;
- }
- .success-message {
- color: #28a745;
- }
- // 预览样式
- .preview-image-container,
- .preview-video-container {
- display: flex;
- justify-content: center;
- }
- .preview-image,
- .preview-video {
- max-width: 100%;
- max-height: 60vh;
- border-radius: 4px;
- }
- .preview-text-container {
- max-height: 60vh;
- overflow-y: auto;
- padding: 10px;
- background-color: #f5f5f5;
- border-radius: 4px;
- }
- //【文生图预览】
- .extra-image-preview {
- color: black;
- margin-top: 10px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 5px;
- background-color: #f9f9f9;
- }
- .extra-preview-image {
- max-width: 100%;
- max-height: 400px;
- border-radius: 4px;
- }
- //台灯
- .lamp-preview-container {
- margin-top: 20px;
- padding: 10px;
- background-color: rgba(255, 255, 255, 0.1);
- border-radius: 8px;
- text-align: center;
- }
- .lamp-display {
- margin: 15px auto;
- position: relative;
- transition: all 0.3s ease;
- }
- .lamp-image {
- width: 100px;
- height: 150px;
- margin: 0 auto;
- background-color: #e0e0e0;
- border-radius: 10px 10px 0 0;
- position: relative;
- box-shadow: 0 0 40px 20px #ffffff80;
- transition: box-shadow 0.5s ease;
- &::before {
- content: "";
- position: absolute;
- top: 10px;
- left: 10px;
- right: 10px;
- bottom: 40px;
- background-color: #f5f5f5;
- border-radius: 5px;
- }
- &::after {
- content: "";
- position: absolute;
- bottom: 10px;
- left: 40px;
- width: 20px;
- height: 30px;
- background-color: #888888;
- }
- }
- .lamp-info {
- margin-top: 10px;
- font-size: 14px;
- color: #ffffff;
- }
- </style>
|