diff --git a/.cursor/plans/修复dm列注释读取_2231760d.plan.md b/.cursor/plans/修复dm列注释读取_2231760d.plan.md new file mode 100644 index 0000000..676fcac --- /dev/null +++ b/.cursor/plans/修复dm列注释读取_2231760d.plan.md @@ -0,0 +1,50 @@ +--- +name: 修复DM列注释读取 +overview: 修复达梦数据库列注释(COMMENT)未被正确读取的问题,导致管理端所有字段显示为数据库字段名而非中文备注。 +todos: + - id: fix-dm-col-comments + content: 修改 makecode.go 列信息查询,列注释改用 USER_COL_COMMENTS 替代 ALL_COL_COMMENTS + status: completed +isProject: false +--- + +# 修复达梦数据库列注释读取失败 + +## 问题分析 + +通过 MCP 查询 `adminDB.json` 确认:所有表的字段 label 全部等于字段名本身(如 `"id"`, `"name"`, `"phone"`),没有中文备注。但表级 label 正常(如"消息主表"、"用户表")。 + +用户确认数据库字段确实有备注。 + +### 根因 + +[makecode.go](d:\work\hotimev1.5\code\makecode.go) 第 170 行当前的列查询 SQL: + +```sql +FROM ALL_TAB_COLUMNS c +LEFT JOIN ALL_COL_COMMENTS m + ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME AND c.OWNER=m.OWNER +WHERE c.TABLE_NAME='...' AND c.OWNER='ZWBG' +``` + +`LEFT JOIN` 条件中 `c.OWNER=m.OWNER` 要求 `ALL_COL_COMMENTS` 的 `OWNER` 也等于 `'ZWBG'`。但在 DM 中,`ALL_COL_COMMENTS` 的 `OWNER` 值可能与 `ALL_TAB_COLUMNS` 不一致(DM 的 Oracle 兼容视图在 OWNER 语义上可能存在差异),导致 JOIN 匹配不上,`m.COMMENTS` 全部为 NULL。 + +对比:表注释用的 `ALL_TAB_COMMENTS WHERE OWNER='ZWBG'` 能正确返回中文备注,说明表级视图的 OWNER 一致,但列级视图可能不同。 + +## 修复方案 + +修改 [makecode.go](d:\work\hotimev1.5\code\makecode.go) 第 169-171 行,列注释改用 `USER_COL_COMMENTS`(自动使用当前 schema 上下文 ZWBG,无需 OWNER 过滤),列元数据继续用 `ALL_TAB_COLUMNS`(保留 OWNER 过滤防止系统表): + +```go +if db.Type == "dm" || db.Type == "dameng" { + tableInfo = db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must", c.DATA_DEFAULT AS "dflt_value" FROM ALL_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + v.GetString("name") + `' AND c.OWNER='` + strings.ToUpper(db.DBName) + `' ORDER BY c.COLUMN_ID`) +} +``` + +关键变化: + +- `ALL_COL_COMMENTS` 改为 `USER_COL_COMMENTS` +- 去掉 JOIN 中的 `AND c.OWNER=m.OWNER`(`USER_COL_COMMENTS` 没有 OWNER 列) +- WHERE 条件 `c.OWNER='ZWBG'` 保留(作用于 `ALL_TAB_COLUMNS`,防止系统表列混入) + +原理:框架连接 DM 用 `?schema=ZWBG`,`USER_COL_COMMENTS` 自动返回当前 schema(ZWBG)的列注释,绕开了 OWNER 不一致问题。 \ No newline at end of file diff --git a/.cursor/plans/修复dm类型映射大小写_b8477014.plan.md b/.cursor/plans/修复dm类型映射大小写_b8477014.plan.md new file mode 100644 index 0000000..696e517 --- /dev/null +++ b/.cursor/plans/修复dm类型映射大小写_b8477014.plan.md @@ -0,0 +1,81 @@ +--- +name: 修复DM类型映射大小写 +overview: DM 返回的列类型为大写(BIGINT、VARCHAR、INT、TIMESTAMP),但 ColumnDataType 的匹配键是小写且 strings.Contains 区分大小写,导致类型映射全部失败。 +todos: + - id: fix-type-case + content: 修改 makecode.go 类型比较加 strings.ToLower 忽略大小写 + status: completed + - id: add-dm-types + content: 在 config.go 的 ColumnDataType 中追加 DM 特有数据类型映射 + status: completed +isProject: false +--- + +# 修复达梦数据库列类型映射失败 + +## 问题分析 + +通过 MCP 查询 `adminDB.json` 确认:大量字段 type 仍为 DM 原始类型(`BIGINT`、`VARCHAR`、`INT`、`TIMESTAMP`、`DECIMAL`),未映射到框架类型(`number`、`text`、`time`),导致前端编辑页无法识别控件类型。 + +### 根因 + +[config.go](d:\work\hotimev1.5\code\config.go) 第 43-59 行定义的 `ColumnDataType` 映射表键全部为小写: + +```go +var ColumnDataType = map[string]string{ + "int": "number", + "char": "text", + "text": "text", + "time": "time", + // ... +} +``` + +[makecode.go](d:\work\hotimev1.5\code\makecode.go) 第 191-194 行的匹配逻辑使用 `strings.Contains`,**区分大小写**: + +```go +for k, v1 := range ColumnDataType { + if strings.Contains(info.GetString("type"), k) { // "BIGINT" 不包含 "int" + info["type"] = v1 + break + } +} +``` + +DM 返回的类型全部为大写(`INT`、`VARCHAR`、`BIGINT`),而键为小写(`int`、`char`),`strings.Contains("BIGINT", "int")` 返回 `false`,映射全部跳过。 + +## 修复方案 + +### 修改 1:类型比较忽略大小写 - [makecode.go](d:\work\hotimev1.5\code\makecode.go) + +第 192 行,将 `info.GetString("type")` 转为小写再匹配: + +```go +if strings.Contains(strings.ToLower(info.GetString("type")), k) { +``` + +效果验证: + +- `"BIGINT"` -> `"bigint"` -> contains `"int"` -> `"number"` +- `"VARCHAR"` -> `"varchar"` -> contains `"char"` -> `"text"` +- `"INT"` -> `"int"` -> contains `"int"` -> `"number"` +- `"TIMESTAMP"` -> `"timestamp"` -> contains `"time"` -> `"time"` +- `"DECIMAL"` -> `"decimal"` -> contains `"decimal"` -> `"number"` + +### 修改 2:补充 DM 特有类型 - [config.go](d:\work\hotimev1.5\code\config.go) + +在 `ColumnDataType` 中追加 DM 特有的数据类型,确保 CLOB、NUMBER 等也能被识别: + +```go +var ColumnDataType = map[string]string{ + // 现有 MySQL/SQLite 类型... + // DM 特有类型 + "clob": "text", + "number": "number", + "numeric": "number", + "binary": "text", + "boolean": "number", + "bit": "number", +} +``` + diff --git a/.cursor/plans/修复table和edit组件_aafba288.plan.md b/.cursor/plans/修复table和edit组件_aafba288.plan.md new file mode 100644 index 0000000..1465b01 --- /dev/null +++ b/.cursor/plans/修复table和edit组件_aafba288.plan.md @@ -0,0 +1,99 @@ +--- +name: 修复Table和Edit组件 +overview: 修复 Table.vue 的重复请求和搜索防抖问题,以及 Edit.vue 密码改为弹窗独立修改。 +todos: + - id: fix-double-request + content: "Table.vue: 添加 _skipFormWatch 标志,修复 mounted 和 watcher 导致的重复请求" + status: completed + - id: fix-keyword-search + content: "Table.vue: 关键字搜索改为点击按钮/Enter触发,不再实时触发" + status: completed + - id: fix-password-dialog + content: "Edit.vue: 密码字段改为弹窗独立修改,主表单提交排除密码数据" + status: completed +isProject: false +--- + +# 修复 Table 重复请求、搜索防抖、Edit 密码弹窗 + +## 1. 修复重复请求 - [Table.vue](d:\work\myst-web\src\components\Table.vue) + +**根因**:`mounted()` 中既修改了 form 属性(触发 deep watcher),又显式调用了 `formDebounce`,两者可能各触发一次请求。 + +**方案**:在 data 中增加 `_skipFormWatch: true` 标志,mounted 中所有 form 初始化完成后调用一次 `formDebounce`,然后在 `$nextTick` 中将标志设为 false。watcher 中检查该标志,初始化期间跳过。 + +```javascript +// data 中 +_skipFormWatch: true, + +// watcher 中 +form: { + handler() { + if (this._skipFormWatch) return; + this.formDebounce(this); + }, + deep: true +} + +// mounted 末尾 +this.formDebounce(this); +this.$nextTick(() => { this._skipFormWatch = false; }); +``` + +## 2. 搜索改为点击触发 - [Table.vue](d:\work\myst-web\src\components\Table.vue) + +**方案**:将关键字搜索从实时触发改为点击搜索按钮触发。 + +- 新增 `searchKeyword: ""` data 属性,输入框 `v-model` 改为绑定 `searchKeyword` +- 搜索按钮添加 `@click="doSearch"` 事件 +- `doSearch` 方法将 `searchKeyword` 赋值给 `form.keyword`(触发 watcher 进行搜索) +- 输入框支持 Enter 键触发搜索(`@keyup.enter`) +- 重置按钮也同步清空 `searchKeyword` + +关键模板改动: + +```html + + + +``` + +## 3. 密码改为弹窗独立修改 - [Edit.vue](d:\work\myst-web\src\components\Edit.vue) + +**方案**: + +- 主表单中隐藏 password 类型字段,改为显示"修改密码"按钮 +- 新增密码修改弹窗(`el-dialog`),包含新密码 + 确认密码两个输入 +- 弹窗提交时校验两次输入一致,然后仅发送密码字段调用 `edit` API +- 主表单 `onSubmit` 提交数据时过滤掉 password 类型字段 + +关键改动点: + +**模板** - 密码字段区域(约第55行): + +```html + + + 修改密码 + + + +``` + +**data** 新增: + +```javascript +showPasswordDialog: false, +passwordForm: { password: '', confirmPassword: '' }, +passwordColumn: null, +``` + +**methods** 新增 `submitPassword` 和修改 `onSubmit`: + +- `submitPassword`:校验密码一致性,调用 `edit` API 仅提交密码字段 +- `onSubmit`:提交时从 form 中排除 password 类型字段的数据 + diff --git a/.cursor/plans/修复达梦数据库越界和引号_eafbbf76.plan.md b/.cursor/plans/修复达梦数据库越界和引号_eafbbf76.plan.md new file mode 100644 index 0000000..d6dbb58 --- /dev/null +++ b/.cursor/plans/修复达梦数据库越界和引号_eafbbf76.plan.md @@ -0,0 +1,110 @@ +--- +name: 修复达梦数据库越界和引号 +overview: 修复 HoTime 框架代码生成器的两个达梦(DM)数据库问题:(1) 获取表列表时越界包含了系统表;(2) SQL 查询使用了 MySQL 反引号语法导致达梦解析失败。 +todos: + - id: fix-dm-table-filter + content: 修改 code/makecode.go 的达梦表列表和列信息查询,使用 ALL_TAB_COMMENTS + OWNER 过滤 + status: completed + - id: fix-backtick-convert + content: 修改 db/identifier.go 的 ProcessFieldList,增加 convertQuotes 调用转换残留反引号 + status: completed + - id: regen-admin-json + content: 删除/清理 myst-go/config/admin.json 中的系统表条目,重新生成配置 + status: completed +isProject: false +--- + +# 修复达梦数据库越界获取系统表和反引号语法错误 + +## 问题分析 + +### 问题 1:越界获取非 ZWBG 数据库的表 + +[code/makecode.go](d:\work\hotimev1.5\code\makecode.go) 第 121-122 行: + +```go +nowTables = db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM USER_TAB_COMMENTS WHERE TABLE_TYPE='TABLE'`) +``` + +使用 `SYSDBA` 用户连接达梦时,`USER_TAB_COMMENTS` 返回该用户下**所有表**,包括达梦系统表(`SYSJOBS`、`SYSJOBSCHEDULES`、`SYSALERTS`、`##HISTOGRAMS_TABLE` 等),导致 [admin.json](d:\work\myst-go\config\admin.json) 中混入了大量不属于 ZWBG 业务的系统表。 + +同样的问题存在于第 169-170 行的列信息查询(`USER_TAB_COLUMNS` / `USER_COL_COMMENTS`)。 + +### 问题 2:反引号导致 SQL 语法错误(遗留问题) + +`makecode.go` 中 `Info`(第 725 行)和 `Search`(第 958 行)使用反引号拼接列名字符串:``col1`,`col2`,...`。 + +`crud.go` 的 `Select` 方法有**两种字段处理路径**: + +- **Slice 路径**(第 100-109 行):每个列名走 `ProcessColumnNoPrefix` -> `QuoteIdentifier`,能正确转换引号 +- **String 路径**(第 93-99 行):整个字符串走 `ProcessFieldList` -> `ProcessConditionString`,三个正则都只匹配 `table.column` 格式(含 `.`),不处理单独的反引号列名 + +`makecode.go` 传的是拼接好的**字符串**,走的是 String 路径,所以反引号不会被自动转换,导致达梦报语法错误。 + +--- + +## 修复方案 + +### 修改 1:过滤达梦系统表 - [code/makecode.go](d:\work\hotimev1.5\code\makecode.go) + +将 `USER_TAB_COMMENTS` / `USER_TAB_COLUMNS` 改为 `ALL_TAB_COMMENTS` / `ALL_TAB_COLUMNS`,并通过 `OWNER` 过滤为 `db.DBName`(即 `ZWBG`),确保只获取目标 schema 的业务表。 + +**第 121-122 行**,表列表查询改为: + +```go +if db.Type == "dm" || db.Type == "dameng" { + nowTables = db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM ALL_TAB_COMMENTS WHERE TABLE_TYPE='TABLE' AND OWNER='` + strings.ToUpper(db.DBName) + `'`) +} +``` + +**第 169-170 行**,列信息查询改为: + +```go +if db.Type == "dm" || db.Type == "dameng" { + tableInfo = db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must", c.DATA_DEFAULT AS "dflt_value" FROM ALL_TAB_COLUMNS c LEFT JOIN ALL_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME AND c.OWNER=m.OWNER WHERE c.TABLE_NAME='` + v.GetString("name") + `' AND c.OWNER='` + strings.ToUpper(db.DBName) + `' ORDER BY c.COLUMN_ID`) +} +``` + +### 修改 2:修复反引号转换 - [db/identifier.go](d:\work\hotimev1.5\db\identifier.go) + +`makecode.go` 的反引号属于遗留代码,理论上 `Select` 的 Slice 路径已能自动处理引号,但 `makecode.go` 传的是字符串。最小改动:在 `ProcessFieldList` 末尾加 `convertQuotes` 调用,将 `ProcessConditionString` 未能匹配到的残留反引号统一转为当前方言引号: + +```go +func (p *IdentifierProcessor) ProcessFieldList(fields string) string { + if fields == "" || fields == "*" { + return fields + } + result := p.ProcessConditionString(fields) + result = p.convertQuotes(result) + return result +} +``` + +`convertQuotes`(第 240-248 行)已实现引号转换:达梦将反引号转为 `"`,MySQL 保持不变(no-op)。这样无需修改 `makecode.go` 中的遗留反引号代码,所有走 `ProcessFieldList` 的字符串都能被正确处理。 + +### 修改 3:重新生成 admin.json - [config/admin.json](d:\work\myst-go\config\admin.json) + +代码修复后,需要删除当前 `admin.json`(已包含系统表污染)并重启应用让其自动重新生成。需要注意的是: + +- 如果 `admin.json` 中有手工定制的菜单/权限配置,**需先备份**再删除 +- 或者手动从 `admin.json` 中删除以下系统表条目:`SYSJOBSCHEDULES`、`SYSJOBSTEPS`、`SYSJOBHISTORIES`、`SYSJOBHISTORIES2`、`SYSSTEPHISTORIES2`、`SYSALERTHISTORIES`、`SYSALERTNOTIFICATIONS`、`SYSALERTS`、`SYSMAILINFO`、`SYSOPERATORS`、`SYSJOBS`、`##HISTOGRAMS_TABLE`、`cached`、`hotime_cache`、`test_batch` + +--- + +## 数据流概览 + +```mermaid +flowchart LR + subgraph code_gen [代码生成 - makecode.go] + A["Db2JSON()"] -->|"USER_TAB_COMMENTS (修复前)"| B["获取所有表含系统表"] + A -->|"ALL_TAB_COMMENTS + OWNER (修复后)"| C["仅获取ZWBG业务表"] + end + subgraph query [查询执行 - code.go + crud.go] + D["Info/Search 拼字段"] -->|反引号列名| E["ProcessFieldList"] + E -->|"ProcessConditionString (不匹配)"| F["反引号保留 -> DM报错"] + E -->|"+ convertQuotes (修复后)"| G["转为双引号 -> DM正常"] + end +``` + + + diff --git a/.cursor/plans/字段备注空格扩展_e164bdb6.plan.md b/.cursor/plans/字段备注空格扩展_e164bdb6.plan.md new file mode 100644 index 0000000..2915bce --- /dev/null +++ b/.cursor/plans/字段备注空格扩展_e164bdb6.plan.md @@ -0,0 +1,72 @@ +--- +name: 字段备注空格扩展 +overview: 在 hotime 的列注释解析中,除保留「首段为系统内 label」外,将「第一个空格之后、选项/花括号规则之前」的内容单独写入新字段(如 remark),供前端展示;并澄清:管理端若列来自已有配置或 JSON,可能未经过 DB 新列分支,故不会表现为「整段 label 被丢掉」。 +todos: + - id: parse-makecode + content: 在 makecode.go 中实现 SplitN + 空格拆分 + remark 字段,并统一 options/ps 解析顺序 + status: pending + - id: ui-remark + content: 在 myst-web Edit/Info 中展示 column.remark(可选样式) + status: pending + - id: docs-format + content: 补充列注释格式说明文档 + status: pending +isProject: false +--- + +# 表字段备注:空格后扩展备注(修正认知) + +## 为何你能在管理端看到「完整」label + +`[hotimev1.5/code/makecode.go](d:/work/hotimev1.5/code/makecode.go)` 中,**空格截断与 `:` 处理仅发生在 `if coloum == nil` 分支**(约 189–325 行),即:**仅当该列尚未在 `TableColumns` 中存在、完全由本次从库表结构推断新建列时**才会执行。 + +若列定义已来自 `[config/adminDB.json](d:/work/myst-go/config/adminDB.json)` 等已有配置,会走 `coloum != nil`,**不会执行**上述截断逻辑,管理端下发的 `column.label` 仍可能是你在 JSON 里写的整段字符串,因此**不会出现「整段在 UI 里被丢掉」的现象**。 + +你遇到的「断言/解析」问题更可能出在:**需要同时支持「系统内短 label」与「空格后的说明」两种语义**,而不是单纯丢失数据。 + +## 目标行为(约定) + +对**数据库列注释**(以及与之对齐的生成逻辑)采用统一解析顺序,避免与现有 `:` 选项、`{...}` 的 `ps` 冲突: + +1. 用 `**SplitN(raw, ":", 2)`** 拆出「主文案」与「选项串」(替代当前对整串 `strings.Split(..., ":")`,避免备注里出现冒号时误伤)。 +2. 在「主文案」中按 **第一个空格** 拆成: + - **第一段**:`label`(系统内字段名/短标题,与现有一致) + - **第二段及以后**(或仅第二段,按产品定):trim 后写入新字段,建议键名 `**remark`**(若你更倾向 `note` / `labelRemark` 可再定) +3. `**{...}` → `ps`**:仍在**完整主文案或完整 raw** 上提取(与现有行为一致),避免破坏依赖 `ps` 的表单。 +4. **选项解析**:使用 `SplitN` 的第二段,保持 `1-男,2-女` 这类格式不变。 + +示例:`姓名 对外展示说明 :1-男,2-女` → `label=姓名`,`remark=对外展示说明`,options 从 `:1-男,2-女` 解析。 + +## 实现位置 + + +| 层级 | 文件 | 内容 | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 后端 | `[hotimev1.5/code/makecode.go](d:/work/hotimev1.5/code/makecode.go)` | 重构 218–233 与 300–308 行附近逻辑:先 `:` 再空格,并设置 `coloum["remark"]`;无空格时行为与现有一致 | +| 前端(可选但推荐) | `[myst-web/src/components/Edit.vue](d:/work/myst-web/src/components/Edit.vue)`、`[Info.vue](d:/work/myst-web/src/components/Info.vue)` | 在表单项标签旁或下方展示 `column.remark`(小字/灰色提示),无则不占位 | +| 文档 | `[myst-go/docs/](d:/work/myst-go/docs/)` 或 QUICKSTART | 简短说明列注释格式:`label 备注 :选项` | + + +依赖关系:`[myst-go/go.mod](d:/work/myst-go/go.mod)` 中 `replace code.hoteas.com/golang/hotime => ../hotimev1.5`,改 hotime 源码即可被 myst-go 使用。 + +## 兼容与回归 + +- **无空格**、仅 `label:选项`、仅 `{ps}`:行为与现网一致。 +- **adminDB.json 手工写的长 label**:若需同样拆分,可在后续加「对已有列合并 DB 注释时补写 remark」或提供一次性脚本;首版以 **Db2JSON 新列路径** 与 **重新生成/同步列** 为主。 + +## 流程示意 + +```mermaid +flowchart LR + raw[DB列注释raw] + raw --> splitColon["SplitN 第一处冒号"] + splitColon --> mainPart[主文案] + splitColon --> optPart[选项串] + main --> splitSpace["第一处空格"] + splitSpace --> label[label] + splitSpace --> remark[remark] + raw --> psBrace["提取 ps 花括号"] +``` + + + diff --git a/.cursor/plans/字段提示括号扩展_91441aee.plan.md b/.cursor/plans/字段提示括号扩展_91441aee.plan.md new file mode 100644 index 0000000..1730c19 --- /dev/null +++ b/.cursor/plans/字段提示括号扩展_91441aee.plan.md @@ -0,0 +1,102 @@ +--- +name: 字段提示括号扩展 +overview: 在后端解析数据库列注释中的 `()` 内容作为字段提示(ps),修复空格截断 bug,并在前端 Table/Info/Edit 组件中展示提示信息。 +todos: + - id: backend-parse + content: 后端 makecode.go:新增 () 解析,调整解析顺序(先提取括号内容,再空格/冒号截断),存入 ps 字段 + status: completed + - id: backend-fix-space + content: 后端 makecode.go:修复 coloum != nil 分支中 label 未截断空格的问题 + status: completed + - id: frontend-edit + content: 前端 Edit.vue / Add.vue:输入框右侧显示 column.ps 提示文字 + status: completed + - id: frontend-table + content: 前端 Table.vue:列头加 el-tooltip 展示 td.ps + status: completed + - id: frontend-info + content: 前端 Info.vue:详情字段名加 tooltip 展示 column.ps + status: completed +isProject: false +--- + +# 字段提示:支持 () 括号 + 前端展示 + +## 现状问题 + +1. 后端已经能从 `{}` 提取 `ps`,但前端从未使用 `ps` 字段 +2. `{}` 写法不直观,用户希望用更常见的 `()` 来写字段提示 +3. 管理端某些列的 label 包含了空格后面的文字(未经截断) + +## 数据库注释格式约定(举例) + + +| 注释写法 | label | ps | options | +| --------------------------- | ----- | ------- | -------------- | +| `姓名` | 姓名 | 无 | 无 | +| `状态(当前审核状态)` | 状态 | 当前审核状态 | 无 | +| `性别:1-男,2-女` | 性别 | 无 | 男/女 | +| `状态(当前审核状态):1-待审,2-通过,3-驳回` | 状态 | 当前审核状态 | 待审/通过/驳回 | +| `备注{请输入详细说明}` | 备注 | 请输入详细说明 | 无 | +| `姓名 xxx说明` | 姓名 | 无 | 无(空格后丢弃,同现有行为) | + + +`()` 和 `{}` 都提取到同一个 `ps` 字段(功能一致,只是写法不同)。 + +## 后端改动 + +文件:[hotimev1.5/code/makecode.go](d:/work/hotimev1.5/code/makecode.go) 约 218-233 行 + +当前解析顺序是:空格截断 -> 花括号提取ps -> 冒号截断。需要调整为: + +1. **新增 `()` 解析**:在原有 `{}` 提取 ps 的逻辑旁边,增加对 `(` 和 `)` 的检测,提取内容同样存入 `coloum["ps"]` +2. **调整解析顺序**:先提取 `()` / `{}`(从原始注释中),再做空格截断和冒号截断,避免截断后丢失括号内容 +3. **提取后清理 label**:将 `(...)` 或 `{...}` 从 label 中移除,确保 label 干净 + +改后伪逻辑(自然语言): + +``` +原始注释 = "状态(当前审核状态):1-待审,2-通过,3-驳回" + +第一步:从原始注释中找 () 或 {},提取内容 → ps = "当前审核状态" +第二步:把 (...) 或 {...} 从注释中去掉 → "状态:1-待审,2-通过,3-驳回" +第三步:空格截断(取第一个空格前的部分) +第四步:冒号截断 label → label = "状态" +第五步:冒号后面解析选项 → options +``` + +## 前端改动 + +### 1. Edit.vue / Add.vue — 输入框右侧显示提示 + +在 `el-form-item` 内、输入控件后面追加一个小字提示: + +```html +{{ column.ps }} +``` + +样式为灰色小字,紧贴输入框右侧。 + +### 2. Table.vue — 列头 tooltip + +给 `el-table-column` 加 header 插槽,当 `td.ps` 存在时用 `el-tooltip` 包裹列头文字: + +```html + +``` + +无 `ps` 的列保持原样。 + +### 3. Info.vue — 字段名 tooltip + +类似地,在详情页的 `el-descriptions-item` 的 label 上加 tooltip 展示 `column.ps`。 + +## 关于"管理端空格后 label 未截断"的 bug + +这个问题出在:当列已存在于配置(`coloum != nil` 分支),不会执行空格截断逻辑。如果 adminDB.json 里的 label 本身就包含空格后的文字,它会原样显示。 + +修复方式:在 makecode.go 的 `coloum != nil` 分支(即已有配置的列),也对 label 做同样的空格截断和 `()` / `{}` 提取处理。或者确认 adminDB.json 中的 label 是否需要手动清理。需要先确认具体是哪些列出现了这个问题。 \ No newline at end of file diff --git a/code/config.go b/code/config.go index 0cdb962..3406f3b 100644 --- a/code/config.go +++ b/code/config.go @@ -42,20 +42,34 @@ var DefaultMenuParentName = "sys" var ColumnDataType = map[string]string{ //sqlite专有类型 - "real": "number", - //mysql数据类型宽泛类型 + "real": "number", + "integer": "number", + //mysql数据类型 "int": "number", "float": "number", "double": "number", "decimal": "number", - "integer": "number", //sqlite3 "char": "text", "text": "text", "blob": "text", "date": "time", "time": "time", "year": "time", - "geometry": "gis", //不建议使用gis类型,建议使用其他代替 + "bit": "number", + "binary": "text", + "geometry": "gis", + //postgresql数据类型 + "numeric": "number", + "boolean": "number", + "serial": "number", + "json": "text", + "uuid": "text", + "bytea": "text", + "interval": "text", + "money": "number", + //达梦(DM)/Oracle数据类型 + "clob": "text", + "number": "number", } type ColumnShow struct { diff --git a/code/makecode.go b/code/makecode.go index daa0af9..637e883 100644 --- a/code/makecode.go +++ b/code/makecode.go @@ -119,7 +119,7 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { nowTables = db.Select("sqlite_master", "name", Map{"type": "table"}) } if db.Type == "dm" || db.Type == "dameng" { - nowTables = db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM USER_TAB_COMMENTS WHERE TABLE_TYPE='TABLE'`) + nowTables = db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM ALL_TAB_COMMENTS WHERE TABLE_TYPE='TABLE' AND OWNER='` + db.DBName + `'`) } //idSlice=append(idSlice,nowTables) for _, v := range nowTables { @@ -130,6 +130,14 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { if v.GetString("label") == "" { v["label"] = v.GetString("name") } + if strings.HasSuffix(v.GetString("label"), "表") { + withoutTable := strings.TrimSuffix(v.GetString("label"), "表") + if len([]rune(withoutTable)) <= 3 { + v["label"] = withoutTable + "管理" + } else { + v["label"] = withoutTable + } + } that.TableConfig[v.GetString("name")] = Map{ "label": v.GetString("label"), "table": v.GetString("name"), @@ -167,7 +175,7 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { tableInfo = db.Query("pragma table_info([" + v.GetString("name") + "]);") } if db.Type == "dm" || db.Type == "dameng" { - tableInfo = db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must", c.DATA_DEFAULT AS "dflt_value" FROM USER_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + v.GetString("name") + `' ORDER BY c.COLUMN_ID`) + tableInfo = db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must", c.DATA_DEFAULT AS "dflt_value" FROM ALL_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + v.GetString("name") + `' AND c.OWNER='` + db.DBName + `' ORDER BY c.COLUMN_ID`) } idSlice = append(idSlice, tableInfo) @@ -181,7 +189,7 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { if coloum == nil { //根据类型判断真实类型 for k, v1 := range ColumnDataType { - if strings.Contains(info.GetString("type"), k) { + if strings.Contains(strings.ToLower(info.GetString("type")), k) { info["type"] = v1 break } @@ -207,22 +215,26 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { coloum["must"] = true } - //备注以空格隔开,空格后的是其他备注 - indexNum := strings.Index(info.GetString("label"), " ") - if indexNum > -1 { - coloum["label"] = info.GetString("label")[:indexNum] + //提取括号内的提示信息:支持 ()、()、{} + rawLabel := info.GetString("label") + for _, pair := range [][2]string{{"(", ")"}, {"(", ")"}, {"{", "}"}} { + start := strings.Index(rawLabel, pair[0]) + end := strings.Index(rawLabel, pair[1]) + if start != -1 && end > start { + coloum["ps"] = rawLabel[start+len(pair[0]) : end] + rawLabel = rawLabel[:start] + rawLabel[end+len(pair[1]):] + break + } } - psStart := strings.Index(info.GetString("label"), "{") - psEnd := strings.Index(info.GetString("label"), "}") - if psStart != -1 && psEnd > psStart { - coloum["ps"] = info.GetString("label")[psStart+1 : psEnd] + //空格截断,空格后的内容丢弃 + if idx := strings.Index(rawLabel, " "); idx > -1 { + rawLabel = rawLabel[:idx] } - - //去除数据信息,是用:号分割的 - indexNum = strings.Index(coloum.GetString("label"), ":") - if indexNum > -1 { - coloum["label"] = coloum.GetString("label")[:indexNum] + //冒号截断,去除选项数据部分 + if idx := strings.Index(rawLabel, ":"); idx > -1 { + rawLabel = rawLabel[:idx] } + coloum["label"] = rawLabel for _, ColumnName := range that.RuleConfig { if (ColumnName.GetBool("strict") && coloum.GetString("name") == ColumnName.GetString("name")) || @@ -311,6 +323,25 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { } //} + } else { + //对已有配置列也提取 ps 并清理 label + rawLabel := coloum.GetString("label") + for _, pair := range [][2]string{{"(", ")"}, {"(", ")"}, {"{", "}"}} { + start := strings.Index(rawLabel, pair[0]) + end := strings.Index(rawLabel, pair[1]) + if start != -1 && end > start { + coloum["ps"] = rawLabel[start+len(pair[0]) : end] + rawLabel = rawLabel[:start] + rawLabel[end+len(pair[1]):] + break + } + } + if idx := strings.Index(rawLabel, " "); idx > -1 { + rawLabel = rawLabel[:idx] + } + if idx := strings.Index(rawLabel, ":"); idx > -1 { + rawLabel = rawLabel[:idx] + } + coloum["label"] = rawLabel } //暂时不关闭参数,保证表数据完全读取到 @@ -803,7 +834,7 @@ func (that *MakeCode) Add(table string, user Map, req *http.Request) Map { data[v.GetString("name")] = time.Now().Unix() } if v.GetString("type") == "time" { - data[v.GetString("name")] = time.Now().Format("2006-01-02 15-04-05") + data[v.GetString("name")] = time.Now().Format("2006-01-02 15:04:05") } continue } @@ -813,7 +844,7 @@ func (that *MakeCode) Add(table string, user Map, req *http.Request) Map { data[v.GetString("name")] = time.Now().Unix() } if v.GetString("type") == "time" { - data[v.GetString("name")] = time.Now().Format("2006-01-02 15-04-05") + data[v.GetString("name")] = time.Now().Format("2006-01-02 15:04:05") } } } @@ -862,7 +893,7 @@ func (that *MakeCode) Edit(table string, req *http.Request) Map { data[v.GetString("name")] = time.Now().Unix() } if v.GetString("type") == "time" { - data[v.GetString("name")] = time.Now().Format("2006-01-02 15-04-05") + data[v.GetString("name")] = time.Now().Format("2006-01-02 15:04:05") } } } @@ -1178,7 +1209,14 @@ func (that *MakeCode) Search(table string, userData Map, data Map, req *http.Req } if len(reqValue) != 0 && searchItem.GetString("name") == "sort" && reqValue[0] != "" { - sortMap["ORDER"] = reqValue[0] + parts := strings.Fields(reqValue[0]) + if len(parts) == 2 { + col := parts[0] + dir := strings.ToUpper(parts[1]) + if (dir == "ASC" || dir == "DESC") && that.TableColumns[table][col] != nil { + sortMap["ORDER"] = col + " " + dir + } + } } continue diff --git a/db/identifier.go b/db/identifier.go index 9344dea..3211e58 100644 --- a/db/identifier.go +++ b/db/identifier.go @@ -187,8 +187,9 @@ func (p *IdentifierProcessor) ProcessFieldList(fields string) string { return fields } - // 使用与 ProcessConditionString 相同的逻辑 - return p.ProcessConditionString(fields) + result := p.ProcessConditionString(fields) + result = p.convertQuotes(result) + return result } // stripQuotes 去除标识符两端的引号(反引号或双引号) diff --git a/docs/CodeGen_使用说明.md b/docs/CodeGen_使用说明.md index 771ab1a..02fcd3f 100644 --- a/docs/CodeGen_使用说明.md +++ b/docs/CodeGen_使用说明.md @@ -148,12 +148,15 @@ func main() { 支持从数据库字段备注中提取信息: ```sql --- 字段备注格式: 标签名:选项1-名称1,选项2-名称2 {提示信息} +-- 字段备注格式: 标签名(提示信息):选项1-名称1,选项2-名称2 +-- 括号支持三种写法:()、()、{} -- 例如: -status TINYINT COMMENT '状态:0-禁用,1-启用 {用户账号状态}' +status TINYINT COMMENT '状态(用户账号状态):0-禁用,1-启用' +parent_id INT COMMENT '父级ID(顶级为NULL)' +phone VARCHAR(20) COMMENT '手机号(请输入11位手机号)' ``` -生成的配置: +生成的配置(以第一行为例): ```json { @@ -168,6 +171,10 @@ status TINYINT COMMENT '状态:0-禁用,1-启用 {用户账号状态}' } ``` +`ps` 字段在前端的展示效果: +- 编辑/新增页面:输入框右侧灰色小字提示 +- 表格/详情页面:鼠标悬停字段名时气泡提示 + --- ## 自定义规则 diff --git a/docs/DatabaseDesign_数据库设计规范.md b/docs/DatabaseDesign_数据库设计规范.md index b88a491..8bd34ce 100644 --- a/docs/DatabaseDesign_数据库设计规范.md +++ b/docs/DatabaseDesign_数据库设计规范.md @@ -229,15 +229,17 @@ SQLite 不支持表备注,代码生成器会使用表名作为默认显示名 字段注释支持以下格式组合: ``` -显示名称:选项值 附加备注{前端提示} +显示名称(前端提示):选项值 附加备注 +显示名称(前端提示):选项值 附加备注 +显示名称{前端提示}:选项值 附加备注 ``` | 部分 | 分隔符 | 用途 | |------|--------|------| | 显示名称 | 无 | 前端表单/列表的字段标签 | +| 前端提示 | `()`、`()` 或 `{}` | 存储到 `ps` 字段,前端用于字段说明提示(编辑页输入框右侧、表格/详情鼠标悬停气泡) | | 选项值 | `:` 冒号 | select 类型的下拉选项(格式:`值-名称,值-名称`) | | 附加备注 | 空格 | 仅在数据库中查看,不传递给前端 | -| 前端提示 | `{}` | 存储到 `ps` 字段,用于前端显示提示文字 | ### 选择类型字段 @@ -268,28 +270,38 @@ SQLite 不支持表备注,代码生成器会使用表名作为默认显示名 `amount` decimal(10,2) COMMENT '金额 单位为元,精确到分' ``` -### 前端提示({}内) +### 前端提示(括号内) -`{}` 中的内容存储到 `ps` 字段,前端可用于显示输入提示: +`()`、`()` 或 `{}` 中的内容存储到 `ps` 字段,前端展示为字段说明提示。推荐使用 `()` 或 `()`,`{}` 为兼容旧格式保留。 ```sql -`phone` varchar(20) COMMENT '手机号{请输入11位手机号}' +`parent_id` int(11) COMMENT '父级ID(顶级为NULL)' +`status` int(2) COMMENT '状态(当前审核状态):0-待审,1-通过,2-驳回' +`phone` varchar(20) COMMENT '手机号(请输入11位手机号)' `email` varchar(100) COMMENT '邮箱{格式:xxx@xxx.com}' -`content` text COMMENT '内容{支持HTML格式}' ``` +前端展示效果: +- **编辑/新增页面**:输入框右侧显示灰色小字提示 +- **表格列表/详情页面**:鼠标悬停字段名时显示气泡提示 + ### 组合使用 ```sql --- 完整格式示例 -`status` int(2) DEFAULT '0' COMMENT '状态:0-待审核,1-已通过,2-已拒绝 业务状态流转字段{选择当前审核状态}' +-- 推荐写法:使用圆括号 +`status` int(2) DEFAULT '0' COMMENT '状态(选择当前审核状态):0-待审核,1-已通过,2-已拒绝' +`parent_id` int(11) DEFAULT NULL COMMENT '父级ID(顶级为NULL)' + +-- 兼容写法:使用花括号 +`status` int(2) DEFAULT '0' COMMENT '状态{选择当前审核状态}:0-待审核,1-已通过,2-已拒绝' ``` -解析结果: +解析结果(以第一行为例): - `label` = "状态" -- `options` = [{name:"待审核", value:"0"}, {name:"已通过", value:"1"}, {name:"已拒绝", value:"2"}] - `ps` = "选择当前审核状态" -- 空格后的"业务状态流转字段"不会传递给前端 +- `options` = [{name:"待审核", value:"0"}, {name:"已通过", value:"1"}, {name:"已拒绝", value:"2"}] + +三种括号 `()`、`()`、`{}` 功能相同,优先匹配顺序为:`()` > `()` > `{}`,同一注释中只取第一个匹配到的括号对。 ### SQLite 字段备注