@xtalpi/agentic-lab-skills 0.0.4 → 0.0.6

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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/skills/lab-flow-designer/SKILL.md +204 -5
  3. package/skills/lab-flow-designer/embedded-template/SKILL.md +6 -2
  4. package/skills/lab-flow-designer/embedded-template/scripts//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.js +6 -0
  5. package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +1 -1
  6. package/skills/lab-flow-designer/references/agentic-lab-processer.md +1 -0
  7. package/skills/lab-flow-designer/references/agentic-lab-sdk.md +1 -1
  8. package/skills/lab-flow-designer/references/skill-package-layout.md +5 -0
  9. package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +2 -2
  10. package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/250/241/346/235/277.md +169 -0
  11. package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/347/244/272/344/276/213.md +197 -0
  12. package/skills/lab-flow-designer/testing/test-processer.mjs +1082 -0
  13. package/skills/lab-nocobase-flow-generator/SKILL.md +164 -0
  14. package/skills/lab-nocobase-flow-generator/examples/setting/350/241/250/350/216/267/345/217/226/345/244/226/351/203/250/346/234/215/345/212/241.js +70 -0
  15. package/skills/lab-nocobase-flow-generator/examples//346/237/245/350/257/242/345/214/226/345/255/246/345/223/201/344/277/241/346/201/257.js +30 -0
  16. package/skills/lab-nocobase-flow-generator/references/doc-standard.md +84 -0
  17. package/skills/lab-nocobase-flow-generator/references/runtime-api.md +224 -0
  18. package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/346/250/241/346/235/277.md +121 -0
  19. package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/347/244/272/344/276/213.md +67 -0
  20. package/skills/lab-orbit-component-builder/SKILL.md +56 -8
  21. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +3 -0
  22. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +6 -3
  23. package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +4 -1
  24. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +1 -1
  25. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +10 -1
  26. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +18 -10
  27. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +3 -1
  28. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +1 -1
  29. package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +4 -7
  30. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +5 -0
  31. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +7 -0
  32. package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +1 -1
  33. package/skills/lab-orbit-component-builder/references/orbit-vue-conventions.md +133 -0
  34. package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +66 -0
@@ -0,0 +1,67 @@
1
+ # 查询设备统计信息
2
+
3
+ ## 1. 基本信息
4
+
5
+ | 字段 | 值 |
6
+ |------|-----|
7
+ | 脚本名称 | 查询设备统计信息 |
8
+ | 业务场景 | 根据设备型号或工站名称查询 device 表,返回设备列表,并按 `model_name` 分组统计每种设备型号的数量和最后更新时间 |
9
+ | 输入参数来源 | `input_data` |
10
+
11
+ ---
12
+
13
+ ## 2. 输入参数
14
+
15
+ | 参数名 | 类型 | 必填 | 说明 |
16
+ |--------|------|------|------|
17
+ | model_id | number | 否 | 设备型号 ID,传入则只查询该型号的设备 |
18
+ | station_name | string | 否 | 工站名称,传入则只查询该工站的设备 |
19
+
20
+ **参数校验规则:**
21
+ - 两个参数均为可选,都不传则查询全部设备
22
+
23
+ ---
24
+
25
+ ## 3. 数据操作
26
+
27
+ ### 3.1 NocoBase 数据库操作
28
+
29
+ #### 操作 1:查询设备列表
30
+
31
+ | 字段 | 值 |
32
+ |------|-----|
33
+ | 数据表 | `device` |
34
+ | 操作类型 | `find` |
35
+ | 关联加载 | 无 |
36
+
37
+ **过滤条件:**
38
+ - 若传入 `model_id`,按 `model_id` 精确匹配
39
+ - 若传入 `station_name`,按 `station_name` 精确匹配
40
+
41
+ ---
42
+
43
+ ## 4. 核心逻辑
44
+
45
+ 1. 从 `input_data` 解构参数
46
+ 2. 根据传入的参数动态构建过滤条件
47
+ 3. 查询 `device` 表
48
+ 4. 按 `model_name` 分组统计:
49
+ - 每种型号的设备数量
50
+ - 每种型号中最近的 `update_time`(即最后更新时间)
51
+ 5. 返回设备列表和统计结果
52
+
53
+ ---
54
+
55
+ ## 5. 输出结果
56
+
57
+ | 场景 | 返回值 | 类型 |
58
+ |------|--------|------|
59
+ | 成功 | `{ devices: 设备列表, stats: [{ model_name, count, last_update_time }] }` | object |
60
+ | 无数据 | `"No device found"` | string |
61
+ | 异常 | `"Error: 错误信息"` | string |
62
+
63
+ ---
64
+
65
+ ## 6. 备注
66
+
67
+ - 无
@@ -7,7 +7,7 @@ description: >-
7
7
  Use when scaffolding Orbit Vue cells from flow docs, super-table manual pages, bitable columns from pool Schema, or AgenticLab ticket/execution APIs.
8
8
  compatibility: Orbit Book Vue Cell; window.xnb + props.xnbContext; Vite 5 lib build; company private npm per template README
9
9
  metadata:
10
- version: "1.2.4"
10
+ version: "1.3.0"
11
11
  runtime: Orbit-XNB-Vue-Component
