@bovinphang/frontend-craft 2.5.0 → 2.7.0
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/.claude-plugin/marketplace.json +7 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +77 -1
- package/CHANGELOG.zh-CN.md +77 -1
- package/CONTRIBUTING.md +15 -10
- package/CONTRIBUTING.zh-CN.md +15 -10
- package/README.md +69 -43
- package/README.openclaw.md +11 -9
- package/README.openclaw.zh-CN.md +11 -9
- package/README.zh-CN.md +69 -43
- package/agents/fec-typescript-reviewer.md +1 -1
- package/commands/fec-doc-sync.md +7 -6
- package/dist/bin/frontend-craft.js +30 -27
- package/docs/example-prompts.md +82 -69
- package/docs/ja-JP/README.md +110 -84
- package/docs/ko-KR/README.md +100 -74
- package/docs/project-structure.md +8 -4
- package/docs/runtimes/antigravity.md +1 -1
- package/docs/runtimes/augment.md +1 -1
- package/docs/runtimes/claude.md +16 -10
- package/docs/runtimes/claude.zh-CN.md +16 -10
- package/docs/runtimes/cline.md +1 -1
- package/docs/runtimes/codebuddy.md +1 -1
- package/docs/runtimes/codex.md +1 -1
- package/docs/runtimes/copilot.md +1 -1
- package/docs/runtimes/cursor.md +1 -1
- package/docs/runtimes/gemini.md +1 -1
- package/docs/runtimes/kilo.md +1 -1
- package/docs/runtimes/openclaw.md +1 -1
- package/docs/runtimes/opencode.md +1 -1
- package/docs/runtimes/qoder.md +1 -1
- package/docs/runtimes/trae.md +1 -1
- package/docs/runtimes/windsurf.md +1 -1
- package/docs/zh-CN/example-prompts.md +82 -69
- package/docs/zh-CN/project-structure.md +8 -4
- package/docs/zh-TW/README.md +88 -62
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
- package/skills/eval_queries.json +179 -56
- package/skills/fec-accessibility-check/SKILL.md +1 -1
- package/skills/fec-alchemy/SKILL.md +74 -0
- package/skills/fec-alchemy/assets/absorption-plan-template.md +47 -0
- package/skills/fec-alchemy/assets/intake-template.md +55 -0
- package/skills/fec-alchemy/assets/review-checklist.md +31 -0
- package/skills/fec-alchemy/references/methodology.md +94 -0
- package/skills/fec-alchemy/references/originality-and-licensing.md +57 -0
- package/skills/fec-backend-requirements-handoff/SKILL.md +45 -0
- package/skills/fec-browser-storage/SKILL.md +1 -1
- package/skills/fec-canvas-threejs/SKILL.md +1 -1
- package/skills/fec-code-review/SKILL.md +11 -3
- package/skills/fec-data-fetching/SKILL.md +15 -15
- package/skills/fec-data-fetching/references/query-patterns.md +27 -1
- package/skills/fec-debug-framework/SKILL.md +14 -19
- package/skills/fec-debug-framework/references/report-template.md +58 -0
- package/skills/fec-doc-sync/SKILL.md +19 -17
- package/skills/fec-drawio-studio/SKILL.md +69 -0
- package/skills/fec-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
- package/skills/fec-drawio-studio/data/brand-icons.json +878 -0
- package/skills/fec-drawio-studio/data/shape-index.json.gz +0 -0
- package/skills/fec-drawio-studio/references/data-residency.md +27 -0
- package/skills/fec-drawio-studio/references/diagram-patterns.md +41 -0
- package/skills/fec-drawio-studio/references/layout-and-export.md +57 -0
- package/skills/fec-drawio-studio/references/troubleshooting.md +29 -0
- package/skills/fec-drawio-studio/references/xml-and-mermaid.md +54 -0
- package/skills/fec-drawio-studio/scripts/brand-symbols.mjs +99 -0
- package/skills/fec-drawio-studio/scripts/diagram-lint.mjs +143 -0
- package/skills/fec-drawio-studio/scripts/diagram-url.mjs +102 -0
- package/skills/fec-drawio-studio/scripts/layout-graph.mjs +107 -0
- package/skills/fec-drawio-studio/scripts/png-embed-fix.mjs +26 -0
- package/skills/fec-drawio-studio/scripts/scan-go-packages.mjs +36 -0
- package/skills/fec-drawio-studio/scripts/scan-js-modules.mjs +52 -0
- package/skills/fec-drawio-studio/scripts/scan-python-classes.mjs +30 -0
- package/skills/fec-drawio-studio/scripts/scan-python-modules.mjs +55 -0
- package/skills/fec-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
- package/skills/fec-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
- package/skills/fec-drawio-studio/scripts/shape-query.mjs +81 -0
- package/skills/fec-drawio-studio/scripts/studio-core.mjs +139 -0
- package/skills/fec-drawio-studio/styles/built-in/corporate.json +49 -0
- package/skills/fec-drawio-studio/styles/built-in/default.json +49 -0
- package/skills/fec-drawio-studio/styles/built-in/handdrawn.json +49 -0
- package/skills/fec-drawio-studio/styles/schema.json +112 -0
- package/skills/fec-e2e-testing/SKILL.md +5 -3
- package/skills/fec-e2e-testing/references/e2e-ci-reporting.md +62 -0
- package/skills/fec-e2e-testing/references/e2e-special-scenarios.md +52 -0
- package/skills/fec-e2e-testing/references/e2e-visual-regression.md +60 -0
- package/skills/fec-form-handling/SKILL.md +11 -11
- package/skills/fec-form-handling/references/advanced-form-patterns.md +34 -5
- package/skills/fec-image-generation/SKILL.md +56 -0
- package/skills/fec-image-generation/references/artifact-routing.md +80 -0
- package/skills/fec-image-generation/references/diagram-workflows.md +43 -0
- package/skills/fec-image-generation/references/png-qa-autofix.md +30 -0
- package/skills/fec-image-generation/scripts/png-qa.mjs +431 -0
- package/skills/fec-implement-from-design/SKILL.md +9 -2
- package/skills/fec-legacy-to-modern-migration/SKILL.md +29 -163
- package/skills/fec-legacy-to-modern-migration/references/migration-execution-checklist.md +110 -0
- package/skills/fec-legacy-to-modern-migration/references/migration-strategy-and-mapping.md +48 -0
- package/skills/fec-legacy-web-standard/SKILL.md +1 -1
- package/skills/fec-list-virtualization/SKILL.md +3 -3
- package/skills/fec-list-virtualization/references/virtualization-patterns.md +32 -4
- package/skills/fec-motion-interaction/SKILL.md +7 -0
- package/skills/fec-nextjs-project-standard/SKILL.md +7 -1
- package/skills/fec-performance-optimization/SKILL.md +9 -1
- package/skills/fec-performance-optimization/references/framework-performance-patterns.md +38 -0
- package/skills/fec-performance-optimization/references/report-template.md +59 -0
- package/skills/fec-pwa-implementation/SKILL.md +3 -4
- package/skills/fec-react-project-standard/SKILL.md +12 -4
- package/skills/fec-react-project-standard/references/react-performance-patterns.md +35 -0
- package/skills/fec-react-project-standard/references/react-project-structure.md +197 -0
- package/skills/fec-react-project-standard/references/react-quality-patterns.md +131 -0
- package/skills/fec-react-project-standard/references/react-runtime-patterns.md +293 -0
- package/skills/fec-refactor-clean/SKILL.md +4 -1
- package/skills/fec-refactor-clean/references/report-template.md +42 -0
- package/skills/fec-responsive-layout/references/responsive-layout-patterns.md +11 -0
- package/skills/fec-security-review/SKILL.md +3 -3
- package/skills/fec-source-driven-development/SKILL.md +26 -0
- package/skills/fec-state-management/SKILL.md +1 -1
- package/skills/fec-storybook-component-doc/SKILL.md +2 -2
- package/skills/fec-tailwind-design-system/references/tailwind-system-patterns.md +5 -1
- package/skills/fec-typescript-project-standard/SKILL.md +54 -0
- package/skills/{fec-typescript-type-safety → fec-typescript-project-standard}/references/type-safety-patterns.md +1 -1
- package/skills/{fec-typescript-type-safety/SKILL.md → fec-typescript-project-standard/references/type-safety.md} +1 -10
- package/skills/fec-ui-design/SKILL.md +26 -8
- package/skills/fec-ui-design/data/style-archetypes.json +40 -0
- package/skills/fec-ui-design/data/ux-quality-rules.json +35 -0
- package/skills/fec-ui-design/references/design-intelligence.md +66 -6
- package/skills/fec-ui-design/references/pre-delivery-checklist.md +6 -0
- package/skills/fec-ui-design/scripts/design-system.mjs +119 -1
- package/skills/fec-validation-fix/SKILL.md +5 -2
- package/skills/fec-validation-fix/references/report-template.md +43 -0
- package/skills/fec-vue3-project-standard/SKILL.md +12 -3
- package/skills/fec-vue3-project-standard/references/vue3-composables-routing.md +163 -0
- package/skills/fec-vue3-project-standard/references/vue3-performance-patterns.md +13 -0
- package/skills/fec-vue3-project-standard/references/vue3-project-structure.md +177 -0
- package/skills/fec-vue3-project-standard/references/vue3-state-api-quality.md +216 -0
- package/skills/fec-web-video-presentation/SKILL.md +61 -0
- package/skills/fec-web-video-presentation/data/starter-themes.json +59 -0
- package/skills/fec-web-video-presentation/references/chapter-craft.md +37 -0
- package/skills/fec-web-video-presentation/references/content-plan.md +38 -0
- package/skills/fec-web-video-presentation/references/recording-and-audio.md +33 -0
- package/skills/fec-web-video-presentation/references/theme-system.md +36 -0
- package/skills/fec-web-video-presentation/scripts/create-video-presentation.mjs +430 -0
- package/skills/fec-web-workers/SKILL.md +3 -4
- package/skills/metadata.json +381 -168
- package/skills/relations.json +136 -25
- package/templates/shared/rules/fec-ci-cd.md +20 -108
- package/templates/shared/rules/fec-design-system.md +47 -223
- package/templates/shared/rules/fec-error-handling.md +35 -179
- package/templates/shared/rules/fec-i18n.md +43 -91
- package/templates/shared/rules/fec-refactoring.md +4 -0
- package/templates/shared/rules/fec-rendering-patterns.md +28 -171
- package/templates/shared/rules/fec-responsive-design.md +34 -211
- package/templates/shared/rules/fec-state-management.md +41 -144
- package/templates/shared/rules/fec-typescript.md +47 -290
- package/skills/fec-e2e-testing/references/e2e-advanced.md +0 -176
- package/skills/fec-react-project-standard/references/react-project-details.md +0 -642
- package/skills/fec-vue3-project-standard/references/vue3-project-details.md +0 -547
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
# Vue 3 项目规范详解
|
|
2
|
-
|
|
3
|
-
## 项目结构
|
|
4
|
-
|
|
5
|
-
以下为中大型 Vue 3 项目的业界最佳实践结构,按项目实际情况裁剪:
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
src/
|
|
9
|
-
├── app/ # 应用入口与全局配置
|
|
10
|
-
│ ├── App.vue # 根组件
|
|
11
|
-
│ ├── main.ts # 应用启动入口
|
|
12
|
-
│ └── router.ts # 路由实例与配置
|
|
13
|
-
│
|
|
14
|
-
├── pages/ # 页面组件(与路由一一对应)
|
|
15
|
-
│ ├── Dashboard/
|
|
16
|
-
│ │ ├── DashboardPage.vue
|
|
17
|
-
│ │ ├── components/ # 页面私有组件
|
|
18
|
-
│ │ └── composables/ # 页面私有 composables
|
|
19
|
-
│ ├── UserList/
|
|
20
|
-
│ └── Settings/
|
|
21
|
-
│
|
|
22
|
-
├── layouts/ # 布局组件
|
|
23
|
-
│ ├── MainLayout.vue # 主布局(侧边栏 + 顶栏 + 内容区)
|
|
24
|
-
│ ├── AuthLayout.vue # 登录/注册页布局
|
|
25
|
-
│ └── BlankLayout.vue # 空白布局(错误页等)
|
|
26
|
-
│
|
|
27
|
-
├── features/ # 功能模块(按业务领域划分)
|
|
28
|
-
│ ├── auth/
|
|
29
|
-
│ │ ├── components/ # 模块组件
|
|
30
|
-
│ │ ├── composables/ # 模块 composables
|
|
31
|
-
│ │ ├── api.ts # 模块 API 调用
|
|
32
|
-
│ │ ├── types.ts # 模块类型定义
|
|
33
|
-
│ │ ├── constants.ts # 模块常量
|
|
34
|
-
│ │ └── index.ts # 模块公开导出
|
|
35
|
-
│ └── order/
|
|
36
|
-
│
|
|
37
|
-
├── components/ # 全局共享 UI 组件
|
|
38
|
-
│ ├── AppButton/
|
|
39
|
-
│ │ ├── AppButton.vue
|
|
40
|
-
│ │ └── __tests__/
|
|
41
|
-
│ ├── AppModal/
|
|
42
|
-
│ ├── AppForm/
|
|
43
|
-
│ └── AppErrorBoundary/
|
|
44
|
-
│
|
|
45
|
-
├── composables/ # 全局共享 composables
|
|
46
|
-
│ ├── useAuth.ts
|
|
47
|
-
│ ├── useDebounce.ts
|
|
48
|
-
│ └── useMediaQuery.ts
|
|
49
|
-
│
|
|
50
|
-
├── services/ # API 基础层
|
|
51
|
-
│ ├── request.ts # Axios/fetch 实例与拦截器
|
|
52
|
-
│ └── endpoints/ # API 端点定义(如按领域拆分)
|
|
53
|
-
│
|
|
54
|
-
├── stores/ # Pinia 状态管理
|
|
55
|
-
│ ├── authStore.ts
|
|
56
|
-
│ └── uiStore.ts
|
|
57
|
-
│
|
|
58
|
-
├── locales/ # 国际化语言包
|
|
59
|
-
│ ├── zh-CN.json # 中文
|
|
60
|
-
│ ├── en-US.json # 英文
|
|
61
|
-
│ └── index.ts # i18n 实例初始化(vue-i18n)
|
|
62
|
-
│
|
|
63
|
-
├── assets/ # 静态资源
|
|
64
|
-
│ ├── images/ # 图片(PNG、JPG、WebP)
|
|
65
|
-
│ ├── icons/ # SVG 图标
|
|
66
|
-
│ └── fonts/ # 自定义字体
|
|
67
|
-
│
|
|
68
|
-
├── config/ # 应用配置
|
|
69
|
-
│ ├── env.ts # 环境变量类型化封装
|
|
70
|
-
│ └── features.ts # Feature Flags 管理
|
|
71
|
-
│
|
|
72
|
-
├── types/ # 全局共享类型
|
|
73
|
-
│ ├── api.ts # API 响应/请求通用类型
|
|
74
|
-
│ ├── models.ts # 业务实体类型
|
|
75
|
-
│ └── global.d.ts # 全局类型扩展(组件类型、模块声明等)
|
|
76
|
-
│
|
|
77
|
-
├── utils/ # 纯工具函数
|
|
78
|
-
│ ├── format.ts # 日期、数字、货币格式化
|
|
79
|
-
│ ├── validators.ts # 表单校验规则
|
|
80
|
-
│ └── storage.ts # LocalStorage / SessionStorage 封装
|
|
81
|
-
│
|
|
82
|
-
├── directives/ # 自定义指令
|
|
83
|
-
│ ├── vPermission.ts # 权限指令
|
|
84
|
-
│ └── vClickOutside.ts # 点击外部关闭
|
|
85
|
-
│
|
|
86
|
-
├── plugins/ # Vue 插件注册
|
|
87
|
-
│ ├── i18n.ts # vue-i18n 插件配置
|
|
88
|
-
│ └── index.ts # 插件统一注册入口
|
|
89
|
-
│
|
|
90
|
-
├── styles/ # 全局样式与主题
|
|
91
|
-
│ ├── global.css # 全局基础样式(reset / normalize)
|
|
92
|
-
│ ├── variables.css # CSS 变量(颜色、间距、字号)
|
|
93
|
-
│ ├── breakpoints.ts # 响应式断点常量
|
|
94
|
-
│ └── themes/ # 主题定义
|
|
95
|
-
│ ├── light.css # 亮色主题变量
|
|
96
|
-
│ ├── dark.css # 暗色主题变量
|
|
97
|
-
│ └── index.ts # 主题切换逻辑
|
|
98
|
-
│
|
|
99
|
-
└── constants/ # 全局常量
|
|
100
|
-
├── routes.ts # 路由路径常量
|
|
101
|
-
└── config.ts # 业务常量(分页大小、超时时间等)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### 关键原则
|
|
105
|
-
|
|
106
|
-
- `pages/` 做路由映射和布局组合,不放业务逻辑
|
|
107
|
-
- `layouts/` 定义页面骨架(侧边栏、顶栏、面包屑),由路由配置的 `component` 引用
|
|
108
|
-
- `features/` 按业务领域划分,模块内自包含(components + composables + api + types)
|
|
109
|
-
- `components/` 仅放无业务耦合的通用组件,可跨项目复用
|
|
110
|
-
- `composables/` 仅放通用逻辑(防抖、媒体查询等),业务 composables 放到对应 feature 中
|
|
111
|
-
- `locales/` 存放语言包 JSON 文件,模板中使用 `$t('key')` 而非硬编码文案
|
|
112
|
-
- `assets/` 存放静态资源,图标优先使用 SVG,图片优先使用 WebP/AVIF
|
|
113
|
-
- `config/` 封装环境变量和 Feature Flags,禁止组件中直接读取 `import.meta.env`
|
|
114
|
-
- `styles/themes/` 通过 CSS 变量实现主题切换,组件中引用变量而非硬编码颜色
|
|
115
|
-
- 每个模块通过 `index.ts` 管控公开 API,避免深层路径导入
|
|
116
|
-
|
|
117
|
-
## 组件设计规范
|
|
118
|
-
|
|
119
|
-
- 使用 `<script setup lang="ts">`
|
|
120
|
-
- 明确使用 `defineProps` / `defineEmits` 并附带类型
|
|
121
|
-
- 可复用逻辑优先提取到 composables
|
|
122
|
-
- 保持 template 可读,避免过深条件嵌套
|
|
123
|
-
- 优先使用计算属性,而不是重复维护状态
|
|
124
|
-
- 避免构建大型单体组件;**单文件规模**见 `templates/shared/rules/fec-vue.md`「**组件文件规模**」(约 300 行内为佳,逾 500 行或复杂度过高拆子组件与 Composables)
|
|
125
|
-
- 优先使用强类型的 props、emits 和暴露方法
|
|
126
|
-
- 遵循仓库的文件与目录命名规范
|
|
127
|
-
- 优先复用现有 UI 组件和 Token
|
|
128
|
-
|
|
129
|
-
### 组件分层
|
|
130
|
-
|
|
131
|
-
```
|
|
132
|
-
页面组件 (Pages) → 路由映射、布局组合
|
|
133
|
-
└── 容器组件 (Containers) → 数据获取、状态编排
|
|
134
|
-
└── 业务组件 (Features) → 领域逻辑展示
|
|
135
|
-
└── 通用组件 (UI) → 纯展示,无业务耦合
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## 注释规范
|
|
139
|
-
|
|
140
|
-
- **优先使用中文**:解释「为什么这样做」、业务约束、边界情况、非显而易见的权衡时,优先用中文撰写注释,便于团队与业务方阅读。
|
|
141
|
-
- **与代码语言一致时的例外**:对接第三方协议字段名、HTTP 头、规范中的英文术语时,注释里可保留英文专有名词,必要时中英文并列说明。
|
|
142
|
-
- **少而精**:能通过清晰命名与类型表达清楚的逻辑不写废话注释;复杂分支、临时兼容、性能取舍必须写清意图。
|
|
143
|
-
- **公开 API**:composable 或模块的对外契约可用 JSDoc(`@param` / `@returns` / `@example`),说明用中文即可,除非仓库统一要求英文。
|
|
144
|
-
|
|
145
|
-
## TypeScript 规范
|
|
146
|
-
|
|
147
|
-
通用 TypeScript / JavaScript 约定见插件模板 **`templates/shared/rules/fec-typescript.md`**(初始化到项目后为 `.claude/rules/fec-typescript.md`)。
|
|
148
|
-
|
|
149
|
-
### Vue 3 项目补充约定
|
|
150
|
-
|
|
151
|
-
```vue
|
|
152
|
-
<script setup lang="ts">
|
|
153
|
-
interface Props {
|
|
154
|
-
title: string;
|
|
155
|
-
items: Item[];
|
|
156
|
-
loading?: boolean;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
interface Emits {
|
|
160
|
-
(e: "select", item: Item): void;
|
|
161
|
-
(e: "delete", id: string): void;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
165
|
-
loading: false,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const emit = defineEmits<Emits>();
|
|
169
|
-
</script>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
- Props 和 Emits 使用 TypeScript interface 定义
|
|
173
|
-
- 使用 `withDefaults` 设置默认值
|
|
174
|
-
- 禁止使用 `any`,优先使用精确类型
|
|
175
|
-
- `defineExpose` 暴露的方法需有类型约束
|
|
176
|
-
|
|
177
|
-
## Composables 规范
|
|
178
|
-
|
|
179
|
-
### 设计原则
|
|
180
|
-
|
|
181
|
-
- 以 `use` 前缀命名
|
|
182
|
-
- 返回值使用对象,明确标注类型
|
|
183
|
-
- 内部处理 loading / error / data 三态
|
|
184
|
-
- 支持参数响应式(接受 `Ref` 或 getter)
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
export function useUserList(params: MaybeRef<QueryParams>) {
|
|
188
|
-
const data = ref<User[]>([]);
|
|
189
|
-
const loading = ref(false);
|
|
190
|
-
const error = ref<Error | null>(null);
|
|
191
|
-
|
|
192
|
-
async function fetch() {
|
|
193
|
-
loading.value = true;
|
|
194
|
-
error.value = null;
|
|
195
|
-
try {
|
|
196
|
-
const res = await getUserList(toValue(params));
|
|
197
|
-
data.value = res.list;
|
|
198
|
-
} catch (e) {
|
|
199
|
-
error.value = e as Error;
|
|
200
|
-
} finally {
|
|
201
|
-
loading.value = false;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
watchEffect(() => {
|
|
206
|
-
fetch();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
data: readonly(data),
|
|
211
|
-
loading: readonly(loading),
|
|
212
|
-
error: readonly(error),
|
|
213
|
-
refetch: fetch,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### Composable 使用原则
|
|
219
|
-
|
|
220
|
-
- 返回 `readonly` 引用防止外部意外修改
|
|
221
|
-
- 数据请求场景优先使用 VueQuery / VueUse 等库(如项目已引入)
|
|
222
|
-
- `onUnmounted` 中清理定时器、事件监听等副作用
|
|
223
|
-
- 避免在 composable 中直接操作 DOM
|
|
224
|
-
|
|
225
|
-
## Slots 与 Provide/Inject
|
|
226
|
-
|
|
227
|
-
### Slots
|
|
228
|
-
|
|
229
|
-
- 用 `<slot>` 实现组件组合,而非过多 props
|
|
230
|
-
- 具名 slot 用于明确的布局区域
|
|
231
|
-
- 作用域 slot 传递数据给父组件自定义渲染
|
|
232
|
-
|
|
233
|
-
```vue
|
|
234
|
-
<template>
|
|
235
|
-
<div class="card">
|
|
236
|
-
<div class="card-header">
|
|
237
|
-
<slot name="header">{{ title }}</slot>
|
|
238
|
-
</div>
|
|
239
|
-
<div class="card-body">
|
|
240
|
-
<slot :data="processedData" :loading="loading" />
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
|
-
</template>
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Provide/Inject
|
|
247
|
-
|
|
248
|
-
- 用于跨多层级的上下文共享(主题、配置、权限)
|
|
249
|
-
- 提供 InjectionKey 保证类型安全
|
|
250
|
-
- 不要用 provide/inject 替代 props 传递直接父子数据
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
// keys.ts
|
|
254
|
-
export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");
|
|
255
|
-
|
|
256
|
-
// Provider.vue
|
|
257
|
-
provide(ThemeKey, theme);
|
|
258
|
-
|
|
259
|
-
// Consumer.vue
|
|
260
|
-
const theme = inject(ThemeKey);
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## 路由规范
|
|
264
|
-
|
|
265
|
-
### 路由组织
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
// app/router.ts
|
|
269
|
-
const routes: RouteRecordRaw[] = [
|
|
270
|
-
{
|
|
271
|
-
path: "/",
|
|
272
|
-
component: MainLayout,
|
|
273
|
-
children: [
|
|
274
|
-
{
|
|
275
|
-
path: "",
|
|
276
|
-
name: "Dashboard",
|
|
277
|
-
component: () => import("@/pages/Dashboard/DashboardPage.vue"),
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
path: "users",
|
|
281
|
-
name: "UserList",
|
|
282
|
-
component: () => import("@/pages/UserList/UserListPage.vue"),
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
path: "users/:id",
|
|
286
|
-
name: "UserDetail",
|
|
287
|
-
component: () => import("@/pages/UserDetail/UserDetailPage.vue"),
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
path: "settings",
|
|
291
|
-
name: "Settings",
|
|
292
|
-
component: () => import("@/pages/Settings/SettingsPage.vue"),
|
|
293
|
-
},
|
|
294
|
-
],
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
path: "/login",
|
|
298
|
-
name: "Login",
|
|
299
|
-
component: () => import("@/pages/Login/LoginPage.vue"),
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
path: "/:pathMatch(.*)*",
|
|
303
|
-
name: "NotFound",
|
|
304
|
-
component: () => import("@/pages/NotFound.vue"),
|
|
305
|
-
},
|
|
306
|
-
];
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### 路由原则
|
|
310
|
-
|
|
311
|
-
- 路由配置集中管理,每个路由必须有 `name`
|
|
312
|
-
- 页面组件使用动态 `import()` 按需加载
|
|
313
|
-
- 权限控制使用路由守卫(`beforeEach`),而非在每个页面内判断
|
|
314
|
-
- URL 参数(分页、筛选、排序)与路由状态同步
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
// 导航守卫
|
|
318
|
-
router.beforeEach((to) => {
|
|
319
|
-
const authStore = useAuthStore();
|
|
320
|
-
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
|
|
321
|
-
return { name: "Login", query: { redirect: to.fullPath } };
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## 状态管理(Pinia)
|
|
327
|
-
|
|
328
|
-
| 状态类型 | 推荐方案 |
|
|
329
|
-
| ------------------ | ----------------------------- |
|
|
330
|
-
| 组件内临时 UI 状态 | `ref` / `reactive` |
|
|
331
|
-
| 跨组件共享业务状态 | Pinia store |
|
|
332
|
-
| 服务端数据缓存 | VueQuery / 自定义 composable |
|
|
333
|
-
| URL 驱动状态 | 路由参数 / `useRoute().query` |
|
|
334
|
-
| 表单状态 | VeeValidate / FormKit |
|
|
335
|
-
|
|
336
|
-
### Pinia Store 规范
|
|
337
|
-
|
|
338
|
-
使用 Composition API 风格(`setup store`):
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
// stores/authStore.ts
|
|
342
|
-
export const useAuthStore = defineStore("auth", () => {
|
|
343
|
-
const user = ref<User | null>(null);
|
|
344
|
-
const token = ref<string | null>(localStorage.getItem("token"));
|
|
345
|
-
|
|
346
|
-
const isLoggedIn = computed(() => !!token.value);
|
|
347
|
-
|
|
348
|
-
async function login(credentials: LoginParams) {
|
|
349
|
-
const res = await authApi.login(credentials);
|
|
350
|
-
user.value = res.user;
|
|
351
|
-
token.value = res.token;
|
|
352
|
-
localStorage.setItem("token", res.token);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function logout() {
|
|
356
|
-
user.value = null;
|
|
357
|
-
token.value = null;
|
|
358
|
-
localStorage.removeItem("token");
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return { user: readonly(user), isLoggedIn, login, logout };
|
|
362
|
-
});
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Store 原则
|
|
366
|
-
|
|
367
|
-
- 每个 store 职责单一,按领域拆分
|
|
368
|
-
- 对外暴露 `readonly` 的状态,通过 action 修改
|
|
369
|
-
- 不要在 store 中存放 UI 临时状态(modal 开关、表单输入等)
|
|
370
|
-
- 服务端数据优先用请求库管理,而非手动存入 store
|
|
371
|
-
|
|
372
|
-
## API 层规范
|
|
373
|
-
|
|
374
|
-
### 请求实例
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
// services/request.ts
|
|
378
|
-
const request = axios.create({
|
|
379
|
-
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
380
|
-
timeout: 10000,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
request.interceptors.request.use((config) => {
|
|
384
|
-
const authStore = useAuthStore();
|
|
385
|
-
if (authStore.token) {
|
|
386
|
-
config.headers.Authorization = `Bearer ${authStore.token}`;
|
|
387
|
-
}
|
|
388
|
-
return config;
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
request.interceptors.response.use(
|
|
392
|
-
(res) => res.data,
|
|
393
|
-
(error) => {
|
|
394
|
-
if (error.response?.status === 401) {
|
|
395
|
-
const authStore = useAuthStore();
|
|
396
|
-
authStore.logout();
|
|
397
|
-
router.push({ name: "Login" });
|
|
398
|
-
}
|
|
399
|
-
return Promise.reject(normalizeError(error));
|
|
400
|
-
},
|
|
401
|
-
);
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### API 函数
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
// features/user/api.ts
|
|
408
|
-
export function getUserList(
|
|
409
|
-
params: UserQueryParams,
|
|
410
|
-
): Promise<PageResult<User>> {
|
|
411
|
-
return request.get("/users", { params });
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export function updateUser(id: string, data: UpdateUserDTO): Promise<User> {
|
|
415
|
-
return request.put(`/users/${id}`, data);
|
|
416
|
-
}
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
- API 函数按 feature 组织,而非全部堆在一个文件
|
|
420
|
-
- 请求参数和响应都有类型约束
|
|
421
|
-
- 拦截器统一处理认证、错误格式化
|
|
422
|
-
|
|
423
|
-
## 错误处理
|
|
424
|
-
|
|
425
|
-
### 全局错误捕获
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// main.ts
|
|
429
|
-
app.config.errorHandler = (err, instance, info) => {
|
|
430
|
-
reportError(err, { component: instance?.$options.name, info });
|
|
431
|
-
};
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### 组件级错误
|
|
435
|
-
|
|
436
|
-
- 使用 `onErrorCaptured` 在父组件中捕获子组件错误
|
|
437
|
-
- 数据请求失败需有用户可见的提示和重试机制
|
|
438
|
-
- 不要吞掉错误(空 catch 块)
|
|
439
|
-
|
|
440
|
-
## 自定义指令
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
// directives/vPermission.ts
|
|
444
|
-
export const vPermission: Directive<HTMLElement, string> = {
|
|
445
|
-
mounted(el, binding) {
|
|
446
|
-
const authStore = useAuthStore();
|
|
447
|
-
if (!authStore.hasPermission(binding.value)) {
|
|
448
|
-
el.parentNode?.removeChild(el);
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
};
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
- 指令只处理 DOM 层面的操作
|
|
455
|
-
- 业务逻辑不要放在指令中
|
|
456
|
-
- 需要响应式更新时实现 `updated` 钩子
|
|
457
|
-
|
|
458
|
-
## 样式规范
|
|
459
|
-
|
|
460
|
-
- 使用 `<style scoped>` 隔离样式
|
|
461
|
-
- 深度选择器使用 `:deep()` 而非已废弃的 `::v-deep`
|
|
462
|
-
- 与项目现有样式体系保持一致
|
|
463
|
-
- 动态样式优先使用 `:class` / `:style` 绑定
|
|
464
|
-
- 复杂主题切换使用 CSS 变量
|
|
465
|
-
- 主题/全局变量通过 CSS 变量或 Token 管理
|
|
466
|
-
|
|
467
|
-
## 测试规范
|
|
468
|
-
|
|
469
|
-
### 必须测试
|
|
470
|
-
|
|
471
|
-
- 核心交互行为(点击、输入、提交)
|
|
472
|
-
- 条件渲染(loading / error / empty / data)
|
|
473
|
-
- Emits 的触发和 payload
|
|
474
|
-
- 关键 composables 的返回值
|
|
475
|
-
- Pinia store 的 action 和 getter
|
|
476
|
-
|
|
477
|
-
### 测试风格
|
|
478
|
-
|
|
479
|
-
```typescript
|
|
480
|
-
describe("UserForm", () => {
|
|
481
|
-
it("should emit submit with valid data", async () => {
|
|
482
|
-
const wrapper = mount(UserForm);
|
|
483
|
-
|
|
484
|
-
await wrapper.find('[data-testid="username"]').setValue("test");
|
|
485
|
-
await wrapper.find("form").trigger("submit");
|
|
486
|
-
|
|
487
|
-
expect(wrapper.emitted("submit")?.[0]).toEqual([{ username: "test" }]);
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it("should show validation error on empty submit", async () => {
|
|
491
|
-
const wrapper = mount(UserForm);
|
|
492
|
-
await wrapper.find("form").trigger("submit");
|
|
493
|
-
expect(wrapper.text()).toContain("用户名不能为空");
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
### Store 测试
|
|
499
|
-
|
|
500
|
-
```typescript
|
|
501
|
-
describe("authStore", () => {
|
|
502
|
-
beforeEach(() => setActivePinia(createPinia()));
|
|
503
|
-
|
|
504
|
-
it("should login and set user", async () => {
|
|
505
|
-
const store = useAuthStore();
|
|
506
|
-
await store.login({ username: "admin", password: "pass" });
|
|
507
|
-
expect(store.isLoggedIn).toBe(true);
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
## 性能
|
|
513
|
-
|
|
514
|
-
- 使用 `shallowRef` / `shallowReactive` 优化大型对象
|
|
515
|
-
- 大列表使用虚拟滚动
|
|
516
|
-
- 避免在 `v-for` 中使用 `v-if`(提取为 computed 过滤)
|
|
517
|
-
- 使用 `defineAsyncComponent` 懒加载重型组件
|
|
518
|
-
- `v-for` 必须有稳定的 `:key`
|
|
519
|
-
- 路由组件使用动态 `import()` 按需加载
|
|
520
|
-
|
|
521
|
-
## 反模式
|
|
522
|
-
|
|
523
|
-
- 在模板驱动组件中内联大段业务逻辑
|
|
524
|
-
- 本该用 computed 却使用大范围 watcher
|
|
525
|
-
- 事件 payload 不明确
|
|
526
|
-
- 在纯展示组件中直接混入原始 API 调用
|
|
527
|
-
- ref 和 props 没有类型约束
|
|
528
|
-
- 用 `watch` + 手动赋值模拟 computed
|
|
529
|
-
- 在一个组件中混入无关职责
|
|
530
|
-
- 用大范围全局样式覆盖去解决局部 UI 问题
|
|
531
|
-
- 将所有状态推入 Pinia store,而非就近管理
|
|
532
|
-
- 在 `components/` 中放业务耦合组件
|
|
533
|
-
- 直接从 feature 内部深层路径导入,绕过 `index.ts`
|
|
534
|
-
|
|
535
|
-
## 输出检查清单
|
|
536
|
-
|
|
537
|
-
- [ ] 文件结构与项目约定一致(pages / features / components 分离)
|
|
538
|
-
- [ ] 使用 `<script setup lang="ts">`
|
|
539
|
-
- [ ] Props / Emits 类型完整
|
|
540
|
-
- [ ] 解释性注释是否优先使用中文且点到要害
|
|
541
|
-
- [ ] 可复用逻辑已提取到 composable
|
|
542
|
-
- [ ] Loading / Error / Empty 状态均已处理
|
|
543
|
-
- [ ] 路由组件使用动态 import 加载
|
|
544
|
-
- [ ] 状态管理方案合理(就近原则)
|
|
545
|
-
- [ ] API 调用有类型约束和统一错误处理
|
|
546
|
-
- [ ] 样式使用 scoped 隔离
|
|
547
|
-
- [ ] 关键行为有测试覆盖
|