@agile-team/wl-skills-kit 2.8.0 → 2.9.1

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 CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.9.1] - 2026-05-17
4
+
5
+ ### Added
6
+
7
+ - **`validate` 三项新检查**(接 v2.9.0 的 standards/14 落地阻断):
8
+ - 🔴 `error`:页面 `index.vue` 出现 `<C_Splitter>` 标签
9
+ - 🔴 `error`:`index.vue` / `data.ts` 出现 `import C_Splitter`
10
+ - ℹ `info`:提及 `C_Splitter` 的过时注释(如 `已改为 C_Splitter`、`migrate to C_Splitter`、`TODO ... C_Splitter`)
11
+ - **`scripts/lint-skills.js` 新增规则 8**:扫描 `files/**/*.{vue,ts}`,禁止任何 `<C_Splitter>` 标签或 `import C_Splitter`(仅允许 `files/src/components/global/C_Splitter/index.vue` 自身保留废弃声明)。
12
+ - **`.husky/pre-commit` 接入 `lint-skills`**:维护者侧提交时自动守门,从源头阻止 C_Splitter 回潮。
13
+
14
+ ### Notes
15
+
16
+ - 实战验证:wl-mdata 现网 28 个页面 0 命中(之前已全量迁移);故意污染测试用例可精准触发 3 项检查。
17
+ - 兼容性:所有新检查均为加法,未改动既有 validate 规则;下游项目无需调整。
18
+
19
+ ## [2.9.0] - 2026-05-17
20
+
21
+ ### Added
22
+
23
+ - **`files/.github/standards/14-layout-containers.md`**(新规范,🔴 必遵 + 阻断):布局容器规范。根因解析 `C_Splitter` 在 `onMounted` 中调用 `slots.default()` 冻结 vnode 快照、导致子树响应式完全失效;明确左右分割只用 `jh-drag-col`(`#left`/`#right`),上下分栈只用 `jh-drag-row`(`#top`/`#bottom`);附迁移对照表、lint 规则、废弃路线图。
24
+ - **`files/src/components/global/C_Splitter/index.vue`** 加入 `@deprecated` 注释 + 运行时 `console.warn`(同会话仅警告一次),引导改用 `jh-drag-col` / `jh-drag-row`。
25
+
26
+ ### Changed
27
+
28
+ - **`standards/index.md`**:13 条 → 14 条;任务 A(生成新页面)/ B(修改重构)必读集合纳入 `14`。
29
+ - **`standards/13-platform-components.md`** 与 `copilot-instructions.md`:「左右分割」推荐组件由 `C_Splitter` 改为 `jh-drag-col`;明确 `C_Splitter` 已废弃及根因。
30
+ - **TPL-TREE-LIST.md**:模板示例改为 `<jh-drag-col :leftWidth="220">` + `#left`/`#right` 显式插槽;SCSS 去除 `.my-splitter-container :deep` 残留;顶部加「布局硬约束」段。
31
+ - **TPL-DETAIL-TABS.md**:示例由 `<C_Splitter direction="vertical">` 改为 `<jh-drag-row :topHeight="380">` + `#top`/`#bottom`;移除 `import C_Splitter`。
32
+ - **`page-codegen/SKILL.md`** 与 `templates/_index.md`:TREE_LIST 描述同步至 jh-drag-col;上下分栈描述同步至 jh-drag-row。
33
+ - **`prototype-scan/SKILL.md`**:原型扫描产出物示例与说明同步至 jh-drag-col。
34
+ - **`demo/sale/demo/add-demo/`** 与 **`demo/sale/demo/metallurgical-spec/`**:示例 `<C_Splitter>` 全部迁移到 `<jh-drag-row>`;SCSS 注释、README 文案对齐。
35
+
36
+ ### Notes
37
+
38
+ - 兼容性:`C_Splitter` 源文件保留但加废弃警告,**不删除**,避免外部业务项目升级时立即报错;下一个 major 版本前再做物理移除评估。
39
+ - 参考案例:`wl-ui-sale` 项目所有左树右表/上下分栈页面(material、materialCategory、transactionType、unit、price-maintain、ContractEditTab 等)均使用 `jh-drag-col` / `jh-drag-row`,未踩 `C_Splitter` 冻 vnode 坑——本次规范固化的正是该实战路径。
40
+
3
41
  ## [2.8.0] - 2026-05-16
4
42
 
5
43
  ### Added
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agile-team/wl-skills-kit
2
2
 