12
12
  template_path: examples/xnb-component-template
13
13
  ---
@@ -128,6 +128,7 @@ metadata:
128
128
  | **props `xnbContext`** 与 JsCode 对照 | [`references/xnb-context-vue-props.md`](references/xnb-context-vue-props.md) |
129
129
  | 池 **Schema** → 列定义、可编辑字段策略、校验规则 | [`references/pool-schema-to-columns.md`](references/pool-schema-to-columns.md) |
130
130
  | XNBBitable 行为、全量刷新、反模式 | [`references/xnbitable-vue-parity.md`](references/xnbitable-vue-parity.md);模板 [`examples/xnb-component-template/OrbitAPI.md`](examples/xnb-component-template/OrbitAPI.md) |
131
+ | Vue/TS 编码约定、命名、组件拆分策略 | [`references/orbit-vue-conventions.md`](references/orbit-vue-conventions.md) |
131
132
  | 工单列表、`/api/execution/complete`、opener `ticket_ids` | 模板 [`examples/xnb-component-template/AgenticAppAPI.md`](examples/xnb-component-template/AgenticAppAPI.md) |
132
133
  | 装依赖、`dev`/`build`、多入口配置 | 模板 [`examples/xnb-component-template/README.md`](examples/xnb-component-template/README.md) |
133
134
 
@@ -141,9 +142,27 @@ metadata:
141
142
 
142
143
  ## 工作流(给 Agent)
143
144
 
145
+ ### 0.0 理解与确认(编码前)
146
+
147
+ 对于**首次生成**或**较大范围增量修改**,在开始编码前:
148
+
149
+ 1. **理解**:读完流程文档中目标门控的「人工处理」章节、输入池 Schema、以及目标目录下的现有代码
150
+ 2. **确认**:向用户简要复述理解——哪个门控、哪些字段成为列、哪些字段可编辑、校验规则如何,请求确认
151
+ 3. **执行**:确认后按方案实施
152
+
153
+ > 若变更范围明确(如仅修改一列的校验规则或新增一个按钮),可跳过确认步骤直接执行。
154
+
144
155
  ### 0. 判断工程状态:首次生成 or 增量修改
145
156
 
146
- **在做任何事之前,先检查用户指定的目标目录:**
157
+ **在做任何事之前,先确定目标目录,然后检查该目录:**
158
+
159
+ **目标目录优先级**:
160
+
161
+ 1. **用户指定了具体工程路径** → 使用该路径
162
+ 2. **用户仅指定了流程文档路径** → 在文档**所在目录下创建工程子目录**,目录名从流程名称 / 文档文件名提炼为 `kebab-case`(如文档 `fragment-flow-mini-2-pool-20260611-人工录入.md` → 创建 `xnb-component-fragment-flow-mini-2/`)。**禁止**将工程文件直接平铺到文档所在目录
163
+ 3. **均未指定** → 在当前工作目录(CWD)下创建工程子目录,命名规则同上
164
+
165
+ **工程目录命名规则**:`xnb-component-<流程语义>`,从流程名称 / 文档文件名提炼 kebab-case,去掉日期、版本号等非语义后缀。一个流程的所有门控人工处理页统一放在同一个工程目录下(一个工程多入口)。
147
166
 
148
167
  1. 查看目标目录下是否存在 `package.json` 且包含 `vite`(devDependencies)、`vite.config.ts`、`src/use/useBitablePage.ts` 等脚手架标志文件。
149
168
  2. **若不存在** → 进入 **「首次生成」** 流程(步骤 1A)。
@@ -161,7 +180,7 @@ metadata:
161
180
 
162
181
  ### 1A. 首次生成(目标目录无工程)
163
182
 
164
- **目标**:从模板 `examples/xnb-component-template` 搭建完整工程,再生成业务组件。
183
+ **目标**:在目标工程目录下(按 §0 创建或定位),从模板 `examples/xnb-component-template` 搭建完整工程,再生成业务组件。
165
184
 
166
185
  #### 第一步:复制脚手架基础设施
167
186
 
@@ -200,8 +219,8 @@ metadata:
200
219
 
201
220
  1. 新建 `src/views/<业务语义>.vue`(按规则 1-4 实现 opener、列定义、校验、提交)
202
221
  2. 新建 `src/entries/<id>.ts`
203
- 3. 更新 `vite.config.ts` 中的 `COMPONENT_MAP`(**清空**模板示例的 `bitable` / `custom-page` 条目,仅保留业务条目)
204
- 4. `package.json` **原样保留**模板内容(`name`、`version`、`main`、`dependencies`、`devDependencies`、`webb` 等字段**禁止改动**),仅在 `scripts` 中**追加** `dev:<id>` / `build:<id>` 条目
222
+ 3. 更新 `vite.config.ts` 中的 `COMPONENT_MAP`(**清空**模板示例的 `bitable` / `custom-page` 条目,仅保留业务条目);**保留** `build.emptyOutDir: false`(多组件顺序构建时防止后续 build 清空前序产物)
223
+ 4. `package.json`:**`version` 须在每次生成/修改时递增**(初次生成设为 `1.0.0`;迭代修改按变更范围递增 patch/minor/major);其余字段(`name`、`main`、`dependencies`、`devDependencies`、`webb` 等)**原样保留**模板内容**禁止改动**,仅在 `scripts` 中**追加** `dev:<id>` / `build:<id>` 条目;多组件时须有 `build:all`(顺序执行所有 `build:<id>`)。**通用 `dev` 脚本**须更新为默认加载第一个业务组件:`"dev": "VITE_COMPONENT_ID=<首个id> vite --config vite.dev.config.ts"`,确保 `npm run dev` 不会因缺少 `VITE_COMPONENT_ID` 而白屏
205
224
  5. 新建 `src/index.ts` 导出业务默认入口
