# `xls2crud` 工具技术文档

> **用途**：将基于 JSON 描述的数据库模型和 CRUD 配置文件转换为前端 UI 代码（CRUD 或树形结构界面）。

---

## 概述

本脚本是一个命令行工具，用于根据指定的模型目录和 JSON 配置文件生成前端 CRUD（增删改查）或树形结构（Tree）UI 所需的代码。它通过解析 Excel 衍生出的 JSON 结构，并结合环境变量与模板机制，动态生成对应模块的用户界面代码。

主要功能包括：
- 解析命令行参数
- 加载并处理数据库模型定义
- 处理多个 JSON 格式的 CRUD 配置文件
- 支持环境变量注入（模板替换）
- 分发至不同的 UI 构建器（CRUD / Tree）

---

## 依赖说明

### 第三方/内部模块
| 模块 | 用途 |
|------|------|
| `os`, `sys` | 系统路径、环境变量、退出控制 |
| `argparse` | 命令行参数解析 |
| `json`, `codecs` | JSON 文件读取（支持 UTF-8 编码） |
| `copy` | 对象拷贝 |
| `appPublic.argsConvert.ArgsConvert` | 支持 `${}` 形式模板变量替换 |
| `appPublic.dictObject.DictObject` | 将字典转为可点式访问的对象 |
| `xls2crud.build_dbdesc`, `build_crud_ui` | 构建数据库描述及 CRUD 界面 |
| `singletree.build_tree_ui` | 构建树形结构 UI |

> ⚠️ 注意：`appPublic`、`xls2crud` 和 `singletree` 是项目内部模块，请确保已安装或在 Python 路径中可用。

---

## 命令行使用方式

```bash
python xls2crud.py [-m models_dir] [-o output_dir] modulename [json_file ...]
```

### 参数说明

| 参数 | 必需 | 说明 |
|------|------|------|
| `modulename` | ✅ | 指定生成代码所属的模块名称（通常用于命名空间或路由前缀） |
| `json_file ...` | ✅（至少一个） | 一个或多个描述表单逻辑的 `.json` 配置文件路径 |
| `-m`, `--models_dir` | ❌ | 数据库模型文件所在的目录列表（可多个），用于构建 `dbdesc` 对象 |
| `-o`, `--output_dir` | ❌ | 输出代码的目标根目录，默认行为可能由配置决定 |

> 如果未提供任何 JSON 文件，程序将打印用法提示并退出。

---

## 输入文件格式：CRUD JSON 配置

每个 JSON 文件应遵循如下结构：

```json
{
  "tblname": "user",
  "alias": "usr",                // 可选，作为输出目录名
  "uitype": "crud",              // 或 "tree"
  "params": {
    "title": "用户管理",
    "fields": [...],
    "actions": [...]
  }
}
```

### 字段说明

| 字段 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `tblname` | string | ✅ | 关联的数据表名 |
| `alias` | string | ❌ | 输出目录名称；若不存在则使用 `tblname` |
| `uitype` | string | ❌ | UI 类型，可选 `"crud"`（默认）或 `"tree"` |
| `params` | object | ✅ | 包含具体 UI 参数的对象，如标题、字段、操作等 |
| `params.modulename` | string | ❌ | 自动生成，由命令行传入 |
| `params.tblname` | string | ❌ | 自动生成，来自 `tblname` 字段 |

> 💡 支持模板语法 `${VAR_NAME}`，会从环境变量中自动替换。

---

## 环境变量支持（模板替换）

系统使用 `ArgsConvert('${', '}$')` 实现对配置中的 `${...}` 占位符进行替换。

例如：

```json
{
  "params": {
    "api_base": "${API_HOST}/v1/data"
  }
}
```

若环境变量中有 `API_HOST=http://localhost:8000`，则最终值会被替换为：

```json
"api_base": "http://localhost:8000/v1/data"
```

所有当前进程的环境变量均会被加载进命名空间 `ns` 中供替换使用。

---

## 核心流程说明

### 1. 参数解析
使用 `argparse` 解析输入参数，获取：
- 模型目录 (`models_dir`)
- 输出目录 (`output_dir`)
- 模块名 (`modulename`)
- 一个或多个 JSON 配置文件路径

### 2. 构建数据库描述对象 (`dbdesc`)
调用 `build_dbdesc(models_dir)` 从模型目录中提取数据库元信息（如表结构、字段类型等），结果封装为 `DictObject` 以便点式访问。

```python
dbdesc = DictObject(**dbdesc)
```

### 3. 遍历处理每个 JSON 文件
对每一个 JSON 文件执行以下步骤：

#### a. 读取并解析 JSON
使用 `codecs.open(..., 'utf-8')` 安全读取 UTF-8 编码内容，防止中文乱码。

#### b. 模板变量替换
利用 `ArgsConvert` 将 `${VAR}` 替换为环境变量实际值。

#### c. 转换为 `DictObject`
便于后续通过 `obj.field.subfield` 访问嵌套属性。

#### d. 设置输出目录
```python
crud_data.output_dir = os.path.join(args.output_dir, tblname)
```
以表名或别名为子目录创建独立输出路径。

#### e. 注入模块名和表名到 params
```python
crud_data.params.modulename = args.modulename
crud_data.params.tblname = crud_data.tblname
```

#### f. 克隆数据库描述
避免污染原始 `dbdesc`，每次传递副本：
```python
db = dbdesc.copy()
```

#### g. 分发 UI 构建任务
根据 `uitype` 决定调用哪个构建函数：

| `uitype` | 函数调用 |
|---------|--------|
| `"tree"` | `build_tree_ui(crud_data, db)` |
| 其他（默认） | `build_crud_ui(crud_data, db)` |

---

## 示例运行命令

```bash
python xls2crud.py \
  -m ./models/user -m ./models/shared \
  -o ./output \
  admin \
  ./crud_configs/user.json \
  ./crud_configs/org.json
```

输出结构示例：
```
./output/
├── user/
│   ├── form.js
│   ├── list.vue
│   └── api.js
└── org/
    ├── form.js
    ├── list.vue
    └── api.js
```

---

## 错误处理

- 若未提供 JSON 文件，输出帮助信息并退出码为 `1`
- 若文件无法打开或 JSON 格式错误，抛出异常（建议添加 try-except 包裹增强健壮性）

---

## 待改进点（建议）

| 问题 | 建议 |
|------|------|
| 缺少异常捕获 | 添加 `try...except` 处理文件读取、JSON 解析错误 |
| 日志输出简单 | 使用 `logging` 模块替代 `print()` |
| 输出目录自动创建 | 添加 `os.makedirs(crud_data.output_dir, exist_ok=True)` |
| 更详细的帮助信息 | 在 `ArgumentParser` 中补充描述 |

---

## 总结

该脚本是自动化生成前端管理界面的重要一环，适用于基于模型驱动开发的企业级后台系统。通过灵活的配置与模板机制，实现了高复用性和可维护性。

---

## 附录：完整调用链示意图

```
[CLI Input]
     ↓
argparse → args(models_dir, output_dir, modulename, files[])
     ↓
build_dbdesc(models_dir) → dbdesc (DictObject)
     ↓
for each file in files:
   read JSON → a
   ac.convert(a, env) → 替换 ${} 变量
   a → DictObject → crud_data
   crud_data.output_dir = output_dir/tblname
   crud_data.params.{modulename,tblname} = ...
   db = dbdesc.copy()
   if uitype == 'tree':
       build_tree_ui(crud_data, db)
   else:
       build_crud_ui(crud_data, db)
```

--- 

✅ **版本**：1.0  
📅 **最后更新**：2025-04-05