3
- **AI Skill 模板包 v2.8.0** — 一键将 13 条规范、10 个 AI Skill、17 个 MCP Tool、编辑器 MCP 配置、文档导入 Vue 3 项目。
3
+ **AI Skill 模板包 v2.9.1 ** — 一键将 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.8.0
4
+ * wl-skills-kit CLI v2.9.1
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",
@@ -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
- | 左右分割 | C_Splitter | src/components/global |
206
+ | 左右分割 | jh-drag-col | @jhlc/jh-ui (#left/#right slot) |
207
207
  | 树形面板 | C_Tree | src/components/global |
208
208
 
209
209
  ## 组件提取策略
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **读者**:团队技术负责人 / wl-skills-kit 维护者 / 对体系设计感兴趣的团队成员
4
4
  > **更新方式**:重大架构变更后追加对应章节,旧章节原文保留(历史可溯)
5
- > **当前版本**:v2.8.0(2026-05-16
5
+ > **当前版本**:v2.9.1 (2026-05-17
6
6
 
7
7
  ---
8
8
 
@@ -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 | C_Splitter 上Tab表单+下子表 | add-demo / domestic-trade-order |
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` | 左树右列表页(C_Splitter 布局) |
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
- > 布局核心:`C_Splitter direction="vertical"` 垂直分割上下区域。
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
- <C_Splitter direction="vertical">
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
- </C_Splitter>
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 from "@/components/global/C_Splitter/index.vue";
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
- <C_Splitter :left-width="220">
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
- </C_Splitter>
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
- // C_Splitter 需要父容器撑满高度
201
- :deep(.my-splitter-container) {
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` | 左侧树 + 右侧表格 | C_Splitter + C_Tree |
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
- | 左右分割 | C_Splitter | 树形+列表必备,设 `:left-width` |
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` | ❌ 手动 flex/grid |
35
- | 左右分割 | `C_Splitter` | 手动 flex/grid |
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,139 @@
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
+
133
+ ---
134
+
135
+ ## 关联
136
+
137
+ - `12-base-table.md` — BaseTable 内部高度撑满依赖父容器有明确高度,jh-drag-col/row 已正确给子区设 `height: 100%`
138
+ - `13-platform-components.md` — 平台组件对照表已同步移除 C_Splitter
139
+ - 真实场景案例:`demo/produce/aiflow/mmwr-customer-detail/`(master-detail 使用 jh-drag-row)
@@ -5,7 +5,7 @@
5
5
 
6
6
  ---
7
7
 
8
- ## 13 条规范清单
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
 
@@ -10,7 +10,7 @@
10
10
  display: flex;
11
11
  flex-direction: column;
12
12
 
13
- // C_Splitter 区域
13
+ // jh-drag-row 区域
14
14
  .main-splitter {
15
15
  flex: 1;
16
16
  overflow: hidden;
@@ -9,8 +9,9 @@
9
9
  -->
10
10
  <template>
11
11
  <div class="main-maintenance-container">
12
- <!-- C_Splitter 包裹表单区和项次信息 -->
13
- <C_Splitter direction="vertical" class="main-splitter">
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
- </C_Splitter>
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
- import C_Splitter from "@/components/global/C_Splitter/index.vue";
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,
@@ -220,7 +220,7 @@
220
220
 
221
221
  // 明细信息的表格容器(固定高度,给实验表格留空间)
222
222
  &[data-tab="detail"] {
223
- // 移除 table-container 固定高度,让 C_Splitter 控制
223
+ // 移除 table-container 固定高度,让 jh-drag-row 控制
224
224
  .detail-tables-splitter {
225
225
  flex: 1;
226
226
  }
@@ -151,7 +151,8 @@
151
151
 
152
152
  <!-- 选择节点后显示表格数据 -->
153
153
  <div v-else class="tables-content">
154
- <C_Splitter direction="vertical">
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
- </C_Splitter>
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
- import C_Splitter from "@/components/global/C_Splitter/index.vue";
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,
@@ -472,7 +472,7 @@ const defaultForm = generateDefaultFormData(sectionsConfig);
472
472
  - ✅ 真实的保存/草稿/取消逻辑(使用 request + Mock)
473
473
  - ✅ 项次信息管理(带分页,使用 jh-pagination)
474
474
  - ✅ 全屏模式支持
475
- - ✅ C_Splitter 拖动调整布局
475
+ - ✅ jh-drag-row 拖拽调整布局
476
476
 
477
477
  **使用方式**:
478
478
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-kit",
3
- "version": "2.8.0",
4
- "description": "AI Skill 模板包 v2.8.0 — 13 条编码规范 + 10 个 AI Skill + 17 个 MCP Tool + Mock 架构体系,一条命令导入 Vue 3 项目",
3
+ "version": "2.9.1",
4
+ "description": "AI Skill 模板包 v2.9.1 — 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
+ }