@agile-team/wl-skills-kit 2.8.0 → 2.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +1 -1
- package/bin/wl-skills.js +97 -1
- package/files/.github/copilot-instructions.md +1 -1
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/skills/core/page-codegen/SKILL.md +1 -1
- package/files/.github/skills/core/page-codegen/templates/_index.md +1 -1
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +9 -5
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +6 -7
- package/files/.github/skills/core/prototype-scan/SKILL.md +2 -2
- package/files/.github/standards/13-platform-components.md +2 -2
- package/files/.github/standards/14-layout-containers.md +159 -0
- package/files/.github/standards/index.md +4 -3
- package/files/demo/sale/demo/add-demo/index.scss +1 -1
- package/files/demo/sale/demo/add-demo/index.vue +8 -4
- package/files/demo/sale/demo/metallurgical-spec/index.scss +1 -1
- package/files/demo/sale/demo/metallurgical-spec/index.vue +7 -3
- package/files/src/components/global/C_Splitter/index.vue +16 -0
- package/files/src/components/local/c_formSections/README.md +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.9.2] - 2026-05-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`doctor-ui` 新增 C_Splitter 残留扫描(standards/14 一致性)**:
|
|
8
|
+
- 业务代码(`.vue / .ts / .scss / .js`)命中 → `✖ error`,列出文件:行号:片段
|
|
9
|
+
- 文档/规则(`.md / .mdc`)命中 → `⚠ warn`
|
|
10
|
+
- 自动豁免:上下文 ±1 行含 `已废弃 / DEPRECATED / 严禁 / 不再需要 / 已迁移 / deprecated`;`C_Splitter/` 组件目录自身豁免
|
|
11
|
+
- 明细分组打印,单次最多列 60 条,超出汇总"另有 X 处未列出"
|
|
12
|
+
- **`standards/14-layout-containers.md` 扩充**:
|
|
13
|
+
- §6 lint/codegen 强制项追加 `validate` / `doctor-ui` 命令矩阵
|
|
14
|
+
- 新增 §7 FAQ(旧页面是否要改、阈值配置、嵌套性能、过渡期保留)
|
|
15
|
+
- **`tests/doctor-ui.test.js`**:4 项新单测覆盖无残留、业务代码命中、豁免词、自身组件豁免
|
|
16
|
+
|
|
17
|
+
### Notes
|
|
18
|
+
|
|
19
|
+
- 测试矩阵:cli + lint-skills + doctor-ui + registry + version-tools 合计 **53 测试通过**
|
|
20
|
+
- 升级路径:`npx @agile-team/wl-skills-kit@latest update` 同步 standards/14;CI 增挂 `wl-skills doctor-ui` 即可
|
|
21
|
+
|
|
22
|
+
## [2.9.1] - 2026-05-17
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`validate` 三项新检查**(接 v2.9.0 的 standards/14 落地阻断):
|
|
27
|
+
- 🔴 `error`:页面 `index.vue` 出现 `<C_Splitter>` 标签
|
|
28
|
+
- 🔴 `error`:`index.vue` / `data.ts` 出现 `import C_Splitter`
|
|
29
|
+
- ℹ `info`:提及 `C_Splitter` 的过时注释(如 `已改为 C_Splitter`、`migrate to C_Splitter`、`TODO ... C_Splitter`)
|
|
30
|
+
- **`scripts/lint-skills.js` 新增规则 8**:扫描 `files/**/*.{vue,ts}`,禁止任何 `<C_Splitter>` 标签或 `import C_Splitter`(仅允许 `files/src/components/global/C_Splitter/index.vue` 自身保留废弃声明)。
|
|
31
|
+
- **`.husky/pre-commit` 接入 `lint-skills`**:维护者侧提交时自动守门,从源头阻止 C_Splitter 回潮。
|
|
32
|
+
|
|
33
|
+
### Notes
|
|
34
|
+
|
|
35
|
+
- 实战验证:wl-mdata 现网 28 个页面 0 命中(之前已全量迁移);故意污染测试用例可精准触发 3 项检查。
|
|
36
|
+
- 兼容性:所有新检查均为加法,未改动既有 validate 规则;下游项目无需调整。
|
|
37
|
+
|
|
38
|
+
## [2.9.0] - 2026-05-17
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **`files/.github/standards/14-layout-containers.md`**(新规范,🔴 必遵 + 阻断):布局容器规范。根因解析 `C_Splitter` 在 `onMounted` 中调用 `slots.default()` 冻结 vnode 快照、导致子树响应式完全失效;明确左右分割只用 `jh-drag-col`(`#left`/`#right`),上下分栈只用 `jh-drag-row`(`#top`/`#bottom`);附迁移对照表、lint 规则、废弃路线图。
|
|
43
|
+
- **`files/src/components/global/C_Splitter/index.vue`** 加入 `@deprecated` 注释 + 运行时 `console.warn`(同会话仅警告一次),引导改用 `jh-drag-col` / `jh-drag-row`。
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`standards/index.md`**:13 条 → 14 条;任务 A(生成新页面)/ B(修改重构)必读集合纳入 `14`。
|
|
48
|
+
- **`standards/13-platform-components.md`** 与 `copilot-instructions.md`:「左右分割」推荐组件由 `C_Splitter` 改为 `jh-drag-col`;明确 `C_Splitter` 已废弃及根因。
|
|
49
|
+
- **TPL-TREE-LIST.md**:模板示例改为 `<jh-drag-col :leftWidth="220">` + `#left`/`#right` 显式插槽;SCSS 去除 `.my-splitter-container :deep` 残留;顶部加「布局硬约束」段。
|
|
50
|
+
- **TPL-DETAIL-TABS.md**:示例由 `<C_Splitter direction="vertical">` 改为 `<jh-drag-row :topHeight="380">` + `#top`/`#bottom`;移除 `import C_Splitter`。
|
|
51
|
+
- **`page-codegen/SKILL.md`** 与 `templates/_index.md`:TREE_LIST 描述同步至 jh-drag-col;上下分栈描述同步至 jh-drag-row。
|
|
52
|
+
- **`prototype-scan/SKILL.md`**:原型扫描产出物示例与说明同步至 jh-drag-col。
|
|
53
|
+
- **`demo/sale/demo/add-demo/`** 与 **`demo/sale/demo/metallurgical-spec/`**:示例 `<C_Splitter>` 全部迁移到 `<jh-drag-row>`;SCSS 注释、README 文案对齐。
|
|
54
|
+
|
|
55
|
+
### Notes
|
|
56
|
+
|
|
57
|
+
- 兼容性:`C_Splitter` 源文件保留但加废弃警告,**不删除**,避免外部业务项目升级时立即报错;下一个 major 版本前再做物理移除评估。
|
|
58
|
+
- 参考案例:`wl-ui-sale` 项目所有左树右表/上下分栈页面(material、materialCategory、transactionType、unit、price-maintain、ContractEditTab 等)均使用 `jh-drag-col` / `jh-drag-row`,未踩 `C_Splitter` 冻 vnode 坑——本次规范固化的正是该实战路径。
|
|
59
|
+
|
|
3
60
|
## [2.8.0] - 2026-05-16
|
|
4
61
|
|
|
5
62
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @agile-team/wl-skills-kit
|
|
2
2
|
|
|
3
|
-
**AI Skill 模板包 v2.
|
|
3
|
+
**AI Skill 模板包 v2.9.2 ** — 一键将 13 条规范、10 个 AI Skill、17 个 MCP Tool、编辑器 MCP 配置、文档导入 Vue 3 项目。
|
|
4
4
|
|
|
5
5
|
让 AI 编辑器(Copilot / Cursor / Windsurf / Claude Code / Cline / Kiro / Trae / Qoder / 通用 Agents)**真正理解项目规范**,从原型/详设到完整页面代码全流程自动化。
|
|
6
6
|
|
package/bin/wl-skills.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* wl-skills-kit CLI v2.
|
|
4
|
+
* wl-skills-kit CLI v2.9.2
|
|
5
5
|
*
|
|
6
6
|
* 命令:
|
|
7
7
|
* init 全量安装(默认,向后兼容)
|
|
@@ -831,6 +831,17 @@ function scanPageDirs(scanRel) {
|
|
|
831
831
|
hasEmptyOnClick: /onClick\s*:\s*\(\s*[^)]*\s*\)\s*=>\s*\{\s*\}/.test(
|
|
832
832
|
dataContent,
|
|
833
833
|
),
|
|
834
|
+
hasCSplitterTag: /<C_Splitter\b/.test(indexContent),
|
|
835
|
+
hasCSplitterImport:
|
|
836
|
+
/from\s+["'][^"']*C_Splitter[^"']*["']/.test(indexContent) ||
|
|
837
|
+
/from\s+["'][^"']*C_Splitter[^"']*["']/.test(dataContent),
|
|
838
|
+
staleSplitterComments: (
|
|
839
|
+
(indexContent.match(/(?:已改为|migrate to|TODO).{0,40}C_Splitter/g) || [])
|
|
840
|
+
.concat(
|
|
841
|
+
dataContent.match(/(?:已改为|migrate to|TODO).{0,40}C_Splitter/g) ||
|
|
842
|
+
[],
|
|
843
|
+
)
|
|
844
|
+
).length,
|
|
834
845
|
apiUrls: Array.from(
|
|
835
846
|
dataContent.matchAll(/:\s*["']([^"']+\/[^"']+)["']/g),
|
|
836
847
|
).map((m) => m[1]),
|
|
@@ -972,6 +983,27 @@ function runValidate() {
|
|
|
972
983
|
dir: page.dir,
|
|
973
984
|
text: "存在空 onClick: () => {}",
|
|
974
985
|
});
|
|
986
|
+
if (page.hasCSplitterTag)
|
|
987
|
+
issues.push({
|
|
988
|
+
level: "error",
|
|
989
|
+
dir: page.dir,
|
|
990
|
+
text: "禁用 <C_Splitter>:请改用 jh-drag-col(左右)/ jh-drag-row(上下),详 standards/14-layout-containers.md",
|
|
991
|
+
});
|
|
992
|
+
if (page.hasCSplitterImport)
|
|
993
|
+
issues.push({
|
|
994
|
+
level: "error",
|
|
995
|
+
dir: page.dir,
|
|
996
|
+
text: "禁止 import C_Splitter:该组件已废弃(onMounted 冻 vnode 致响应式失效),详 standards/14",
|
|
997
|
+
});
|
|
998
|
+
if (page.staleSplitterComments > 0)
|
|
999
|
+
issues.push({
|
|
1000
|
+
level: "info",
|
|
1001
|
+
dir: page.dir,
|
|
1002
|
+
text:
|
|
1003
|
+
"发现 " +
|
|
1004
|
+
page.staleSplitterComments +
|
|
1005
|
+
" 处提及 C_Splitter 的过时注释,建议清理",
|
|
1006
|
+
});
|
|
975
1007
|
if (page.apiConfigCount > 0 && mockFiles.length === 0)
|
|
976
1008
|
issues.push({
|
|
977
1009
|
level: "warn",
|
|
@@ -1074,11 +1106,75 @@ function runDoctorUi() {
|
|
|
1074
1106
|
);
|
|
1075
1107
|
add("renderOps", /renderOps\s*\(/.test(allSource), "操作列需使用 renderOps");
|
|
1076
1108
|
|
|
1109
|
+
// —— C_Splitter 残留扫描(standards/14 一致性)——
|
|
1110
|
+
// 业务代码(.vue / .scss / .ts)禁止任何 C_Splitter;文档/规则(.md / .mdc)只允许"废弃说明"句式
|
|
1111
|
+
const EXEMPT_KEYWORDS =
|
|
1112
|
+
/已废弃|DEPRECATED|严禁|不再需要|已迁移|deprecated/i;
|
|
1113
|
+
const splitterFiles = files.filter(
|
|
1114
|
+
(rel) =>
|
|
1115
|
+
/\.(ts|vue|scss|js|tsx|md|mdc)$/.test(rel) &&
|
|
1116
|
+
!rel.startsWith("node_modules/") &&
|
|
1117
|
+
!rel.startsWith("dist/") &&
|
|
1118
|
+
!rel.startsWith(".git/"),
|
|
1119
|
+
);
|
|
1120
|
+
const codeHits = [];
|
|
1121
|
+
const docHits = [];
|
|
1122
|
+
for (const rel of splitterFiles) {
|
|
1123
|
+
if (
|
|
1124
|
+
rel.includes("/C_Splitter/") ||
|
|
1125
|
+
rel.endsWith("/C_Splitter/index.vue") ||
|
|
1126
|
+
rel.endsWith("/C_Splitter/index.scss")
|
|
1127
|
+
)
|
|
1128
|
+
continue; // 组件自身保留(带 deprecation warning)
|
|
1129
|
+
const full = path.join(TARGET_DIR, rel);
|
|
1130
|
+
let content;
|
|
1131
|
+
try {
|
|
1132
|
+
content = fs.readFileSync(full, "utf8");
|
|
1133
|
+
} catch {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (!/C_Splitter/.test(content)) continue;
|
|
1137
|
+
const lines = content.split(/\r?\n/);
|
|
1138
|
+
lines.forEach((line, idx) => {
|
|
1139
|
+
if (!/C_Splitter/.test(line)) return;
|
|
1140
|
+
// 取上下文 ±1 行做豁免判断
|
|
1141
|
+
const ctx = [lines[idx - 1] || "", line, lines[idx + 1] || ""].join("\n");
|
|
1142
|
+
if (EXEMPT_KEYWORDS.test(ctx)) return;
|
|
1143
|
+
const item = { rel, line: idx + 1, text: line.trim().slice(0, 100) };
|
|
1144
|
+
if (/\.(vue|ts|scss|js|tsx)$/.test(rel)) codeHits.push(item);
|
|
1145
|
+
else docHits.push(item);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
add(
|
|
1149
|
+
"C_Splitter 业务代码残留",
|
|
1150
|
+
codeHits.length === 0,
|
|
1151
|
+
codeHits.length === 0
|
|
1152
|
+
? "无"
|
|
1153
|
+
: codeHits.length + " 处(详见下方明细,需改 jh-drag-col/-row)",
|
|
1154
|
+
);
|
|
1155
|
+
add(
|
|
1156
|
+
"C_Splitter 文档/规则残留",
|
|
1157
|
+
docHits.length === 0,
|
|
1158
|
+
docHits.length === 0
|
|
1159
|
+
? "无"
|
|
1160
|
+
: docHits.length + " 处(详见下方明细,建议同步说明)",
|
|
1161
|
+
);
|
|
1162
|
+
|
|
1077
1163
|
for (const item of checks) {
|
|
1078
1164
|
console.log(
|
|
1079
1165
|
" " + statusIcon(item.ok) + " " + item.name + " — " + item.detail,
|
|
1080
1166
|
);
|
|
1081
1167
|
}
|
|
1168
|
+
if (codeHits.length || docHits.length) {
|
|
1169
|
+
console.log("");
|
|
1170
|
+
console.log(" ── C_Splitter 残留明细 ──");
|
|
1171
|
+
for (const h of codeHits.slice(0, 30))
|
|
1172
|
+
console.log(" ✖ " + h.rel + ":" + h.line + " " + h.text);
|
|
1173
|
+
for (const h of docHits.slice(0, 30))
|
|
1174
|
+
console.log(" ⚠ " + h.rel + ":" + h.line + " " + h.text);
|
|
1175
|
+
const overflow = codeHits.length + docHits.length - 60;
|
|
1176
|
+
if (overflow > 0) console.log(" … 另有 " + overflow + " 处未列出");
|
|
1177
|
+
}
|
|
1082
1178
|
const failed = checks.filter((item) => !item.ok).length;
|
|
1083
1179
|
console.log("");
|
|
1084
1180
|
console.log(
|
|
@@ -203,7 +203,7 @@ onMounted(() => select());
|
|
|
203
203
|
| 部门选择 | jh-dept-picker | @jhlc/jh-ui |
|
|
204
204
|
| 文件上传 | jh-file-upload | @jhlc/jh-ui |
|
|
205
205
|
| 文本翻译 | jh-text | @jhlc/jh-ui |
|
|
206
|
-
| 左右分割 |
|
|
206
|
+
| 左右分割 | jh-drag-col | @jhlc/jh-ui (#left/#right slot) |
|
|
207
207
|
| 树形面板 | C_Tree | src/components/global |
|
|
208
208
|
|
|
209
209
|
## 组件提取策略
|
|
@@ -1065,7 +1065,7 @@ SYS_MENU_INFO.md 是 menu-sync Skill 的输入数据源:
|
|
|
1065
1065
|
| LIST | templates/universal/TPL-LIST.md | 标准查询+工具栏+表格+分页 | mmwr-customer-archive |
|
|
1066
1066
|
| MASTER_DETAIL | templates/universal/TPL-MASTER-DETAIL.md | jh-drag-row 主从表,双击联动 | ompt-ht-plan-order |
|
|
1067
1067
|
| TREE_LIST | templates/universal/TPL-TREE-LIST.md | 左侧 C_Tree + 右侧列表 | — |
|
|
1068
|
-
| DETAIL_TABS | templates/universal/TPL-DETAIL-TABS.md |
|
|
1068
|
+
| DETAIL_TABS | templates/universal/TPL-DETAIL-TABS.md | jh-drag-row 上Tab表单+下子表 | add-demo / domestic-trade-order |
|
|
1069
1069
|
| FORM_ROUTE | templates/universal/TPL-FORM-ROUTE.md | 复杂表单独立路由(非弹窗) | mmwr-customer-apply-add-form |
|
|
1070
1070
|
| CHANGE_HISTORY | templates/universal/TPL-CHANGE-HISTORY.md | 左历史时间线+右变更详情 | mmwr-customer-apply-change-history |
|
|
1071
1071
|
| RECORD_FORM | templates/universal/TPL-RECORD-FORM.md | BaseQuery选主记录+Form+Table无分页 | mmsm-convert-progress |
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| LIST | `templates/universal/TPL-LIST.md` | 标准列表页(查询+工具栏+表格+分页) |
|
|
12
12
|
| FORM_ROUTE | `templates/universal/TPL-FORM-ROUTE.md` | 复杂表单独立路由页(多 Tab / 多子表) |
|
|
13
13
|
| MASTER_DETAIL | `templates/universal/TPL-MASTER-DETAIL.md` | 主从表页(jh-drag-row 上下分栏) |
|
|
14
|
-
| TREE_LIST | `templates/universal/TPL-TREE-LIST.md` | 左树右列表页(
|
|
14
|
+
| TREE_LIST | `templates/universal/TPL-TREE-LIST.md` | 左树右列表页(jh-drag-col 布局) |
|
|
15
15
|
| DETAIL_TABS | `templates/universal/TPL-DETAIL-TABS.md` | 详情 Tab 页(上方表单 + 下方 Tab 子表) |
|
|
16
16
|
| CHANGE_HISTORY | `templates/universal/TPL-CHANGE-HISTORY.md` | 变更历史比对页(时间线 + 字段差异) |
|
|
17
17
|
| RECORD_FORM | `templates/universal/TPL-RECORD-FORM.md` | 录入型实绩页(无分页,查询 + 内联表单) |
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
4
|
|
|
5
5
|
> 适用场景:编辑/维护页面,上半区为多 Tab 表单(基本信息/客户信息/其他信息),下半区为子项表格。
|
|
6
|
-
>
|
|
6
|
+
> **布局核心**:`jh-drag-row :topHeight="..."` 垂直分割上下区域(严禁 `C_Splitter`,详 standards/14-layout-containers.md)。
|
|
7
7
|
> **参考标杆**:`src/views/sale/demo/add-demo/`、`src/views/sale/demo/domestic-trade-order-mainten/`
|
|
8
8
|
|
|
9
9
|
#### index.vue
|
|
10
10
|
|
|
11
11
|
```vue
|
|
12
12
|
<template>
|
|
13
|
-
<div class="app-container app-page-container">
|
|
14
|
-
<
|
|
13
|
+
<div class="app-container app-page-container" style="height: 100%">
|
|
14
|
+
<jh-drag-row :topHeight="380">
|
|
15
|
+
<template #top>
|
|
15
16
|
<!-- 上:表单区 -->
|
|
16
17
|
<el-card shadow="never" class="form-card">
|
|
17
18
|
<!-- 页头工具栏 -->
|
|
@@ -66,7 +67,9 @@
|
|
|
66
67
|
</el-tab-pane>
|
|
67
68
|
</el-tabs>
|
|
68
69
|
</el-card>
|
|
70
|
+
</template>
|
|
69
71
|
|
|
72
|
+
<template #bottom>
|
|
70
73
|
<!-- 下:子项表格区 -->
|
|
71
74
|
<el-card shadow="never" class="items-card">
|
|
72
75
|
<div class="items-section">
|
|
@@ -97,13 +100,14 @@
|
|
|
97
100
|
/>
|
|
98
101
|
</div>
|
|
99
102
|
</el-card>
|
|
100
|
-
|
|
103
|
+
</template>
|
|
104
|
+
</jh-drag-row>
|
|
101
105
|
</div>
|
|
102
106
|
</template>
|
|
103
107
|
|
|
104
108
|
<script setup lang="ts">
|
|
105
109
|
import { onMounted } from "vue";
|
|
106
|
-
import C_Splitter
|
|
110
|
+
// ⚠️ 不再需要 import C_Splitter——jh-drag-row 是 @jhlc/jh-ui 全局注册组件
|
|
107
111
|
import {
|
|
108
112
|
form,
|
|
109
113
|
rules,
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# TREE_LIST:树形+列表
|
|
2
2
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
|
+
> **布局硬约束**:严禁使用 `C_Splitter`(冻vnode导致响应式完全失效),必须用 `jh-drag-col`(详 standards/14-layout-containers.md)。
|
|
4
5
|
|
|
5
6
|
#### index.vue
|
|
6
7
|
|
|
7
8
|
```vue
|
|
8
9
|
<template>
|
|
9
|
-
<div class="app-container app-page-container">
|
|
10
|
-
<
|
|
10
|
+
<div class="app-container app-page-container" style="height: 100%">
|
|
11
|
+
<jh-drag-col :leftWidth="220">
|
|
11
12
|
<template #left>
|
|
12
13
|
<C_Tree
|
|
13
14
|
:tree-data="treeData"
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
@size-change="select"
|
|
41
42
|
/>
|
|
42
43
|
</template>
|
|
43
|
-
</
|
|
44
|
+
</jh-drag-col>
|
|
44
45
|
</div>
|
|
45
46
|
</template>
|
|
46
47
|
|
|
@@ -197,10 +198,8 @@ export function createPage(editModalRef?: any) {
|
|
|
197
198
|
|
|
198
199
|
```scss
|
|
199
200
|
.app-page-container {
|
|
200
|
-
//
|
|
201
|
-
:
|
|
202
|
-
height: 100%;
|
|
203
|
-
}
|
|
201
|
+
// jh-drag-col 需要父容器擑满高度
|
|
202
|
+
height: 100%;
|
|
204
203
|
}
|
|
205
204
|
```
|
|
206
205
|
|
|
@@ -144,7 +144,7 @@ AI 根据提取的信息,内部构建 page-spec JSON(**不输出给用户**
|
|
|
144
144
|
| --------------- | --------------------- | ------------------------------------------------ |
|
|
145
145
|
| `LIST` | 查询区 + 表格 + 分页 | BaseQuery + BaseTable + jh-pagination |
|
|
146
146
|
| `MASTER_DETAIL` | 上方主表 + 下方明细表 | jh-drag-row(需 `.drager_row { height: 100% }`) |
|
|
147
|
-
| `TREE_LIST` | 左侧树 + 右侧表格 |
|
|
147
|
+
| `TREE_LIST` | 左侧树 + 右侧表格 | jh-drag-col + C_Tree |
|
|
148
148
|
| `FORM_MODAL` | 弹窗中的表单 | el-dialog + el-form |
|
|
149
149
|
| `COMPOSITE` | 多种组合 | 组合使用 |
|
|
150
150
|
|
|
@@ -195,7 +195,7 @@ AI 根据提取的信息,内部构建 page-spec JSON(**不输出给用户**
|
|
|
195
195
|
| 表格 | BaseTable | 通过 `columnsDef()` 声明式配置 |
|
|
196
196
|
| 分页 | jh-pagination | 固定用法,见 copilot-instructions.md |
|
|
197
197
|
| 上下分栏 | jh-drag-row | 主从表必备,需设 `:top-height` |
|
|
198
|
-
| 左右分割 |
|
|
198
|
+
| 左右分割 | jh-drag-col | 树形+列表必备,设 `:leftWidth` + #left/#right slot |
|
|
199
199
|
| 树形面板 | C_Tree | 含搜索+Tab 切换 |
|
|
200
200
|
| 下拉选择 | jh-select | dict 属性自动加载字典数据 |
|
|
201
201
|
| 日期选择 | jh-date / jh-date-range | 参见 `docs/jh-date.md` |
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
| 下拉 / 选择器 | `jh-select` / `jh-picker` | ❌ el-select 手写 options |
|
|
32
32
|
| 只读文本展示 | `jh-text` | ❌ span/div 直接渲染 |
|
|
33
33
|
| 分页 | `jh-pagination` | ❌ el-pagination |
|
|
34
|
-
| 上下分栏 | `jh-drag-row`
|
|
35
|
-
| 左右分割 | `
|
|
34
|
+
| 上下分栏 | `jh-drag-row` (#top/#bottom) | ❌ 手动 flex/grid |
|
|
35
|
+
| 左右分割 | `jh-drag-col` (#left/#right) | ❌️ `C_Splitter`(已废弃,onMounted 冻vnode,导致子树响应式完全失效) |
|
|
36
36
|
| 树形面板 | `C_Tree` | ❌ el-tree 手写 |
|
|
37
37
|
| 状态标签 | `C_TagStatus` | ❌ el-tag + 颜色映射 |
|
|
38
38
|
| HTTP 请求 | `getAction/postAction/putAction/deleteAction` | ❌ axios / fetch 直接调用 |
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# 14 — 布局容器规范(C_Splitter 禁用 + jh-drag-col/row 唯一推荐)
|
|
2
|
+
|
|
3
|
+
> **强制度**:🔴 必遵 + 阻断式(lint 命中即报错)。
|
|
4
|
+
> **背景**:2024 年 12 月一次真实事故,左树右表页面右侧面板永不刷新,最终定位为 `C_Splitter` 在 `onMounted` 中调用 `slots.default()` 冻结 vnode 快照,导致子树所有响应式绑定与父组件 ref 脱钩。
|
|
5
|
+
> **结论**:项目中**禁止再使用 `C_Splitter`**,所有左右/上下分栏一律用 `jh-drag-col` / `jh-drag-row`。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 强制对照
|
|
10
|
+
|
|
11
|
+
| 场景 | ✅ 必用 | ❌ 禁用 |
|
|
12
|
+
| ---------- | -------------------------------------- | ------------------- |
|
|
13
|
+
| 左右分栏 | `<jh-drag-col :leftWidth="240">` + `#left` / `#right` slot | `C_Splitter`、`el-aside`+`el-main` 手写 flex |
|
|
14
|
+
| 上下分栏 | `<jh-drag-row :topHeight="240">` + `#top` / `#bottom` slot | `C_Splitter direction="vertical"`、手写 flex |
|
|
15
|
+
| 嵌套分栏 | 多层 `jh-drag-col` / `jh-drag-row` 直接嵌套 | C_Splitter 嵌套(双倍 vnode 冻结) |
|
|
16
|
+
|
|
17
|
+
> `@jhlc/jh-ui` 的 `jh-drag-col` / `jh-drag-row` 使用 Vue 原生 `<slot />` 直接渲染,**不缓存 vnode**,子组件响应式与父组件 ref 完全连通。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. C_Splitter 为什么必须废弃(根因)
|
|
22
|
+
|
|
23
|
+
`src/components/global/C_Splitter/index.vue` 内部实现:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
onMounted(() => {
|
|
27
|
+
const defaultSlots = slots.default ? slots.default() : [];
|
|
28
|
+
// ...
|
|
29
|
+
vnodes.value = children; // 冻结 vnode 列表
|
|
30
|
+
});
|
|
31
|
+
// 模板里
|
|
32
|
+
// <component :is="item" />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**反应链**:
|
|
36
|
+
|
|
37
|
+
1. `slots.default()` 只在 `onMounted` 执行一次 → 拿到的是**当时**的 vnode 快照
|
|
38
|
+
2. 模板用 `<component :is="item" />` 渲染快照 → 子组件的所有 props/slot props/ref 绑定**永远定格在 mount 那一刻**
|
|
39
|
+
3. 父组件后续修改 ref(`activeModelId.value = "xxx"`)→ 子组件 v-if/v-show/插值**全部失效**
|
|
40
|
+
4. 表现:点击树节点,右侧表格**永远显示初始数据**或空白;vue-devtools 看 ref 已变但 UI 不动
|
|
41
|
+
|
|
42
|
+
> 这是 Vue 3 slot 渲染模型的本质:`slots.default()` 是一次性的 render function 调用,**不能用 ref 缓存其结果当模板用**。任何"缓存 vnode 数组再 component is 渲染"的写法都有同样的 bug。
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. 标准用法
|
|
47
|
+
|
|
48
|
+
### 3.1 左树右表(最常见)
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<template>
|
|
52
|
+
<div class="app-container app-page-container" style="height: 100%">
|
|
53
|
+
<jh-drag-col :leftWidth="240">
|
|
54
|
+
<template #left>
|
|
55
|
+
<C_Tree :data="treeData" @node-click="onNodeClick" />
|
|
56
|
+
</template>
|
|
57
|
+
<template #right>
|
|
58
|
+
<BaseQuery ... />
|
|
59
|
+
<BaseToolbar ... />
|
|
60
|
+
<BaseTable v-if="activeModelId" ... />
|
|
61
|
+
</template>
|
|
62
|
+
</jh-drag-col>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
✅ `v-if`、`ref` 赋值、所有响应式都能正常驱动右侧重渲染。
|
|
68
|
+
|
|
69
|
+
### 3.2 上表下详情(master-detail)
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<jh-drag-row :topHeight="320">
|
|
73
|
+
<template #top>
|
|
74
|
+
<BaseTable ... @row-click="onRowClick" />
|
|
75
|
+
</template>
|
|
76
|
+
<template #bottom>
|
|
77
|
+
<DetailPanel v-if="currentRow" :data="currentRow" />
|
|
78
|
+
</template>
|
|
79
|
+
</jh-drag-row>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3.3 上 Tab 表单 + 下子表
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<jh-drag-row :topHeight="280">
|
|
86
|
+
<template #top>
|
|
87
|
+
<el-tabs v-model="activeTab"> ... </el-tabs>
|
|
88
|
+
</template>
|
|
89
|
+
<template #bottom>
|
|
90
|
+
<BaseTable ... />
|
|
91
|
+
</template>
|
|
92
|
+
</jh-drag-row>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4. 兼容期处理
|
|
98
|
+
|
|
99
|
+
若现存项目仍引用 `C_Splitter`:
|
|
100
|
+
|
|
101
|
+
1. **当前版本**:保留组件文件,在 `onMounted` 顶部加 `console.warn("[C_Splitter 已废弃] ...")` 提示
|
|
102
|
+
2. **下一版本**:删除 `src/components/global/C_Splitter/`,全量替换为 `jh-drag-col/row`
|
|
103
|
+
3. **lint 规则**:`wl-skills validate` / `wl-skills doctor-ui` 命中 `import C_Splitter` 或 `<C_Splitter` 时报 ERROR
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 5. 自动迁移建议
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# 项目根目录执行
|
|
111
|
+
grep -rln "C_Splitter" src/views | while read f; do
|
|
112
|
+
echo "需要人工改造:$f"
|
|
113
|
+
done
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
迁移要点:
|
|
117
|
+
|
|
118
|
+
| 旧写法 | 新写法 |
|
|
119
|
+
| ------ | ------ |
|
|
120
|
+
| `<C_Splitter :left-width="220">` | `<jh-drag-col :leftWidth="220">` |
|
|
121
|
+
| `<C_Splitter direction="vertical">` | `<jh-drag-row :topHeight="...">` |
|
|
122
|
+
| 默认 slot 顺序:第一项 / 第二项 | 显式 `#left` `#right` / `#top` `#bottom` |
|
|
123
|
+
| 拖动配置:`min-left-width="200"` | `jh-drag-col` 内置阈值 |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 6. lint / codegen 强制项
|
|
128
|
+
|
|
129
|
+
- `prototype-scan` / `page-codegen` 生成的模板**禁止**包含 `C_Splitter`
|
|
130
|
+
- TPL-TREE-LIST、TPL-DETAIL-TABS 等模板必须使用 `jh-drag-col` / `jh-drag-row`
|
|
131
|
+
- `wl-skills validate-page` 扫到 `C_Splitter` 直接 fail
|
|
132
|
+
- `wl-skills validate src/views`:业务页面扫描 `<C_Splitter` / `import C_Splitter` / 过时注释(error / info 三级)
|
|
133
|
+
- `wl-skills doctor-ui`:全仓扫描 `.vue/.ts/.scss/.md/.mdc`,区分**业务代码残留(error)**与**文档/规则残留(warn)**;含 `已废弃|DEPRECATED|严禁|不再需要|已迁移` 关键词的上下文自动豁免;`C_Splitter/` 组件目录自身豁免
|
|
134
|
+
|
|
135
|
+
> 推荐在 CI 非阻断阶段挂 `wl-skills doctor-ui`,残留明细一目了然;提交前 pre-commit 由 `lint-skills` 兜底,禁止任何新增引用流入仓库。
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 7. FAQ
|
|
140
|
+
|
|
141
|
+
**Q1:旧页面跑得好好的,为什么也要改?**
|
|
142
|
+
现状只是"还没踩到 ref 变更的场景"。一旦页面后续接入树节点切换 / 全屏刷新 / 编辑回填,就会出现 ref 改了 UI 不动的灵异 bug,排查成本远大于一次性迁移。
|
|
143
|
+
|
|
144
|
+
**Q2:`jh-drag-col` 没有 `min-left-width` 怎么办?**
|
|
145
|
+
内部默认 200~600 阈值已可用;如需自定义,传 `:minLeftWidth` / `:maxLeftWidth`(数值,单位 px)。
|
|
146
|
+
|
|
147
|
+
**Q3:嵌套两层分栏会有性能问题吗?**
|
|
148
|
+
不会。`jh-drag-col` / `jh-drag-row` 都是直接 `<slot />`,没有 vnode 缓存或额外 watcher,嵌套层数与原生 div 等价。
|
|
149
|
+
|
|
150
|
+
**Q4:必须保留 `C_Splitter` 组件文件吗?**
|
|
151
|
+
保留一段过渡期(带 deprecation warning)即可。等仓库扫描 0 命中后,下一个大版本直接删除 `src/components/global/C_Splitter/`。
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 关联
|
|
156
|
+
|
|
157
|
+
- `12-base-table.md` — BaseTable 内部高度撑满依赖父容器有明确高度,jh-drag-col/row 已正确给子区设 `height: 100%`
|
|
158
|
+
- `13-platform-components.md` — 平台组件对照表已同步移除 C_Splitter
|
|
159
|
+
- 真实场景案例:`demo/produce/aiflow/mmwr-customer-detail/`(master-detail 使用 jh-drag-row)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 14 条规范清单
|
|
9
9
|
|
|
10
10
|
| 编号 | 文件 | 主题 | 强制度 |
|
|
11
11
|
| ---- | --------------------------- | ---------------------- | -------------- |
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| 11 | `11-form-validation.md` | 表单与校验 | 🔴 必遵 |
|
|
23
23
|
| 12 | `12-base-table.md` | BaseTable + AGGrid cid | 🔴 必遵 |
|
|
24
24
|
| 13 | `13-platform-components.md` | 平台组件合规(核心) | 🔴 必遵 + 阻断 |
|
|
25
|
+
| 14 | `14-layout-containers.md` | 布局容器(禁用 C_Splitter) | 🔴 必遵 + 阻断 |
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
@@ -32,14 +33,14 @@
|
|
|
32
33
|
### 任务类型 A:生成新页面(page-codegen)
|
|
33
34
|
|
|
34
35
|
```
|
|
35
|
-
必读:01 / 02 / 04 / 12 / 13
|
|
36
|
+
必读:01 / 02 / 04 / 12 / 13 / 14
|
|
36
37
|
按需:09(TS 类型复杂时) / 10(涉及 Store) / 11(FORM_ROUTE 模板)
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
### 任务类型 B:修改/重构既有页面
|
|
40
41
|
|
|
41
42
|
```
|
|
42
|
-
必读:02 / 04 / 13
|
|
43
|
+
必读:02 / 04 / 13 / 14
|
|
43
44
|
按需:09 / 10 / 11
|
|
44
45
|
```
|
|
45
46
|
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
-->
|
|
10
10
|
<template>
|
|
11
11
|
<div class="main-maintenance-container">
|
|
12
|
-
<!--
|
|
13
|
-
<
|
|
12
|
+
<!-- jh-drag-row 包裹表单区和项次信息(上下拖拽) -->
|
|
13
|
+
<jh-drag-row :topHeight="480" class="main-splitter">
|
|
14
|
+
<template #top>
|
|
14
15
|
<!-- 🆕 使用增强版组件(集成所有功能) -->
|
|
15
16
|
<c_formSections
|
|
16
17
|
:sections="sectionsConfig"
|
|
@@ -43,7 +44,9 @@
|
|
|
43
44
|
</el-row>
|
|
44
45
|
</template>
|
|
45
46
|
</c_formSections>
|
|
47
|
+
</template>
|
|
46
48
|
|
|
49
|
+
<template #bottom>
|
|
47
50
|
<el-card
|
|
48
51
|
shadow="never"
|
|
49
52
|
class="items-card"
|
|
@@ -88,7 +91,8 @@
|
|
|
88
91
|
</div>
|
|
89
92
|
</div>
|
|
90
93
|
</el-card>
|
|
91
|
-
|
|
94
|
+
</template>
|
|
95
|
+
</jh-drag-row>
|
|
92
96
|
|
|
93
97
|
<!-- 全屏模式 - 使用 Teleport 传送到 body -->
|
|
94
98
|
<Teleport to="body">
|
|
@@ -136,7 +140,7 @@
|
|
|
136
140
|
<script setup lang="ts">
|
|
137
141
|
import { onMounted } from "vue";
|
|
138
142
|
import { ArrowDown, Close } from "@element-plus/icons-vue";
|
|
139
|
-
|
|
143
|
+
// jh-drag-row 是 @jhlc/jh-ui 全局注册组件,无需 import
|
|
140
144
|
import c_formSections from "@/components/local/c_formSections/index.vue";
|
|
141
145
|
import {
|
|
142
146
|
form,
|
|
@@ -151,7 +151,8 @@
|
|
|
151
151
|
|
|
152
152
|
<!-- 选择节点后显示表格数据 -->
|
|
153
153
|
<div v-else class="tables-content">
|
|
154
|
-
<
|
|
154
|
+
<jh-drag-row :topHeight="320">
|
|
155
|
+
<template #top>
|
|
155
156
|
<div class="main-table-section">
|
|
156
157
|
<BaseTable
|
|
157
158
|
:key="updateKey"
|
|
@@ -163,6 +164,8 @@
|
|
|
163
164
|
@row-click="handleRowClick"
|
|
164
165
|
/>
|
|
165
166
|
</div>
|
|
167
|
+
</template>
|
|
168
|
+
<template #bottom>
|
|
166
169
|
<div>
|
|
167
170
|
<div class="experiment-section">
|
|
168
171
|
<div class="experiment-header">
|
|
@@ -193,7 +196,8 @@
|
|
|
193
196
|
</div>
|
|
194
197
|
</div>
|
|
195
198
|
</div>
|
|
196
|
-
|
|
199
|
+
</template>
|
|
200
|
+
</jh-drag-row>
|
|
197
201
|
</div>
|
|
198
202
|
</div>
|
|
199
203
|
</div>
|
|
@@ -220,7 +224,7 @@
|
|
|
220
224
|
import { onMounted, onUnmounted, watch, nextTick } from "vue";
|
|
221
225
|
import { ArrowDown, ArrowLeft } from "@element-plus/icons-vue";
|
|
222
226
|
import c_formModal from "@/components/local/c_formModal/index.vue";
|
|
223
|
-
|
|
227
|
+
// jh-drag-row 是 @jhlc/jh-ui 全局注册组件,无需 import
|
|
224
228
|
import {
|
|
225
229
|
// 状态
|
|
226
230
|
activeTab,
|
|
@@ -23,6 +23,22 @@
|
|
|
23
23
|
<script setup>
|
|
24
24
|
import { ref, reactive, onMounted, useSlots, onUnmounted } from "vue";
|
|
25
25
|
|
|
26
|
+
// ⚠️ [DEPRECATED] C_Splitter 已废弃,请改用:
|
|
27
|
+
// 左右分割 → <jh-drag-col :leftWidth="260"> #left / #right </jh-drag-col>
|
|
28
|
+
// 上下分栈 → <jh-drag-row :topHeight="200"> #top / #bottom </jh-drag-row>
|
|
29
|
+
// 原因:C_Splitter 在 onMounted 中调用 slots.default() 冻结 vnode 快照,
|
|
30
|
+
// 导致子树响应式绑定(v-if / v-show / 插值)与父组件 ref 完全解耦,
|
|
31
|
+
// ref 赋值不触发重渲染。详见 .github/standards/14-layout-containers.md
|
|
32
|
+
if (typeof window !== "undefined" && !window.__C_SPLITTER_WARNED__) {
|
|
33
|
+
window.__C_SPLITTER_WARNED__ = true;
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.warn(
|
|
36
|
+
"[C_Splitter 已废弃] 已检测到 C_Splitter 使用。请改用 jh-drag-col / jh-drag-row。\n" +
|
|
37
|
+
"原因:slots.default() 被冻结为 vnode 快照,导致子树响应式失效。\n" +
|
|
38
|
+
"详见:.github/standards/14-layout-containers.md"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
26
42
|
const props = defineProps({
|
|
27
43
|
direction: {
|
|
28
44
|
type: String,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agile-team/wl-skills-kit",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI Skill 模板包 v2.
|
|
3
|
+
"version": "2.9.2",
|
|
4
|
+
"description": "AI Skill 模板包 v2.9.2 — 13 条编码规范 + 10 个 AI Skill + 17 个 MCP Tool,一条命令导入 Vue 3 项目",
|
|
5
5
|
"main": "./bin/wl-skills.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"wl-skills": "bin/wl-skills.js"
|
|
@@ -75,4 +75,4 @@
|
|
|
75
75
|
"eslint --fix --no-cache"
|
|
76
76
|
]
|
|
77
77
|
}
|
|
78
|
-
}
|
|
78
|
+
}
|