lcap-frontend-library 0.0.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/README.md +271 -0
- package/bin/lcap-frontend-library.mjs +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +27 -0
- package/dist/init.d.ts +6 -0
- package/dist/init.js +79 -0
- package/dist/sync.d.ts +4 -0
- package/dist/sync.js +70 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +101 -0
- package/package.json +34 -0
- package/packages/lcap-frontend-library/LEARNINGS.md +11 -0
- package/packages/lcap-frontend-library/SKILL.md +86 -0
- package/packages/lcap-frontend-library/commands/migrate.check.md +287 -0
- package/packages/lcap-frontend-library/commands/migrate.green.md +190 -0
- package/packages/lcap-frontend-library/commands/migrate.plan.md +169 -0
- package/packages/lcap-frontend-library/commands/migrate.red.md +160 -0
- package/packages/lcap-frontend-library/commands/migrate.scan.md +151 -0
- package/packages/lcap-frontend-library/commands/migrate.spec.md +144 -0
- package/packages/lcap-frontend-library/commands/migrate.tasks.md +179 -0
- package/packages/lcap-frontend-library/commands/speckit.create.md +201 -0
- package/packages/lcap-frontend-library/commands/speckit.implement.md +88 -0
- package/packages/lcap-frontend-library/commands/speckit.plan.md +79 -0
- package/packages/lcap-frontend-library/commands/speckit.self-check.md +177 -0
- package/packages/lcap-frontend-library/commands/speckit.specify.md +91 -0
- package/packages/lcap-frontend-library/commands/speckit.tasks.md +61 -0
- package/packages/lcap-frontend-library/references/frontend-design/LICENSE.txt +177 -0
- package/packages/lcap-frontend-library/references/frontend-design/SKILL.md +42 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/SKILL.md +360 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/api.md +331 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/block.md +160 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/i18n.md +95 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/icon.md +27 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/container.md +728 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/element.md +312 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/expression.md +154 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/index.md +113 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/modal.md +189 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide/popover.md +171 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/ide.md +799 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/implementation-rules.md +242 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/index.md +27 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/nasl-view-component.md +895 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/accessibility.md +185 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/child.md +82 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/data-source.md +261 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/event.md +171 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/form.md +266 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/function.md +80 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/link.md +137 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/slot.md +128 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/theme-variables-ant-design.md +1470 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/theme-variables-cloud-ui.md +259 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/theme-variables-element-plus.md +580 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/theme-variables-element-ui.md +1007 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/platform/theme-variables-mobile-ui.md +85 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/theme.md +234 -0
- package/packages/lcap-frontend-library/references/lcap-extension-component/workflow-guardrails.md +328 -0
- package/packages/lcap-frontend-library/references/nasl-logic-authoring/SKILL.md +201 -0
- package/packages/lcap-frontend-library/scripts/bash/create-component-files.sh +95 -0
- package/packages/lcap-frontend-library/scripts/bash/create-extension-project.sh +109 -0
- package/packages/lcap-frontend-library/scripts/bash/create-logic-files.sh +149 -0
- package/packages/lcap-frontend-library/scripts/bash/create-spec.sh +109 -0
- package/packages/lcap-frontend-library/scripts/bash/get-available-port.sh +35 -0
- package/packages/lcap-frontend-library/scripts/bash/list-specs.sh +19 -0
- package/packages/lcap-frontend-library/scripts/node/setup-extension-project.mjs +166 -0
- package/packages/lcap-frontend-library/templates/component-self-check.md +31 -0
- package/packages/lcap-frontend-library/templates/component-template.md +96 -0
- package/packages/lcap-frontend-library/templates/library-report-template.md +52 -0
- package/packages/lcap-frontend-library/templates/logic-template.md +44 -0
- package/packages/lcap-frontend-library/templates/migration-manifest-template.md +84 -0
- package/packages/lcap-frontend-library/templates/migration-plan-template.md +138 -0
- package/packages/lcap-frontend-library/templates/migration-report-template.md +227 -0
- package/packages/lcap-frontend-library/templates/migration-spec-template.md +135 -0
- package/packages/lcap-frontend-library/templates/migration-tasks-template.md +129 -0
- package/packages/lcap-frontend-library/templates/plan-template.md +299 -0
- package/packages/lcap-frontend-library/templates/self-check-report-template.md +148 -0
- package/packages/lcap-frontend-library/templates/tasks-template.md +81 -0
- package/packages/lcap-frontend-library/workflows/create/flow.md +199 -0
- package/packages/lcap-frontend-library/workflows/evolve/flow.md +249 -0
- package/packages/lcap-frontend-library/workflows/generate/flow.md +10 -0
- package/packages/lcap-frontend-library/workflows/harness/flow.md +82 -0
- package/packages/lcap-frontend-library/workflows/migrate/flow.md +302 -0
- package/packages/lcap-frontend-library/workflows/migrate/knowledge-base.md +564 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
# Vue2→Vue3 迁移先验知识
|
|
2
|
+
|
|
3
|
+
> **用法**:plan / green / check → Read 全文 + 按 manifest.类型 选用章节;spec → Read 全文但仅提取;scan/tasks/red → 见「阶段索引表」
|
|
4
|
+
> **component 详规**:`references/lcap-extension-component/`(plan/green 额外阅读,不在本文扩写)
|
|
5
|
+
> **logic 详规**:`references/nasl-logic-authoring/SKILL.md`(plan/green/check 额外阅读,见 §logics-boundary)
|
|
6
|
+
|
|
7
|
+
## 阶段索引表
|
|
8
|
+
|
|
9
|
+
| 阶段 | Read 范围 |
|
|
10
|
+
|------|-----------|
|
|
11
|
+
| scan | §process-scaffold, §process-asset-types, §stories-truth, §process-doc, §logics-boundary |
|
|
12
|
+
| spec | 全文(输出纪律见 migrate.spec 禁止条款) |
|
|
13
|
+
| plan | 全文 + 按类型附加 references |
|
|
14
|
+
| tasks | §process-matrix, §process-e2e |
|
|
15
|
+
| red | §process-e2e |
|
|
16
|
+
| green | 全文 + 按类型附加 references |
|
|
17
|
+
| check | 全文 + 按类型选用 §alignment-checklist 或 migrate.check L1–L3/S1–S3 |
|
|
18
|
+
|
|
19
|
+
## §asset-applicability · 章节类型适用表
|
|
20
|
+
|
|
21
|
+
| KB 章节 | component | logic | shared-composable |
|
|
22
|
+
|---------|-----------|-------|-------------------|
|
|
23
|
+
| §alignment-checklist | ✅ check 主清单 | ❌ | ❌ |
|
|
24
|
+
| §stories-truth | ✅ spec/plan | ❌ | ❌ |
|
|
25
|
+
| §css-vars / §plugin-barrel | ✅ green/check | ❌ | ❌ |
|
|
26
|
+
| §mixin-composable / §pitfalls | ✅ | 部分(纪律) | ✅ |
|
|
27
|
+
| §implementation-order | ✅ green | ❌(用 create-logic-files) | 部分 |
|
|
28
|
+
| §logics-boundary → nasl-logic-authoring | ❌ | ✅ plan/green/check | ❌ |
|
|
29
|
+
| §process-e2e | ✅ red/tasks | ❌ | ❌ |
|
|
30
|
+
|
|
31
|
+
## §discipline · 迁移纪律与核心原则
|
|
32
|
+
|
|
33
|
+
## When to apply
|
|
34
|
+
|
|
35
|
+
用户在迁移或实现 **LCAP 扩展组件**、提到 **Vue 2 / Vue 3**、**api.ts**、**NASL**、**Storybook**、**`block.stories` / `example.stories`**、**`docs/blocks.md` / `docs/examples.md`**、**Web Worker / Vite**、**`install` / `app.component` 全局注册**、**`src/components/index.ts` 桶导出**、**Mixins / Hooks**,或通过 **`.specify/scripts/bash/create-component-files.sh`** 生成组件骨架时,按本 Skill 执行;默认目标为 **1:1 逻辑与元数据对齐**,不做未经要求的产品改动。
|
|
36
|
+
|
|
37
|
+
## Instructions(Agent 必须遵守)
|
|
38
|
+
|
|
39
|
+
1. **禁止过度优化**:模板结构、报错文案、交互与 Vue 2 版一致;Hook 仅重组代码,不改对外行为与 API 面。
|
|
40
|
+
2. **Mixin → Composable**:用 `useXxx.ts` 按职责拆分;`ref`/SDK 句柄通过参数传入或 return,避免隐式 `this`。
|
|
41
|
+
3. **副作用与第三方实例**:定时器、监听、地图/SDK 等须在 `onUnmounted` / `reload` 等路径显式销毁,防止泄漏。
|
|
42
|
+
4. **第三方库初始化时机**:凡是依赖“容器/DOM 已就绪”或库自身提供的 `ready/complete/onXxxReady` 回调才能正常工作的插件(如 tooltip、marker、事件绑定、样式/DOM 监听器),其初始化代码必须放在对应 ready 回调里;不得仅凭 `onMounted` 猜测时机。
|
|
43
|
+
5. **依赖与构建一致性**:迁移后若出现 Rollup/Vite 解析失败(如 `Failed to resolve` / `Cannot find module`),优先核对 `package.json` 已安装依赖与 Vue2 源一致;若 Vue2 通过某依赖完成日期/格式/渲染逻辑,迁移时不得“省掉依赖”导致行为差异。
|
|
44
|
+
6. **NASL**:`api.ts` 与原版一致(含多端、`ideusage`、`ExtensionComponent` type 等);装饰器 `name` 与目录名一致。
|
|
45
|
+
7. **验证**:对齐 Storybook(含 `demos` 复杂例)并跑 `npm run build`;处理控制台错误。
|
|
46
|
+
8. **Web Worker**:在 Vite 下用 `?url` / `?worker` 等官方方式引用 worker,配置放在消费该能力的**模块顶部**、任何异步调用之前;见下文「Web Worker(Vite)」。
|
|
47
|
+
9. **Block Story(溯源分源项目)**:Vue 2 **Vite 工程**以原仓库 **`block.stories.js` / `block.stories.ts`** 为真值做 **1:1**;**旧工程**以 **`docs/blocks.md`** 为真值。勿凭记忆重写;见下文「Block(block.stories)一一对齐」。
|
|
48
|
+
10. **Example Story(溯源分源项目)**:Vue 2 **Vite 工程**以原仓库 **`example.stories.js` / `example.stories.ts`** 为真值做 **1:1**;**旧工程**以 **`docs/examples.md`**(及团队约定下的 `demos`)为真值。见下文「Example(example.stories)一一对齐」。
|
|
49
|
+
11. **插件 `install` 与组件桶文件**:若入口通过 `import * as Components from './components'` 再 `Object.keys(Components).forEach` 注册,**`src/components/index.ts` 必须为每个组件提供与注册名一致的具名导出**,禁止仅靠「子目录 `export default` + `export *`」导致命名空间只有 `default` 或键名不符合预期;见下文「插件 install 与 components 桶导出」。
|
|
50
|
+
|
|
51
|
+
## 核心原则
|
|
52
|
+
|
|
53
|
+
| 原则 | 要求 |
|
|
54
|
+
|------|------|
|
|
55
|
+
| 1:1 逻辑 | 模板、文案、行为与 Vue 2 一致 |
|
|
56
|
+
| 架构 | Mixins → Composition API(`useXxx`) |
|
|
57
|
+
| 生命周期 | 第三方实例显式销毁 |
|
|
58
|
+
| NASL | `api.ts` 与原版一致(含多端属性) |
|
|
59
|
+
|
|
60
|
+
## §mixin-composable · Mixin → Hooks
|
|
61
|
+
|
|
62
|
+
## Mixin → Hooks(要点)
|
|
63
|
+
|
|
64
|
+
- **拆分**:按业务边界合并 `data` / `computed` / `methods` / `watch`,一边界一 `useXxx`。
|
|
65
|
+
- **响应式**:`data` → `ref`/`reactive`;`computed` → `computed()`;`watch` 对齐 `deep`、`immediate`。
|
|
66
|
+
- **生命周期**:`mounted` 等 → `onMounted` 等;副作用配套清理。
|
|
67
|
+
- **对外方法**:原 Mixin 供模板/父组件调用的方法,经 Hook **return**;`defineExpose` 仅暴露原版已有能力(如 `reload`),勿随意加新导出。
|
|
68
|
+
- **`$emitPrevent`**:Vue 2 Emitter mixin 提供的 `$emitPrevent(name, payload, senderVM)` 在 Vue 3 中无法直接使用,迁移为本地工具函数:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
function emitPrevent(name: string, payload: any): boolean {
|
|
72
|
+
let cancel = false;
|
|
73
|
+
emit(name as any, Object.assign(payload || {}, {
|
|
74
|
+
preventDefault: () => { cancel = true; },
|
|
75
|
+
}));
|
|
76
|
+
return cancel;
|
|
77
|
+
}
|
|
78
|
+
// 使用:if (emitPrevent('before-upload', { file: files[0] })) return;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## §vue3-children-clone · $children → cloneVNode
|
|
82
|
+
|
|
83
|
+
## `$children` 操作 → `cloneVNode` 注入
|
|
84
|
+
|
|
85
|
+
Vue 2 通过 `$children` 遍历 + `$set(vm, 'loading', true)` 直接修改 slot 子组件的 data/props,Vue 3 已废弃 `$children` 且禁止从外部 mutate 组件内部状态。
|
|
86
|
+
|
|
87
|
+
**Vue 3 等效方案:`cloneVNode` + 功能组件**
|
|
88
|
+
|
|
89
|
+
在渲染 slot 时用 `cloneVNode` 克隆 VNode 并注入额外 props,对目标子组件(可通过 `vnode.props?.flag` 等特征识别)自动补充状态,使用方**无需修改任何调用代码**:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { cloneVNode, useSlots } from 'vue';
|
|
93
|
+
|
|
94
|
+
const uploadingCount = ref(0);
|
|
95
|
+
const slots = useSlots();
|
|
96
|
+
|
|
97
|
+
// 功能组件:渲染时自动向带有 flag="xxx-button" 的子组件注入 loading/disabled
|
|
98
|
+
function SlotWithButtonState() {
|
|
99
|
+
const vnodes = slots.default?.() ?? [];
|
|
100
|
+
const isUploading = uploadingCount.value > 0;
|
|
101
|
+
return vnodes.map((vnode) => {
|
|
102
|
+
if (vnode.props?.flag === 'large-file-uploader-button') {
|
|
103
|
+
return cloneVNode(vnode, { loading: isUploading, disabled: isUploading });
|
|
104
|
+
}
|
|
105
|
+
return vnode;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
模板中以 `<SlotWithButtonState />` 替换原来的 `<slot></slot>`。`uploadingCount` 为响应式 ref,变化后功能组件自动重渲染、重新注入正确的 props,无需手动"reset"。
|
|
111
|
+
|
|
112
|
+
> **适用场景**:任何 Vue 2 中通过 `$children` 查找特定子组件并设置其属性的模式,均可用此方案替换。
|
|
113
|
+
|
|
114
|
+
## §alignment-checklist · 对齐清单
|
|
115
|
+
|
|
116
|
+
## 对齐清单(迁移时逐项勾选)
|
|
117
|
+
|
|
118
|
+
**Template**
|
|
119
|
+
|
|
120
|
+
- [ ] 嵌套结构与 Vue 2 一致(注意 Fragment 导致样式差异)
|
|
121
|
+
- [ ] 报错、空状态等文案 1:1
|
|
122
|
+
- [ ] 作用域插槽:`#item="slotProps"`,变量名与原版一致
|
|
123
|
+
|
|
124
|
+
**Logic**
|
|
125
|
+
|
|
126
|
+
- [ ] 逐条核对 `watch`(如部分组件需 `clearSelectedItem`,海量点等可能不需要)
|
|
127
|
+
- [ ] `defineExpose` 仅导出原版方法
|
|
128
|
+
- [ ] 特殊清理(如某 Layer 样式)在组件内按需实现
|
|
129
|
+
|
|
130
|
+
**第三方库集成**
|
|
131
|
+
|
|
132
|
+
- [ ] 第三方库初始化放在其 `ready/complete/onXxxReady` 回调或对应挂载完成点(而非仅 `onMounted` 猜测)
|
|
133
|
+
- [ ] DOM 监听器(`MutationObserver` / `ResizeObserver` / 鼠标事件 / tooltip DOM 事件)创建时机保证目标 DOM 存在;在 `onUnmounted` / `reload` 中执行 `disconnect/removeEventListener` 等清理
|
|
134
|
+
- [ ] 布局高度语义与 Vue 2 保持一致:固定值保持固定;基于容器继承的 `height: 100%/flex` 保持链路(根节点与 IDE 父容器的高度继承关系验证);若 Vue 2 有 `updateStyles/root.style.height`,Vue 3 需沿用同样触发时机
|
|
135
|
+
- [ ] Vue 2 mixin/support 里“工具方法/数据预处理/格式化函数”的清单已全量迁移;迁移后不应只保留一部分工具导致数据/渲染缺失
|
|
136
|
+
|
|
137
|
+
**LCAP / NASL**
|
|
138
|
+
|
|
139
|
+
- [ ] 多端:`@ExtensionComponent({ type: 'both' })` 等配置正确
|
|
140
|
+
- [ ] 组件名不含数字(如 `3d` → `three-dim`)
|
|
141
|
+
- [ ] `ideusage`:`container` / `element` 等与原版一致
|
|
142
|
+
- [ ] 设计器:`window.$uilibenv?.IDE_DESIGNER === true`;**仅**对依赖容器尺寸才能正常渲染的组件(图表、地图、PDF 等)加固定高度 + `overflow: hidden`,防止撑开画布;**简单容器(如触发按钮、下载包装器等)无需此处理**,强加反而会裁掉插槽内容
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<template>
|
|
146
|
+
<div :class="[isDesigner && $style.designerRoot]">
|
|
147
|
+
<!-- 组件内容 -->
|
|
148
|
+
</div>
|
|
149
|
+
</template>
|
|
150
|
+
|
|
151
|
+
<script setup lang="ts">
|
|
152
|
+
const isDesigner = (window as any).$uilibenv?.IDE_DESIGNER === true;
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<style module>
|
|
156
|
+
.designerRoot {
|
|
157
|
+
height: 300px;
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
border: 1px solid #ccc;
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Storybook**
|
|
165
|
+
|
|
166
|
+
- [ ] 还原原项目 `demos` / `examples` 复杂示例
|
|
167
|
+
- [ ] 动态交互(如切换数据)用 `ref` 等在 Story 中模拟
|
|
168
|
+
- [ ] `block.stories.*`:IDE 左侧预览;**Vite 源项目**对齐原 **`block.stories.js` / `block.stories.ts`**,**旧工程**对齐 **`docs/blocks.md`**(见下节「Block」)
|
|
169
|
+
- [ ] `example.stories.*`:运行时 Demo;**Vite 源项目**对齐原 **`example.stories.js` / `example.stories.ts`**,**旧工程**对齐 **`docs/examples.md`** / `demos`(见下节「Example」)
|
|
170
|
+
|
|
171
|
+
**api.ts**
|
|
172
|
+
|
|
173
|
+
- [ ] `@nasl/types` 等约定;`Component` 等装饰器 `name` 与组件目录名一致
|
|
174
|
+
- [ ] `EnumSelectSetter` 的 `options` 数组中**禁止写 `castFunction` 字段**(该字段不在类型定义中,会导致 `tsc` build error);选项只需 `{ title, value }`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ❌ 错误
|
|
178
|
+
setter: { concept: 'EnumSelectSetter', options: [{ title: 'JSON', castFunction: '', value: 'json' }] }
|
|
179
|
+
// ✅ 正确
|
|
180
|
+
setter: { concept: 'EnumSelectSetter', options: [{ title: 'JSON', value: 'json' }] }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- [ ] **prop 默认值以 `api.yaml` / `api.ts` 为准**:若 Vue 2 组件代码漏写默认值但 `api.yaml` 已声明,Vue 3 迁移时按 `api.yaml` 补齐,不应照搬 Vue 2 组件代码的遗漏
|
|
184
|
+
- [ ] **LCAP v-model 约定**:LCAP 平台使用 `value` prop + `emit('update:value', ...)` 的 sync 模式(而非 Vue 3 标准的 `modelValue` + `update:modelValue`);模板中使用 `v-model:value="xxx"`
|
|
185
|
+
|
|
186
|
+
**Plugin / 入口导出(`install`)**
|
|
187
|
+
|
|
188
|
+
- [ ] 若存在 `install` 内 `import * as Components` + 按 key 遍历 `app.component(name, …)`:**`components/index.ts`** 已用 **`export { default as Xxx } from './…'`** 等形式,使 `Components` 的键为 **`CwDocPreview` 等真实组件名**,而非仅 `default`
|
|
189
|
+
- [ ] 页面中自定义标签不解析 / 组件未按预期挂载时,核对 **`app.component` 注册名**与模板标签是否一致
|
|
190
|
+
|
|
191
|
+
## §stories-truth · Stories 溯源
|
|
192
|
+
|
|
193
|
+
## Block(block.stories)一一对齐
|
|
194
|
+
|
|
195
|
+
迁移中最易偏离的是新区块 Story 与**溯源真值**不一致。先判断 **Vue 2 源仓库**是 **Vite 工程**还是 **旧工程**:
|
|
196
|
+
|
|
197
|
+
| 源项目类型 | 真值来源 | 对齐方式 |
|
|
198
|
+
|------------|----------|----------|
|
|
199
|
+
| **Vue 2 + Vite** | 原组件目录下 **`block.stories.js`** 或 **`block.stories.ts`** | 迁移后的 `block.stories.*` 与源文件 **1:1**(template / 元数据 / 静态 `args` 等按团队约定逐项对照) |
|
|
200
|
+
| **旧工程** | **`docs/blocks.md`**(或团队约定的等价路径) | Story 中 **`template`(或等价渲染结构)** 与文档内 HTML 片段 **逐段对齐** |
|
|
201
|
+
|
|
202
|
+
**旧工程 · Template 注意**:允许为 Story API 包一层容器,但**不要为了「代码整洁」合并 DOM 层级、改写 `:class` / 类名**,以免与 IDE 区块预览或全局样式不一致。
|
|
203
|
+
|
|
204
|
+
**Vue2 内置组件标签替换**:Vue2 block.stories 模板中常见 `u-button`、`van-button` 等 Cloud UI / Vant 专属标签,这些在 Vue3 项目中不存在。迁移时须替换为 Vue3 项目实际可用的组件,如 `el-button`(Element Plus);若不确定,询问用户或查看 Vue3 项目已安装的 UI 库。
|
|
205
|
+
|
|
206
|
+
## Example(example.stories)一一对齐
|
|
207
|
+
|
|
208
|
+
运行时示例须与**溯源真值**一致,同样按源项目类型分支:
|
|
209
|
+
|
|
210
|
+
| 源项目类型 | 真值来源 | 对齐方式 |
|
|
211
|
+
|------------|----------|----------|
|
|
212
|
+
| **Vue 2 + Vite** | 原组件目录下 **`example.stories.js`** 或 **`example.stories.ts`** | 迁移后的 `example.stories.*` 与源文件 **1:1**(交互、变量名、方法名、调用顺序) |
|
|
213
|
+
| **旧工程** | **`docs/examples.md`**(及原 **`demos`** 中 Vue 示例,若文档与 demos 并存则以团队约定优先) | 与文档 / demos 中的示例 **1:1**,禁止凭印象重写后「差不多能用」 |
|
|
214
|
+
|
|
215
|
+
## §css-vars · CSS 变量替换
|
|
216
|
+
|
|
217
|
+
## CSS 变量替换(Cloud UI → Element Plus)
|
|
218
|
+
|
|
219
|
+
Vue 2 cloud-ui 组件使用 `--uploader-*`、`--linear-progress-*`、`--cursor-pointer`、`--transition-duration-base` 等私有 CSS 变量,Vue 3 项目不引入 cloud-ui,需替换为 Element Plus 全局变量。
|
|
220
|
+
|
|
221
|
+
**常用映射(参考,具体以语义匹配为准):**
|
|
222
|
+
|
|
223
|
+
| Cloud UI 变量 | Element Plus 替代 |
|
|
224
|
+
|---|---|
|
|
225
|
+
| `--cursor-pointer` | `pointer`(直接写死) |
|
|
226
|
+
| `--transition-duration-base` | `var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier)` |
|
|
227
|
+
| 背景填充(浅) | `var(--el-fill-color-lighter)` |
|
|
228
|
+
| 背景填充(悬浮) | `var(--el-fill-color)` |
|
|
229
|
+
| 主色 / 链接色 | `var(--el-color-primary)` |
|
|
230
|
+
| 主色深(悬浮) | `var(--el-color-primary-dark-2)` |
|
|
231
|
+
| 边框色 | `var(--el-border-color)` |
|
|
232
|
+
| 边框圆角 | `var(--el-border-radius-base)` |
|
|
233
|
+
| 次要文字色 | `var(--el-text-color-secondary)` |
|
|
234
|
+
| 占位符/图标色 | `var(--el-text-color-placeholder)` |
|
|
235
|
+
| 错误/危险色 | `var(--el-color-danger)` |
|
|
236
|
+
| 成功色 | `var(--el-color-success)` |
|
|
237
|
+
| 字体大小(小) | `var(--el-font-size-extra-small)` |
|
|
238
|
+
|
|
239
|
+
**Storybook 配置步骤:**
|
|
240
|
+
|
|
241
|
+
1. 创建 `.storybook/vars.css`,内容复制自 `lcap-extension-component/component/platform/theme-variables-element-plus.md`
|
|
242
|
+
2. 在 `.storybook/preview.ts` 中注册 Element Plus 并导入变量文件:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import ElementPlus from 'element-plus';
|
|
246
|
+
import 'element-plus/dist/index.css';
|
|
247
|
+
import './vars.css';
|
|
248
|
+
|
|
249
|
+
setup((app) => {
|
|
250
|
+
app.use(ElementPlus);
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
3. 将 `element-plus` 加入 `devDependencies`(`npm install element-plus --save-dev`)
|
|
255
|
+
|
|
256
|
+
## §web-worker · Web Worker(Vite)
|
|
257
|
+
|
|
258
|
+
在 Vite 中处理 **Web Worker**(含第三方库内置 worker)时,避免继续沿用「CDN 链接 / Webpack entry / 手写复制 `*.min.js`」等旧习惯,优先用构建器解析出的 **稳定 URL 或 Worker 构造**。
|
|
259
|
+
|
|
260
|
+
**原则**
|
|
261
|
+
|
|
262
|
+
- **统一由 Vite 解析 worker 资源**:使用 `?url` 得到 worker 脚本 URL,或按需使用 `?worker` / `new URL(..., import.meta.url)` 等 [Vite 文档](https://vitejs.dev/guide/features.html#web-workers) 推荐写法。
|
|
263
|
+
- **前置初始化**:设置 `workerSrc`、`new Worker(...)` 等逻辑放在**实际消费方模块的顶部**(该文件内任何 `async` 业务逻辑执行之前),避免首次调用时 worker 尚未就绪。
|
|
264
|
+
- **SSR**:若该模块会在 SSR 阶段执行,对 `Worker` / `window` 相关代码加环境守卫,仅在浏览器侧初始化。
|
|
265
|
+
|
|
266
|
+
**第三方示例:`pdfjs-dist`**
|
|
267
|
+
|
|
268
|
+
将 worker 脚本作为 URL 模块导入,并赋给官方全局配置(路径以当前安装的 `pdfjs-dist` 版本为准):
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
272
|
+
import pdfWorker from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
|
273
|
+
|
|
274
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
自研 worker 可类似使用 `import MyWorker from './my.worker?url'` 再 `new Worker(MyWorker, { type: 'module' })`(是否 `module` 以浏览器与打包目标为准)。
|
|
278
|
+
|
|
279
|
+
## §plugin-barrel · 插件 install 与 components 桶导出
|
|
280
|
+
|
|
281
|
+
## 插件 `install` 与 `components` 桶导出
|
|
282
|
+
|
|
283
|
+
扩展包常在入口(如 `src/index.ts`)用 **Plugin** 做全局注册:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import * as Components from './components';
|
|
287
|
+
|
|
288
|
+
export const install: Plugin = (app, options) => {
|
|
289
|
+
Object.keys(Components).forEach((name) => {
|
|
290
|
+
app.component(name, (Components as Record<string, Component>)[name]);
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
此处要求 **`import * as Components` 得到的命名空间以「组件名」为键**(如 `CwDocPreview`),否则 `app.component` 的**注册名**会错,页面表现为**标签未解析、组件不渲染**。
|
|
296
|
+
|
|
297
|
+
**典型问题**:子目录仅 `export default`、桶文件写 **`export * from './cw-doc-preview'`** 等——在配合 `import *` 自动注册时,**无法为命名空间提供稳定的 PascalCase 键名**,易出现**仅存在 `default` 键**或与预期组件名不一致。
|
|
298
|
+
|
|
299
|
+
**推荐写法**:在 **`src/components/index.ts`(或等价桶文件)** 使用**具名再导出**,显式指定键名:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
export { default as CwDocPreview } from './cw-doc-preview';
|
|
303
|
+
export { default as CwPdfPreview } from './cw-pdf-preview';
|
|
304
|
+
// …
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
这样 `Components` 为 `{ CwDocPreview: …, CwPdfPreview: … }`,与 `app.component('CwDocPreview', …)` 及模板中的标签名一致。
|
|
308
|
+
|
|
309
|
+
**经验**:组件库入口**宁可手写具名导出多几行**,也不要依赖模糊的 `export *` 聚合 default;迁移后若组件「完全不显示」,**优先查注册名与导出键是否一致**。
|
|
310
|
+
|
|
311
|
+
## §pitfalls · 常见坑
|
|
312
|
+
|
|
313
|
+
## 常见坑
|
|
314
|
+
|
|
315
|
+
- **`$children` 操控 slot 子组件**:Vue 3 无 `$children`,且不允许从外部 mutate 子组件 props/data;用 `cloneVNode` + 功能组件注入 props,见上文「`$children` 操作 → `cloneVNode` 注入」。
|
|
316
|
+
- **CSS 属性选择器依赖的 prop 必须显式声明并绑定**:若 Vue 2 中某 prop(如 `display`)未在 `props` 里声明、靠 attr 透传到根节点触发 CSS 属性选择器(如 `.root[display="inline"]`),Vue 3 迁移时必须:① 在 `defineProps` 中声明该 prop;② 在根节点显式绑定 `:display="display"`——否则 `display` 值为 `undefined`,CSS 选择器永远不命中。
|
|
317
|
+
- **`$emitPrevent`**:Vue 2 Emitter mixin 方法,Vue 3 迁移为本地工具函数,见上文「Mixin → Hooks」。
|
|
318
|
+
- **地图类**:`await map.on('complete')` 后再初始化聚合/海量点等;慢网否则易错。
|
|
319
|
+
- **插件/第三方初始化时机错误**:依赖库的 tooltip/marker/事件绑定若放在 `onMounted`,可能出现 tooltip 不显示、DOM 节点丢失或样式不同步等问题;应迁移到库的 `ready/complete/onXxxReady` 回调内初始化。
|
|
320
|
+
- **构建/解析失败(依赖缺失)**:`moment`/日期格式化/渲染工具等如果在 Vue2 源中被使用,迁移时必须保证依赖已安装且导入路径一致;出现 `Failed to resolve` 时优先检查 `package.json`。
|
|
321
|
+
- **布局高度语义丢失**:固定高度/`height:100%`/flex 继承链路在 Vue3 根节点或容器上若不一致,容易导致表格遮挡、内容高度塌陷或无法自适应;迁移后需对照 Vue2 高度策略验证。
|
|
322
|
+
- **DOM 同步监听器未断开**:`MutationObserver`/`ResizeObserver`/事件监听若只在迁移中“创建了”但未在 `onUnmounted`/`reload` 中 `disconnect/removeEventListener`,会导致重复触发或渲染异常。
|
|
323
|
+
- **Vue2 工具方法漏迁**:如果 Vue2 mixin/support 存在数据预处理/格式化/normalize 工具方法,迁移时只迁了部分函数会导致渲染缺失或 IDE 样式配置不生效。
|
|
324
|
+
- **地图内存**:如 `destroyAMap`;`reload` 与 `onUnmounted` 先销毁再建新实例。
|
|
325
|
+
- **构建**:`process.env`(Webpack)→ `import.meta.env`(Vite)。
|
|
326
|
+
- **资源**:静态资源同步到 `src/assets` 并修正引用。
|
|
327
|
+
- **Hooks 复用**:勿强行统一不同组件对同一事件(如 Zoom)的处理。
|
|
328
|
+
- **依赖**:第三方库写入 `package.json`;大库(如 `pdf.js`)优先 npm + ESM,Worker 见上文「Web Worker(Vite)」。
|
|
329
|
+
- **全局注册**:`install` + `import * as Components` 时,**桶文件须具名导出**,见上文「插件 install 与 components 桶导出」。
|
|
330
|
+
- **LCAP `Current<T>` 作用域插槽传参**:`api.ts` 中 `slotDefault: (current: nasl.ui.Current<T>)` 时,组件内应 `<slot :item="row" :index="i">`,父模板 `#default="current"` 使用 `current.item`。**禁止**写成 `<slot :current="{ item: row, index: i }">`——会导致 props 嵌套为 `{ current: { item, index } }`,`current.item` 变成对象、画布空白。对齐 Vue2 源库的 `:item`/`:index` 写法。
|
|
331
|
+
- **`vue-draggable-plus` 与循环 slot**:`VueDraggable` 内 `v-for` 渲染 scoped slot 时,**勿**在 `div` 内再包一层 `<template><slot/></template>`(Vue2 `vuedraggable@2` 可能可用,Vue3 下易出现空 `<template>`、插槽不渲染)。slot 应直接作为列表项 DOM 的子节点。
|
|
332
|
+
- **block.stories 预览 IDE 容器**:组件靠 `inIDE` 分支 + `dataSource.display: 3` 展示占位项、block 文档又无 `dataSource` 时,Storybook 需在 story 内临时设置 `window.$uilibenv = { IDE_DESIGNER: true }`(`unmounted` 清理),否则运行时 `v-for` 遍历空数组、区块 Story 空白。
|
|
333
|
+
|
|
334
|
+
## §implementation-order · 工作流
|
|
335
|
+
|
|
336
|
+
## 工作流
|
|
337
|
+
|
|
338
|
+
1. 读原版 `api.ts`、`index.vue`;判断 Vue 2 源工程是否为 **Vite**:若是,打开原 **`block.stories.js` / `block.stories.ts`**、**`example.stories.js` / `example.stories.ts`** 作为 Story 真值;若为**旧工程**,读 **`docs/blocks.md`**、**`docs/examples.md`**(及 `demos`,若存在)
|
|
339
|
+
2. 在仓库根目录执行 `bash .specify/scripts/bash/create-component-files.sh` 生成骨架(参数以脚本说明或团队约定为准)
|
|
340
|
+
3. 配 `api.ts`(type、props、events)
|
|
341
|
+
4. 迁移前列出 Vue2 mixin/support 的“工具方法/数据预处理/格式化函数”全量清单;**先逐项确认该方法/属性在组件模板或 methods 中是否有实际调用点**——有调用才迁移为 composable,从未被调用的死代码无需创建 composable,只需保留对应的 prop 声明即可;逐项核对迁移结果不缺失(再接通用 Hooks + 组件特有逻辑)。若有 **`install` 全局注册**,核对 **`src/components/index.ts`** 具名导出与 `app.component` 注册名一致
|
|
342
|
+
5. 第三方库初始化点对齐其 `ready/complete/onXxxReady` 回调;同步梳理 DOM 监听器/observer 的创建时机与 `onUnmounted` / `reload` 清理路径
|
|
343
|
+
6. 分别还原 **`block.stories.*`** 与 **`example.stories.*`**:**Vite 源**对齐原 **`block.stories.js` / `block.stories.ts`**、**`example.stories.js` / `example.stories.ts`**;**旧工程**对齐 **`docs/blocks.md`** / **`docs/examples.md`**(及 `demos`)
|
|
344
|
+
7. Storybook 预览 + `npm run build`;若出现解析失败,优先检查依赖安装是否与 Vue2 源一致
|
|
345
|
+
8. 核对 `package.json` 的 `title`、`description`
|
|
346
|
+
|
|
347
|
+
## §logics-boundary · 逻辑迁移边界
|
|
348
|
+
|
|
349
|
+
**仅当**迁移涉及 `logics/` 下依赖库逻辑(`@NaslLogic`、`nasl` 类型、可变参数 / 泛型 / 高阶函数等)时,plan / green / check 额外遵循:
|
|
350
|
+
|
|
351
|
+
**`references/nasl-logic-authoring/SKILL.md`**
|
|
352
|
+
|
|
353
|
+
logic 资产**不适用**本 KB 的 §alignment-checklist、§stories-truth、§css-vars、§plugin-barrel、§process-e2e。
|
|
354
|
+
|
|
355
|
+
check 审计维 L1–L3 定义在 `commands/migrate.check.md`,不在本文重复。
|
|
356
|
+
|
|
357
|
+
## §process-doc · 迁移文档职责
|
|
358
|
+
|
|
359
|
+
| 文件 | 模板 | 写什么 | 不写什么 | 由谁写 |
|
|
360
|
+
|------|------|--------|----------|--------|
|
|
361
|
+
| `manifest.md` | `migration-manifest-template.md` | 资产清单 + 状态流转 | 详细报告内容 | scan(创建)/ 各命令(更新状态行) |
|
|
362
|
+
| `spec.md` | `migration-spec-template.md` | Vue2 行为真值(完整规格提取) | 转换方案、Vue3 实现细节 | spec |
|
|
363
|
+
| `plan.md` | `migration-plan-template.md` | 转换策略、文件路径规划、风险评估 | TDD 步骤编排、状态记录 | plan |
|
|
364
|
+
| `tasks.md` | `migration-tasks-template.md` | TDD 编排 + 行为等价矩阵 | 转换策略、源码分析 | tasks(创建)/ red+green+check(填充矩阵) |
|
|
365
|
+
| `report.md` | `migration-report-template.md` | 5 维审计 + 矩阵验证 + 终端证据 + 机制差异 | 其他资产状态 | check |
|
|
366
|
+
| `library-report.md` | `library-report-template.md` | 库级三门禁 + 产物验证 + 资产统计 | 单资产细节 | check §E |
|
|
367
|
+
| `specs/manifest.md` | `migration-manifest-template.md` | 资产清单 + depends_on + library_status | 详细报告 | scan / 各命令 |
|
|
368
|
+
|
|
369
|
+
**文档位置**:所有文档位于 `<Vue3工程>/specs/<NN>-<资产名>/` 目录内(manifest.md 在 `specs/` 根)。
|
|
370
|
+
|
|
371
|
+
**矩阵填充规则**:
|
|
372
|
+
- `tasks.md` 创建矩阵骨架(行为描述 + 维度)
|
|
373
|
+
- `migrate.red` 填充"RED 测试编码"列
|
|
374
|
+
- `migrate.green` 填充"GREEN 实现"列
|
|
375
|
+
- `migrate.check` 填充"等价状态"列(最终判定)
|
|
376
|
+
|
|
377
|
+
## §process-matrix · 行为等价矩阵
|
|
378
|
+
|
|
379
|
+
行为等价矩阵是 Superpowers 迁移的核心追踪机制,记录 Vue2 行为从「定义 → 测试编码 → 实现 → 验证」的完整链路。
|
|
380
|
+
|
|
381
|
+
### 矩阵结构
|
|
382
|
+
|
|
383
|
+
| # | 行为描述 | 维度 | RED 测试编码 | GREEN 实现 | 等价状态 |
|
|
384
|
+
|:--|:---|:---|:---|:---|:---|
|
|
385
|
+
|
|
386
|
+
- **行为描述**:来自 spec.md,一行 = 一个可独立测试的 Vue2 行为
|
|
387
|
+
- **维度**:Props / Events / Methods / Slots / State / Lifecycle
|
|
388
|
+
- **RED 测试编码**:测试文件路径 + 行号(如 `__tests__/index.spec.ts:42`)
|
|
389
|
+
- **GREEN 实现**:实现文件 + 函数名(如 `index.vue:handlePlay`)
|
|
390
|
+
- **等价状态**:最终验证结论
|
|
391
|
+
|
|
392
|
+
### 等价状态值
|
|
393
|
+
|
|
394
|
+
| 状态 | 含义 | 处理方式 |
|
|
395
|
+
|:---|:---|:---|
|
|
396
|
+
| ✅ 等价 | 测试通过,实现与 spec 一致 | 无需处理 |
|
|
397
|
+
| ⚠️ 有意差异 | Vue3 框架限制导致的差异 | 记录原因,归入 report 维度 5 |
|
|
398
|
+
| ❌ 缺失 | 无测试 / 测试未通过 / 实现遗漏 | 必须修复至 ✅ 或 ⚠️ |
|
|
399
|
+
| `—` | 尚未填充 | 等待对应阶段处理 |
|
|
400
|
+
|
|
401
|
+
### 填充时机
|
|
402
|
+
|
|
403
|
+
```
|
|
404
|
+
tasks → 创建矩阵骨架(行为描述 + 维度列)
|
|
405
|
+
red → 填充"RED 测试编码"列(测试文件:行号)
|
|
406
|
+
green → 填充"GREEN 实现"列(实现文件:函数名)
|
|
407
|
+
check → 填充"等价状态"列 + 最终判定
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### 完成标准
|
|
411
|
+
|
|
412
|
+
- **RED 完成**:测试编码列无空行(每行为有测试)
|
|
413
|
+
- **GREEN 完成**:实现列无空行(每行为有实现)
|
|
414
|
+
- **CHECK 通过**:等价状态列无 ❌ 行
|
|
415
|
+
|
|
416
|
+
### 三类型矩阵行差异
|
|
417
|
+
|
|
418
|
+
| 类型 | 典型维度 | E2E |
|
|
419
|
+
|------|----------|-----|
|
|
420
|
+
| component | Props / Events / Methods / Slots / State / Lifecycle | 必需 |
|
|
421
|
+
| logic | Params / Return / Generic / Edge | 禁止 |
|
|
422
|
+
| shared-composable | Methods / SideEffect / Contract | 禁止 |
|
|
423
|
+
|
|
424
|
+
## §process-scaffold · 项目创建约束
|
|
425
|
+
|
|
426
|
+
### create-extension-project.sh
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
bash <skill-scripts>/create-extension-project.sh \
|
|
430
|
+
--name <name> --title <title> \
|
|
431
|
+
--target-dir <dir> \
|
|
432
|
+
[--template vue3|vue2]
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
- `--name`:仅 `[a-z_]+`
|
|
436
|
+
- **`--target-dir`**:放置目录(**不含库名**);脚本在其下创建 `<name>/`
|
|
437
|
+
- **`--template`**:默认 `vue3`;**仅** `vue2` | `vue3`(react 暂不支持)
|
|
438
|
+
- stdout:`libraryDir=...`、`template=...`
|
|
439
|
+
- 验证:`npm install --prefix "<dir>/<name>" && npm run dev --prefix "<dir>/<name>"`
|
|
440
|
+
- **不**在本脚本内执行 `lcap install`
|
|
441
|
+
|
|
442
|
+
### create-component-files.sh
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
bash <skill-scripts>/create-component-files.sh \
|
|
446
|
+
--library-dir <libraryDir> \
|
|
447
|
+
--name <name> --title <title> [--type pc|h5|both]
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
- **`--library-dir`**:已存在库根(含 `package.json`、`@lcap/builder`)
|
|
451
|
+
- 前置:`npm install --prefix "<libraryDir>"` 已完成
|
|
452
|
+
- 产出:`src/components/<name>/`(或 CLI 决定的目录)
|
|
453
|
+
- **后续只能 Edit,禁止 Write 全量重写**
|
|
454
|
+
|
|
455
|
+
## §process-asset-types · 迁移资产类型
|
|
456
|
+
|
|
457
|
+
| 类型 | scan 检测 | E2E | 单资产产物验证 |
|
|
458
|
+
|------|-----------|-----|----------------|
|
|
459
|
+
| `component` | 含 api.ts + index.vue 的目录 | 必需 | ZIP + viewComponents 含本组件 |
|
|
460
|
+
| `logic` | logics/ 下 @NaslLogic 导出 | 跳过 | 仅 build exit 0 |
|
|
461
|
+
| `shared-composable` | Mixin 被 ≥2 组件引用 | 跳过 | 仅 build exit 0 |
|
|
462
|
+
|
|
463
|
+
各 migrate 命令必须读取 manifest 中该资产的「类型」列,走对应分支(→ 各 command 内「资产类型分支」节)。
|
|
464
|
+
|
|
465
|
+
### 共享 Composable 升格规则(scan 执行)
|
|
466
|
+
|
|
467
|
+
- Mixin 被 **≥2** 组件 import → manifest 新增 `shared-composable` 资产,资产名 = mixin 文件名去扩展名(如 `emitter.js` → `emitter`)
|
|
468
|
+
- 仅 1 组件引用 → **不**单独建资产,在该 component 的 plan 内 composable 化
|
|
469
|
+
|
|
470
|
+
### 产出路径(默认)
|
|
471
|
+
|
|
472
|
+
- `src/composables/use<MixinPascal>.ts`
|
|
473
|
+
- `src/composables/__tests__/use<MixinPascal>.spec.ts`
|
|
474
|
+
|
|
475
|
+
plan 可改路径,但须在文件路径表锁定。
|
|
476
|
+
|
|
477
|
+
### depends_on
|
|
478
|
+
|
|
479
|
+
- shared-composable 行 `depends_on` 为空
|
|
480
|
+
- 引用它的 component 行 `depends_on` 填 shared 资产名(多个逗号分隔)
|
|
481
|
+
|
|
482
|
+
### 环检测
|
|
483
|
+
|
|
484
|
+
scan 拓扑排序时若发现 depends_on 环 → 报告错误并终止,要求用户手动解环。
|
|
485
|
+
|
|
486
|
+
推荐顺序:**shared-composable → logic → component**(manifest 拓扑排序;环则 scan 报错终止)。
|
|
487
|
+
|
|
488
|
+
## §process-e2e · E2E 规则(仅 component)
|
|
489
|
+
|
|
490
|
+
> **仅 component 资产**编写 E2E;logic / shared-composable **禁止** E2E 任务。
|
|
491
|
+
|
|
492
|
+
> 与 create/evolve 工作流共享同一规范(来源:`references/lcap-extension-component/SKILL.md`)。
|
|
493
|
+
|
|
494
|
+
### Story ID 生成规则
|
|
495
|
+
|
|
496
|
+
storyId = `<title-kebab-case>--<export-name-kebab-case>`
|
|
497
|
+
|
|
498
|
+
- **title**:`export default { title: 'xxx' }` 的值,Storybook 自动 kebab 化
|
|
499
|
+
- **export name**:具名导出变量名(PascalCase → kebab-case)
|
|
500
|
+
|
|
501
|
+
示例:
|
|
502
|
+
```js
|
|
503
|
+
export default { title: 'cw-audio-view-examples' };
|
|
504
|
+
export const BasicUsage = { ... };
|
|
505
|
+
// → storyId: "cw-audio-view-examples--basic-usage"
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### E2E 访问方式
|
|
509
|
+
|
|
510
|
+
**必须用 iframe 直链**:`/iframe.html?id=<storyId>&viewMode=story`
|
|
511
|
+
|
|
512
|
+
**禁止**:`/?path=/story/...`、中文 title、路径分隔符(`/`)。
|
|
513
|
+
|
|
514
|
+
### Story Title 命名约定
|
|
515
|
+
|
|
516
|
+
| stories 文件 | title 格式 |
|
|
517
|
+
|---|---|
|
|
518
|
+
| block.stories | `<组件名-kebab>-blocks` |
|
|
519
|
+
| example.stories | `<组件名-kebab>-examples` |
|
|
520
|
+
|
|
521
|
+
### 验证 story ID
|
|
522
|
+
|
|
523
|
+
编写 E2E 前**必须**确认 storyId:
|
|
524
|
+
- `npm run build-storybook && grep <组件名> storybook-static/index.json`
|
|
525
|
+
- 或运行时访问 `http://localhost:<port>/index.json`
|
|
526
|
+
|
|
527
|
+
不可凭推测编写 storyId。
|
|
528
|
+
|
|
529
|
+
### 选择器策略
|
|
530
|
+
|
|
531
|
+
**优先级**:
|
|
532
|
+
|
|
533
|
+
1. `data-testid`(最稳定)
|
|
534
|
+
2. Accessibility role
|
|
535
|
+
3. 语义 class(库产出的稳定 class)
|
|
536
|
+
4. CSS 选择器
|
|
537
|
+
|
|
538
|
+
**第三方库 DOM 改写**:重型第三方库会包裹/替换原始 DOM;选择器**须以库实际产出 DOM 为准**;编写 E2E 前在 DevTools 确认实际结构;禁止假设原始 HTML 标签直接可选。
|
|
539
|
+
|
|
540
|
+
**规则**:
|
|
541
|
+
|
|
542
|
+
- 禁止裸标签选择器不加限定(会匹配 Storybook 控件区)
|
|
543
|
+
- 匹配多个时用 `.first()` / `.nth()`
|
|
544
|
+
- 第三方库渲染有延迟:用 `waitForSelector` 而非固定 timeout
|
|
545
|
+
|
|
546
|
+
**Storybook 运行时验证**:`npm run build` 不能替代 dev 运行时验证;E2E 须监听 `page.on('pageerror')` 和 `page.on('console')`(过滤网络 404:`msg.text().includes('404')` 或 `net::ERR`)。详细 guardrails 见 `workflow-guardrails.md`。
|
|
547
|
+
|
|
548
|
+
## §process-library-finalize · 库级收尾
|
|
549
|
+
|
|
550
|
+
### 触发条件(migrate.check §E)
|
|
551
|
+
|
|
552
|
+
1. manifest 全部非 `🚫 blocked` 资产均为 `✅ verified`;或
|
|
553
|
+
2. 用户传入 `--finalize-library`(仅跑库级段,不要求资产名)
|
|
554
|
+
|
|
555
|
+
### 动作
|
|
556
|
+
|
|
557
|
+
1. 全量 `npm run test` + `npm run build` + `npm run test:e2e`(无 component 时 e2e 标注 N/A)
|
|
558
|
+
2. 产物验证 4 项(与 create flow BUILD-VERIFY 一致,viewComponents **全量**非空)
|
|
559
|
+
3. 写入 `specs/library-report.md`
|
|
560
|
+
4. manifest:`library_status → 🏁 library-verified`,记录 `library_verified_at`
|
|
561
|
+
|
|
562
|
+
### library-report.md 结构
|
|
563
|
+
|
|
564
|
+
见 `templates/library-report-template.md`。
|