Vue实现图形化积木式编程(十三)

article/2025/10/7 12:53:01

步骤运行代码块高亮

  • 路由
    • 历史回顾
      • Babylon.js部分
      • Blockly部分
  • 前言
  • 最终实现效果
  • 本文内容
  • 实现
    • 1. 安装依赖
    • 2. 简化语法
    • 3. 引入js解析器
    • 4. 运行代码
    • 5. 加入高亮
  • 完整代码
  • 本文章的用例代码已经同步到github上,运行程序后访问链接即可查看效果: [http://localhost:3000/#/Class/blockclass4](http://localhost:3000/#/Class/blockclass4)
  • 开源项目GitHub链接
  • 资源下载链接
  • 你的点赞是我继续编写的动力

路由

历史回顾

Babylon.js部分

  • Vue实现图形化积木式编程(一) ---- Babylon.js基础场景搭建
  • Vue实现图形化积木式编程(二) ---- Babylon.js加载模型到场景中
  • Vue实现图形化积木式编程(三) ---- Babylon.js点击拖拽移动模型
  • Vue实现图形化积木式编程(四) ---- Babylon.js实现碰撞效果
  • Vue实现图形化积木式编程(五) ---- Babylon.js自定义启动界面
  • Vue实现图形化积木式编程(六) ---- Babylon.js相机控制与相机动画
  • Vue实现图形化积木式编程(七) ---- babylonjs-gui 按钮实现
  • Vue实现图形化积木式编程(八) ---- 将3d界面放入可拖动窗口中

Blockly部分

  • Vue实现图形化积木式编程(九) ---- Blockly代码块编辑区域基本场景搭建
  • Vue实现图形化积木式编程(十) ---- Blockly自定义块
  • Vue实现图形化积木式编程(十一) ---- Blockly插件使用
  • Vue实现图形化积木式编程(十二) ---- 执行Blockly生成代码

前言

TIPS:该案例设计主要参考iRobot Coding,只用做学习用途,侵删。

https://code.irobot.com/#/

最终实现效果

最终实现效果

本文内容

  • 步骤运行代码块高亮步骤运行代码块高亮

实现

1. 安装依赖

  • 安装js解析器
npm install js-interpreter

2. 简化语法

在上一篇文章中提到了由于代码块是异步任务,而异步任务需要顺序执行,所以语法上需要构造成一个用async函数包裹的立即执行函数,虽然它的执行逻辑符合需求,但是对于没学过编程的同学第一眼看上去会觉得很乱,所以这里需要引入js-interpreter来简化生成代码语法。

在这里插入图片描述

(async ()=>{
robot.init();await robot.move(50);await robot.arc(0, 90, 50);
robot.stop();
})()
  • 从原来的async代码块形式改为:
robot.init();robot.move(50);robot.arc(0, 90, 50);
robot.stop();
  • 相应的,生成代码的地方改成如下形式:
/*** 自定义组件生成代码* @param block* @returns {string}*/
Blockly.JavaScript['while_program_start'] = function (block) {let while_content = Blockly.JavaScript.statementToCode(block, 'while_content');while_content = while_content.slice(0, -1) // 去除最后一个\nconst code = `
robot.init();
${while_content}
robot.stop();
`return code;
};Blockly.JavaScript['move'] = function (block) {var text_move_distance = block.getFieldValue('move_distance');var code = `robot.move(${text_move_distance});\n`;return code;
};Blockly.JavaScript['turn'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var code = `robot.turn(${dropdown_dirction}, ${angle_degree});\n`;return code;
};Blockly.JavaScript['arc'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var radius = block.getFieldValue('radius');var code = `robot.arc(${dropdown_dirction}, ${angle_degree}, ${radius});\n`;return code;
};

3. 引入js解析器

  • 当然了,上面简化后的语句使用eval执行并不能实现串行顺序执行的效果,我们需要实例化一个JS Interpreter
  • JS Interpreter是一个与浏览器完全隔离的沙箱环境,任何函数或者变量都需要添加到解释器中。
  • 需要注意的是,interpreter.createNativeFunction接受的函数的最后一个参数callback(额外参数)必须调用才视为异步函数调用结束,具体见链接: JS-Interpreter文档,也可加下图
    在这里插入图片描述
import Interpreter from 'js-interpreter'
// 将blockly工作区挂载到dom树上
let workspace = Blockly.inject(this.$refs.blocklyDiv, this.options)
// 这个Robot实例对象在上一篇文章有定义,其中包含最基本的init()、stop()、move(dir)、arc(direction, degree, distance)等方法
let robotController = new Robot()
// let code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)
// 假设已经通过BlocklyJS.workspaceToCode获取到了blocky代码块生成的代码字符串
let code = `
robot.init();robot.move(50);robot.arc(0, 90, 50);
robot.stop();
` function runCode(code) {// 实例化js解析器// 在创建js解析器期间,会调用initApi方法创建解析器的全局变量let myInterpreter = new Interpreter(code, initApi);
}function initApi(interpreter, globalObject) {// 创建 'robot' 的全局对象var robot = interpreter.nativeToPseudo({});interpreter.setProperty(globalObject, 'robot', robot);// 定义 'robot.init' 的函数let iniWrapper = function init() {return robotController.init();};interpreter.setProperty(robot, 'init',interpreter.createNativeFunction(iniWrapper));// 定义 'robot.stop' 的函数var stopWrapper = function stop() {return robotController.stop();};interpreter.setProperty(robot, 'stop',interpreter.createNativeFunction(stopWrapper));// 定义 'robot.move' 的函数// interpreter.createAsyncFunction接受的函数最后一个参数为callback必须调用了才视为异步函数执行完成var moveWrapper = function move(distance, callback) {console.log('调用moveWrapper', that.robot,that.robot.move)robotController.move(distance).then(()=>{console.log('move完成了')callback(1)})};interpreter.setProperty(robot, 'move',interpreter.createAsyncFunction(moveWrapper));// 定义 'robot.arc' 的函数var arcWrapper = function arc(dir, degree, radius, callback) {robotController.arc(dir, degree, radius).then(()=>{console.log('arc完成了')callback()})};interpreter.setProperty(robot, 'arc',interpreter.createAsyncFunction(arcWrapper));}

4. 运行代码

  • 上一个操作中js-Interpreter已经将code解析成一系列可单步执行的代码块
  • 可通过myInterpreter.step()来单步执行代码,或者通过myInterpreter.run()一次性执行所有
function runStepByStep(myInterpreter){if (myInterpreter) {// run函数是一次性执行完所有的,单步执行可使用myInterpreter.step()var hasMore = myInterpreter.run();if (hasMore) {// 当前程序处于某个异步调用函数中,被阻塞了,设置延迟再调用。setTimeout(runStepByStep, 10, myInterpreter);} else {console.log('代码全部执行完了');}}}

5. 加入高亮

使用workspace.highlightBlock(id) 能高亮某个代码块
使用Blockly.JavaScript.STATEMENT_SUFFIX 可再每个语句前插入highlightBlock函数- 具体见: STATEMENT_PREFIX解释
在这里插入图片描述

  • 在创建worksapce之前设置在每个语句之前出入高亮块
function initHighlightBlock() {// 可以在生成JavaScript代码之前通过设置STATEMENT_PREFIX在逐条语句级别上完成此操作Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';// 将highlightBlock添加为保留字Blockly.JavaScript.addReservedWords('highlightBlock');}
  • 在原来initApi函数基础上再加一个
      // 创建 'highlightBlock' 的函数var hightlightWrapper = function(id) {// console.log("highlightBlock")id = String(id || '');return workspace.highlightBlock(id);};interpreter.setProperty(globalObject, 'highlightBlock',interpreter.createNativeFunction(hightlightWrapper));
  • 在原来runStepByStep函数基础上,调用结束后设置workspace.highlightBlock(null)
function runStepByStep(myInterpreter){if (myInterpreter) {// run函数是一次性执行完所有的,单步执行可使用myInterpreter.step()var hasMore = myInterpreter.run();if (hasMore) {// 当前程序处于某个异步调用函数中,被阻塞了,设置延迟再调用。setTimeout(runStepByStep, 10, myInterpreter);} else {console.log('代码全部执行完了');workspace.highlightBlock(null)}}}

完整代码

  • 测试用例
<template><div id="blockly"><!-- 工作区 --><div id="blocklyDiv" ref="blocklyDiv" style="height: 500px; width: 800px;"></div><button style="position: fixed;left: 50px;top: 10px;" @click="block2code">生成代码</button><!-- 代码显示区 --><div style="background-color: lightgrey;width: 800px;text-align: left"><pre v-html="code?code:'请点击生成代码按钮'"></pre></div><button style="position: fixed;left: 150px;top: 10px;" @click="runCode">eval执行代码</button><button style="position: fixed;left: 300px;top: 10px;" @click="runCode2">new Function执行代码</button><button style="position: fixed;left: 500px;top: 10px;" @click="runCode3">js-interpreter执行代码</button></div>
</template><script>
import Blockly from 'blockly'
import BlocklyJS from 'blockly/javascript';
import './customBlock'
import Robot from './robot'
import Interpreter from "js-interpreter";
export default {name: "blocklyClass4",data() {return {code: '',options: {horizontalLayout: true,//工具箱水平toolboxPosition: "end",//工具箱在底部toolbox: {"kind": "flyoutToolbox","contents": [{"kind": "block","type": "while_program_start",},{"kind": "block","type": "move",},{"kind": "block","type": "turn",},{"kind": "block","type": "arc"},{"kind": "block","type": "draw"},{"kind": "block","type": "pencilcolor"},{"kind": "block","type": "controls_repeat_ext"},{"kind": "block","type": "controls_whileUntil"},{"kind": "block","type": "controls_for"},{"kind": "block","type": "controls_if"},{"kind": "block","type": "logic_compare"},{"kind": "block","type": "logic_operation"},{"kind": "block","type": "logic_negate"},{"kind": "block","type": "logic_boolean"},{"kind": "sep","gap": "32"},{"kind": "block","blockxml": "<block type='math_number'><field name='NUM'>10</field></block>"},{"kind": "block","type": "math_arithmetic"},{"kind": "block","type": "math_single"},{"kind": "block","type": "text"},{"kind": "block","type": "text_length"},{"kind": "block","type": "text_print"},{"kind": "block","type": "variables_get"},{"kind": "block","type": "variables_set"},]}},workspace: null}},mounted() {this.initHighlightBlock()this.workspace = Blockly.inject(this.$refs.blocklyDiv, this.options);this.robot = new Robot()},methods: {/*** block代码块转为代码*/block2code() {this.code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)},/*** 执行生成代码*/runCode() {if (!this.code) {alert('请先点击生成代码');return}window.robot = this.roboteval(this.code)},runCode2() {if (!this.code) {alert('请先点击生成代码');return}let fn = new Function('robot', this.code)fn(this.robot)},runCode3() {if (!this.code) {alert('请先点击生成代码');return}// 实例化js解析器// 在创建js解析器期间,会调用initApi方法创建解析器的全局变量let myInterpreter = new Interpreter(this.code, this.initApi);console.log('myInterpreter', myInterpreter)// this.initJsInterpreter(this.code)this.runStepByStep(myInterpreter)},runStepByStep(myInterpreter){if (myInterpreter) {var hasMore = myInterpreter.run();if (hasMore) {// 执行当前被某个异步调用阻止。//请稍后再试。setTimeout(this.runStepByStep, 10, myInterpreter);} else {this.highlightBlock(null);console.log('代码全部执行完了');}}},initHighlightBlock() {// 可以在生成JavaScript代码之前通过设置STATEMENT_PREFIX在逐条语句级别上完成此操作Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';// 将highlightBlock添加为保留字Blockly.JavaScript.addReservedWords('highlightBlock');},highlightBlock(id) {this.workspace.highlightBlock(id);},initApi(interpreter, globalObject) {let that = this// 创建 'highlightBlock' 的函数var hightlightWrapper = function(id) {// console.log("highlightBlock")id = String(id || '');return that.highlightBlock(id);};interpreter.setProperty(globalObject, 'highlightBlock',interpreter.createNativeFunction(hightlightWrapper));// 创建 'robot' 的全局对象var robot = interpreter.nativeToPseudo({});interpreter.setProperty(globalObject, 'robot', robot);// 定义 'robot.init' 的函数let iniWrapper = function init() {return that.robot.init();};interpreter.setProperty(robot, 'init',interpreter.createNativeFunction(iniWrapper));// 定义 'robot.stop' 的函数var stopWrapper = function stop() {return that.robot.stop();};interpreter.setProperty(robot, 'stop',interpreter.createNativeFunction(stopWrapper));// 定义 'robot.move' 的函数// interpreter.createAsyncFunction接受的函数最后一个参数为callback必须调用了才视为异步函数执行完成var moveWrapper = function move(distance, callback) {that.robot.move(distance).then(()=>{console.log('move完成了')callback(1)})};interpreter.setProperty(robot, 'move',interpreter.createAsyncFunction(moveWrapper));// 定义 'robot.arc' 的函数var arcWrapper = function arc(dir, degree, radius, callback) {that.robot.arc(dir, degree, radius).then(()=>{console.log('arc完成了')callback()})};interpreter.setProperty(robot, 'arc',interpreter.createAsyncFunction(arcWrapper));}}
}
</script><style scoped>
#blockly {position: absolute;left: 50px;top: 50px;bottom: 0;width: calc(100vw - 50px);height: calc(100vh - 50px);display: flex;flex-direction: column;
}
</style>
  • Blockly自定义组件
import * as Blockly from 'blockly/core'import * as hans from 'blockly/msg/zh-hans'Blockly.setLocale(hans);//汉化/*** 自定义组件注册*/
Blockly.defineBlocksWithJsonArray([//事件{"type": "while_program_start","message0": "当程序运行 %1 %2","args0": [{"type": "input_dummy"},{"type": "input_statement","name": "while_content"}],"previousStatement": null,"nextStatement": null,"colour": "#609FD6","strokeColour": "#4088C8","tooltip": "123","helpUrl": "1"},//指令{"type": "move","message0": "移动 %1 CM","args0": [{"type": "field_input","name": "move_distance","text": "50"}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "turn","message0": "向 %1 %2","args0": [{"type": "field_dropdown","name": "dirction","options": [["左转","0"],["右转","1"]]},{"type": "field_angle","name": "degree","angle": 90}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "arc","message0": "弧形 %1 %2 ,半径 %3 CM","args0": [{"type": "field_dropdown","name": "dirction","options": [["向左","0"],["向右","1"]]},{"type": "field_angle","name": "degree","angle": 90},{"type": "field_number","name": "radius","value": 50,"min": 1,"max": 100}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "draw","message0": "设置 %1","args0": [{"type": "field_dropdown","name": "pencilState","options": [[{"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjEtMDgtMjNUMTE6Mzk6NTQrMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTA4LTIzVDExOjUxOjAxKzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA4LTIzVDExOjUxOjAxKzA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmVkZGRkMGNlLTU4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","width": 50,"height": 50,"alt": "pencil down"},"1"],[{"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjEtMDgtMjNUMTE6NDA6MTErMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTA4LTIzVDExOjUyOjA2KzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA4LTIzVDExOjUyOjA2KzA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU3Y2UzN2ViLTQ0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","width": 50,"height": 50,"alt": "pencil up"},"0"]]}],"previousStatement": null,"nextStatement": null,"colour": "#81C679","tooltip": "","helpUrl": ""},{"type": "pencilcolor","message0": "设置笔颜色: 红 %1 绿 %2 蓝 %3","args0": [{"type": "field_number","name": "red","value": 100,"min": 0,"max": 255},{"type": "field_number","name": "green","value": 100,"min": 0,"max": 255},{"type": "field_number","name": "blue","value": 100,"min": 0,"max": 255}],"previousStatement": null,"nextStatement": null,"colour": "#81C679","tooltip": "","helpUrl": ""}]
);/*** 自定义组件生成代码* @param block* @returns {string}*/
Blockly.JavaScript['while_program_start'] = function (block) {let while_content = Blockly.JavaScript.statementToCode(block, 'while_content');while_content = while_content.slice(0, -1) // 去除最后一个\nconst code =
`robot.init();
${while_content}
robot.stop();
`return code;
};Blockly.JavaScript['move'] = function (block) {var text_move_distance = block.getFieldValue('move_distance');var code = `robot.move(${text_move_distance});\n`;return code;
};Blockly.JavaScript['turn'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var code = `robot.turn(${dropdown_dirction}, ${angle_degree});\n`;return code;
};Blockly.JavaScript['arc'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var radius = block.getFieldValue('radius');var code = `robot.arc(${dropdown_dirction}, ${angle_degree}, ${radius});\n`;return code;
};Blockly.JavaScript['draw'] = function (block) {var dropdown_pencilstate = block.getFieldValue('pencilState');var code = `robot.drawable(${dropdown_pencilstate});\n`;return code;
};Blockly.JavaScript['pencilcolor'] = function (block) {var number_red = block.getFieldValue('red') / 255.0;var number_green = block.getFieldValue('green') / 255.0;var number_blue = block.getFieldValue('blue') / 255.0;var code = `robot.pencilcolor(${number_red}, ${number_green}, ${number_blue});\n`;return code;
};
  • Robot控制类
// eslint-disable-next-line no-unused-vars
class Robot {constructor() {this.isRun = false}init() {console.log("robot模块化程序初始化")this.isRun = true}stop() {console.log("robot模块运行结束")this.isRun = false}checkStatus() {if (!this.isRun) {throw '程序需要初始化模块'}}async move(distance) {this.checkStatus()// 模拟小车运动return new Promise(resolve => {let moveDis = 0let interval = setInterval(() => {if (moveDis < distance) {console.log(`move ${moveDis++}`)} else {clearInterval(interval)interval = undefinedresolve()}}, 100)})}async arc(direction, degree, distance) {this.checkStatus()// 模拟小车运动return new Promise(resolve => {let moveDis = 0let interval = setInterval(() => {if (moveDis < distance) {console.log(`direction: ${direction}, move:${moveDis++}, degree: ${degree}`)} else {clearInterval(interval)interval = undefinedresolve()}}, 100)})}
}
export default Robot

本文章的用例代码已经同步到github上,运行程序后访问链接即可查看效果: http://localhost:3000/#/Class/blockclass4

开源项目GitHub链接

https://github.com/Wenbile/Child-Programming-Web

资源下载链接

  • Vue前端源码
  • ThinkJS后端源码

你的点赞是我继续编写的动力


http://chatgpt.dhexx.cn/article/fJBGeuPa.shtml

相关文章

搭积木php编程,Primo Toys,一款可以同时搭积木与编程的玩具

原标题&#xff1a;Primo Toys,一款可以同时搭积木与编程的玩具 时下社会&#xff0c;“编程”一词早已不是成人世界的一个职业&#xff0c;如今&#xff0c;编程能力就像蜗牛日常的读写一样&#xff0c;渗透到孩子生活的方方面面&#xff0c;不论是APP&#xff0c;游戏&#x…

python的积木式编程

edu.codemao.cn https://wood.codemao.cn/ https://static.codemao.cn/wood_docs/web/code/Control.html 编程猫和scratch大同小异&#xff0c;和其他Python平台相比较&#xff1a; 优势&#xff1a; 支持硬件编程丰富的Python库积木/代码一键转换搭积木 学编程使用方便&a…

c语言的积木编程,c语言入门第3节,掌握它就能随心所欲的编程了,自己造积木...

前面两节介绍了如何快速搭建 C 语言开发学习环境&#xff0c;并且给出并解释了 hello world 的代码。上一节介绍了C语言的函数&#xff0c;应该注意到&#xff0c;到目前为止&#xff0c;我们使用的函数都是现有的系统函数(例如 printf&#xff0c;sin 等函数)。事实上&#xf…

等边三角形的积木编程

用Scratch里的画笔&#xff0c;可以很方便的画出等边三角形。步骤如下&#xff1a; 1、定义"等边三角形"积木 点击Scratch里的"自制积木" --》制作新的积木 --》直线 --》添加4个参数&#xff0c;如图(1)、(2)所示&#xff1a; 图(1) 定义"等边三角…

积木编程软件

当然这种观点是很荒谬的&#xff0c;不会编程不等于文盲。虽然大多数父母都有文化和读写能力&#xff0c;但是大多数父母都不是程序员&#xff0c;也不知道程序员需要什么样的技能。格物斯坦认为&#xff1a;针对孩子们的编程书籍给出的都是一些问题的“标准”答案。如果你的孩…

金字塔的积木编程

金字塔是一个上尖下方的四面体&#xff0c;它的截面是一个三角形&#xff0c;可以用长方形的砖块去堆叠成一个大的三角形来表示金字塔。     下面介绍使用Scratch来画金字塔。 1、定义"砖块"积木 图(1) 定义"砖块"积木 图(2) "砖块"积木代码 …

OpenBlock Desktop v2.3.1积木编程

最新版 2.3.1 Release OpenBlock Desktop v2.3.1 openblockcc/openblock-desktop GitHubhttps://github.com/openblockcc/openblock-desktop/releases/tag/v2.3.1 新功能 用户可以在串口终端中使用回车键发送数据。提高 esp32/8266 的默认上传波特率以提高上传速度。删除 es…

python积木编程软件_积木编程软件手机版下载

积木编程软件是可以在线可以学习编程的软件,平台给用户提供了大量的编程学习教程&#xff0c;软件还支持在线编程&#xff0c;学习内容丰富&#xff0c;让用户学习起来不会枯燥乏味&#xff0c;就算你是零基础的小白&#xff0c;也可以体验到编程的乐趣。 积木编程官方简介 积木…

零代码积木编程案例分享

和大家分享一个通过积木编程技术实现的复杂业务系统的案例&#xff0c;主要比较传统有代码和零代码积木编程在项目各个阶段的人力投入和成本比较。 客户是一家工业丝线工厂&#xff0c;期望实现生产、仓库、设备的管理&#xff0c;同时连线近百台设备&#xff0c;还要集成AGV、…

时钟的积木编程

家里壁挂的时钟滴答滴答响着&#xff0c;里面有时针、分钟和秒针。在时钟刻度盘里&#xff0c;秒针每60秒走完一周&#xff0c;即秒针的最小角度为360/60 6&#xff1b;分针每60分钟走完一周&#xff0c;即分针的最小角度为360/60 6&#xff1b;时针每12小时&#xff0c;走完一…

长方形的积木编程

用Scratch画长方形有多种方法&#xff0c;可以用一个“点长宽”来画&#xff0c;也可以使用"长方形的2个对角顶点来画"&#xff0c;还可以使用"长翻转90宽翻转90来画"。 1、"点长宽"方式 1.1 定义"直线"积木 点击Scratch左侧的&quo…

Netsparker介绍

Netsparker是一款综合型的web应用安全漏洞扫描工具&#xff0c;它分为专业版和免费版&#xff0c;免费版的功能也比较强大。Netsparker与其他综合性的web应用安全扫描工具相比的一个特点是它能够更好的检测SQL Injection和 Cross-site Scripting类型的安全漏洞。‍‍ 转载于:ht…

使用Netsparker扫描及**某站点

1.1使用Netsparker扫描及某站点 目前市面上漏洞扫描软件有多款,前面介绍了一些扫描软件例如awvs对目标站点进行扫描,其实国外还有一款扫描软件Netsparker也挺厉害,扫描误报率低,扫描效果还不错。在本文中,对一个目标站点进行扫描,并未发现高危漏洞,但通过列目录漏洞…

开源的网络监控工具:Sniffnet,简单而有趣!

在当今数字化的世界中&#xff0c;网络监控工具对于管理和保护网络的安全至关重要。网络管理员和安全专业人员需要一种高效且易于使用的工具&#xff0c;以监视和分析网络流量&#xff0c;识别潜在的安全威胁和性能问题。Sniffnet 是一个开源的网络监控工具&#xff0c;它提供了…

Netspark自动批量扫描powershell脚本

脚本需要创建2个文件&#xff0c;1个文件夹&#xff0c;都放到Netsparker目录下&#xff0c;将脚本中涉及到netspark目录的修改成自己的目录。 注意&#xff1a;Netsparker目录不能含有中文名。 文件1&#xff1a;脚本文件&#xff0c;文件名auto.ps1&#xff0c;内容如下&am…

WebSocket in ASP.NET Core

回到目录 一、WebSocket WebSocket是HTML5出的东西&#xff08;协议&#xff09;&#xff0c;也就是说HTTP协议没有变化&#xff0c;或者说没关系&#xff0c;但HTTP是不支持持久连接的&#xff08;长连接&#xff0c;循环连接的不算&#xff09; 首先HTTP有1.1和1.0之说&#…

NetScaler

NetScaler 简介 Citrix NetScaler 产品是一种应用交换机&#xff0c;用于执行特定于应用的流量分析&#xff0c;从而智能地分配和优化 Web 应用 4 - 7 层 (L4– L7) 的网络流量&#xff0c;并确保其安全。 例如&#xff0c;NetScaler 根据单个 HTTP 请求而非持续的 TCP 连接做出…

SocketTools.NET 11.0 Crack

在所有 HTTP 客户端组件中添加对 HTTP/2.0 协议的支持。 2023 年 5 月 17 日 - 10:58新版本 特征 在所有 HTTP 客户端组件中添加了对 HTTP/2.0 协议的支持。 更新了 TLS 1.2&#xff08;及更高版本&#xff09;和 SSH 2.0 的安全选项&#xff0c;以使用 Microsoft Windows 11 和…

四款Web扫描器

四款扫描器&#xff1a; appscan IBM公司 awvs 国外 xray 长亭科技 Netsparker 俗称“鲨鱼” 另外补充&#xff1a;绿盟极光、安恒明鉴。 一、appscan 本次案例&#xff1a;版本10.0.4破解版&#xff0c;安装完成后许…

ASP.NETCore

0. ASP.NETCore 介绍 ASP.NETCore是一个新的开源和跨平台的框架&#xff0c;用于构建如Web应用、物联网&#xff08;IoT)应用和移动后端应用等连接到互联网的基于云的现代应用程序。ASP.NET Core应用可运行于.NET Core和完整的.NET Framework之上。构建它的目的是为那些部署在云…