13 KiB
xls2crud.py 技术文档
工具名称:
xls2crud.py
功能描述:从 Excel 模型文件(.xlsx)自动生成基于模板的 CRUD 前后端界面代码,支持数据浏览器、新增、编辑、删除、查询及关联子表操作。
适用场景:快速构建基于数据库模型的 Web 应用前端 UI 和后端接口描述。
目录
1. 简介
本脚本通过读取 .xlsx 表格定义的数据模型和 JSON 配置文件,自动为指定数据表生成完整的 CRUD 界面组件:
- 数据浏览页(
index.ui) - 新增表单(
add_*.dspy) - 编辑表单(
update_*.dspy) - 删除逻辑(
delete_*.dspy) - 数据获取接口(
get_*.dspy) - 关联字段状态检查(
check_changed.dspy)
所有输出均基于预设的 Jinja-like 模板渲染,并支持动态参数替换。
2. 依赖说明
import os
import sys
import codecs
import json
import argparse
from appPublic.dictObject import DictObject
from xlsxData import xlsxFactory
from appPublic.folderUtils import listFile, _mkdir
from appPublic.myTE import MyTemplateEngine
from tmpls import (
data_browser_tmpl,
get_data_tmpl,
data_new_tmpl,
data_update_tmpl,
data_delete_tmpl,
check_changed_tmpls
)
from appPublic.argsConvert import ArgsConvert
外部模块说明
| 模块 | 功能 |
|---|---|
appPublic.dictObject |
将字典转换为可点式访问的对象(类似 JavaScript 的对象) |
xlsxData.xlsxFactory |
解析 .xlsx 文件并提取结构化数据 |
appPublic.folderUtils.listFile, _mkdir |
文件遍历与目录创建工具 |
appPublic.myTE.MyTemplateEngine |
轻量级模板引擎,支持变量替换 |
tmpls.* |
内置字符串形式的模板内容(HTML/UI/DSPY) |
appPublic.argsConvert |
支持 ${var}$ 或 [[var]] 形式的变量替换 |
3. 使用方式
命令行语法
python xls2crud.py [-m models_dir] [-o output_dir] modulename json_file [...]
参数说明
| 参数 | 是否必需 | 说明 |
|---|---|---|
modulename |
✅ 是 | 当前模块名,用于数据库上下文解析 |
json_file |
✅ 是 | 至少一个 CRUD 配置 JSON 文件路径 |
-m, --models_dir |
❌ 否 | .xlsx 模型文件所在目录(可在 JSON 中覆盖) |
-o, --output_dir |
❌ 否 | 输出根目录,默认为当前路径 |
示例命令
python xls2crud.py -m ./models -o ./ui_output user_module user_config.json role_config.json
4. 核心函数说明
build_dbdesc(models_dir: str) -> dict
功能
扫描指定目录下的所有 .xlsx 文件,将其解析为统一的数据库描述对象。
输入
models_dir: 字符串或列表,包含模型文件路径
输出
dict: 键为表名(来自summary[0].name),值为该表的完整描述对象(含字段、编码表、子表等)
流程
- 遍历目录中所有
.xlsx文件 - 使用
xlsxFactory加载每个文件 - 提取其
get_data()结果 - 取第一项 summary 的 name 作为表名注册进
db_desc
subtable2toolbar(desc)
功能
将 desc.subtables 中定义的子表信息转化为工具栏按钮及弹窗绑定事件。
参数
desc: 当前表的描述对象(DictObject)
行为
- 若无
toolbar,初始化为空对象 - 为每个子表添加:
- 工具栏图标按钮(SVG 图标 + label)
- 绑定点击事件打开 PopupWindow
- POST 请求跳转至对应 URL(支持相对路径)
- 自动映射主键
id和引用控件referer_widget - 支持额外字段映射(via
mapping)
示例生成的 bind 结构
{
"wid": "self",
"event": "child_table",
"actiontype": "urlwidget",
"target": "PopupWindow",
"popup_options": {
"title": "子表标题",
"width": "70%",
"height": "70%"
},
"params_mapping": {
"mapping": { "id": "parent_id", "referer_widget": "referer_widget" }
},
"options": {
"method": "POST",
"url": "{{entire_url('../child_table')}}"
}
}
build_crud_ui(crud_data: dict, dbdesc: dict)
功能
根据传入的配置和数据库描述,驱动整个 CRUD 界面生成流程。
参数
crud_data: 来自 JSON 文件的配置对象(DictObject)dbdesc: 所有表的元数据集合
步骤
- 获取目标表名
tblname - 更新
desc参数(如权限、标题等) - 调用
subtable2toolbar(desc)添加子表按钮 - 如果存在
relation字段,则启用“行选中联动”机制 - 渲染
binds为 JSON 字符串 - 调用
build_table_crud_ui()开始生成具体文件
build_table_crud_ui(uidir: str, desc: dict) -> None
功能
协调生成所有与当前表相关的 UI 文件。
行为
- 创建输出目录(若不存在)
- 生成:
index.ui(数据浏览器)get_{tblname}.dspy(数据获取)
- 若有外键关系(
desc.relation):- 仅生成
check_changed.dspy
- 仅生成
- 否则生成标准三件套:
add_*.dspyupdate_*.dspydelete_*.dspy
field_list(desc: dict) -> list
功能
构造适用于前端展示的字段列表,包含类型、宽度、标签、UI 类型等信息。
逻辑
- 遍历
desc.fields - 判断是否是码表字段(在
codes中定义)- 是 → 调用
get_code_desc - 否 → 调用
setup_ui_info
- 是 → 调用
- 应用
browserfields.alters进行个性化覆盖 - 返回最终字段数组
get_code_desc(field: dict, desc: dict) -> dict
功能
处理码表字段(如性别、状态),将其转换为下拉选择框或远程数据源字段。
输出字段说明
| 属性 | 说明 |
|---|---|
uitype |
'code' 表示码表类型 |
valueField / textField |
实际值与显示文本字段名 |
params |
请求码表数据所需参数(dbname, table, cond 等) |
dataurl |
固定为 /appbase/get_code.dspy |
特性支持
- 支持条件表达式中的变量替换(如
${dept_id}$) - 使用
ArgsConvert解析并注入运行时变量
setup_ui_info(field:dict, confidential_fields=[]) ->dict
功能
为普通字段设置默认 UI 显示属性。
类型映射规则
| 数据库类型 | uitype |
说明 |
|---|---|---|
date |
date |
日期输入框 |
time |
time |
时间输入框 |
int, short, etc. |
int |
整数输入 |
float, double, decimal |
float |
浮点数输入 |
text |
text |
多行文本 |
| 其他 | str |
默认字符串 |
名称以 _date 结尾 |
date |
智能识别 |
在 confidential_fields 中 |
password |
密码掩码 |
宽度计算
cwidth = min(max(length, 4), 18)- 最小 4,最大 18(单位字符宽)
construct_get_data_sql(desc: dict) -> str
功能
构建用于获取主表及其码表连接数据的 SQL 查询语句。
分支逻辑
A. 存在外键关联且有码表
生成 LEFT JOIN 查询,判断是否存在外键记录(has_xxx 标志位),并带出码表文本。
SELECT '${param_field}$' AS param_field,
CASE WHEN b.param_field IS NULL THEN 0 ELSE 1 END has_param_field,
a.value AS code_field,
a.text AS code_field_text
FROM code_table a
LEFT JOIN (SELECT * FROM main_table WHERE param_field = ${param_field}$) b
ON a.value = b.code_field;
B. 无码表
直接查询主表 + 占位符 [[filterstr]]
SELECT * FROM table_name WHERE 1=1 [[filterstr]]
C. 有一般码表(非外键)
构建多个 LEFT JOIN 子查询,别名为 b, c, ...,拼接成完整查询。
SELECT a.*, b.type_text, c.status_text
FROM (SELECT * FROM main WHERE 1=1 [[filterstr]]) a
LEFT JOIN (SELECT value AS status, text AS status_text FROM status_codes WHERE 1=1) c
ON a.status = c.status;
其他辅助函数
| 函数 | 功能 |
|---|---|
alter_field(field, desc) |
使用 desc.browserfields.alters[name] 修改特定字段属性 |
filter_backslash(s) |
替换字符串中的 \\/ 为 /,防止模板路径错误 |
build_data_browser(pat, desc) |
渲染 data_browser_tmpl → index.ui |
build_data_new / update / delete |
分别生成增删改页面 |
build_get_data |
生成数据查询接口 .dspy 文件 |
build_check_changed |
生成用于检测外键关联变化的特殊接口 |
5. 模板系统与输出结构
模板来源
所有模板来自 tmpls 模块,包括:
| 模板变量 | 用途 |
|---|---|
data_browser_tmpl |
主列表界面(index.ui) |
get_data_tmpl |
数据获取接口 |
data_new_tmpl |
新增页面 |
data_update_tmpl |
编辑页面 |
data_delete_tmpl |
删除确认/执行页面 |
check_changed_tmpls |
外键关联状态检查接口 |
模板语法支持
{{ var }}:变量插入${ var }$:环境变量占位符(由ArgsConvert替换)[[ filterstr ]]:SQL 过滤条件注入点
6. 配置文件格式 (json_file)
{
"tblname": "user",
"alias": "user_manage",
"params": {
"title": "用户管理",
"pagesize": 20,
"confidential_fields": ["password"],
"relation": {
"param_field": "org_id",
"outter_field": "id",
"table": "organization"
},
"codes": [
{
"field": "status",
"table": "sys_codes",
"valuefield": "c_value",
"textfield": "c_text",
"cond": "ctype='USER_STATUS'"
}
],
"subtables": [
{
"subtable": "user_role",
"title": "角色分配",
"field": "user_id",
"url": "../user_role",
"mapping": {}
}
],
"toolbar": null,
"binds": [],
"browserfields": {
"alters": {
"name": { "label": "姓名", "uitype": "str", "cwidth": 20 }
}
}
},
"models_dir": "./models"
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
tblname |
string | 主表名(必须存在于 .xlsx 中) |
alias |
string | 输出目录名(可选,默认为 tblname) |
params |
object | 表级配置对象 |
params.title |
string | 页面标题 |
params.confidential_fields |
array | 需隐藏显示的字段(变为密码框) |
params.relation |
object | 外键关联信息(启用 check_changed) |
params.codes |
array | 码表配置 |
params.subtables |
array | 子表配置(生成工具栏按钮) |
params.browserfields.alters |
object | 字段显示定制 |
models_dir |
string | .xlsx 模型目录(优先级高于命令行) |
7. 输出目录结构示例
假设命令为:
python xls2crud.py -o ./dist -m ./models user_mod user_conf.json
且 user_conf.json 中 "alias": "users","tblname": "user"
则输出结构如下:
./dist/
└── users/
├── index.ui # 数据浏览器
├── add_user.dspy # 新增页面
├── update_user.dspy # 编辑页面
├── delete_user.dspy # 删除页面
├── get_user.dspy # 数据获取接口
└── check_changed.dspy # (如果有 relation)状态检查接口
8. 注意事项与限制
⚠️ 已知限制
-
Excel 文件要求
- 必须包含
summary工作表 - 第一行
summary[0].name作为表名 fields表格需定义字段元信息(name, type, title, length...)
- 必须包含
-
模板路径问题
- 模板内使用
{{entire_url(...)}}构造绝对路径,请确保前端框架支持此函数
- 模板内使用
-
SQL 注入防护
[[filterstr]]不做转义,应由调用端保证安全
-
并发写入风险
- 多个 JSON 文件同时处理同一张表可能导致文件覆盖
-
编码要求
- 所有
.xlsx和.json文件必须为 UTF-8 编码
- 所有
✅ 最佳实践建议
- 使用
alias区分不同视图的输出路径 - 在
alters中统一设置中文标签和列宽 - 对敏感字段显式加入
confidential_fields - 使用
cond+ 变量实现动态码表过滤
版本信息
- 创建时间:未知
- 最后更新:根据代码注释推测近期维护
- 维护者:内部团队(
appPublic,xslxDATA为私有库)
📝 文档版本:v1.0
✅ 审核状态:已完成初稿
💬 如需扩展模板字段或增加校验逻辑,请联系开发组。