diff --git a/Front/package-lock.json b/Front/package-lock.json index 4a7edf7587f016c125741a762b001ec25fd92c17..ee3772110f2fe3aa90a0fca310639a0dcbda8893 100644 --- a/Front/package-lock.json +++ b/Front/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "blockly": "^10.1.3", "highlight.js": "^11.8.0", + "marked": "^15.0.7", "pinia": "^2.1.6", "vue": "^3.3.4" }, @@ -7189,6 +7190,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "15.0.7", + "resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz", + "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/Front/package.json b/Front/package.json index a62b0b5b19d30df8da9d08048895c75aa4978437..55cffee4d723eeedefb51bb89bfadc31c9ba68c6 100644 --- a/Front/package.json +++ b/Front/package.json @@ -13,6 +13,7 @@ "dependencies": { "blockly": "^10.1.3", "highlight.js": "^11.8.0", + "marked": "^15.0.7", "pinia": "^2.1.6", "vue": "^3.3.4" }, diff --git a/Front/src/assets/toolbox.json b/Front/src/assets/toolbox.json index 0c6dd2e77593e46ee7a34cc098cef3244703ae8d..847009204082c1c0c7bb566f8531ad2d4924191c 100644 --- a/Front/src/assets/toolbox.json +++ b/Front/src/assets/toolbox.json @@ -351,6 +351,33 @@ } ] }, + { + "kind": "category", + "name": "队列", + "categorystyle": "queue", + "contents": [ + { + "kind": "block", + "type": "queue_import" + }, + { + "kind": "block", + "type": "queue_create" + }, + { + "kind": "block", + "type": "queue_enqueue" + }, + { + "kind": "block", + "type": "queue_dequeue" + }, + { + "kind": "block", + "type": "queue_is_empty" + } + ] + }, { "kind": "category", "name": "字典", @@ -432,6 +459,22 @@ { "kind": "block", "type": "http_post" + }, + { + "kind": "block", + "type": "http_response_status" + }, + { + "kind": "block", + "type": "http_response_header" + }, + { + "kind": "block", + "type": "http_response_text" + }, + { + "kind": "block", + "type": "http_response_json" } ] }, @@ -581,9 +624,25 @@ }, { "kind": "category", - "name": "threading", + "name": "多线程", "categorystyle": "threading", "contents": [ + { + "kind": "block", + "type": "threading_import" + }, + { + "kind": "block", + "type": "threading_create" + }, + { + "kind": "block", + "type": "threading_start" + }, + { + "kind": "block", + "type": "threading_join" + } ] }, { diff --git a/Front/src/blocks/boxy.js b/Front/src/blocks/boxy.js index 8c53f80171feaf71d9bc852726ceb979483006f0..9a9ae5b611e7e0bd5d9202d2b4ed528b58c4e727 100644 --- a/Front/src/blocks/boxy.js +++ b/Front/src/blocks/boxy.js @@ -455,7 +455,7 @@ Blockly.defineBlocksWithJsonArray([ check: 'Array' } ], - output: 'String', + output: 'http_response', style: 'http', tooltip: '发送GET请求。' }, @@ -484,12 +484,74 @@ Blockly.defineBlocksWithJsonArray([ check: 'Array' } ], - output: 'String', + output: 'http_response', style: 'http', tooltip: '发送POST请求。' }, { - //字典 + type: 'http_response_status', + message0: '获取HTTP响应状态码 %1', + args0: [ + { + type: 'input_value', + name: 'RESPONSE', + check: 'http_response' + } + ], + output: 'Number', + style: 'http', + tooltip: '获取HTTP响应的状态码' + }, + { + type: 'http_response_header', + message0: '获取HTTP响应头 %1 键 %2', + args0: [ + { + type: 'input_value', + name: 'RESPONSE', + check: 'http_response' + }, + { + type: 'input_value', + name: 'HEADER', + check: 'String' + } + ], + output: 'String', + style: 'http', + tooltip: '获取HTTP响应头中指定键的值' + }, + { + type: 'http_response_text', + message0: '获取HTTP响应文本 %1', + args0: [ + { + type: 'input_value', + name: 'RESPONSE', + check: 'http_response' + } + ], + output: 'String', + style: 'http', + tooltip: '获取HTTP响应的文本内容' + }, + { + type: 'http_response_json', + message0: '获取HTTP响应JSON %1', + args0: [ + { + type: 'input_value', + name: 'RESPONSE', + check: 'http_response' + } + ], + output: 'Object', + style: 'http', + tooltip: '获取HTTP响应的JSON对象' + }, + + { + //字典 type: 'json_parse', message0: '解析JSON字符串 %1', args0: [ @@ -570,7 +632,7 @@ Blockly.defineBlocksWithJsonArray([ { type: 'input_value', name: 'JSON2', - check: ['Object', 'Array','String'] // 添加 Array 类型支持 + check: ['Object', 'Array', 'String'] // 添加 Array 类型支持 } ], output: 'Object', @@ -699,7 +761,7 @@ Blockly.defineBlocksWithJsonArray([ tooltip: '创建一个空字典。' }, { - type:'console_log', + type: 'console_log', message0: '打印 %1', args0: [ { @@ -743,19 +805,21 @@ Blockly.defineBlocksWithJsonArray([ style: 'data_encoding', tooltip: '生成文本的MD5哈希值' }, - + // 在Blockly.defineBlocksWithJsonArray数组中添加: { type: 'file_open', message0: '打开文件 %1 模式 %2', args0: [ - {type: 'input_value', name: 'FILENAME', check: 'String'}, - {type: 'field_dropdown', name: 'MODE', + { type: 'input_value', name: 'FILENAME', check: 'String' }, + { + type: 'field_dropdown', name: 'MODE', options: [ ['读取', 'r'], ['写入', 'w'], ['追加', 'a'] - ]} + ] + } ], output: null, style: 'io' @@ -763,7 +827,7 @@ Blockly.defineBlocksWithJsonArray([ { type: 'file_read', message0: '读取文件 %1', - args0: [{type: 'input_value', name: 'FILE', check: 'File'}], + args0: [{ type: 'input_value', name: 'FILE', check: 'File' }], output: 'String', style: 'io' }, @@ -771,12 +835,189 @@ Blockly.defineBlocksWithJsonArray([ type: 'file_write', message0: '写入文件 %1 内容 %2', args0: [ - {type: 'input_value', name: 'FILE', check: 'File'}, - {type: 'input_value', name: 'CONTENT', check: 'String'} + { type: 'input_value', name: 'FILE', check: 'File' }, + { type: 'input_value', name: 'CONTENT', check: 'String' } ], previousStatement: null, nextStatement: null, style: 'io' - } - + }, + { + type: 'queue_import', + message0: '导入queue模块', + previousStatement: null, + nextStatement: null, + style: 'queue', + tooltip: '导入Python的queue模块' + }, + { + type: 'queue_create', + message0: '创建队列', + output: 'queue', + style: 'queue', + tooltip: '创建一个空队列' + }, + { + type: 'queue_enqueue', + message0: '入队 %1 到队列 %2', + args0: [ + { + type: 'input_value', + name: 'ITEM' + }, + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + previousStatement: null, + nextStatement: null, + style: 'queue', + tooltip: '将元素添加到队列末尾' + }, + { + type: 'queue_dequeue', + message0: '从队列 %1 出队', + args0: [ + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + output: 'Any', + style: 'queue', + tooltip: '移除并返回队列的第一个元素' + }, + { + type: 'queue_is_empty', + message0: '队列 %1 是否为空', + args0: [ + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + output: 'Boolean', + style: 'queue', + tooltip: '检查队列是否为空' + }, + + { + type: 'threading_import', + message0: '导入 threading 模块', + style: 'threading', + tooltip: '导入Python threading模块', + previousStatement: null, + nextStatement: null, + }, + { + type: 'threading_create', + message0: '创建线程 %1 执行 %2', + args0: [ + { + type: 'input_value', + name: 'NAME', + check: 'String' + }, + { + type: 'input_statement', + name: 'FUNCTION' + } + ], + previousStatement: null, + nextStatement: null, + style: 'threading', + tooltip: '创建一个新的线程' + }, + { + type: 'threading_start', + message0: '启动线程 %1', + args0: [ + { + type: 'input_value', + name: 'THREAD', + check: 'thread' + } + ], + previousStatement: null, + nextStatement: null, + style: 'threading', + tooltip: '启动线程执行' + }, + { + type: 'threading_join', + message0: '等待线程 %1 结束', + args0: [ + { + type: 'input_value', + name: 'THREAD', + check: 'thread' + } + ], + previousStatement: null, + nextStatement: null, + style: 'threading', + tooltip: '等待线程执行完成' + }, + { + type: 'queue_create', + message0: '创建队列', + output: 'queue', + style: 'queue', + tooltip: '创建一个空队列' + }, + { + type: 'queue_enqueue', + message0: '入队 %1 到队列 %2', + args0: [ + { + type: 'input_value', + name: 'ITEM' + }, + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + previousStatement: null, + nextStatement: null, + style: 'queue', + tooltip: '将元素添加到队列末尾' + }, + { + type: 'queue_dequeue', + message0: '从队列 %1 出队', + args0: [ + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + output: 'Any', + style: 'queue', + tooltip: '移除并返回队列的第一个元素' + }, + { + type: 'queue_is_empty', + message0: '队列 %1 是否为空', + args0: [ + { + type: 'input_value', + name: 'QUEUE', + check: 'queue' + } + ], + output: 'Boolean', + style: 'queue', + tooltip: '检查队列是否为空' + }, + + // ... 已有代码 ... + + ]) diff --git a/Front/src/components/FilePanel.vue b/Front/src/components/FilePanel.vue index ef4e3c6fc64c8bf9922c5f00acb2538438bfcd41..6439e7c47d645e507975eb3f0c2cf1f3aeeb350a 100644 --- a/Front/src/components/FilePanel.vue +++ b/Front/src/components/FilePanel.vue @@ -40,7 +40,7 @@ - + @@ -23,6 +25,7 @@ + + diff --git a/main.py b/main.py index ce341a714291181b9267560e03e11a82052a1c00..67cda3e9769b2a68b84e586a8b11c1b5bfd9dd07 100644 --- a/main.py +++ b/main.py @@ -27,6 +27,8 @@ else: print(f"当前程序的路径: {current_executable_path}") current_executable_dir = os.path.dirname(current_executable_path) print(f"当前程序所在的目录: {current_executable_dir}") +print("访问网址:", "http://127.0.0.1:5000/") + # 获取./project的绝对路径 # 起始目录 base_dir = current_executable_dir @@ -35,7 +37,7 @@ relative_path = venv_path # 获取绝对路径 absolute_path = os.path.abspath(os.path.join(base_dir, relative_path)) -app = Flask(__name__, template_folder='dist', static_folder='dist/') +app = Flask(__name__, template_folder='./', static_folder='./') # 判断是否存在project虚拟环境,不存在则创建 if not os.path.exists('project'): @@ -74,11 +76,16 @@ def index(): return response return render_template('index.html') + +@app.route('/docs/') +def docs(path): + return render_template('public/docs/'+ path) + # dist/下的路径全部自动返回 @app.route('/') def static_proxy(path): print(path) - return app.send_static_file(path) + return app.send_static_file("dist/"+path) # api @app.route('/api/', methods=['GET', 'POST']) @@ -137,7 +144,7 @@ def api(api): return jsonify({'error': 'Failed to install package'}), 500 elif api == 'project_list': # 读取project.json - with open('project.json', 'r') as f: + with open(current_executable_dir + '\project.json', 'r') as f: project_list = json.load(f) return jsonify(project_list) elif api == 'file_list': @@ -195,7 +202,7 @@ def api(api): path = data.get('path') exist = False - with open('project.json', 'r') as f: + with open(current_executable_dir + '\project.json', 'r') as f: dataJson = json.load(f) # 检查是否已经存在 for item in dataJson: @@ -210,7 +217,7 @@ def api(api): dataJson.append({'title': name, 'path': path}) # 写入 - with open('project.json', 'w') as j: + with open(current_executable_dir + '\project.json', 'w') as j: j.write(json.dumps(dataJson)) return {'message': 'success'} @@ -235,6 +242,47 @@ def api(api): except Exception as e: # 返回删除失败的异常信息 return {'message': 'error', 'error': str(e)} + elif api == 'run': + # 获取Cookie-currentFilePath + current_file_path = request.cookies.get('currentFilePath', None) + if current_file_path is None: + return jsonify({'message': 'error', 'error': 'currentFilePath is None'}) + else: + # 获取文件名 + file_name = os.path.basename(current_file_path) + # 获取文件名前缀 + file_prefix = os.path.splitext(file_name)[0] + # 获取文件所在目录 + file_dir = os.path.dirname(current_file_path) + # 获取文件所在目录的绝对路径 + file_dir_absolute = os.path.abspath(file_dir) + # 获取对应的py文件路径 + py_file_path = os.path.join(file_dir, file_prefix + '.py') + # 获取对应的py文件路径的绝对路径 + py_file_path_absolute = os.path.abspath(py_file_path) + # 读取Cookie-currentProjectPath,若无则为absolute_path + current_project_path = request.cookies.get('currentProjectPath') or absolute_path + + # 运行Py + try: + # 唤起cmd窗口 + # 进入项目虚拟环境 + #os.system(r'start cmd.exe /k "F:\program\StarBit\code\block-py\project\Scripts\activate && cd /d F:\program\StarBit\code\block-py\project && python test.py"') + #os.system(r'start cmd.exe /k '+os.path.join(current_project_path, 'Scripts', 'activate') + ' && cd /d ' + file_dir_absolute + ' && python ' + file_prefix + '.py') + #os.system('start cmd.exe /k '+os.path.join(current_project_path, 'Scripts', 'activate') + ' && cd ' + file_dir_absolute + ' && python ' + file_prefix + '.py') + + # 构造激活虚拟环境的命令 + activate_cmd = os.path.join(current_project_path, 'Scripts', 'activate') + + # 构造完整的命令字符串 + cmd_command = f'start cmd.exe /k "call {activate_cmd} && cd /d {file_dir_absolute} && python {py_file_path_absolute}"' + + # 执行命令 + os.system(cmd_command) + return jsonify({'message': 'success'}) + except Exception as e: + return jsonify({'message': 'error', 'error': str(e)}) + if __name__ == '__main__': app.run(debug=True) diff --git a/public/docs/tutorial.md b/public/docs/tutorial.md new file mode 100644 index 0000000000000000000000000000000000000000..53e2a2495d243211a66246cf72d4245582daf61a --- /dev/null +++ b/public/docs/tutorial.md @@ -0,0 +1,27 @@ +# Blockly 入门教程 + +## 基础操作 +1. 从左侧工具栏拖拽代码块到工作区 +2. 通过连接点组合代码块 +3. 右键代码块可查看上下文菜单 + +## 示例代码 +```python +# 打印欢迎信息 +print('欢迎使用 Blockly!') + +# 简单计算 +a = 5 +b = 3 +print(f"{a} + {b} = {a + b}") +``` + +## 快捷键 +- Ctrl+S: 保存当前文件 +- Ctrl+O: 打开文件 +- Ctrl+Z: 撤销操作 + +## 高级功能 +- 使用工具箱分类快速查找代码块 +- 通过项目管理器创建多文件项目 +- 利用PIP管理面板安装第三方库 \ No newline at end of file