Build List(动态列表构建器)
读取 list-spec.md(新增)或 列表 XH(修改),构建列表配置并提交平台。
> ⚠️ 本技能是远程配置操作,不是代码开发任务。 通过 HTTP API 在远程 PowerData 平台上创建/修改列表配置数据,与当前项目源码完全无关。
> - 禁止:因为 cwd 在项目目录就去 ls / rg --files / 读业务代码,或用 read_file / module_report / read_symbol / ast_grep / lsp_* 探查项目结构与代码。
> - 数据来源只有三个:① 远程 API(表结构、字段元数据、代码集)② 本 SKILL.md ③ 用户提供(spec 或列表 XH)。本地代码库里没有任何你需要的东西。
🔴 HARD-GATE:新增必须 spec,修改按有无 spec 分流
> [!important] 硬约束,不可协商
> 【新增入口】唯一输入是 list-spec.md。禁止从用户口语/图片直接推断并构建列表。
> 用户没给 spec/业务名 → 停止,引导去 pacp-design-list。
> 【修改入口】输入是列表 XH + 修改意图,按是否有 spec 分流:
> - 有 spec(构建记录段匹配到该 XH)→ 改 spec → 重新构建覆盖线上,保持 spec 与线上一致
> - 无 spec(无任何 list-spec 构建记录匹配该 XH)→ 直接拉线上配置修改,不回写 spec;可提示用户用 design-list 补 spec 以便后续复用
> 反查机制:用列表 XH 扫描 .pacp/specs/动态渲染/*__列表.md 的「## 构建记录」段匹配。
> 两条入口都不接受"凭口语直接构建"。
> [!warning] 一致性提醒(有 spec 重建场景)
> 若列表曾被绕过 spec 直接在线上改过,"走 spec 重建"会用 spec 覆盖线上手工改动——重建前需提示用户确认。
反合理化表(AI 跳过 spec 的借口及反驳):
| AI 内心独白(Red Flag) | 反驳 |
|---|---|
| "这列表就几列,直接生成了吧" | 几列也要 spec——列类型/宽度/查询条件/系统列没确认,生成必跑偏 |
| "用户描述很清楚了,我直接构建" | 口语清晰 ≠ spec 完整。代码集编号?主键?多表关联关系?没确认就是赌 |
| "我先建个草稿再让用户改" | 草稿赌错方向,用户改的成本 > 先出 spec 对齐的成本 |
| "spec 阶段太慢,用户等不及" | spec 是一次性对齐,构建是落子无悔。慢在前面省在后面 |
统一工作根目录:.pacp/
> [!important] 所有 pacp-* skill 共用 .pacp/ 作为工作根目录
定位方式:AI 运行时的 cwd(当前工作目录)下的 .pacp/。
> [!warning] cwd 边界
> "别探查项目结构"约束依然成立——AI 只往 .pacp/ 读写工作文件,不触碰项目内其他任何文件。
{cwd}/.pacp/
specs/ ← list-spec.md(pacp-design-list 产出,本 skill 读取)
动态渲染/ ← design 产出的隔离子文件夹(优先查找)
{业务名}__列表.md ← 列表 spec(新命名,design 产出,最优先)
{业务名}__表单.md ← 表单 spec(关联表单字段引用其文件名 + 构建记录段取 templateId)
{业务名}.md ← 旧命名兜底(兼容迁移前的 spec)
session.json ← 共享会话凭证(本 skill 启动时读取复用)
active/ ← 工作区(trace、构建脚本、payload、列对照表)
{业务名}_{时间戳}/
field-mapping.md ← spec→config 列对照表(构建前置产物)
list-config.json ← 从 spec 映射出的构建配置(喂给 list_creator.js)
> [!important] session 跨 skill 共享
> 启动时先读 .pacp/session.json,有效则直接复用;过期(401)则重新调 pacp-system-login 并更新。session.json 是可选缓存优化。
入口分流 + 文件存在性闸门
启动第一件事——按入口类型分流:
启动分流:
1. 修改入口(用户给了列表 XH + 修改意图)
→ 反查 spec:扫描 .pacp/specs/动态渲染/*__列表.md 的「## 构建记录」段,匹配列表 XH
├─ 命中 spec(某 spec 构建记录的「列表 XH」= 用户给的 XH)→ 走 spec 重建:改 spec → 重新构建覆盖线上
└─ 未命中任何 spec → 走直改线上:getQueryConfig(XH) → 推断改动并生成 diff → 用户确认 → 提交(不回写 spec)
2. 新增入口(默认)
→ 文件存在性闸门:
a. 用户给了业务名 → 按优先级查 spec 文件是否存在(命中即停,后者仅兜底):
① .pacp/specs/动态渲染/{业务名}__列表.md(新命名,design 产出,最优先)
② .pacp/specs/动态渲染/{业务名}.md(旧命名兜底,兼容迁移前的 spec)
├─ 存在 → 轻量校验(元信息段 + 列定义段存在且非空)→ 有效则继续
└─ 不存在 → 停止,引导去 pacp-design-list
b. 用户直接给了 spec 文件路径 → 读取,轻量校验
c. 用户什么都没给 → 停止,提示:
「需要先有规格文档。请用 pacp-design-list 生成。」
> [!note] 轻量校验
> 闸门只校验两段存在(元信息段存在、列定义段存在且非空),不校验段内细节。段内细节由 design 的质量门保障。
> 用户手写 spec(未经 design 质量门)直接喂给 build 时,提示「未经 design 质量门校验,可能有缺项」但不拦截。
关联表单的 templateId 解析(构建前置)
> [!important] list-spec「关联表单」字段填的是表单 spec 文件名,不是 templateId
> list-spec 元信息的「关联表单」字段填的是表单 spec 文件名(如 调查查询问通知书__表单),不是 templateId——设计阶段表单可能尚未构建、没有 templateId。build-list 构建前需解析出 templateId:
>
> 1. 读 .pacp/specs/动态渲染/{关联表单值}.md(如 调查询问通知书__表单.md)
> 2. 查找该 spec 的「## 构建记录」段,取 templateId
> ├─ 有构建记录 → 取 templateId,传入 --template-id
> └─ 无构建记录 → 停止,提示用户「关联表单尚未构建,请先用 pacp-build-form 构建 {文件名}」
> 3. 表单 spec 文件不存在 → 停止,提示文件名有误
>
> 这套机制让设计阶段(表单未构建)也能产出完整的列表 spec,把对 templateId 的依赖推迟到构建阶段——此时表单必然已构建。
> 「关联表单」字段为「无」时不解析,构建纯展示列表(LX=1)。
数据来源
- 数据来源只有三个:① 远程 API(表结构、字段元数据、代码集)② 本 SKILL.md ③ 用户提供(spec 或列表 XH)。本地代码库里没有任何你需要的东西。
操作模式
- 新增:用户提供 list-spec.md(或业务名),构建并提交新列表
- 修改:用户提供列表 XH + 修改意图,按是否有 spec 分流(见上方入口分流)
重要约束:远程配置任务,不读任何源码(含当前项目)
> 本技能是在远程平台上创建/修改配置数据,不是在本地项目里开发代码。禁止读取任何源码——既包括本 Skill 的 lib/scripts,也包括当前项目(cwd)里的业务代码。
本 Skill 的 lib/、scripts/ 目录下的 .js 文件是运行时代码,由 execute_code 或 CLI node 命令通过 require() 加载执行。禁止使用 read_file、cat、search_files 等工具查看这些文件的内容——所有 API 签名、参数说明、示例代码都在本 SKILL.md 中,无需阅读源码实现。
行为约束
| # | 约束 | 说明 |
|---|---|---|
| 1 | 只用 CLI 命令操作 | 所有操作必须通过本 Skill 提供的 CLI 命令(list_creator.js)或 BwaiClient API 完成。禁止自行编写新脚本绕过 CLI。如果 CLI 报错,直接将错误信息报告给用户,不尝试自行修复或绕过 |
| 2 | 临时文件路径 | 所有生成的文件(JSON 配置、列对照表、探查结果、临时脚本)必须写入 .pacp/active/{业务名}_{时间戳}/ 工作区目录内。禁止在其他位置创建文件——包括子仓库根目录、当前工作目录、/tmp 等。CLI 的 --config 参数必须指向工作区内的文件路径 |
| 3 | 代码集必须用户指定 | 涉及公共代码集(commonCode)时,dmjbh(小写)必须由用户明确指定(spec 已填则用 spec 的,标「待定」则停下来问用户)。禁止 AI 根据字段语义自行推断代码集编号 |
| 4 | SQL WHERE 默认 1=1 |
sqlWhere 默认使用 1=1(无过滤),除非用户明确要求加条件。禁止 AI 自行添加 SFYX = '1' 等过滤条件——不确定表中是否存在该列,随意添加会导致查询报错 |
| 5 | SQL 方言敏感 | sqlSelect / sqlWhere / sqlOrderBy 中的 SQL 必须使用标准通用写法(ANSI SQL)。如果必须使用数据库特有语法(如 LIMIT / TOP / ROWNUM),必须先明确目标数据库类型。非必要不使用特有语法 |
| 6 | 主动交互等待 | 需要用户确认或提供信息时,必须提供选项让用户选择,使用主动提问的方式让用户明确知道 AI 在等待输入。适用场景:代码集编号确认、数据库类型确认、有 spec 重建覆盖线上的确认等 |
| 7 | 缓存主动失效 | CLI 会自动缓存表结构等查询结果(默认 7 天过期)。当 AI 执行了修改操作(新增/修改代码集、修改表结构等)后,应使用 --clear-cache --base-url <url> 清除对应缓存,确保后续查询拿到最新数据 |
| 8 | Excel 导入后端资源禁止编造 | 接入 Excel 导入时(详见 references/excel-import-guide.md),jsonFilePath/importExcelService/templatePath 三项后端资源必须由用户提供,不生成后端代码、不推断 Service 类名、不编造 JSON/模板文件名 |
代码结构
本 Skill 由四个核心文件组成:
| 文件 | 职责 |
|---|---|
lib/bwai_client.js |
BwaiClient 类:HTTP 客户端、数据源探查(getTables/getColumns)、列表 CRUD API、工作区管理(.pacp/)、session 共享 |
scripts/col_widgets/ |
自包含列组件目录(每个组件独立文件) |
scripts/col_widgets/index.js |
自动发现+注册中心(inferType/resolve/listTypes) |
scripts/col_widgets/seq.js |
序号列 |
scripts/col_widgets/checkbox.js |
选择框列 |
scripts/col_widgets/operate.js |
操作列 |
scripts/col_widgets/common_code.js |
代码集列 |
scripts/col_widgets/default.js |
普通数据列(含推断) |
scripts/col_widgets/date_col.js |
日期列(含推断) |
scripts/list_builder.js |
高级编排:createListConfig(client, opts) 构建并提交列表配置;associateForm(client, listXH, templateId, options) 关联动态表单 |
scripts/script_gen.js |
脚本生成:dynamicFormScript(formId, pkField) 生成动态表单脚本 |
引入方式:
const { BwaiClient } = require('./lib/bwai_client');
const colWidgets = require('./scripts/col_widgets');
const { createListConfig, associateForm } = require('./scripts/list_builder');
const { dynamicFormScript } = require('./scripts/script_gen');
API 速查
require 引入
const { BwaiClient } = require('./lib/bwai_client');
const { makeColumn, makeQueryField, makeSeqColumn, makeOperateColumn,
makeButton, makeCheckboxColumn, makeCommonCodeColumn,
makeCodesetQueryField } = require('./scripts/col_widgets');
const { createListConfig, associateForm } = require('./scripts/list_builder');
const { dynamicFormScript } = require('./scripts/script_gen');
BwaiClient 方法
| 方法签名 | 返回值 |
|---|---|
new BwaiClient(baseUrl, sessionId) |
客户端实例 |
await client.getTables() |
[{ TABLE_NAME, TABLE_COMMENT }] |
await client.getColumns(tableName) |
[{ COLUMN_NAME, COLUMN_COMMENT, DATA_TYPE }] |
await client.getPrimaryKey(tableName) |
主键列名字符串 |
await client.getCommonCodeClassList({ pageSize: 10000 }) |
{ list: [{ DMJBH, DMJMC }] } |
await client.listQueryConfigs(page, size) |
{ list: [{ XH, MC, PZLB, PZCX, JB, LX, ... }] } |
await client.getQueryConfig(xh) |
单条列表配置 |
await client.saveQueryConfig(opts) |
{ XH, PZLB, PZCX, ... } |
await client.deleteQueryConfig(xh) |
删除结果 |
client.setTraceDir(dir) |
void |
client._writeSummary(obj) |
void |
BwaiClient.createWorkspace(name) |
工作区绝对路径(基于 .pacp/active/) |
BwaiClient.loadSession(baseUrl?) |
读共享 JSESSIONID(.pacp/session.json,按 baseUrl 取,与 pacp-system-login 共享),无则 null |
BwaiClient.saveSession(baseUrl, jsessionid, extra?) |
void |
工厂方法速查
makeSeqColumn(displayName, index)
→ { name:'ROW_ID', type:'#XH', displayName, index, width:'4%', ... }
makeColumn(name, displayName, type?, index?, width?, isShow?, align?)
默认: type='string', index=1, width='10%', isShow=true, align='left'
→ { name, type, displayName, index, width, rowspan:1, colspan:1, isShow, events:{}, display:{align} }
makeCommonCodeColumn(name, displayName, dmjbh, index?, width?, align?)
默认: index=1, width='10%', align='center'
→ 同 makeColumn + display.type='COMMON_CODE', display.content.dmjbh
makeQueryField(name, title, dataType?, fieldType?, operate?, alias?, quickMatch?)
默认: dataType='string', fieldType='text', operate='LIKE', alias='', quickMatch=false
→ { name, dataType, title, type:fieldType, operate }
makeCodesetQueryField(name, title, dmjbh, opts?)
opts 默认: { dataType:'string', operate:'=', quickMatch:false, multi:false, alignType:'select', isEmpty:false, initValue:'' }
→ { name, dataType, title, type:'codeset', dataSourceType:'codeset', dmjbh, dm:'', fdm:'', multi, alignType, isEmpty, initValue }
makeButton(key, displayName, functionName)
→ { key, displayName, type:'normal', function }
makeOperateColumn(buttons, width?)
buttons: makeButton 返回值的数组
→ { name:'操作', type:'#CZL', display:{ type:'OPERATE_BTN', content:buttons } }
makeCheckboxColumn()
→ { name:'sjxzBtn', type:'#XZAN', displayName:'选择', index:1, width:'5%', ... }
dynamicFormScript(formId, pkField?)
pkField 默认: 'XH'
→ Vue 3 CRUD 脚本字符串(addClick/editClick/delRowClick/delClick)
createListConfig(client, opts)
字段 类型 必填 默认值 说明
title string 是 - 列表标题(同时用作 MC)
sqlSelect string 是 - SQL 查询片段(不含 SELECT 关键字),如 'XH, MC FROM T_XXX'。支持多表 JOIN(见下方"多表联查")
columns array 是 - 列定义数组
sqlWhere string 否 '1=1' WHERE 条件
sqlOrderBy string 否 '' ORDER BY
sqlGroupBy string 否 '' GROUP BY
queryFields array 否 [] 查询字段数组
quickMatch array 否 [] 快速匹配字段名数组
buttons array 否 [] 工具栏按钮
xh string 否 '' 空=新增,非空=更新
jb/ys/tbxx/wbxx string 否 '' 脚本/样式/页头/页尾
mc string 否 title 列表名称
sfyx string 否 '1' 是否有效
lx string 否 '1' 列表类型('1'=普通列表, '2'=选择器列表, '3'=动态表单。lx='2' 时自动注入选择列/按钮/脚本)
返回值:{ XH, PZLB, PZCX, ... }
associateForm(client, listXH, templateId, options?)
字段 类型 必填 默认值 说明
primaryKey string 否 - 主键字段名(空=用列表已有的)
injectButtons bool 否 true 注入新增/删除按钮
injectOperateColumn bool 否 true 注入操作列
injectCheckbox bool 否 true 注入选择框
injectScript bool 否 true 注入 CRUD 脚本
返回值:{ listXH, formTemplateId, primaryKey, listUrl, formUrl }
CLI 调用方式(推荐)
本 skill 提供命令行入口,无需编写 Node.js 脚本,通过 JSON 配置 + CLI 命令即可完成列表创建和表单关联。
> [!important] spec → list-config.json 的映射由 AI 在会话内完成
> 与 pacp-build-form 对称:list_creator.js 吃 list-config.json(不吃 list-spec.md)。AI 读 spec → 产出列对照表 field-mapping.md → 基于对照表产出 list-config.json → 喂给 list_creator.js。spec→config 的映射在会话内完成,保证过程可见、可审计。
前置条件
本 skill 需要 --session-id(即服务端的 JSESSIONID cookie)。
获取优先级(按顺序尝试,命中即停):
- 读
.pacp/session.json→ 有效则直接复用 - 用户直接提供了 JSESSIONID → 直接使用
- 用户没提供 → 使用
pacp-system-loginskill 获取(基于 chrome-devtools MCP) - 获取失败 → 提示用户在浏览器手动登录后提供 JSESSIONID
> [!important] 登录强制 chrome-devtools MCP
> 获取 JSESSIONID 只接受两种方式(按优先级):用户直接提供 / 通过 pacp-system-login(内部基于 chrome-devtools MCP)。❌ 不接受 evaluate_script 读 cookie、手动猜测等不可靠方式。chrome-devtools MCP 不可用时,建议用户安装或直接提供 JSESSIONID。
命令格式
# 创建列表 + 关联表单(templateId 由 spec 关联表单字段解析得到)
node skills/pacp-build-list/scripts/list_creator.js \
--base-url http://localhost:8080/basesystem \
--session-id $SESSION_ID \
--template-id <由关联表单 spec 构建记录解析> \
--config list-config.json
# Step 1: 获取列索引(只有列名+注释,精简,用于补全 spec 字段名/核对列)
node skills/pacp-build-list/scripts/list_creator.js \
--base-url http://localhost:8080/basesystem \
--session-id $SESSION_ID \
--check-only \
--config - <<'EOF'
{"tableName": "T_HJJC_WTJCGZDJ"}
EOF
# → 返回 78 列的 name + comment + primaryKey
# 查看可用列组件类型
node skills/pacp-build-list/scripts/list_creator.js --list-types
参数说明
| 参数 | 必填 | 说明 |
|---|---|---|
| --base-url | 是 | 服务地址 |
| --session-id | 是 | 有效的 sessionId |
| --config | 是 | JSON 配置文件路径,传 "-" 从 stdin 读取 |
| --template-id | 否 | 表单 templateId(由 spec「关联表单」字段解析得到),提供则自动关联表单 |
| --lx | 否 | 列表类型:1=普通列表(默认), 2=选择器列表, 3=动态表单。也可在 JSON 配置中用 lx 字段指定 |
| --xh | 否 | 修改模式:指定列表 XH(优先级 --xh > config.xh > 空=新建) |
| --check-only | 否 | 仅探查表结构不创建 |
JSON 配置格式(由 spec 映射产出)
{
"title": "污染源委托监测登记",
"tableName": "T_HJJC_WTJCGZDJ",
"sqlSelect": "* FROM T_HJJC_WTJCGZDJ",
"sqlWhere": "1=1",
"sqlOrderBy": "",
"primaryKey": "RWBH",
"columns": [
{ "type": "seq", "displayName": "序号" },
{ "name": "RWBH", "displayName": "任务编号", "width": "15%" },
{ "name": "SJCDWMC", "displayName": "受监测单位名称", "width": "20%" },
{ "name": "JCLB", "displayName": "监测类别", "type": "commonCode", "dmjbh": "DMJ_XX" },
{ "type": "operate", "width": "12%", "buttons": [
{ "key": "add", "displayName": "新增", "function": "addClick" },
{ "key": "edit", "displayName": "编辑", "function": "editClick" },
{ "key": "delete", "displayName": "删除", "function": "delRowClick" }
]}
],
"queryFields": [
{ "name": "RWBH", "title": "任务编号", "operate": "LIKE", "quickMatch": true },
{ "name": "JCLB", "title": "监测类别", "type": "codeset", "dmjbh": "DMJ_XX" }
],
"quickMatch": ["RWBH", "SJCDWMC"],
"associateForm": true
}
字段说明:
- title/tableName: 必填,sqlSelect 默认 "* FROM {tableName}"
- columns[].type: seq(序号) / checkbox(选择框) / operate(操作列) / commonCode(代码集列) / 其他(普通列)
- columns[].colType: 可省略,CLI 根据 DATA_TYPE 自动推断:datetime→date, decimal/int→number, 其他→string。只有需要覆盖推断结果时才显式指定。
- columns[].name: 字段名(所有列必需。seq 列用 makeSeqColumn() 自动处理;operate 列用 makeOperateColumn() 自动填充 name:'操作';手动构建 JSON 时需填 name)
- columns[].width: 列宽,如 "15%"。可省略,AI 应根据列名汉字数自动估算宽度,参考规则:2字以内(序号、操作)→ 5%;3-4字(行业类型)→ 8%;5字(排放口总数)→ 9%;6字(超标排放口数)→ 10%;长文本列(单位名称)→ 18-20%;日期时间列 → 12%;编号/编码列 → 13-15%。总宽度约等于 100%
- columns[].dmjbh: commonCode 类型的代码集编号
- columns[].buttons[].key/displayName/function: 操作按钮定义
- queryFields[].type: codeset(代码集下拉) / 其他(普通文本)
- queryFields[].dmjbh: codeset 类型的代码集编号
- queryFields[].quickMatch: boolean
- quickMatch: 快速匹配字段名数组
- associateForm: boolean,配合 --template-id 自动关联
- primaryKey: 用于 associateForm,空=自动获取
多表联查(JOIN)
> [!important] 多表 JOIN 由 spec「关联关系」驱动拼接
> spec 不写 SQL 语句,但元信息记录了「主表 + 关联表 + 关联关系(字段对应)」。build 据此拼 JOIN:
> - 单表字段 → sqlSelect 直接写 字段名
> - 多表字段 → sqlSelect 写 别名.字段名,表别名由 build 自动派生(如 T_ZF_AJ→a,T_ZF_JCJL→b)
> - ON 条件 → 取自 spec 关联关系的字段对应(如 T_ZF_JCJL.AJ_XH → T_ZF_AJ.XH → ON b.AJ_XH = a.XH)
> - 关联关系缺失 → 停止并询问用户(不靠命名猜测)
sqlSelect 支持标准 SQL JOIN 语法。tableName 填主表名(用于列元数据推断),sqlSelect 用 JOIN 引入其他表。
{
"title": "执法案件列表",
"tableName": "T_ZF_AJ",
"sqlSelect": "a.XH, a.AJBH, a.AJMC, a.LARQ, b.JCRQ AS LATEST_JCRQ, b.JCRY AS LATEST_JCRY FROM T_ZF_AJ a LEFT JOIN T_ZF_JCJL b ON a.XH = b.AJBH AND b.JCRQ = (SELECT MAX(JCRQ) FROM T_ZF_JCJL WHERE AJBH = a.XH)",
"sqlWhere": "1=1",
"sqlOrderBy": "a.LARQ DESC",
"columns": [
{ "type": "seq", "displayName": "序号" },
{ "name": "AJBH", "displayName": "案件编号", "width": "15%" },
{ "name": "AJMC", "displayName": "案件名称", "width": "20%" },
{ "name": "LATEST_JCRQ", "displayName": "最新检查日期", "colType": "date", "width": "15%" },
{ "name": "LATEST_JCRY", "displayName": "最新检查人员", "width": "15%" }
]
}
注意事项:
- sqlSelect 不含 SELECT 关键字,直接写列名+FROM(CLI 会自动去掉多余的 SELECT)
- columns[].name 必须与 sqlSelect 中的列名一致(含别名)
- JOIN 查询中使用了表别名时,columns[].name 应使用最终列名(别名或原列名)
- tableName 仍然填主表名,用于 CLI 的列元数据推断;JOIN 的子表列需要用 colType 显式指定类型
- sqlWhere 中可以引用别名,如 a.LARQ >= '2024-01-01'
- SQL 方言:示例中的 LEFT JOIN + MAX() 子查询是 ANSI SQL 通用写法,适用于所有数据库。如需使用方言特有语法(如 LIMIT / TOP / ROWNUM),必须先确认目标数据库类型(见行为约束 #5)
聚合查询(SUM/COUNT + GROUP BY)
当列表需要显示汇总数据(如总金额、明细笔数),使用聚合查询:
{
"title": "项目经费列表",
"tableName": "T_XMJF_SP",
"sqlSelect": "a.XH, a.XMBH, a.XMMC, SUM(b.JE) AS TOTAL_JE, COUNT(b.XH) AS MX_COUNT FROM T_XMJF_SP a LEFT JOIN T_XMJF_MX b ON a.XH = b.XMH",
"sqlGroupBy": "a.XH, a.XMBH, a.XMMC",
"sqlWhere": "1=1",
"sqlOrderBy": "a.XMBH",
"columns": [
{ "name": "XMBH", "displayName": "项目编号", "width": "15%" },
{ "name": "XMMC", "displayName": "项目名称", "width": "20%" },
{ "name": "TOTAL_JE", "displayName": "总金额", "colType": "number", "width": "10%" },
{ "name": "MX_COUNT", "displayName": "明细笔数", "colType": "number", "width": "10%" }
]
}
注意事项:
- 聚合列(SUM/COUNT/MAX 等)必须用 AS 别名,columns[].name 对应别名
- sqlGroupBy 必须包含所有非聚合列(主表的字段都要写进去)
- 聚合列需要用 colType 显式指定类型(CLI 无法从 sqlSelect 自动推断)
- tableName 仍填主表名
输出格式(JSON 到 stdout)
创建列表 成功:
{
"success": true,
"listXH": "178074...",
"title": "xxx",
"columnCount": 10,
"queryFieldCount": 2,
"listUrl": "http://...basesystem/app/index.html#/list-render/xxx",
"associatedForm": true,
"formUrl": "http://...basesystem/app/index.html#/form-render/xxx",
"templateId": "xxx",
"primaryKey": "RWBH"
}
check-only Step 1(不带 config.columns):
{
"success": true,
"mode": "check-only-index",
"tableName": "T_HJJC_WTJCGZDJ",
"primaryKey": "RWBH",
"columns": [
{ "name": "RWBH", "comment": "任务编号" },
{ "name": "SJCDWMC", "comment": "受监测单位名称" }
],
"columnCount": 78
}
check-only Step 2(带 config.columns 白名单):
{
"success": true,
"mode": "check-only-detail",
"tableName": "T_HJJC_WTJCGZDJ",
"primaryKey": "RWBH",
"columns": [
{ "name": "RWBH", "comment": "任务编号", "dataType": "varchar", "typeCategory": "string" },
{ "name": "NCYJCRQ", "comment": "拟采样监测日期", "dataType": "datetime", "typeCategory": "date" }
],
"columnCount": 9,
"existingListCount": 15
}
失败:
{ "success": false, "error": "错误信息" }
进度信息输出到 stderr,不污染 stdout 的 JSON。
处理流程:spec 驱动(新增)+ XH 驱动(修改)
入口分流已在上方「入口分流 + 文件存在性闸门」定义。以下为两条入口的具体执行流程。
新增入口:spec 驱动
阶段0 文件存在性闸门 → 阶段1 登录+工作区 → 阶段2 [按需]探查列/代码集 → 阶段3 spec→config 列对照表 + 构建 JSON → 阶段4 提交 + spec 回写 → 阶段5 端到端验证
阶段0 文件存在性闸门
见上方「入口分流 + 文件存在性闸门」。按优先级查找 list-spec 文件(.pacp/specs/动态渲染/{业务名}__列表.md 优先),轻量校验两段存在。
阶段1 登录前置 + 工作区初始化
> 🔴 登录是第一件事:先读 .pacp/session.json 复用;无缓存或过期则通过 pacp-system-login skill 取 JSESSIONID。
登录完成后,从 spec 元信息提取业务名,在 .pacp/active/ 下创建工作区子目录:
const workspace = path.join(process.cwd(), '.pacp', 'active', businessName + '_' + timestamp);
fs.mkdirSync(workspace, { recursive: true });
创建 BwaiClient 实例后,立即开启请求追踪:
const { BwaiClient } = require('./lib/bwai_client');
const client = new BwaiClient(BASE_URL, SESSION_ID);
client.setTraceDir(workspace);
// 之后所有 API 调用的请求/响应自动记录到 workspace/api-trace.jsonl
// 高级方法(createListConfig/associateForm)的入参出参记录到 workspace/trace-summary.jsonl
阶段2 [按需] 探查列/代码集
> 按需执行:仅当 spec 字段需核对列细节、代码集字段标了「待定」、或多表联查需确认关联表列时才执行。
使用 lib/bwai_client.js 的 BwaiClient 类探查:
// 主表列(用于核对 spec 字段名、补全列类型)
const columns = await client.getColumns(spec.主表);
// 多表联查时,关联表的列也需探查(核对来源表字段名)
if (spec.关联表) {
for (const t of spec.关联表) {
await client.getColumns(t);
}
}
> [!important] 阶段2 探查时遇到"代码集编号:待定"的处理
> spec 里标"待定"的代码集列/查询条件,停下来问用户要 dmjbh 编号(保持"代码集必须用户给,AI 不猜"铁律)。
> - 用户给了编号 → 核对编号存在性(可查 ggdmz/list)→ 回填 spec → 继续
> - 用户给不出 → 字段先按普通文本列构建,构建后提醒用户自行配置代码集
将列信息和代码集核验结果保存到工作区 table-info.md。
阶段3 spec → config 列对照表 + 构建 JSON
> [!important] 前置步骤:spec→config 逐列对照(强制)
> 构建 JSON 前,必须先产出一份列对照表,写在工作区 .pacp/active/{任务}/field-mapping.md:
>
> | spec 列 | 来源表 | 列类型 | →组件 | 宽度语义 | →width | 代码集 | 备注 |
> |---------|--------|--------|-------|---------|--------|--------|------|
> | AJBH 案件编号 | T_ZF_AJ | 普通文本 | default | 窄 | 10% | - | - |
> | JCRQ 检查日期 | T_ZF_JCJL | 日期 | date | 日期 | 12% | - | JOIN来源 |
> | AJLB 案件类别 | T_ZF_AJ | 代码集 | commonCode | 窄 | 10% | DMJ_AJLB | - |
> | [序号] | - | 序号 | seq | 序号 | 5% | - | 系统列 |
> | [选择框] | - | 选择框 | checkbox | 选择框 | 5% | - | 系统列 |
> | [操作] | - | 操作 | operate | 操作 | 12% | - | 系统列 |
>
> 强制要求:
> - 每个 spec 数据列 + 系统列必须出现在对照表中(不允许遗漏)
> - width 值必须显式写出,标注来源(spec 列定义的宽度语义行),不允许使用默认值
> - 多表联查列必须标注「来源表」和 JOIN 来源
> - 代码集列必须写出 dmjbh(spec 已填或阶段2 回填)
>
> 目的:构建过程可见、可审计。产物列不对时,对照表能快速定位是 spec 写错还是 build 映射错。
列词汇 → 组件映射(spec 驱动构建的核心):
| spec 列词汇 | → CLI columns[].type | 说明 |
|---|---|---|
| 普通文本 | (省略,走 default 推断) | 普通数据列 |
| 数字 | (省略,colType=number) | 数值列 |
| 日期 | (colType=date) | 日期列 |
| 长文本 | (省略,走 default) | 长文本列 |
| 代码集 | commonCode + dmjbh |
代码值翻译列 |
| 序号 | seq |
系统列 |
| 选择框 | checkbox |
系统列 |
| 操作 | operate + buttons |
系统列 |
| ⚠️自定义 | AI 自行决定(兜底) | 映射不到时 |
宽度语义 → width 映射:
| spec 宽度语义 | → width |
|---|---|
| 窄 | 10% |
| 中 | 15% |
| 宽 | 20% |
| 日期 | 12% |
| 操作 | 12% |
| 序号 | 5% |
| 选择框 | 5% |
查询条件词汇 → queryFields 映射:
| spec 输入类型 | → queryFields[].type | 操作符 |
|---|---|---|
| 普通文本 | (省略,走 text) | LIKE |
| 下拉(代码集) | codeset + dmjbh |
= |
| 下拉(静态枚举) | (省略,走 text 或 select) | = |
| 日期区间 | (type=text, operate=RANGE) | RANGE |
| 代码集 | codeset + dmjbh |
= |
> [!important] 系统列机制:spec 声明 → config 显式 → associateForm 让位
> 序号/选择框/操作三列由 design-list 强制写进 spec(用户可控是否显示、宽度)。build 映射时把系统列写进 list-config.json 的 columns,由 col_widgets 工厂处理:
> - width 读 config 覆盖默认:seq/checkbox/operate factory 读 col.width,默认值统一对齐宽度语义词汇表(序号 5% / 选择框 5% / 操作 12%)
> - name 由 factory 自动补全(seq→ROW_ID / checkbox→sjxzBtn / operate→操作),config 无需显式写 name(type_check 已对系统列放宽 name 校验,seq 列只写 {type:"seq"} 不会报错)
> - isShow 读 config:spec 某系统列「显示:否」→ config 该列 isShow:false
> - associateForm 不覆盖 config 声明的系统列:config 已有 #CZL 操作列 / #XZAN 选择框列时保留不注入;仅 config 无对应列时才兜底注入默认列
>
> 一致性自检:构建前 field-mapping.md 列对照表标注的每个系统列 width,必须在 list-config.json 对应列体现(显式 width 或确认走默认)——对照表与 config 不一致即修正 config,保证对照表可审计。
基于对照表产出 list-config.json(写入工作区),然后喂给 list_creator.js。
阶段4 提交 + spec 回写
提交成功后,打开列表页面让用户自行查看:
- 打开列表页(两步导航,见下方刷新策略):先导航到中转页,再进
list-render/{listXH}。若无浏览器工具,把 URL 给用户自行打开。 - 告知用户:「列表已提交并在浏览器打开,请查看渲染是否正常。有问题告诉我,没问题即可。」
- 等用户反馈:不主动做快照验证、不自动修复。
> [!important] 浏览器预览刷新策略(Vue Router hash 模式)
> 平台前端用 Vue Router hash 模式。直接从 list-render/A 改 URL 到 list-render/B,Vue Router 检测到"同组件 param 变化",不触发组件重载。
> 正确操作(两步导航):先导航到中转页(任何非 list-render 的页面)→ 再导航到 #/list-render/{listXH}。
> [!important] spec 回写(闭环)
> 提交成功后,向 spec 回写事实信息(不回写组件/width 等实现细节,保持 spec 纯需求层):
>
> 回写内容:
> | 回写项 | 写在哪 |
> |--------|--------|
> | 列表 XH + listUrl(访问地址)+ 构建时间 | spec 末尾的 ## 构建记录 段(design-list 落盘时留空,build 回填) |
> | 关联表单 templateId(构建时解析得到) | 构建记录段的「关联表单 templateId」行 |
> | 代码集编号(spec 标「待定」→ build 问到后填入) | 列定义对应列的「代码集编号」行(待定 → 实际编号) |
>
> 示例——spec 末尾的构建记录段:
> markdown
> ## 构建记录
> - 列表 XH:1780749234641011735040
> - 构建时间:2026-07-15T15:30:00
> - 关联表单 templateId:13f56f3f-9f10-4ad3-bbf3-cf55c49e5c42
> - 备注:首次构建
>
>
> 不回写的:组件映射(commonCode/seq 等)、width 值、列顺序——这些是实现细节,回写会污染 spec 的纯需求语义。
阶段5 端到端验证
提交成功后,验证列表页面渲染是否正常:
🔴 验证不通过时必须报告用户 — 自动修复最多重试 2 次,超过后必须停止并报告。
验证内容:页面正常加载、列表数据正常加载(表头行 + 无 SQL 报错弹窗)、控制台无 JS 错误。
实现方式:使用浏览器工具(chrome-devtools MCP)打开 list-render/{XH} URL 验证;无浏览器工具则提示用户手动打开列表 URL 确认。
验证失败处理:AI 可定位根因(如 SQL 方言错误、字段属性配置错误)→ 自动修复配置并重新提交 → 再次验证;AI 无法判断 → 截图或错误信息报告用户;自动修复最多重试 2 次,超过后报告用户,不进入无限循环。
> [!note] 列表渲染路由需要菜单权限
> /#/list-render/{xh} 直接访问可能 404(需菜单注册)。验证时如遇 404,提示用户通过菜单进入或先注册菜单权限(见已知陷阱 #8)。
修改入口:XH 驱动(按有无 spec 分流)
getQueryConfig(XH) / 读 spec → 推断改动并生成 diff → 用户确认 → 提交
分流逻辑(见上方入口分流):
- 有 spec(扫描 .pacp/specs/动态渲染/*__列表.md 构建记录段命中该 XH)→ 改 spec → 重新构建覆盖线上(保持 spec 与线上一致)。⚠️ 若线上曾被手工改过,重建前提示用户确认。
- 无 spec(无任何 list-spec 构建记录匹配)→ getQueryConfig(xh) 拉线上配置 → 解析 PZLB/PZCX → 对比变更(diff 分析)→ 确认摘要高亮变更(🟢新增/🔴删除/🟡修改)→ 提交(不回写 spec)。可提示用户用 design-list 补 spec 以便后续复用。
详见下方「修改模式」章节。
数据结构
PZLB(列表配置)
{
"targeDatatType": "sql|table|view|form|dataService",
"targetData": {
"dataSource": "jdbc/default",
"select": "SQL 片段或表名",
"where": "WHERE 条件",
"groupBy": "GROUP BY",
"orderBy": "ORDER BY"
},
"isShowSeq": true,
"isShowCheck": true,
"isPageable": true,
"isShowTitle": true,
"titleName": "列表标题",
"pageSize": 20,
"buttons": [],
"columns": [{ "name", "type", "displayName", "index", "width", "isShow", "events", "display" }]
}
PZCX(查询配置)
{
"quickMatch": ["FIELD1", "FIELD2"],
"queryField": [{ "name", "dataType", "title", "type", "operate", "alias?", "quickMatch?" }]
}
列宽规范
列宽通过 width 字段控制(百分比字符串),影响列表各列的显示宽度。
| 列类型 | 推荐宽度 | 说明 |
|---|---|---|
序号列(type: 'seq') |
5% |
固定窄列 |
| 选择框列(自动注入) | 5% |
固定窄列 |
操作列(type: 'operate') |
12% |
编辑+删除两个按钮 |
| 短文本列(编号、代码、类别) | 10% |
单词/短语级内容 |
| 中等文本列(名称、标题) | 15% |
2-4 个汉字 |
| 长文本列(描述、备注、地址) | 20% |
长文本内容 |
| 日期列 | 12% |
yyyy-MM-dd 格式 |
原则:
- 所有列 width 之和不应超过 100%(选择框和序号已占 10%)
- 列顺序:选择框 → 序号 → 数据列 → 操作列(操作列始终放在最后一列)
列类型
运行 node list_creator.js --list-types 查看所有可用列组件类型和配置示例。
格式渲染类型(display.type)
列的 display 对象包含 type 和 content 字段,控制列的渲染方式:
display.type |
display.content 类型 |
说明 |
|---|---|---|
'' 或未设置 |
任意 | 无格式渲染(默认) |
USER |
boolean | 人员类型(content=true 显示全称) |
DEPT |
boolean | 部门类型 |
HYLX |
boolean | 行业类型 |
DATE |
{ format: 'yyyy-MM-dd' } |
日期类型 |
COMMON_CODE |
{ dmjbh: 'DMJ_XXX' } |
公共代码值(⚠️ 小写 dmjbh) |
CUSTOM_CODE |
{ ... } |
自定义代码(后续支持) |
XZQY |
{ SHENG, SHI, QU, JD } |
行政区域 |
HTML |
string |
列模板 HTML |
OPERATE_BTN |
Array<{ key, displayName, function, type }> |
操作按钮 |
OTHER_DATA |
任意 | 其他数据 |
> ⚠️ display.content.dmjbh 中的 dmjbh 是小写,与后端 API 参数 DMJBH(大写)不同,不要混用。
查询控件类型
| type | 说明 | 适用 dataType |
|---|---|---|
text |
文本输入(默认) | string |
date |
日期选择器 | date |
codeset |
代码集下拉 | string |
user |
用户选择器 | string |
dept |
部门选择器 | string |
codeset 查询条件扩展字段
当 type='codeset' 时,查询字段对象包含以下额外字段:
| 字段 | 必填 | 默认 | 说明 |
|---|---|---|---|
dataSourceType |
是 | — | 'codeset'(本次只支持 codeset) |
dmjbh |
必填 | — | 公共代码集编号 |
dm |
否 | '' |
代码值过滤 |
fdm |
否 | '' |
父代码值过滤(树形代码集) |
multi |
否 | false |
是否多选 |
alignType |
否 | 'select' |
'tag' / 'select' / 'radio' / 'checkbox' |
isEmpty |
否 | false |
是否显示空值选项 |
initValue |
否 | '' |
默认值(代码值 DM) |
查询操作符
| 操作符 | 说明 |
|---|---|
LIKE |
模糊匹配 |
= |
精确匹配 |
RANGE |
范围(日期、数字) |
脚本类型
| 字段 | 说明 |
|---|---|
JB |
JavaScript 生命周期回调 + 按钮事件 |
YS |
CSS 样式 |
TBXX |
页头 HTML |
WBXX |
页尾 HTML |
修改模式
修改入口按「是否有 spec」分流(决策记录⑥):
方式一:有 spec → 改 spec → 重新构建覆盖线上
当列表 XH 能在某个 list-spec 的「## 构建记录」段反查到时,保持 spec 与线上一致:
- 读 spec(
.pacp/specs/动态渲染/{业务名}__列表.md) - 按用户修改意图改 spec(如加列、改查询条件)
- ⚠️ 若线上曾被手工改过(绕过 spec),重建前提示用户确认「重建会用 spec 覆盖线上手工改动」
- 走新增入口的 spec 驱动流程构建,提交时传入已有
XH(覆盖更新) - spec 回写更新构建记录
方式二:无 spec → 直改线上配置,不回写 spec
当列表 XH 在任何 list-spec 构建记录都反查不到时(历史/别处创建的列表):
- 用
client.getQueryConfig(xh)或client.listQueryConfigs()获取现有配置 - 解析 PZLB/PZCX JSON
- 对比变更(diff 分析)
- 确认摘要高亮变更(🟢新增/🔴删除/🟡修改)
- 提交时传入已有的
XH(覆盖更新),不回写 spec - 可提示用户「此列表无 spec,可用 pacp-design-list 补一份 spec 以便后续复用」
传入 XH 的三种方式
方式一:CLI --xh 参数(推荐)
node list_creator.js \
--base-url http://host:port/contextPath \
--session-id $SESSION_ID \
--xh 1781077346641011735040 \
--template-id FORM_TEMPLATE_XH \
--config list-config.json
方式二:JSON 配置中写 xh
{
"xh": "1781077346641011735040",
"title": "...",
"...": "..."
}
方式三:编程 API
const result = await createListConfig(client, {
// ... 同新增的参数
xh: existingXH, // 传入已有 XH 进入更新模式
});
> 优先级:--xh 命令行参数 > config.xh JSON 字段 > 空(新建)
表单关联(可选)
如果 spec 元信息「关联表单」字段有值(非「无」),需要关联动态表单(列表升级为 LX=3,支持增删改)。
前置:解析 templateId
按上方「关联表单的 templateId 解析」章节,读表单 spec 的构建记录段取 templateId。表单未构建则停止提示用户先构建。
调用 associateForm
使用 associateForm(client, listXH, templateId, options) 关联列表与表单:
// listXH 来自 createListConfig() 的返回值
// templateId 来自关联表单 spec 的构建记录段
const assocResult = await associateForm(client, listXH, templateId, {
primaryKey: 'XH', // 可选:主键字段名(spec 元信息「主键」)
// 以下默认为 true,用户要求关闭时设为 false:
// injectButtons: true, // 工具栏新增/删除按钮
// injectOperateColumn: true, // 行内编辑/删除按钮
// injectCheckbox: true, // 选择框列
// injectScript: true, // 动态表单脚本
});
// assocResult.listUrl, assocResult.formUrl
> [!important] 系统列注入与 config 声明的关系(自动让位,无需手动关闭)
> associateForm 会自动识别 config 已声明的系统列:config 已有 #CZL 操作列或 #XZAN 选择框列时,保留 config 的、不重复注入(含其自定义 width/按钮)。因此 spec 声明的系统列写进 config 后,associateForm 自动让位,无需手动设 injectOperateColumn: false / injectCheckbox: false。仅当 config 完全没声明对应系统列时,associateForm 才兜底注入默认列。
LX 类型说明
| LX 值 | 说明 | 使用场景 |
|---|---|---|
'1' |
普通列表(默认) | 纯数据展示,无表单操作 |
'2' |
选择器列表 | 弹窗选择场景:列表渲染为对话框,用户勾选行后通过确定/清除/关闭按钮交互 |
'3' |
动态表单模式 | 列表+表单联动,支持增删改(associateForm 自动升级到此) |
选择器列表(LX=2)
用于弹窗选择场景(如选择人员、选择部门、选择关联记录)。列表渲染为对话框,用户勾选行后通过确定/清除/关闭按钮与父组件交互。
创建方式:createListConfig(client, { lx: '2', ... }),或 CLI --lx 2,或 JSON 配置 "lx": "2"。
自动注入(调用方无需手动配置):
- columns 头部注入 #XZAN 类型选择列(不要额外设 isShowSelect=true,否则会重复渲染选择列)
- pzlb.buttons — 注入三个标准按钮:确定(confirmClick)、清除(clearClick)、关闭(closeClick)
- JB 脚本 — 默认实现三个函数(平台「#24 动态数据选择器」规范,JB 必须用 return {...} 对象包裹)
⚠️ 三按钮不可缺失(必要条件):选择器列表必须含「确定(confirmClick)、清除(clearClick)、关闭(closeClick)」三按钮及对应 JB 函数。
约束:
- 不传 buttons/jb → createListConfig 自动注入三按钮和脚本(推荐)
- 自定义 buttons(非空数组)→ 不覆盖,但调用方必须自行包含这三个按钮
- 自定义 jb → 不覆盖,但调用方必须自行实现上述三个函数
- 选择器列表不应再调用 associateForm()(LX=2 与 LX=3 互斥)
Excel 导入接入(按需加载)
用户要求列表支持「导入 Excel / 批量导入」时,动态列表运行时已内置 openImportDialog / downloadImportTemplate 能力,只需配置工具栏按钮 + JB 脚本即可接入。
> 硬约束:Excel 导入的后端三件套(importExcelService 业务 Service 类名 / jsonFilePath 列映射 JSON / templatePath xlsx 模板)必须由用户明确提供,AI 禁止编造。若用户未提供,必须主动询问。
按需加载:涉及 Excel 导入配置时,必须先加载 references/excel-import-guide.md 获取完整的触发词识别、后端三件套索取规范、按钮配置、JB 脚本模板、参数说明、完整示例和常见问题排查。
5.x 平台知识权威源(必读)
> 涉及以下主题时,优先加载 pacp-dynamic-wiki skill 的 references(本 skill 自带的 list-script-guide.md 为兜底副本):
>
> - 列表脚本写法(ScriptContext API、CRUD 四函数、选择器三按钮、openFormDialog 参数、jsTools 约束)→ wiki 的 5x/list-script-guide.md
> - 列表渲染契约(display.type、查询字段类型、列宽规范、LX 类型机制)→ wiki 的 5x/list-render-contract.md
> - 平台 API 端点(datasources/tables/dynamicqueryforconfig/...)→ wiki 的 5x/platform-api-endpoints.md
> - 平台数据结构(PZLB/PZCX/Template JSON/MODELJSON)→ wiki 的 5x/platform-data-structures.md
> - 平台机制(LX=2 选择器自动注入、关联表单、多表 JOIN)→ wiki 的 5x/platform-mechanisms.md
> - 平台陷阱(tempateId 拼写、_ELEMENT 后缀、getcloumninfo 小写、SQL 方言)→ wiki 的 5x/platform-pitfalls.md
> - Preview 渲染规范(--preview 输出格式)→ wiki 的 5x/preview-mode-design.md
>
> ZCode 会根据 description 自动触发 wiki skill(progressive disclosure)。
> 如未自动触发,AI 应主动通过 Skill 工具调用 pacp-dynamic-wiki。
>
> 5.x 平台升级时,wiki skill 是单一同步点,本 skill 不再单独维护这些知识。
API 端点(已验证 ✅)
| 端点 | 方法 | 说明 | 验证 |
|---|---|---|---|
/api/v2/platform/database/datasources |
POST | 数据源列表 | ✅ |
/api/v2/platform/database/tables |
POST | 表列表 | ✅ |
/api/.../getcloumninfo |
POST | 列信息(tablename 小写) |
✅ |
/api/.../dynamicqueryforconfig |
POST | 执行 SQL 获取字段 | ✅ |
/api/.../getformconfig |
POST | 查询列表配置列表 | ✅ |
/api/.../getDynamicQueryPzByXh |
GET | 按 XH 获取配置 | ✅ |
/api/.../saveformconfig |
POST | 保存列表配置 | ✅ |
/api/.../deldynamicqueryinfo |
POST | 删除列表配置 | ✅ |
/api/v2/platform/system/commoncode/ggdmj/query |
POST | 公共代码集分页查询 | ✅ |
/api/v2/platform/system/commoncode/ggdmz/query |
POST | 公共代码值分页查询 | ✅ |
/api/v2/platform/system/commoncode/ggdmz/list |
POST | 公共代码值列表(不分页) | ✅ |
> /api/.../ 是 /api/v2/common/component/queryservice/design/controller/dynamicquerycontroller/ 的缩写
认证
通过 --session-id 传入有效的 sessionId(优先读 .pacp/session.json 复用)。Session 过期时(401),提示用户提供新 sessionId。
错误处理
- Session 过期(401):提示用户提供新 Session
- 业务错误(500 errorCode):分析错误信息,修正后重试
- 列信息为空:提示用户确认表名是否正确
工作区文件
| 文件 | 用途 |
|---|---|
api-trace.jsonl |
所有 API 请求/响应的完整追踪(每行一个 JSON,含请求体、响应体、耗时) |
trace-summary.jsonl |
高级方法(createListConfig/associateForm)的结构化入参/出参摘要 |
table-info.md |
数据表结构信息 |
field-mapping.md |
spec→config 列对照表(构建前置产物) |
list-config.json |
从 spec 映射出的构建配置(喂给 list_creator.js) |
existing-list.json |
已有配置(修改模式) |
⚠️ 已知陷阱(必读)
以下陷阱均来自实际调试中遇到的血泪教训。创建列表前必须逐条检查。
| # | 陷阱 | 正确做法 | 症状 |
|---|---|---|---|
| 1 | MC 字段缺失 |
saveQueryConfig 必须传入 MC(列表名称),否则前端不显示 |
保存成功但设计列表页搜不到 |
| 2 | SFYX 未设为 1 |
SFYX: '1' 表示有效,'0' 或缺失 = 隐藏 |
列表存在但前端不可见 |
| 3 | LX 未设置 |
LX: '1' 表示普通列表,'0' 或缺失 = 类型异常 |
列表类型显示为空 |
| 4 | getcloumninfo 参数大小写 |
参数名是全小写 tablename,不是 tableName |
API 调用失败 |
| 5 | 列名与数据库不一致 | makeColumn 的 name 必须与 SELECT 中的列名一致 |
列表渲染时该列无数据 |
| 6 | tempateId 拼写(表单关联时) |
后端字段名是 tempateId(少一个 l) |
空字符串 → 错误码 120340 |
| 7 | attrbutevalues 直接格式 |
属性是直接键值对 {FIELD: 'xxx'},不是 {FIELD: {value: 'xxx'}} |
后端解析异常 |
| 8 | 列表渲染路由需要菜单权限 | /#/list-render/{xh} 需要菜单注册才能直接访问 |
直接 URL 访问 404 |
| 9 | dmjbh 大小写 |
display.content 中是小写 dmjbh,后端 API 参数是大写 DMJBH |
列渲染失败或代码值查询为空 |
| 10 | codeset operate 默认值 | codeset 查询条件 operate 默认 '='(精确匹配),不是 'LIKE' |
下拉筛选结果不正确 |
| 11 | sqlWhere 禁止加 SFYX | 表中可能没有 SFYX 列,sqlWhere 必须默认 1=1,禁止自行添加 SFYX = '1' |
查询报错"字段不存在" |
| 12 | associateForm 的 listXH 无效 | 调用前确认 createListConfig 返回了有效的 XH |
associateForm 报错 "找不到列表" |
| 13 | 数据库方言错误 | SELECT TOP n 是 SQL Server,LIMIT n 是 MySQL/PostgreSQL,ROWNUM <= n 是 Oracle。未确认数据库类型前禁止使用任何方言特有语法,必须用 ANSI SQL 通用写法(如 LEFT JOIN + MAX() 子查询替代 LIMIT 1) |
列表查询报错 SQLSyntaxErrorException |
| 14 | 选择器列表(LX=2)缺三按钮 | LX=2 必须有确定(confirmClick)/清除(clearClick)/关闭(closeClick)三按钮及对应 JB 函数;走默认流程会自动注入,但自定义 buttons 时也必须包含这三个(function 名必须对应) |
弹窗能打开却无法确认/清除/关闭,选择器不可用 |
跨 Skill 协作
与 pacp-design-list 联动(新增入口)
用户:"帮我设计/做一个 XX 列表"
↓
1. 先执行 pacp-design-list → 产出 list-spec.md(.pacp/specs/动态渲染/{业务名}__列表.md)
↓
2. 再执行 pacp-build-list(本 skill)→ 读 spec → 列对照表 → list-config.json → 构建 → spec 回写
与 pacp-build-form 联动(关联表单)
列表的 spec「关联表单」字段填表单 spec 文件名。build-list 构建前读表单 spec 的构建记录段取 templateId。完整 CRUD 流程:
用户:"帮我做 XX 的增删改查(表单+列表)"
↓
1. pacp-design-form → 产出 {业务名}__表单.md
2. pacp-build-form → 构建 → 回写表单 spec 的「构建记录」段(含 templateId)
3. pacp-design-list → 产出 {业务名}__列表.md(关联表单字段填 {业务名}__表单)
4. pacp-build-list(本 skill)→ 读列表 spec → 解析关联表单 templateId → 构建 + associateForm → 回写列表 spec 的构建记录段
两个 skill 的 BwaiClient 是独立的,方法集不同,必须各自创建各自的实例,不可共享。但两个 CLI 共用同一个 session-id(.pacp/session.json)。
// ─── 各自引入各自的模块 ───────────────────────────────────
const { BwaiClient: FormClient } = require('./skills/pacp-build-form/lib/bwai_client');
const { createFormConfig } = require('./skills/pacp-build-form/scripts/form_builder');
const { BwaiClient: ListClient } = require('./skills/pacp-build-list/lib/bwai_client');
const { createListConfig, associateForm } = require('./skills/pacp-build-list/scripts/list_builder');
// ─── 各自创建各自的 client,使用同一个 sessionId ──────────
const fClient = new FormClient(baseUrl, SESSION_ID);
const lClient = new ListClient(baseUrl, SESSION_ID);
⚠️ 关键约束:fClient 和 lClient 是不同类的实例,方法集不同,不可混用(fClient 有 saveForm 没 saveQueryConfig,lClient 有 saveQueryConfig 没 saveForm)。
完整 CRUD 流程(CLI 方式)
# 第1步:构建表单(pacp-build-form),其 templateId 会回写到 {业务名}__表单.md 的构建记录段
node skills/pacp-build-form/scripts/form_creator.js \
--base-url http://localhost:8080/basesystem \
--session-id $SESSION_ID \
--config form-config.json
# 第2步:构建列表 + 关联表单(templateId 从列表 spec 的关联表单字段解析,或手动传)
node skills/pacp-build-list/scripts/list_creator.js \
--base-url http://localhost:8080/basesystem \
--session-id $SESSION_ID \
--template-id "$TEMPLATE_ID" \
--config list-config.json