import * as Blockly from "blockly"; import 'blockly/msg/zh-hans'; import {javascriptGenerator} from "blockly/javascript"; import {pythonGenerator} from "blockly/python"; // 导入图片资源 import about_turnImage from '@/assets/images/blockly/component/about_turn.png'; import move_backwardImage from '@/assets/images/blockly/component/move_backward.png'; import move_forwardImage from '@/assets/images/blockly/component/move_forward.png'; import pick_upImage from '@/assets/images/blockly/component/pick_up.png'; import turn_leftImage from '@/assets/images/blockly/component/turn_left.png'; import turn_rightImage from '@/assets/images/blockly/component/turn_right.png'; import useImage from '@/assets/images/blockly/component/use.png'; import pauseImage from '@/assets/images/blockly/component/pause.png'; import play_soundImage from '@/assets/images/blockly/component/sound.png'; import constructImage from '@/assets/images/blockly/component/construct.png'; import {DICT_TYPE} from '@/utils/dictUtils.js'; // 安全地获取和解析字典数据 const getDictDataFromStorage = (dictType) => { try { const data = localStorage.getItem(dictType); return data ? JSON.parse(data) : []; } catch (error) { console.error(`解析${dictType}字典数据失败:`, error); return []; } }; // 地图元素类型字典 const savedMapTypeDict = getDictDataFromStorage(DICT_TYPE.AI_BLOCKLY_MAP_TYPE); const BLOCKLY_MAP_TYPE_DICT = savedMapTypeDict.reduce((acc, item) => { acc[item.value.toUpperCase()] = item.value; return acc; }, {}); // 特殊积木字典 const blocklyMapSpecialDict = getDictDataFromStorage(DICT_TYPE.BLOCKLY_MAP_SPECIAL); const BLOCKLY_MAP_SPECIAL_DICT = blocklyMapSpecialDict.reduce((acc, item) => { acc[item.value.toUpperCase()] = item.value; return acc; }, {}); // 地图标记字典 const blocklyMapMarkDict = getDictDataFromStorage(DICT_TYPE.BLOCKLY_MAP_MARK); const BLOCKLY_MAP_MARK_DICT = blocklyMapMarkDict.reduce((acc, item) => { acc[item.value.toUpperCase()] = { value: item.value, label: item.label, img: item.remark }; return acc; }, {}); /** * 定义所有可用的自定义积木配置 */ const availableBlocks = { // 通用积木 - 始终可用(移动控制) move_forward: { jsonConfig: { "type": "move_forward", "message0": "%1 向前移动", "args0": [ { "type": "field_image", "src": move_forwardImage, "width": 20, "height": 20, "alt": "向前移动" } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向前移动一格", "helpUrl": "" }, isGeneral: true // 标记为通用积木 }, move_forward_param: { jsonConfig: { "type": "move_forward_param", "message0": "%1 向前移动 %2 次", "args0": [ { "type": "field_image", "src": move_forwardImage, "width": 20, "height": 20, "alt": "向前移动多次" }, { "type": "field_number", "name": "PARAM", "value": 1, "min": 1, "max": 10, "precision": 1 } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向前移动指定次数", "helpUrl": "" }, isGeneral: true // 标记为通用积木 }, move_backward: { jsonConfig: { "type": "move_backward", "message0": "%1 向后移动", "args0": [ { "type": "field_image", "src": move_backwardImage, "width": 20, "height": 20, "alt": "向后移动" } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向后移动一格", "helpUrl": "" }, isGeneral: true }, move_backward_param: { jsonConfig: { "type": "move_backward_param", "message0": "%1 向后移动 %2 次", "args0": [ { "type": "field_image", "src": move_backwardImage, "width": 20, "height": 20, "alt": "向后移动多次" }, { "type": "field_number", "name": "PARAM", "value": 1, "min": 1, "max": 10, "precision": 1 } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向后移动指定次数", "helpUrl": "" }, isGeneral: true }, turn_left: { jsonConfig: { "type": "turn_left", "message0": "%1 向左转", "args0": [ { "type": "field_image", "src": turn_leftImage, "width": 20, "height": 20, "alt": "向左转" } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向左转", "helpUrl": "" }, isGeneral: true }, turn_right: { jsonConfig: { "type": "turn_right", "message0": "%1 向右转", "args0": [ { "type": "field_image", "src": turn_rightImage, "width": 20, "height": 20, "alt": "向右转" } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向右转", "helpUrl": "" }, isGeneral: true }, turn_around: { jsonConfig: { "type": "turn_around", "message0": "%1 向后转", "args0": [ { "type": "field_image", "src": about_turnImage, "width": 20, "height": 20, "alt": "向后转" } ], "previousStatement": null, "nextStatement": null, "colour": 230, "tooltip": "控制角色向后转", "helpUrl": "" }, isGeneral: true }, // 通用积木 - 始终可用(功能积木) pickup_item: { jsonConfig: { "type": "pickup_item", "message0": "%1 拾取物品", "args0": [ { "type": "field_image", "src": pick_upImage, "width": 20, "height": 20, "alt": "拾取物品" } ], "previousStatement": null, "nextStatement": null, "colour": 30, "tooltip": "尝试拾取当前位置的物品", "helpUrl": "" }, isGeneral: true }, use_item: { jsonConfig: { "type": "use_item", "message0": "%1 使用物品", "args0": [ { "type": "field_image", "src": useImage, "width": 20, "height": 20, "alt": "使用物品" } ], "previousStatement": null, "nextStatement": null, "colour": 30, "tooltip": "在当前位置使用物品", "helpUrl": "" }, isGeneral: true }, // 当经过某个形状的积木(可作为参数使用的value类型) when_passed: { jsonConfig: { "type": "when_passed", "message0": "当经过 : %1", "args0": [ { "type": "field_dropdown", "name": "SHAPE", "options": Object.values(BLOCKLY_MAP_MARK_DICT).map(shape => [ { "src": shape.img, "width": 30, "height": 30, "alt": shape.label }, shape.value ]) } ], "output": "Boolean", "colour": 30, "tooltip": "检查角色是否经过指定形状,可作为参数使用", "helpUrl": "" }, isGeneral: true }, // 特殊积木 - 可根据配置动态加载 pause: { jsonConfig: { "type": "pause", "message0": "%1 暂停 %2 秒", "args0": [ { "type": "field_image", "src": pauseImage, "width": 20, "height": 20, "alt": "暂停" }, { "type": "field_number", "name": "SECONDS", "value": 1, "min": 1, "max": 10, "precision": 1 } ], "previousStatement": null, "nextStatement": null, "colour": 0, "tooltip": "让角色在原地停留指定的秒数", "helpUrl": "" }, isGeneral: false }, play_sound: { jsonConfig: { "type": "play_sound", "message0": "%1 声音", "args0": [ { "type": "field_image", "src": play_soundImage, "width": 20, "height": 20, "alt": "声音" } ], "previousStatement": null, "nextStatement": null, "colour": 160, "tooltip": "自动触发特效提示音,并根据当前方格类型执行相应操作", "helpUrl": "" }, isGeneral: false }, construct: { jsonConfig: { "type": "construct", "message0": "%1 修建", "args0": [ { "type": "field_image", "src": constructImage, "width": 20, "height": 20, "alt": "修建" } ], "previousStatement": null, "nextStatement": null, "colour": 160, "tooltip": "修建处理", "helpUrl": "" }, isGeneral: false } }; /** * 定义所有可用的JavaScript生成器配置 */ const availableGenerators = { // 通用生成器 - 始终可用 move_forward: function(block) { return 'await moveForward();\n'; }, move_forward_param: function(block) { const stepCount = block.getFieldValue('PARAM'); return `await moveForward(${stepCount});\n`; }, move_backward: function(block) { return 'await moveBackward();\n'; }, move_backward_param: function(block) { const stepCount = block.getFieldValue('PARAM'); return `await moveBackward(${stepCount});\n`; }, turn_left: function(block) { return 'await turnLeft();\n'; }, turn_right: function(block) { return 'await turnRight();\n'; }, turn_around: function(block) { return 'await turnAround();\n'; }, pickup_item: function(block) { return 'await pickupItem();\n'; }, use_item: function(block) { return 'await useItem();\n'; }, when_passed: function(block) { const shape = block.getFieldValue('SHAPE'); return [`whenPassed('${shape}')`, javascriptGenerator.ORDER_FUNCTION_CALL]; }, // 特殊生成器 - 可根据配置动态加载 pause: function(block) { const seconds = block.getFieldValue('SECONDS'); return `await pause(${seconds});\n`; }, play_sound: function(block) { return 'await playSound();\n'; }, construct: function(block) { return 'await construct();\n'; } }; /** * 定义所有可用的Python生成器配置 */ const availablePythonGenerators = { // 通用生成器 - 始终可用 move_forward: function(block) { return 'move_forward()\n'; }, move_forward_param: function(block) { const stepCount = block.getFieldValue('PARAM'); return `move_forward(${stepCount})\n`; }, move_backward: function(block) { return 'move_backward()\n'; }, move_backward_param: function(block) { const stepCount = block.getFieldValue('PARAM'); return `move_backward(${stepCount})\n`; }, turn_left: function(block) { return 'turn_left()\n'; }, turn_right: function(block) { return 'turn_right()\n'; }, turn_around: function(block) { return 'turn_around()\n'; }, pickup_item: function(block) { return 'pickup_item()\n'; }, use_item: function(block) { return 'use_item()\n'; }, when_passed: function(block) { const shape = block.getFieldValue('SHAPE'); return [`when_passed('${shape}')`, pythonGenerator.ORDER_FUNCTION_CALL]; }, // 特殊生成器 - 可根据配置动态加载 pause: function(block) { const seconds = block.getFieldValue('SECONDS'); return `pause(${seconds})\n`; }, play_sound: function(block) { return 'play_sound()\n'; }, construct: function(block) { return 'construct()\n'; } }; /** * 注册自定义Blockly积木 * @param {Array} blocklySpecialBlocks - 特殊积木方法标识集合 */ export function registerCustomBlocks(blocklySpecialBlocks = []) { // 确保blocklySpecialBlocks始终是数组 blocklySpecialBlocks = Array.isArray(blocklySpecialBlocks) ? blocklySpecialBlocks : []; // 遍历所有可用积木 for (const [blockName, blockConfig] of Object.entries(availableBlocks)) { // 如果是通用积木,或者是允许的特殊积木,则注册 if (blockConfig.isGeneral || blocklySpecialBlocks.includes(blockName)) { Blockly.Blocks[blockName] = { init: function() { this.jsonInit(blockConfig.jsonConfig); } }; } } } /** * 设置Blockly中文本地化 */ export function setupBlocklyChineseLocale() { // 使用扩展方式覆盖默认的英文文本为中文 const locale = { // 逻辑积木中文配置 CONTROLS_IF_MSG_IF: "如果", CONTROLS_IF_MSG_ELSEIF: "否则如果", CONTROLS_IF_MSG_ELSE: "否则", CONTROLS_IF_MSG_THEN: "执行", CONTROLS_IF_MSG_DO: "执行", CONTROLS_IF_TOOLTIP_1: "如果条件为真,则执行相应的代码。", CONTROLS_IF_TOOLTIP_2: "如果条件为真,则执行相应的代码;否则执行其他代码。", CONTROLS_IF_TOOLTIP_3: "如果条件为真,则执行相应的代码;否则检查其他条件。", CONTROLS_IF_TOOLTIP_4: "如果条件为真,则执行相应的代码;否则检查其他条件,如果都不满足则执行最后代码。", // 控制块的弹出菜单文本 CONTROLS_IF_IF: "如果", CONTROLS_IF_ELSEIF: "否则如果", CONTROLS_IF_ELSE: "否则", CONTROLS_IF_IF_TITLE: "如果", CONTROLS_IF_IF_TITLE_IF: "如果", CONTROLS_IF_ELSEIF_TITLE_ELSEIF: "否则如果", CONTROLS_IF_ELSE_TITLE: "否则", CONTROLS_IF_ELSE_TITLE_ELSE: "否则", // 条件块的提示文本和帮助URL CONTROLS_IF_HELPURL: "条件语句帮助", CONTROLS_IF_TOOLTIP_IF: "添加或删除条件。", CONTROLS_IF_TOOLTIP_ELSE: "添加或删除否则块。", // 添加设置按钮相关的中文配置 CONTROLS_IF_ELSEIF_TOOLTIP: "否则如果条件为真,则执行相应的代码。", CONTROLS_IF_ELSE_TOOLTIP: "否则执行相应的代码。", // 比较运算符中文配置 LOGIC_COMPARE_EQ: "等于", LOGIC_COMPARE_NEQ: "不等于", LOGIC_COMPARE_LT: "小于", LOGIC_COMPARE_LTE: "小于等于", LOGIC_COMPARE_GT: "大于", LOGIC_COMPARE_GTE: "大于等于", LOGIC_COMPARE_TOOLTIP_EQ: "比较两个值是否相等。", LOGIC_COMPARE_TOOLTIP_NEQ: "比较两个值是否不相等。", LOGIC_COMPARE_TOOLTIP_LT: "比较第一个值是否小于第二个值。", LOGIC_COMPARE_TOOLTIP_LTE: "比较第一个值是否小于或等于第二个值。", LOGIC_COMPARE_TOOLTIP_GT: "比较第一个值是否大于第二个值。", LOGIC_COMPARE_TOOLTIP_GTE: "比较第一个值是否大于或等于第二个值。", // 逻辑运算中文配置 LOGIC_OPERATION_AND: "且", LOGIC_OPERATION_OR: "或", LOGIC_OPERATION_TOOLTIP_AND: "如果两个条件都为真,则结果为真。", LOGIC_OPERATION_TOOLTIP_OR: "如果任一条件为真,则结果为真。", // 布尔值配置 LOGIC_BOOLEAN_TRUE: "真", LOGIC_BOOLEAN_FALSE: "假", LOGIC_BOOLEAN_TOOLTIP: "返回一个布尔值:真或假。", // 循环积木中文配置 CONTROLS_REPEAT_TITLE: "重复%1次", CONTROLS_REPEAT_TOOLTIP: "重复执行内部代码指定次数。", CONTROLS_REPEAT_MSG_DO: "执行", CONTROLS_REPEAT_INPUT_DO: "执行", CONTROLS_WHILEUNTIL_MSG_DO: "执行", CONTROLS_WHILEUNTIL_INPUT_DO: "执行", CONTROLS_FOR_MSG_DO: "执行", CONTROLS_FOR_INPUT_DO: "执行", CONTROLS_FOR_EACH_MSG_DO: "执行", CONTROLS_FOR_EACH_INPUT_DO: "执行", // 数学积木中文配置 MATH_NUMBER_TOOLTIP: "一个数字。在编辑器中双击以更改。", MATH_ADDITION_SYMBOL: "加", MATH_SUBTRACTION_SYMBOL: "减", MATH_MULTIPLICATION_SYMBOL: "乘", MATH_DIVISION_SYMBOL: "除", MATH_ARITHMETIC_TOOLTIP_ADD: "返回两个数的和。", MATH_ARITHMETIC_TOOLTIP_SUBTRACT: "返回第一个数减去第二个数的差。", MATH_ARITHMETIC_TOOLTIP_MULTIPLY: "返回两个数的积。", MATH_ARITHMETIC_TOOLTIP_DIVIDE: "返回第一个数除以第二个数的商。" }; // 使用Object.assign来合并配置,避免直接修改导入对象 Object.assign(Blockly.Msg, locale); } /** * 注册JavaScript生成器 * @param {Array} blocklySpecialBlocks - 特殊积木方法标识集合 */ export function registerJavaScriptGenerators(blocklySpecialBlocks = []) { // 确保blocklySpecialBlocks始终是数组 blocklySpecialBlocks = Array.isArray(blocklySpecialBlocks) ? blocklySpecialBlocks : []; // 遍历所有可用生成器 for (const [blockName, generatorFunc] of Object.entries(availableGenerators)) { // 如果是通用生成器,或者是允许的特殊生成器对应的生成器,则注册 if (availableBlocks[blockName].isGeneral || blocklySpecialBlocks.includes(blockName)) { javascriptGenerator.forBlock[blockName] = generatorFunc; } } // 为text_print块添加生成器,用于调试(始终可用) javascriptGenerator.forBlock['text_print'] = function(block) { const msg = javascriptGenerator.valueToCode(block, 'TEXT', javascriptGenerator.ORDER_NONE) || ''; return msg; }; } /** * 注册Python生成器 * @param {Array} blocklySpecialBlocks - 特殊积木方法标识集合 */ export function registerPythonGenerators(blocklySpecialBlocks = []) { // 确保blocklySpecialBlocks始终是数组 blocklySpecialBlocks = Array.isArray(blocklySpecialBlocks) ? blocklySpecialBlocks : []; // 遍历所有可用生成器 for (const [blockName, generatorFunc] of Object.entries(availablePythonGenerators)) { // 如果是通用生成器,或者是允许的特殊生成器对应的生成器,则注册 if (availableBlocks[blockName].isGeneral || blocklySpecialBlocks.includes(blockName)) { pythonGenerator.forBlock[blockName] = generatorFunc; } } // 为text_print块添加生成器,用于调试(始终可用) pythonGenerator.forBlock['text_print'] = function(block) { const msg = pythonGenerator.valueToCode(block, 'TEXT', pythonGenerator.ORDER_NONE) || ''; return msg; }; } /** * 初始化Blockly工作区 * @param {HTMLElement} blocklyDiv - Blockly容器元素 * @param {HTMLElement} toolbox - 工具箱元素 * @returns {Blockly.WorkspaceSvg} - Blockly工作区实例 */ export function initBlockly(options = {}) { // 应用中文配置 setupBlocklyChineseLocale(); const defaultOptions = { toolbox: document.getElementById('toolbox'), collapse: false, comments: true, disable: false, // 设为false以允许编辑 maxBlocks: Infinity, trashcan: true, horizontalLayout: false, toolboxPosition: 'start', toolboxCollapse: false, // 设置工具箱默认展开 toolboxAlwaysExpanded: true, // 确保点击类别后保持展开状态 css: true, media: 'https://unpkg.com/blockly/media/', rtl: false, scrollbars: true, sounds: false, // 禁用声音以提高性能 oneBasedIndex: true, grid: { spacing: 20, length: 3, colour: "#ccc", snap: true }, zoom: { controls: true, wheel: true, startScale: 1.0, maxScale: 3, minScale: 0.3, scaleSpeed: 1.2 } }; // 合并默认选项和用户提供的选项 const finalOptions = { ...defaultOptions, ...options }; return Blockly.inject('blocklyDiv', finalOptions); } /** * 获取所有可用的积木配置 * @returns {Object} 所有可用积木的配置对象 */ export function getAllBlocksConfig() { return {...availableBlocks}; } /** * 获取所有可用的JavaScript生成器配置 * @returns {Object} 所有可用JavaScript生成器的配置对象 */ export function getAllGeneratorsConfig() { return {...availableGenerators}; } /** * 获取所有可用的Python生成器配置 * @returns {Object} 所有可用Python生成器的配置对象 */ export function getAllPythonGeneratorsConfig() { return {...availablePythonGenerators}; } // 导出字典常量 export { BLOCKLY_MAP_TYPE_DICT, BLOCKLY_MAP_MARK_DICT, BLOCKLY_MAP_SPECIAL_DICT }; export default { registerCustomBlocks, registerJavaScriptGenerators, registerPythonGenerators, setupBlocklyChineseLocale, initBlockly, getAllBlocksConfig, getAllGeneratorsConfig, BLOCKLY_MAP_TYPE_DICT, BLOCKLY_MAP_MARK_DICT, BLOCKLY_MAP_SPECIAL_DICT };