206
225
  6. 写入 `.env.local`(从 `.env.local.example` 复制并填入文档中的 `AppBaseUrl` 等)
207
226
 
@@ -221,8 +240,8 @@ metadata:
221
240
 
222
241
  **禁止修改**(除非用户明确要求):
223
242
 
224
- - `package.json` 中的 `dependencies` / `devDependencies`
225
- - `vite.config.ts`(除 `COMPONENT_MAP` 外)、`vite.dev.config.ts`、`tsconfig.json`
243
+ - `package.json` 中的 `dependencies` / `devDependencies`(`version` 字段除外——每次修改**必须**递增 `version`)
244
+ - `vite.config.ts`(除 `COMPONENT_MAP` 和 `define` 外)、`vite.dev.config.ts`、`tsconfig.json`
226
245
  - `dev/` 目录
227
246
  - `src/utils/orbitHttpClient.ts`
228
247
 
@@ -236,6 +255,15 @@ metadata:
236
255
  | 修改 API | 编辑 `src/api/agenticlabTickets.ts`(新增接口、修改映射等) |
237
256
  | 修改类型 | 编辑 `src/types/xnb-context.ts`(扩展 context 类型等) |
238
257
  | 修改环境配置 | 编辑 `.env.local`(新增/修改环境变量) |
258
+ | 递增版本 | 修改 `package.json` 的 `version` 字段(每次生成/修改必须递增) |
259
+
260
+ #### 增量修改原则
261
+
262
+ - **Composable 扩展**:新增函数添加到现有 composable 的 `return {}` 中,不新建重复功能的 composable
263
+ - **组件拆分**:单个 `.vue` 超过 600 行时按功能块拆为子组件,保持原组件为入口
264
+ - **不重新生成**:只修改需要变更的部分,不重新生成未涉及的代码段
265
+ - **参数兼容**:修改函数签名时用可选参数扩展,不破坏现有调用
266
+ - **依赖管控**:不添加新 npm 依赖,除非向用户说明原因并获得同意
239
267
 
240
268
  **增量修改前,先读取现有代码**:
241
269
 
@@ -261,8 +289,23 @@ metadata:
261
289
  6. **提交**:`submitRows` 先调 `checkRows`,再将可编辑字段映射回 ticket detail 路径,调 `postAgenticlabExecutionComplete`。
262
290
  7. **AgenticLab**:`AppBaseUrl` → `VITE_AGENTICLAB_API_URL`;Token → `VITE_DEV_TOKEN`。
263
291
  8. **语言**:流程文档「语言类型」为英文时,UI 文案使用英文。
292
+ 9. **产物版本**:每次生成/修改时递增 `package.json` 的 `version` 字段(初次生成为 `1.0.0`)。`vite.config.ts` 通过 `define` 注入 `__APP_VERSION__`(读自 `pkg.version`)和 `__APP_SKILL__`(固定为 `'lab-orbit-component-builder'`)。仅在**最上层入口组件**(`src/entries/<id>.ts` 对应的 `src/views/<业务>.vue`)的 `<script setup>` 中 imports 之后、`defineProps` 之前添加 `console.info(\`[OrbitComponent] v\${__APP_VERSION__} (skill: \${__APP_SKILL__})\`)`;子组件**不需要**版本打印。
293
+
294
+ ### 3. 质量检查(生成/修改完成后必须执行)
295
+
296
+ 完成代码生成或修改后,若目标工程已执行过 `npm install`,**必须**依次运行以下命令并修复发现的问题:
297
+
298
+ ```bash
299
+ npm run lint:fix # 自动修复代码格式与规范问题
300
+ npm run typecheck # TypeScript 类型检查(vue-tsc --noEmit)
301
+ npm run build # 构建验证
302
+ ```
303
+
304
+ - 以上命令使用模板 `package.json` 中预定义的 npm scripts,不依赖特定 Agent 平台
305
+ - 若目标工程尚未 `npm install`(如首次生成刚复制完脚手架),跳过此步骤,在输出中提醒用户执行 `npm install` 后手动验证
306
+ - 若检查失败,自动修复后再次验证;仅需要业务决策的问题才向用户确认
264
307
 
265
- ### 3. 输出与自检
308
+ ### 4. 输出与自检
266
309
 
267
310
  - 列出新增/修改文件路径;说明每个入口组件对应的门控名称与 `dist/registrarName`。
268
311
  - 自检项:
@@ -280,6 +323,7 @@ metadata:
280
323
  - [ ] 全量刷新已按 §8.5.2 处理(整表替换 items)
