# drag 拖拽编程画布模块 元景 W-09 拖拽编程画布模块:把可视化拖拽块(事件/逻辑/动作/变量/表达式)连线成流程, 编译为 script_engine 可执行 Python 脚本(script_type=0)。 ## 功能 - **画布**:打开/加块/移动/连接/删除/保存(graph_save) - **块体系**:事件(开始/点击/悬停/定时/碰撞/键盘)、逻辑(顺序/循环/条件判断/分叉/等待/并行/函数定义/函数调用)、 动作(移动/旋转/缩放/改属性/播放动画/播放声音/显隐/相机)、变量(创建/赋值/读取)、表达式(算术/比较/逻辑) - **实体选择器**:绑定块(entity 参数 → 实体下拉) - **参数校验**:必填/类型/范围/枚举/标识符/变量引用/函数引用/孤立块/无入口/环形连线 - **编译**:compile_graph → script_engine 可执行脚本(变量提升、函数区、事件入口分支、控制流展开) - **发布**:graph_publish 写入 script_engine 模块(script_type=0) - **模板**:drag_template 表 + template_use 一键生成新画布 ## 数据表(drag 库) | 表 | 说明 | |----|------| | drag_graph | 画布:name / description / blocks(JSON) / connections(JSON) / content(编译产物) / status | | drag_template | 模板:name / category / description / blocks(JSON) / connections(JSON) | ## 目录结构 ``` drag/ ├── drag/ # Python 包 │ ├── __init__.py # 导出(三处同步注册) │ ├── init.py # load_drag() 挂载全部函数 │ ├── blocks.py # 块定义 JSON(唯一事实源) │ ├── validator.py # 图校验器 │ └── compiler.py # 图 → Python 脚本编译器 ├── wwwroot/ # index.ui / editor.ui / drag-canvas.js / drag-editor.js / drag.css / api/*.dspy ├── models/ # drag_graph.json / drag_template.json ├── json/ # drag_graph.json / drag_template.json(CRUD) ├── init/data.json # appcodes(drag_status) ├── scripts/load_path.py # RBAC └── skill/SKILL.md # 模块技能 ``` ## 安装与集成 1. `pip install .` 安装模块包 2. 宿主应用 `app/scense.py`:`from drag.init import load_drag` + `load_drag()`(init() 内) 3. `build.sh`:生成 DDL / CRUD UI / 软链 wwwroot 到宿主 `/d/scense/scense_app/wwwroot/drag` 4. RBAC:执行 `scripts/load_path.py`(或在中心 load_path.py 添加 drag 路径) 5. 菜单:global_menu.ui 增加 `{"name":"drag","label":"拖拽编程","url":"{{entire_url('/drag/index.ui')}}"}` ## 关键接口(load_drag() 挂载) | 函数 | 说明 | |------|------| | get_block_defs_api() | 块定义(categories + blocks) | | graph_save(ns) | 保存画布(新建/更新,保存即校验+编译) | | graph_compile(ns) | 编译 → {success, content, errors} | | graph_validate(ns) | 校验 → {valid, errors, warnings} | | graph_publish(ns) | 发布到 script_engine | | list/get/create/update/delete_drag_graph | 画布 CRUD | | list/get/create/update/delete_drag_template | 模板 CRUD | | template_use(ns) | 模板 → 新画布 | | get_entity_options() | 实体下拉 | ## 编译产物示例 ```python # ===== 变量声明 ===== score = 0 # ===== 函数定义 ===== def jump(): move(entity='hero', x=0, y=100, duration=0.5) # ===== 入口: 开始 ===== if score > 100: play_animation(entity='hero', animation='run') else: wait(seconds=1) ``` ## 陷阱 - 取库名用 `ServerEnv().get_module_dbname('drag')`,禁止硬编码 DBNAME - 块定义修改需同步:blocks.py(唯一源)+ 前端渲染自动读取(block_defs.dspy 下发) - .dspy 无 import/print/uuid/f-string,显式 return;helper 全部在 init.py - `sor.C/U/D/R` 只有这四个 sqlor 方法;分页用 `sor.sqlPaging`,不硬编码 LIMIT - 保存时 `created_at/updated_at = curDateString()`,否则 sor.C 静默丢记录 - 编译产物仅含白名单语法(赋值/def/for/while/if-else/函数调用/注释),供 script_engine 白名单执行 - 事件块无输入端口 = 天然入口;事件块不能作为连线目标 - 动态端口(分叉/并行)由 `dynamic_outputs` 声明,按 branch_count 生成输出端口