approve: 测试执行 - world_snapshot 世界快照模块
This commit is contained in:
parent
7ff253692c
commit
b2db4764e2
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,10 +1,21 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
*$py.class
|
||||
|
||||
# Distribution / packaging
|
||||
build/
|
||||
dist/
|
||||
models/mysql.ddl.sql
|
||||
wwwroot/entity/
|
||||
wwwroot/entity_import/
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
|
||||
# CRUD-generated wwwroot subdirs (build artifacts, regenerated by build.sh)
|
||||
wwwroot/entity_list/
|
||||
wwwroot/entity_import_list/
|
||||
|
||||
# Editors / OS
|
||||
*.swp
|
||||
*.swo
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
59
README.md
59
README.md
@ -1,35 +1,44 @@
|
||||
# entity 模块
|
||||
# entity 模块(W-03 实体管理)
|
||||
|
||||
实体管理模块,含实体导入。依赖 `world`、`scene` 模块。
|
||||
实体管理模块:实体表(entity)增删改查、分页列表查询、实体文件导入(entity_import 记录表)。依赖 world、scene(逻辑关联,不加物理外键)。
|
||||
|
||||
## 功能
|
||||
- 实体表(entity)CRUD:实体名称/编码/所属世界/所属场景/实体类型/状态/属性 JSON
|
||||
- 实体导入:上传 CSV/JSON 文件 → 解析 → 逐条校验 → 批量落库 → 写导入记录(entity_import)
|
||||
|
||||
- **实体 CRUD**:create_entity / update_entity / delete_entity / get_entity / list_entities
|
||||
- **列表查询**:sqlPaging 分页 `{list, total}`,支持 world_id / scene_id / entity_type / status / name / code 过滤 + sort / order
|
||||
- **文件导入**:api/entity_import.dspy 接收文件(JSON 数组 / JSON Lines / CSV 表头),事务批量 + 失败整批回滚无脏数据
|
||||
- **字典下拉**:entity_type / entity_status / import_status(appcodes)幂等落库;world / scene 下拉
|
||||
- **REST**:统一前缀 `/api/*`(宿主挂载后 `/entity/api/*.dspy`),错误结构 `{code,message,field,detail}`,分页 `{list,total}`,非法输入 100% 拦截不落库
|
||||
|
||||
## 数据表
|
||||
|
||||
| 表 | 说明 |
|
||||
|----|------|
|
||||
| entity | 实体表 |
|
||||
| entity_import | 实体导入记录表 |
|
||||
| entity | 实体表:id / world_id / scene_id / name / code(唯一) / entity_type / status / attributes_json / created_at / updated_at |
|
||||
| entity_import | 导入记录表(只读):id / world_id / scene_id / file_name / total / success / fail / status / created_at |
|
||||
|
||||
## 目录结构
|
||||
```
|
||||
entity/
|
||||
├── entity/ # Python 包(init.py 定义 load_entity 及业务函数)
|
||||
├── models/ # 表定义(entity.json / entity_import.json)
|
||||
├── json/ # CRUD 定义(entity.json / entity_import.json)
|
||||
├── wwwroot/ # 前端(index.ui / import_page.ui / api/*.dspy)
|
||||
├── init/data.json # appcodes 字典种子(entity_type / entity_status / import_status)
|
||||
├── scripts/load_path.py # RBAC 权限注册
|
||||
└── skill/SKILL.md # 模块技能文档
|
||||
- `entity.scene_id → scene.id`、`entity.world_id → world.id` 均为**逻辑关联**(codes 段配置,不加物理外键)
|
||||
- 编码字典 entity_type / entity_status / import_status 经 init/data.json 幂等落库到 appbase
|
||||
|
||||
## 集成
|
||||
|
||||
1. `load_entity()` 挂载:在宿主应用 `init()` 中调用(函数注册到 ServerEnv,含复数别名)
|
||||
2. wwwroot 符号链接到宿主 wwwroot/entity/
|
||||
3. 运行 `scripts/load_path.py` 注册 RBAC(或同步中央 load_path.py)
|
||||
4. 构建:`json2ddl mysql . > mysql.ddl.sql`(models/)、`xls2ui -m ../models -o ../wwwroot entity *.json`(json/)
|
||||
|
||||
## 关键接口(经 load_entity() 注册)
|
||||
|
||||
- `list_entities(params)` → `{list, total}`
|
||||
- `create_entity(ns)` / `update_entity(ns)` / `delete_entity(ns)` / `get_entity(ns)`
|
||||
- `import_entities({world_id, scene_id, file_name, rows})` → `{success, total, success_count, fail}`
|
||||
- `parse_entity_file(content, filename)` → `{rows: [...]}`
|
||||
- `get_world_options()` / `get_scene_options()` / `get_entity_type_options()` / `get_entity_status_options()` / `get_import_status_options()` → `[{value, text}]`
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
cd modules/entity && pip install .
|
||||
```
|
||||
|
||||
## 集成方式
|
||||
应用入口 `app/{应用名}.py` 中 `from entity.init import load_entity`,并在 `init()` 里调用
|
||||
`load_entity()`。库名由宿主应用的 `get_module_dbname('entity')` 决定,模块内不硬编码。
|
||||
|
||||
## 关键接口
|
||||
- `load_entity()`:挂载模块函数到 ServerEnv
|
||||
- `wwwroot/api/entity_import.dspy`:实体导入(world_id + scene_id + file)
|
||||
- `wwwroot/api/entity_create.dspy` / `entity_update.dspy` / `entity_delete.dspy`:实体 CRUD
|
||||
- `wwwroot/api/get_search_*.dspy`:下拉数据源
|
||||
详见 skill/SKILL.md。
|
||||
|
||||
@ -29,4 +29,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -38,4 +38,4 @@
|
||||
"update_data_url": "{{entire_url('../api/entity_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/entity_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,4 +22,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,4 +30,4 @@
|
||||
{"field": "entity_type", "table": "appcodes_kv", "cond": "parentid='entity_type'", "valuefield": "k", "textfield": "v"},
|
||||
{"field": "status", "table": "appcodes_kv", "cond": "parentid='entity_status'", "valuefield": "k", "textfield": "v"}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -26,4 +26,4 @@
|
||||
{"field": "scene_id", "table": "scene", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "status", "table": "appcodes_kv", "cond": "parentid='import_status'", "valuefield": "k", "textfield": "v"}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ build-backend = "setuptools.build_meta"
|
||||
[project]
|
||||
name = "entity"
|
||||
version = "1.0.0"
|
||||
description = "实体管理模块(W-03)——实体表 CRUD、列表查询、实体文件导入(entity_import 记录)"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["sqlor", "bricks_for_python"]
|
||||
|
||||
|
||||
@ -1,63 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""entity 模块 RBAC 权限注册脚本(显式路径,禁止通配符)。"""
|
||||
"""entity 模块 RBAC 权限注册(scripts/load_path.py)。
|
||||
|
||||
每次代码变更如有新 path 出现,需同步更新本脚本。禁止通配符,路径全显式。
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
MODULE = 'entity'
|
||||
|
||||
PATHS_ANY = []
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 显式路径清单(禁止 % / * 通配符)
|
||||
PATHS_LOGINED = [
|
||||
'/%s' % MODULE,
|
||||
'/%s/index.ui' % MODULE,
|
||||
'/%s/import_page.ui' % MODULE,
|
||||
'/%s/entity' % MODULE,
|
||||
'/%s/entity/index.ui' % MODULE,
|
||||
'/%s/entity/get_entity.dspy' % MODULE,
|
||||
'/%s/entity/add_entity.dspy' % MODULE,
|
||||
'/%s/entity/update_entity.dspy' % MODULE,
|
||||
'/%s/entity/delete_entity.dspy' % MODULE,
|
||||
'/%s/entity_import' % MODULE,
|
||||
'/%s/entity_import/index.ui' % MODULE,
|
||||
'/%s/entity_import/get_entity_import.dspy' % MODULE,
|
||||
'/%s/api/entity_import.dspy' % MODULE,
|
||||
'/%s/api/entity_create.dspy' % MODULE,
|
||||
'/%s/api/entity_update.dspy' % MODULE,
|
||||
'/%s/api/entity_delete.dspy' % MODULE,
|
||||
'/%s/api/entity_import_guard.dspy' % MODULE,
|
||||
'/%s/api/get_search_world_id.dspy' % MODULE,
|
||||
'/%s/api/get_search_scene_id.dspy' % MODULE,
|
||||
'/%s/api/get_search_entity_type.dspy' % MODULE,
|
||||
'/%s/api/get_search_status.dspy' % MODULE,
|
||||
# 页面
|
||||
'/entity/index.ui',
|
||||
'/entity/import_page.ui',
|
||||
# CRUD 列表(xls2ui 生成的别名目录 5 件套)
|
||||
'/entity/entity_list',
|
||||
'/entity/entity_list/index.ui',
|
||||
'/entity/entity_list/get_entity_list.dspy',
|
||||
'/entity/entity_list/add_entity_list.dspy',
|
||||
'/entity/entity_list/update_entity_list.dspy',
|
||||
'/entity/entity_list/delete_entity_list.dspy',
|
||||
'/entity/entity_import_list',
|
||||
'/entity/entity_import_list/index.ui',
|
||||
'/entity/entity_import_list/get_entity_import_list.dspy',
|
||||
'/entity/entity_import_list/add_entity_import_list.dspy',
|
||||
'/entity/entity_import_list/update_entity_import_list.dspy',
|
||||
'/entity/entity_import_list/delete_entity_import_list.dspy',
|
||||
# REST API(统一 /api/* 前缀)
|
||||
'/entity/api/entity_list.dspy',
|
||||
'/entity/api/entity_get.dspy',
|
||||
'/entity/api/entity_create.dspy',
|
||||
'/entity/api/entity_update.dspy',
|
||||
'/entity/api/entity_delete.dspy',
|
||||
'/entity/api/entity_import.dspy',
|
||||
'/entity/api/get_search_world_id.dspy',
|
||||
'/entity/api/get_search_scene_id.dspy',
|
||||
'/entity/api/get_search_entity_type.dspy',
|
||||
'/entity/api/get_search_status.dspy',
|
||||
'/entity/api/get_search_import_status.dspy',
|
||||
]
|
||||
|
||||
|
||||
def find_sage_root():
|
||||
candidates = [
|
||||
os.path.expanduser('~/repos/sage'),
|
||||
os.path.expanduser('~/sage'),
|
||||
]
|
||||
for c in candidates:
|
||||
if os.path.isdir(os.path.join(c, 'wwwroot')) and os.path.isdir(os.path.join(c, 'py3', 'bin')):
|
||||
return c
|
||||
return None
|
||||
PATHS_ANY = []
|
||||
|
||||
|
||||
def main():
|
||||
sage_root = find_sage_root()
|
||||
if not sage_root:
|
||||
print('Sage root not found, skip entity RBAC registration')
|
||||
sys.path.insert(0, HERE)
|
||||
try:
|
||||
from set_role_perm import set_role_perm
|
||||
except Exception:
|
||||
# 找不到 set_role_perm 时回退到中央 sage/load_path.py 已注册的路径,仅提示
|
||||
print('WARN: set_role_perm 不可用,请确保中央 load_path.py 已包含上述路径')
|
||||
return
|
||||
sys.path.insert(0, sage_root)
|
||||
from set_role_perm import set_role_perm # noqa: E402
|
||||
|
||||
for p in PATHS_ANY:
|
||||
set_role_perm(p, 'any')
|
||||
for p in PATHS_LOGINED:
|
||||
set_role_perm(p, 'logined')
|
||||
print('entity RBAC registered: %d any + %d logined' % (len(PATHS_ANY), len(PATHS_LOGINED)))
|
||||
for path in PATHS_LOGINED:
|
||||
set_role_perm(path, 'logined')
|
||||
for path in PATHS_ANY:
|
||||
set_role_perm(path, 'any')
|
||||
print(f'entity: registered {len(PATHS_LOGINED)} logined + {len(PATHS_ANY)} any paths')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@ -1,29 +1,75 @@
|
||||
---
|
||||
name: entity
|
||||
description: entity 模块技能文档——实体管理(实体表 CRUD + 实体导入 entity_import + api/entity_import.dspy),通过 load_entity() 挂载。
|
||||
description: 实体管理模块(W-03)——实体表(entity)CRUD、列表查询、实体文件导入(entity_import 记录),通过 load_entity() 挂载。
|
||||
---
|
||||
|
||||
# entity 模块
|
||||
|
||||
## 概述
|
||||
实体管理模块,提供实体表(entity)增删改查与文件导入(entity_import 记录表)。依赖 world、scene 模块。
|
||||
实体管理模块,提供实体表(entity)增删改查与文件导入(entity_import 记录表)。依赖 world、scene 模块(逻辑关联,不加物理外键)。
|
||||
|
||||
## 数据模型
|
||||
- `entity`:id / world_id(→world.id)/ scene_id(→scene.id)/ name / code / entity_type(appcodes entity_type)/ status(appcodes entity_status)/ attributes_json(text) / created_at / updated_at。唯一索引 code。
|
||||
- `entity_import`:id / world_id / scene_id / file_name / total / success / fail / status(appcodes import_status)/ created_at。只读记录。
|
||||
|
||||
## 关键接口
|
||||
- `load_entity()`:注册 create_entity/update_entity/delete_entity/entity_import 到 ServerEnv(含复数别名)。
|
||||
- `wwwroot/api/entity_import.dspy`:文件导入 → 解析(JSON 数组或 CSV 表头)→ 逐条校验 name/code → sor.C('entity') → 统计 → sor.C('entity_import')。返回 `{success,total,success_count,fail,file_name}`。
|
||||
- `wwwroot/api/entity_{create,update,delete}.dspy`:实体 CRUD(委托 init.py 函数)。
|
||||
- `wwwroot/api/get_search_{world_id,scene_id,entity_type,status}.dspy`:下拉数据源。
|
||||
### 表 `entity`(models/entity.json)
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | str(32) | 主键 |
|
||||
| world_id | str(32) | 所属世界(→world.id 逻辑关联) |
|
||||
| scene_id | str(32) | 所属场景(→scene.id 逻辑关联,codes 段配置,不加物理外键) |
|
||||
| name | str(255) | 实体名称 |
|
||||
| code | str(64) | 实体编码(唯一,idx_entity_code) |
|
||||
| entity_type | str(16) | 实体类型(appcodes entity_type) |
|
||||
| status | str(16) | 状态(appcodes entity_status) |
|
||||
| attributes_json | text | 属性 JSON |
|
||||
| created_at | timestamp | 创建时间 |
|
||||
| updated_at | timestamp | 更新时间 |
|
||||
|
||||
- 主键 `["id"]`;唯一索引 `idx_entity_code(code)`;索引 `idx_entity_world(world_id)`、`idx_entity_scene(scene_id)`
|
||||
- codes:world_id → world(id/name)、scene_id → scene(id/name)、entity_type → appcodes_kv(parentid='entity_type')、status → appcodes_kv(parentid='entity_status')
|
||||
|
||||
### 表 `entity_import`(models/entity_import.json,只读)
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | str(32) | 主键 |
|
||||
| world_id | str(32) | 目标世界 |
|
||||
| scene_id | str(32) | 目标场景 |
|
||||
| file_name | str(255) | 导入文件名 |
|
||||
| total / success / fail | int | 总数 / 成功 / 失败(默认 0) |
|
||||
| status | str(16) | 导入状态(appcodes import_status:0 进行中/1 成功/2 失败) |
|
||||
| created_at | timestamp | 导入时间 |
|
||||
|
||||
- 主键 `["id"]`;索引 `idx_entity_import_world(world_id)`;**只读记录,不可手工增删改**
|
||||
|
||||
## 关键接口(load_entity() 挂载到 ServerEnv)
|
||||
- `list_entities(params)` → `{list, total}`(sqlPaging 分页,支持 world_id/scene_id/entity_type/status/name/code 过滤 + sort/order)
|
||||
- `get_entity({id})` → `{data}` 或错误
|
||||
- `create_entity(ns)` → 校验 + world/scene 存在校验 + 自动 id/created_at/updated_at;返回 `{success, id}`
|
||||
- `update_entity(ns)` → 写 updated_at、剔除 `_text` 后缀、world/scene 校验
|
||||
- `delete_entity({id})` → `{success, id}`
|
||||
- `import_entities({world_id, scene_id, file_name, rows})` → `{success, total, success_count, fail, file_name}`
|
||||
(**事务批量**:先全量解析+校验(含文件内编码查重),再逐条插入;任一条失败整批回滚,无脏数据;success_count 避免与布尔 success 冲突)
|
||||
- `parse_entity_file(content, filename)` → `{rows: [...]}`(JSON 数组 / JSON Lines / CSV 表头)
|
||||
- `get_world_options()` / `get_scene_options()` / `get_entity_type_options()` / `get_entity_status_options()` / `get_import_status_options()` → `[{value, text}]`
|
||||
|
||||
### wwwroot/api/*.dspy(REST 统一前缀 /api/*,宿主挂载后为 /entity/api/*.dspy)
|
||||
- `entity_list.dspy`(分页 `{list,total}`)、`entity_get.dspy`、`entity_create.dspy`、`entity_update.dspy`、`entity_delete.dspy`
|
||||
- `entity_import.dspy`(world_id + file → parse_entity_file → import_entities)
|
||||
- `get_search_world_id.dspy` / `get_search_scene_id.dspy` / `get_search_entity_type.dspy` / `get_search_status.dspy` / `get_search_import_status.dspy`(字典下拉)
|
||||
|
||||
### 错误结构(统一)
|
||||
`{code, message, field, detail}`;code:PARAM_REQUIRED / FIELD_REQUIRED / FIELD_TOO_LONG /
|
||||
WORLD_NOT_FOUND / SCENE_NOT_FOUND / DUPLICATE_CODE / PARSE_ERROR / INVALID_JSON / DB_ERROR / NOT_FOUND。
|
||||
非法输入 100% 拦截不落库。
|
||||
|
||||
## 陷阱
|
||||
- 库名禁止硬编码:.py 用 `ServerEnv().get_module_dbname('entity')`,.dspy 用 `get_module_dbname('entity')` / `get_sor_context(request._run_ns, 'entity')`。
|
||||
- dspy 内禁止 import(仅 `from sqlor.filter import DBFilter` 例外),业务逻辑放 `entity/init.py` 并通过 `load_entity()` 导出。
|
||||
- `sor.C()` 不会自动填 created_at,必须显式 `ns['created_at'] = curDateString()`。
|
||||
- 实体导入记录(entity_import)只读,不可手工增删改。
|
||||
- 库名禁止硬编码:.py 用 `ServerEnv().get_module_dbname("entity")`,.dspy 用 `get_module_dbname("entity")`(全局);world/scene 表分别在 world/scene 模块库(`get_module_dbname("world"/"scene")`),appcodes 在 `get_module_dbname("appbase")`
|
||||
- dspy 无 import(json/get_sor_context/debug/params_kw 均预加载全局);函数经 load_entity() 注册后为全局,直接调用
|
||||
- `create_entity` / `import_entities` 必须显式设置 `created_at = curDateString()`,否则 sor.C 静默丢记录
|
||||
- `entity_import` 记录 total/success/fail 必须为 int
|
||||
- `import_entities` 返回用 `success_count`(避免与布尔 `success` 键冲突)
|
||||
- 三处同步注册:entity/__init__.py 导出 ← entity/init.py 实现 ← load_entity() 注册(含复数别名 create_entities/update_entities/delete_entities)
|
||||
- 返回 `{list,total}` 分页用 `sor.sqlPaging`,不要硬编码 LIMIT/OFFSET
|
||||
- CRUD json `new_data_url` 指向自定义 `api/entity_create.dspy`;entity_import 为只读列表,无 editable
|
||||
|
||||
## 依赖
|
||||
- world、scene(下拉数据源 world / scene 表)
|
||||
- 基础:sqlor、ahserver、bricks、appbase(appcodes)
|
||||
- world(world_id 逻辑关联 + 世界下拉)、scene(scene_id 逻辑关联 + 场景下拉)、appbase(appcodes 字典)、rbac(权限)
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"label": "实体导入", "fontSize": "24px"}},
|
||||
{"widgettype": "Text", "options": {"label": "支持 CSV(首行表头)或 JSON 数组,字段:name、code、entity_type、status、attributes_json"}},
|
||||
{"widgettype": "Form", "options": {
|
||||
"title": "导入实体数据",
|
||||
"submit_url": "{{entire_url('/entity/api/entity_import.dspy')}}",
|
||||
"method": "POST",
|
||||
"fields": [
|
||||
{"name": "world_id", "label": "目标世界", "uitype": "code", "required": true,
|
||||
"dataurl": "{{entire_url('/entity/api/get_search_world_id.dspy')}}"},
|
||||
{"name": "scene_id", "label": "目标场景", "uitype": "code",
|
||||
"dataurl": "{{entire_url('/entity/api/get_search_scene_id.dspy')}}"},
|
||||
{"name": "file", "label": "导入文件", "uitype": "file", "required": true}
|
||||
]
|
||||
}}
|
||||
]
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"label": "实体文件导入", "fontSize": "20px"}},
|
||||
{"widgettype": "Text", "options": {"label": "支持 JSON 数组 / JSON Lines / CSV(表头 name,code,world_id,scene_id,entity_type,status,attributes_json)。事务批量:任一条校验失败整批回滚,无脏数据。"}},
|
||||
{"widgettype": "Form",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"submit_url": "{{entire_url('/entity/api/entity_import.dspy')}}",
|
||||
"method": "POST",
|
||||
"fields": [
|
||||
{"name": "world_id", "label": "目标世界ID", "uitype": "code", "dataurl": "{{entire_url('/entity/api/get_search_world_id.dspy')}}"},
|
||||
{"name": "scene_id", "label": "目标场景ID", "uitype": "code", "dataurl": "{{entire_url('/entity/api/get_search_scene_id.dspy')}}"},
|
||||
{"name": "file_name", "label": "文件名", "uitype": "text"},
|
||||
{"name": "file", "label": "导入文件", "uitype": "file", "required": true}
|
||||
],
|
||||
"buttons": [{"label": "开始导入", "type": "submit"}]
|
||||
}}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,22 +1,19 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"label": "实体管理", "fontSize": "24px"}},
|
||||
{"widgettype": "ResponsableBox", "options": {"gap": "16px", "minWidth": "250px"}, "subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content",
|
||||
"options": {"url": "{{entire_url('/entity/entity/index.ui')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "实体列表"}}]},
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content",
|
||||
"options": {"url": "{{entire_url('/entity/import_page.ui')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "实体导入"}}]},
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content",
|
||||
"options": {"url": "{{entire_url('/entity/entity_import/index.ui')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "导入记录"}}]}
|
||||
]},
|
||||
{"widgettype": "VBox", "id": "app.entity_content", "options": {"width": "100%", "flex": "1", "marginTop": "20px"}}
|
||||
]
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"label": "实体管理(W-03)", "fontSize": "24px"}},
|
||||
{"widgettype": "ResponsableBox", "options": {"gap": "16px", "minWidth": "250px"}, "subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content", "mode": "replace", "options": {"url": "{{entire_url('/entity/entity_list')}}"}}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "实体列表"}}]},
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content", "mode": "replace", "options": {"url": "{{entire_url('/entity/import_page.ui')}}"}}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "实体导入"}}]},
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.entity_content", "mode": "replace", "options": {"url": "{{entire_url('/entity/entity_import_list')}}"}}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "导入记录"}}]}
|
||||
]},
|
||||
{"widgettype": "VBox", "id": "app.entity_content", "options": {"width": "100%", "flex": "1", "marginTop": "20px"}}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user