281
324
  - [ ] `DEMO_*` 常量已替换为业务语义命名
282
325
  - [ ] 字段值映射使用 `String()` 转换,未直接调用 `.trim()` 等字符串方法
326
+ - [ ] `package.json` 的 `version` 已设置/递增;最上层入口视图含 `console.info` 版本打印(子组件无需);`vite.config.ts` 含 `define` 注入 `__APP_VERSION__` 和 `__APP_SKILL__`
283
327
 
284
328
  ---
285
329
 
@@ -289,6 +333,7 @@ metadata:
289
333
  - **首次生成时必须**复制完整脚手架基础设施(见步骤 1A),确保工程可独立 `npm install && npm run dev`。
290
334
  - **首次生成时禁止**复制模板示例文件(`views/bitable.vue`、`custom-page.vue`、`entries/bitable.ts`、`custom-page.ts`、`useSuperCellDemo.ts`)。
291
335
  - **增量修改时禁止**覆盖基础设施文件(`vite.config.ts` 非 COMPONENT_MAP 部分、`vite.dev.config.ts`、`dev/` 等),除非用户明确要求。
336
+ - **多组件工程**必须保留 `vite.config.ts` 中 `build.emptyOutDir: false`;`build:all` 顺序执行各 `build:<id>` 时,禁止清空前序产物。
292
337
 
293
338
  - **必须**以模板 **`examples/xnb-component-template`** 为脚手架基准(脚本、别名、`orbitHttpClient`、`types/xnb-context.ts`)。
294
339
  - **必须**通过 **props** 接收 `xnbContext`,不在 SFC 顶层假定存在全局 `xnbContext`。
@@ -303,3 +348,6 @@ metadata:
303
348
  - Token / BaseURL:**禁止**将真实生产密钥写入仓库;使用 `.env.local`(gitignore)与文档说明。
304
349
  - **页面入口** `.vue`(`src/views/` 下新建 Cell 根组件)**必须**含 `orbit-quasar-host` 与 `@import '@/styles/orbit-quasar-host.scss'`(`<style scoped lang="scss">`);主题调整**优先**改 `src/styles/orbit-quasar-host.scss`。超级表格工具栏:`primary` 仅提交类持久化动作,`outline` 仅页面数据交互(刷新/检查),见规则 2。
305
350
  - **禁止**对 API 返回值、行字段值等可能为非字符串的值直接调用 `.trim()` / `.toLowerCase()` 等字符串原型方法;必须先用 `String(value)` 转换。
351
+ - **必须**在每次生成/修改时递增 `package.json` 的 `version` 字段(初次 `1.0.0`;迭代修改按范围递增)。
352
+ - **必须**在 `vite.config.ts` 中通过 `define` 注入 `__APP_VERSION__`(`JSON.stringify(pkg.version)`)和 `__APP_SKILL__`(`JSON.stringify('lab-orbit-component-builder')`)。
353
+ - **必须**在每个生成的**最上层入口组件**(`src/views/<业务>.vue`)的 `<script setup>` 中添加 `console.info` 打印版本信息;子组件不需要版本打印。
@@ -2,6 +2,9 @@ declare module '@xtalpi/webb-api'
2
2
  declare module 'axios'
3
3
  declare module 'lodash'
4
4
 
5
+ declare const __APP_VERSION__: string
6
+ declare const __APP_SKILL__: string
7
+
5
8
  interface XnbHttpClient {
6
9
  request: (options: {
7
10
  method: string;
@@ -50,14 +50,17 @@ import App from './App.vue'
50
50
 
51
51
  // ==================== 多入口视图加载 ====================
52
52
 
53
- const COMPONENT_ID = import.meta.env.VITE_COMPONENT_ID || 'bitable'
54
53
  const viewModules = import.meta.glob('../../src/views/*.vue', { eager: true })
54
+ const viewKeys = Object.keys(viewModules)
55
+ const COMPONENT_ID = import.meta.env.VITE_COMPONENT_ID
56
+ || (viewKeys.length === 1 ? viewKeys[0].replace('../../src/views/', '').replace('.vue', '') : '')
57
+
55
58
  const viewKey = `../../src/views/${COMPONENT_ID}.vue`
56
59
  const viewModule = viewModules[viewKey] as { default: any } | undefined
57
60
  if (!viewModule) {
58
- const available = Object.keys(viewModules).map((k: string) => k.replace('../../src/views/', '').replace('.vue', '')).join(', ')
61
+ const available = viewKeys.map((k: string) => k.replace('../../src/views/', '').replace('.vue', '')).join(', ')
59
62
  throw new Error(
60
- `[dev] VITE_COMPONENT_ID=${COMPONENT_ID} 无对应视图。当前已有: ${available}`
63
+ `[dev] VITE_COMPONENT_ID=${COMPONENT_ID} 无对应视图。可用: ${available}。请在 .env.local 设置 VITE_COMPONENT_ID 或使用 npm run dev:<id>`
61
64
  )
62
65
  }
63
66
  const View = viewModule.default
@@ -16,7 +16,9 @@
16
16
  "build:custom-page": "VITE_COMPONENT_ID=custom-page vite build --mode func",
17
17
  "build:all": "npm run build:bitable && npm run build:custom-page",
18
18
  "build:es": "vite build",
19
- "lint": "eslint --ext .js,.ts,.tsx,.vue src"
19
+ "lint": "eslint --ext .js,.ts,.tsx,.vue src",
20
+ "lint:fix": "eslint --fix --ext .js,.ts,.tsx,.vue src",
21
+ "typecheck": "vue-tsc --noEmit"
20
22
  },
21
23
  "dependencies": {
22
24
  "@vueuse/core": "^10.2.1"
@@ -38,6 +40,7 @@
38
40
  "rollup-plugin-external-globals": "^0.13.0",
39
41
  "sass": "~1.77.6",
40
42
  "typescript": "~5.5.0",
43
+ "vue-tsc": "^2.0.0",
41
44
  "vite": "^5.4.0",
42
45
  "vite-plugin-compression": "^0.5.1",
43
46
  "vite-plugin-css-injected-by-js": "^3.5.2",
@@ -6,7 +6,7 @@
6
6
  * 复用:在 SFC 的 `<style scoped lang="scss">` 内首行 `@import '@/styles/orbit-quasar-host.scss';`
7
7
  * (须 scoped + `:deep(.q-btn)`,否则子组件 DOM 无法命中)
8
8
  * 主题调整:新增/修改主色或全局 `q-btn` 表现请只改本文件;页面结构见 `src/views/bitable.vue`。
9
- * 按钮分工:`primary` 仅提交等持久化写回;`outline` 仅刷新/检查等页面数据交互(见 studio-orbit-vue-component SKILL 规则 2)。
9
+ * 按钮分工:`primary` 仅提交等持久化写回;`outline` 仅刷新/检查等页面数据交互(见 lab-orbit-component-builder SKILL 规则 2)。
10
10
  */
11
11
  .orbit-quasar-host {
12
12
  --orbit-primary: #123aff;
@@ -98,12 +98,21 @@ export function useBitablePage (getContext: () => XnbSuperCellContext): UseBitab
98
98
  }
99
99
  }
100
100
 
101
+ let _bootstrapCellKey = ''
102
+ let _bootstrapping = false
103
+
101
104
  async function runBootstrapSafe (): Promise<void> {
102
- if (!choreo.value) return
105
+ if (!choreo.value || _bootstrapping) return
106
+ const key = `${getContext().bookPath}|${getContext().cellUid}`
107
+ if (key === _bootstrapCellKey) return
108
+ _bootstrapping = true
109
+ _bootstrapCellKey = key
103
110
  try {
104
111
  await bootstrap()
105
112
  } catch {
106
113
  /* composable 内已 notify */
114
+ } finally {
115
+ _bootstrapping = false
107
116
  }
108
117
  }
109
118
 
@@ -188,18 +188,20 @@ export function useSuperTableBitableLifecycle (
188
188
  const lastAgenticRawTickets = ref<Record<string, unknown>[]>([])
189
189
  const cellUid = computed(() => getContext().cellUid || '')
190
190
 
191
+ let _prevBookPath = ''
192
+ let _prevCellUid = ''
193
+
191
194
  watch(
192
195
  () => [getContext().bookPath, getContext().cellUid, getContext().XNBBitable] as const,
193
196
  () => {
194
197
  const ctx = getContext()
198
+ const bp = ctx.bookPath
199
+ const cu = ctx.cellUid
200
+ if (bp === _prevBookPath && cu === _prevCellUid && choreo.value != null) return
201
+ _prevBookPath = bp
202
+ _prevCellUid = cu
195
203
  const Ctor = ctx.XNBBitable
196
- choreo.value =
197
- Ctor != null
198
- ? new Ctor({
199
- bookPath: ctx.bookPath,
200
- cellUid: ctx.cellUid
201
- })
202
- : null
204
+ choreo.value = Ctor != null ? new Ctor({ bookPath: bp, cellUid: cu }) : null
203
205
  },
204
206
  { immediate: true }
205
207
  )
@@ -242,7 +244,7 @@ export function useSuperTableBitableLifecycle (
242
244
  await x.addColumns(bitable, cols, [])
243
245
  handleBitable(bitable, sub)
244
246
  await x.updateSubBitable([bitable], { isMerge: false, name: DEMO_SUB_TABLE_NAME })
245
- if (typeof x.delay === 'function') await x.delay(20)
247
+ if (typeof x.delay === 'function') await x.delay(300)
246
248
  }
247
249
 
248
250
  async function createEmptyBitable (isMerge = false): Promise<void> {
@@ -254,13 +256,18 @@ export function useSuperTableBitableLifecycle (
254
256
  await x.addColumns(bitable, dataColumns, [])
255
257
  handleBitable(bitable, listSub)
256
258
  await x.updateSubBitable([bitable], { isMerge, name: DEMO_SUB_TABLE_NAME })
257
- if (typeof x.delay === 'function') await x.delay(10)
259
+ if (typeof x.delay === 'function') await x.delay(300)
258
260
  return
259
261
  }
260
262
  const bitable: Record<string, unknown> = { items: [], data_columns: [] }
261
263
  handleBitable(bitable, listSub)
262
264
  await x.addColumns(bitable, dataColumns, [])
263
- await x.createSubBitable([bitable], DEMO_SUB_TABLE_NAME)
265
+ try {
266
+ await x.createSubBitable([bitable], DEMO_SUB_TABLE_NAME)
267
+ } catch {
268
+ if (typeof x.delay === 'function') await x.delay(500)
269
+ await x.createSubBitable([bitable], DEMO_SUB_TABLE_NAME)
270
+ }
264
271
  }
265
272
 
266
273
  async function fetchDemoList (): Promise<DemoListRow[]> {
@@ -367,6 +374,7 @@ export function useSuperTableBitableLifecycle (
367
374
 
368
375
  if (dataColumnsMismatch(bitable, dataColumns) || !((bitable.data_columns as unknown[])?.length)) {
369
376
  await rebuildSubBitableColumns(bitable, listSub, dataColumns)
377
+ bitable = (await resolveBitableBySub(listSub)) ?? bitable
370
378
  }
371
379
 
372
380
  const rows = await fetchDemoList()
@@ -21,13 +21,15 @@
21
21
  <script lang="ts" setup>
22
22
  /**
23
23
  * 模板内演示用超级表格工具栏示例(非业务交付物)。
24
- * studio-orbit-vue-component 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 `src/views/<业务>.vue` 并参考此处写法。
24
+ * lab-orbit-component-builder 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 `src/views/<业务>.vue` 并参考此处写法。
25
25
  *
26
26
  * 刷新 / 提交:在 `useBitablePage` 内经 Quasar 对话框确认后再执行。
27
27
  */
28
28
  import { useBitablePage } from '@/use/useBitablePage'
29
29
  import type { XnbSuperCellContext } from '@/types/xnb-context'
30
30
 
31
+ console.info(`[OrbitComponent] v${__APP_VERSION__} (skill: ${__APP_SKILL__})`)
32
+
31
33
  const props = defineProps<{
32
34
  xnbContext: XnbSuperCellContext
33
35
  }>()
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  模板内演示用多区页面示例(非业务交付物)。
3
- studio-orbit-vue-component 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 views 与 entries。
3
+ lab-orbit-component-builder 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 views 与 entries。
4
4
 
5
5
  场景:复杂多组件页面(不使用超级表格 / XNBBitable)
6
6
  入口文件名:custom-page.vue(与 xnb-components 中 id 一致)
@@ -35,14 +35,11 @@
35
35
  "include": [
36
36
  "src/**/*.ts",
37
37
  "src/**/*.tsx",
38
- "src/**/*.vue",
39
- "dev/src/**/*.ts",
40
- "dev/src/**/*.tsx",
41
- "dev/src/**/*.vue",
42
- "vite.config.ts",
43
- "vite.dev.config.ts"
38
+ "src/**/*.vue"
44
39
  ],
45
40
  "exclude": [
46
- "node_modules"
41
+ "node_modules",
42
+ "dev",
43
+ "dist"
47
44
  ]
48
45
  }
@@ -109,6 +109,10 @@ export default defineConfig(({ mode }) => {
109
109
  deleteOriginFile: false
110
110
  })
111
111
  ],
112
+ define: {
113
+ __APP_VERSION__: JSON.stringify(pkg.version),
114
+ __APP_SKILL__: JSON.stringify('lab-orbit-component-builder'),
115
+ },
112
116
  resolve: {
113
117
  alias: {
114
118
  '@': resolve(__dirname, 'src'),
@@ -136,6 +140,7 @@ export default defineConfig(({ mode }) => {
136
140
  },
137
141
  build: {
138
142
  outDir: resolve(__dirname, 'dist'),
143
+ emptyOutDir: false,
139
144
  sourcemap: true,
140
145
  cssCodeSplit: false,
141
146
  minify: false,
@@ -2,6 +2,9 @@ import { defineConfig, loadEnv } from 'vite'
2
2
  import vue from '@vitejs/plugin-vue'
3
3
  import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
4
4
  import { resolve } from 'path'
5
+ import { readFileSync } from 'fs'
6
+
7
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8'))
5
8
 
6
9
  export default defineConfig(({ mode }) => {
7
10
  const env = loadEnv(mode, process.cwd(), '')
@@ -12,6 +15,10 @@ export default defineConfig(({ mode }) => {
12
15
  vue({ template: { transformAssetUrls } }),
13
16
  quasar({ sassVariables: 'quasar/src/css/variables.sass' })
14
17
  ],
18
+ define: {
19
+ __APP_VERSION__: JSON.stringify(pkg.version),
20
+ __APP_SKILL__: JSON.stringify('lab-orbit-component-builder'),
21
+ },
15
22
  resolve: {
16
23
  alias: {
17
24
  '@': resolve(__dirname, 'src'),
@@ -39,7 +39,7 @@
39
39
  | 文档配置项 | 落地建议 |
40
40
  |------------|----------|
41
41
  | **AppBaseUrl** | 本地/CI:`VITE_AGENTICLAB_API_URL`(见模板 `.env.local.example`);Orbit 内 HTTP 走 `orbitRequestJson` / `window.xnb.http.client.request`,基址以宿主代理为准 |
42
- | **数据查询上限** | 列表请求 `limit` 默认值(如 `9000`);与 **flow-package-from-design** 列表类约定一致时注意网关上限 |
42
+ | **数据查询上限** | 列表请求 `limit` 默认值(如 `9000`);与 **lab-flow-designer** 列表类约定一致时注意网关上限 |
43
43
  | **语言类型** | 影响 UI 文案语言(中/英);未给定时与团队默认一致 |
44
44
 
45
45
  门控级 **脚本配置项**(如 StationBaseURL)属于 **后置/工站脚本**,不默认写进 Vue 页;若文档明确要求页面内调用,再在 `AgenticAppAPI` 之外单独增加 `src/api/*` 与环境变量。
@@ -0,0 +1,133 @@
1
+ # Orbit Vue 组件编码约定
2
+
3
+ 本文档定义本技能生成/修改 Orbit Vue 组件时**必须遵循**的编码模式与约定。
4
+
5
+ ---
6
+
7
+ ## 1. 技术栈约束
8
+
9
+ - **框架**:Vue 3.2(Composition API,`<script setup lang="ts">`)
10
+ - **UI 组件库**:Quasar 2.x
11
+ - **语言**:TypeScript 5.x(strict)
12
+ - **构建工具**:Vite 5
13
+ - **禁止使用**:`any`、`enum`、Pinia、Vuex、vue-router、Tailwind、fetch、axios
14
+
15
+ ## 2. 命名约定
16
+
17
+ | 类别 | 规则 | 示例 |
18
+ |------|------|------|
19
+ | 组件 `.vue` 文件 | PascalCase 或 kebab-case(与门控 id 对齐) | `WeighingConfirm.vue`、`sample-prep.vue` |
20
+ | Composable 文件 | camelCase + 功能后缀 | `useSuperTableBitableLifecycle.ts` |
21
+ | 类型 / 接口 | PascalCase,**无 `I` 前缀** | `WeighingRow`、`SamplePrepConfig` |
22
+ | 常量 | UPPER_SNAKE_CASE | `SUB_TABLE_NAME`、`LIST_COLS` |
23
+ | 联合类型 | `type XxxKind = 'a' | 'b'` | `type DosingMethod = 'liquid' | 'neo'` |
24
+ | 私有函数 | `_` 前缀,不 export | `_mapTicketToRow`、`_buildSubmitPayload` |
25
+ | CSS 类名 | kebab-case | `.weighing-toolbar`、`.row-count` |
26
+
27
+ ## 3. Singleton Composable 模式
28
+
29
+ 状态 `ref` 定义在函数**外部**(模块级单例),`computed` / 方法在函数**内部**。此模式与 `useSuperTableBitableLifecycle.ts` 一致。
30
+
31
+ ```typescript
32
+ import { ref, computed } from 'vue'
33
+
34
+ // 模块级单例状态
35
+ const items = ref<RowType[]>([])
36
+ const loading = ref(false)
37
+
38
+ export function useXxxLifecycle() {
39
+ const validItems = computed(() => items.value.filter(r => r.valid))
40
+
41
+ async function refreshTable(): Promise<void> {
42
+ loading.value = true
43
+ try {
44
+ // ...
45
+ } finally {
46
+ loading.value = false
47
+ }
48
+ }
49
+
50
+ return { items, loading, validItems, refreshTable }
51
+ }
52
+ ```
53
+
54
+ **规则**:
55
+ - `loading` 在请求前设 `true`,在 `finally` 中设 `false`
56
+ - 私有函数以 `_` 前缀命名,不放入 `return`
57
+
58
+ ## 4. TypeScript 纪律
59
+
60
+ - **禁止 `any`**:使用 `unknown` + 类型断言(`as XxxType`)
61
+ - **禁止 `enum`**:使用联合类型 `type Status = 'pending' | 'completed'`
62
+ - **接口名无 `I` 前缀**:`interface WeighingRow`(不是 `IWeighingRow`)
63
+ - **新增字段标为可选**:`newField?: string`,不删除已有字段
64
+ - **字段值安全转换**:从 API / 行数据取出的值先 `String()` 再做字符串操作
65
+
66
+ ```typescript
67
+ // ✅ 正确
68
+ fields[field] = raw == null ? '' : String(raw)
69
+
70
+ // ❌ 错误:raw 可能是 number / null
71
+ fields[field] = raw.trim()
72
+ ```
73
+
74
+ ## 5. 组件编写规则
75
+
76
+ ### 基本结构(最上层入口组件)
77
+
78
+ ```vue
79
+ <template>
80
+ <div class="orbit-quasar-host component-name">
81
+ <!-- 内容 -->
82
+ </div>
83
+ </template>
84
+
85
+ <script setup lang="ts">
86
+ import type { XnbSuperCellContext } from '@/types/xnb-context'
87
+
88
+ // 仅最上层入口组件打印版本,子组件不需要
89
+ console.info(`[OrbitComponent] v${__APP_VERSION__} (skill: ${__APP_SKILL__})`)
90
+
91
+ const props = defineProps<{ xnbContext: XnbSuperCellContext }>()
92
+ // ...
93
+ </script>
94
+
95
+ <style scoped lang="scss">
96
+ @import '@/styles/orbit-quasar-host.scss';
97
+ .component-name { /* ... */ }
98
+ </style>
99
+ ```
100
+
101
+ ### Props
102
+
103
+ ```typescript
104
+ defineProps<{
105
+ xnbContext: XnbSuperCellContext
106
+ title?: string
107
+ variant?: 'primary' | 'outline'
108
+ }>()
109
+
110
+ defineEmits<{ (e: 'submit', payload: unknown): void }>()
111
+ ```
112
+
113
+ ### 组件大小限制
114
+
115
+ - 单个 `.vue` 文件**不超过 600 行**
116
+ - 超过时按功能区块拆分为子组件,原组件保持入口职责
117
+ - 拆分后子组件通过 props / emits 通信,不共享模块级 ref
118
+
119
+ ## 6. 样式约定
120
+
121
+ - **页面入口**(`src/views/<id>.vue`):根节点带 `orbit-quasar-host`,`<style scoped lang="scss">` 首行 `@import '@/styles/orbit-quasar-host.scss'`
122
+ - **子组件**:`<style scoped>` **纯 CSS**,禁止 `lang="scss"`;样式变量用 CSS 自定义属性 `var(--c-xxx)`
123
+ - 主题调整**优先改** `src/styles/orbit-quasar-host.scss`,不在业务 SFC 散落主题变量
124
+ - 使用 `:deep()` 穿透 Quasar 组件内部样式
125
+
126
+ ## 7. 增量开发原则
127
+
128
+ - **增量优先**:优先修改现有文件,新增字段加到现有 interface,新函数加到现有 composable
129
+ - **向后兼容**:新增字段标为可选(`?`),需移除时先标记 `@deprecated`
130
+ - **Composable 扩展**:新增函数添加到现有 composable 的 `return {}` 中,不新建重复功能的 composable
131
+ - **不重新生成**:只修改需要变更的部分,不重新生成未涉及的代码段
132
+ - **参数兼容**:修改函数签名时用可选参数扩展,不破坏现有调用
133
+ - **依赖管控**:不添加新 npm 依赖,除非向用户说明原因并获得同意
@@ -111,3 +111,69 @@
111
111
  ## 静态加载契约(宿主)
112
112
 
113
113
  README 中 JsCode 返回 `component.view / filename / source / params` 的占位;生成业务组件后须同步 **`registrarName` 与 `distBaseName`**(在 `vite.config.ts` 的 `COMPONENT_MAP` 中定义)到业务文档。
114
+
115
+ ---
116
+
117
+ ## 质量检查 npm scripts(平台无关)
118
+
119
+ 模板 `package.json` 内置以下质量检查脚本,任何 Agent 平台或人工均可直接调用:
120
+
121
+ | 命令 | 作用 |
122
+ |------|------|
123
+ | `npm run lint` | ESLint 检查(仅报告,不修改) |
124
+ | `npm run lint:fix` | ESLint 自动修复 |
125
+ | `npm run typecheck` | TypeScript 类型检查(`vue-tsc --noEmit`) |
126
+ | `npm run build` | 构建验证 |
127
+
128
+ 生成/修改代码后,按 `lint:fix → typecheck → build` 顺序执行,修复发现的问题。
129
+
130
+ ---
131
+
132
+ ## 可选:Claude Code PostToolUse Hook
133
+
134
+ > 以下配置**仅适用于 Claude Code**,其它 Agent 平台请使用上方的 npm scripts。
135
+
136
+ 为生成的 Orbit Vue 工程配置 PostToolUse hook,在每次编辑文件后自动对单文件运行 `eslint --fix`:
137
+
138
+ ### `.claude/settings.json`
139
+
140
+ ```json
141
+ {
142
+ "hooks": {
143
+ "PostToolUse": [
144
+ {
145
+ "matcher": "Edit|Write",
146
+ "hooks": [
147
+ {
148
+ "type": "command",
149
+ "command": "bash .claude/hooks/post-edit-lint.sh"
150
+ }
151
+ ]
152
+ }
153
+ ]
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### `.claude/hooks/post-edit-lint.sh`
159
+
160
+ ```bash
161
+ #!/bin/bash
162
+ # PostToolUse hook: auto eslint --fix on edited .vue/.ts/.tsx files
163
+
164
+ INPUT=$(cat)
165
+ FILE=$(echo "$INPUT" | python3 -c "
166
+ import sys, json
167
+ try:
168
+ d = json.load(sys.stdin)
169
+ print(d.get('tool_input', {}).get('file_path', ''))
170
+ except:
171
+ pass
172
+ " 2>/dev/null)
173
+
174
+ if [[ -n "$FILE" && ("$FILE" == *.vue || "$FILE" == *.ts || "$FILE" == *.tsx) ]]; then
175
+ cd "$(dirname "$0")/../.." && npx eslint --fix "$FILE" 2>&1 | tail -5
176
+ fi
177
+ ```
178
+
179
+ > 首次生成工程时,可将 `.claude/` 目录一并创建到用户目标工程中(如用户同意)。记得 `chmod +x .claude/hooks/post-edit-lint.sh`。