create-dwy 0.7.1 → 0.9.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.
Files changed (96) hide show
  1. package/package.json +1 -1
  2. package/src/create.js +50 -3
  3. package/src/sync.js +228 -130
  4. package/src/utils.js +4 -0
  5. package/templates/claude-global/CLAUDE.md +81 -6
  6. package/templates/claude-global/rules/dwy-postgres.md +89 -0
  7. package/templates/claude-global/rules/dwy-python-backend.md +492 -0
  8. package/templates/claude-global/rules/dwy-python-core.md +94 -634
  9. package/templates/claude-global/rules/dwy-python-testing.md +115 -13
  10. package/templates/claude-global/rules/dwy-redis.md +88 -0
  11. package/templates/claude-global/rules/dwy-vue-core.md +341 -645
  12. package/templates/claude-global/rules/dwy-vue-pinia.md +47 -34
  13. package/templates/claude-global/rules/dwy-vue-testing.md +120 -55
  14. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +1 -1
  15. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +1 -1
  16. package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +22 -15
  17. package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +23 -0
  18. package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +100 -1
  19. package/templates/claude-global/skills/dwy-ekit/SKILL.md +142 -276
  20. package/templates/claude-global/skills/dwy-eui/SKILL.md +264 -182
  21. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +117 -129
  22. package/templates/claude-global/skills/dwy-git-commit/SKILL.md +125 -0
  23. package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -1
  24. package/templates/claude-global/{rules/dwy-tech-stack.md → skills/dwy-tech-stack/SKILL.md} +4 -3
  25. package/templates/project/fullstack-monorepo/CLAUDE.md.hbs +81 -0
  26. package/templates/project/fullstack-monorepo/backend/.python-version +1 -0
  27. package/templates/project/fullstack-monorepo/backend/CLAUDE.md.hbs +68 -0
  28. package/templates/project/fullstack-monorepo/backend/Dockerfile.dev +23 -0
  29. package/templates/project/fullstack-monorepo/backend/alembic/env.py.hbs +68 -0
  30. package/templates/project/fullstack-monorepo/backend/alembic/script.py.mako +26 -0
  31. package/templates/project/fullstack-monorepo/backend/alembic.ini.hbs +43 -0
  32. package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/pyproject.toml +16 -0
  33. package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/src/dwy_shared/__init__.py +12 -0
  34. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/pyproject.toml.hbs +20 -0
  35. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/__init__.py.hbs +40 -0
  36. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/config.py +9 -0
  37. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/models.py.hbs +17 -0
  38. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/protocols.py +14 -0
  39. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/router.py.hbs +22 -0
  40. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/schemas.py +21 -0
  41. package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/service.py +11 -0
  42. package/templates/project/fullstack-monorepo/backend/pyproject.toml.hbs +84 -0
  43. package/templates/project/fullstack-monorepo/backend/src/app/__init__.py +3 -0
  44. package/templates/project/fullstack-monorepo/backend/src/app/config.py.hbs +21 -0
  45. package/templates/project/fullstack-monorepo/backend/src/app/exceptions.py +26 -0
  46. package/templates/project/fullstack-monorepo/backend/src/app/factories.py +16 -0
  47. package/templates/project/fullstack-monorepo/backend/src/app/infra/__init__.py +1 -0
  48. package/templates/project/fullstack-monorepo/backend/src/app/main.py.hbs +65 -0
  49. package/templates/project/fullstack-monorepo/backend/src/app/middleware/__init__.py +1 -0
  50. package/templates/project/fullstack-monorepo/backend/tests/__init__.py +0 -0
  51. package/templates/project/fullstack-monorepo/backend/tests/conftest.py.hbs +42 -0
  52. package/templates/project/fullstack-monorepo/dev.sh.hbs +83 -0
  53. package/templates/project/fullstack-monorepo/docker-compose.dev.yml.hbs +36 -0
  54. package/templates/project/fullstack-monorepo/frontend/CLAUDE.md.hbs +35 -0
  55. package/templates/project/fullstack-monorepo/frontend/Dockerfile +18 -0
  56. package/templates/project/fullstack-monorepo/frontend/apps/web/index.html.hbs +13 -0
  57. package/templates/project/fullstack-monorepo/frontend/apps/web/package.json.hbs +31 -0
  58. package/templates/project/fullstack-monorepo/frontend/apps/web/src/App.vue.hbs +10 -0
  59. package/templates/project/fullstack-monorepo/frontend/apps/web/src/api/client.ts.hbs +11 -0
  60. package/templates/project/fullstack-monorepo/frontend/apps/web/src/features/{{initialDomain}}/.gitkeep +0 -0
  61. package/templates/project/fullstack-monorepo/frontend/apps/web/src/index.css +12 -0
  62. package/templates/project/fullstack-monorepo/frontend/apps/web/src/main.ts +17 -0
  63. package/templates/project/fullstack-monorepo/frontend/apps/web/src/router/index.ts.hbs +24 -0
  64. package/templates/project/fullstack-monorepo/frontend/apps/web/src/stores/.gitkeep +0 -0
  65. package/templates/project/fullstack-monorepo/frontend/apps/web/src/types/.gitkeep +0 -0
  66. package/templates/project/fullstack-monorepo/frontend/apps/web/src/views/HomeView.vue.hbs +36 -0
  67. package/templates/project/fullstack-monorepo/frontend/apps/web/tsconfig.json +7 -0
  68. package/templates/project/fullstack-monorepo/frontend/apps/web/vite.config.ts.hbs +27 -0
  69. package/templates/project/fullstack-monorepo/frontend/nginx.conf +11 -0
  70. package/templates/project/fullstack-monorepo/frontend/package.json.hbs +13 -0
  71. package/templates/project/fullstack-monorepo/frontend/packages/core/package.json.hbs +15 -0
  72. package/templates/project/fullstack-monorepo/frontend/packages/core/src/index.ts +3 -0
  73. package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/package.json.hbs +14 -0
  74. package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/index.ts.hbs +16 -0
  75. package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/pages/IndexPage.vue.hbs +9 -0
  76. package/templates/project/fullstack-monorepo/frontend/packages/ui/package.json.hbs +15 -0
  77. package/templates/project/fullstack-monorepo/frontend/packages/ui/src/index.ts +3 -0
  78. package/templates/project/fullstack-monorepo/frontend/pnpm-workspace.yaml.hbs +23 -0
  79. package/templates/project/fullstack-monorepo/frontend/tsconfig.base.json.hbs +23 -0
  80. package/templates/claude-global/rules/dwy-api-design.md +0 -110
  81. package/templates/claude-global/rules/dwy-git-commit-convention.md +0 -82
  82. package/templates/claude-global/rules/dwy-git-security.md +0 -53
  83. package/templates/claude-global/rules/dwy-payload-limits.md +0 -186
  84. package/templates/claude-global/rules/dwy-prefer-open-source.md +0 -32
  85. package/templates/claude-global/rules/dwy-project-structure.md +0 -181
  86. package/templates/claude-global/rules/dwy-python-fastapi.md +0 -272
  87. package/templates/claude-global/rules/dwy-python-orm.md +0 -61
  88. package/templates/claude-global/rules/dwy-python-security.md +0 -636
  89. package/templates/claude-global/rules/dwy-server-security.md +0 -518
  90. package/templates/claude-global/rules/dwy-vue-api.md +0 -146
  91. package/templates/claude-global/skills/dwy-ekit/references/data.md +0 -135
  92. package/templates/claude-global/skills/dwy-ekit/references/form.md +0 -68
  93. package/templates/claude-global/skills/dwy-ekit/references/request.md +0 -183
  94. package/templates/claude-global/skills/dwy-ekit/references/ui.md +0 -109
  95. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +0 -890
  96. /package/templates/{claude-global/commands → project/fullstack-monorepo/backend/alembic/versions}/.gitkeep +0 -0
@@ -1,890 +0,0 @@
1
- ---
2
- name: dwy-vue-core
3
- description: "Vue 3 + TypeScript + Vite + Tailwind 项目基础代码风格规范。触发条件:编写或审查 .vue 组件、vite.config、composables、路由配置;需要确认组件结构 / 命名规范 / TS 类型标注 / 模板与样式写法 / 导入顺序 / 错误处理 / 中文注释规范 / 常见反模式 / 代码自检清单 / 代码简约原则。"
4
- ---
5
-
6
- # Vue 3 基础风格规范
7
-
8
- ## 一、项目结构
9
-
10
- ### 强制规则
11
- - 使用 `pnpm` 作为包管理器
12
- - 使用 `Vite` 作为构建工具
13
- - 必须有 `.node-version` 文件声明 Node 版本
14
- - 必须有 `.npmrc` 配置安装源
15
-
16
- ## 二、组件规范
17
-
18
- ### 强制使用 Composition API + `<script setup>`
19
-
20
- ```vue
21
- <!-- ✅ 正确 -->
22
- <script setup lang="ts">
23
- import { ref, computed, onMounted } from 'vue'
24
-
25
- const count = ref(0)
26
- const doubled = computed(() => count.value * 2)
27
- </script>
28
-
29
- <!-- ❌ 禁止 Options API -->
30
- <script lang="ts">
31
- export default {
32
- data() { return { count: 0 } },
33
- computed: { doubled() { return this.count * 2 } }
34
- }
35
- </script>
36
- ```
37
-
38
- ### 组件文件结构
39
-
40
- ```vue
41
- <script setup lang="ts">
42
- // 1. 类型导入
43
- import type { UserInfo } from '@/types/user'
44
-
45
- // 2. 组件/工具导入
46
- import { ref, computed, onMounted } from 'vue'
47
- import { useRouter } from 'vue-router'
48
- import { useAuthStore } from '@/stores/auth'
49
-
50
- // 3. Props/Emits 定义
51
- const props = defineProps<{
52
- userId: number
53
- title?: string
54
- }>()
55
-
56
- const emit = defineEmits<{
57
- submit: [data: UserInfo]
58
- cancel: []
59
- }>()
60
-
61
- // 4. Store/Router 实例
62
- const router = useRouter()
63
- const authStore = useAuthStore()
64
-
65
- // 5. 响应式状态
66
- const loading = ref(false)
67
- const formData = ref<UserInfo | null>(null)
68
-
69
- // 6. 计算属性
70
- const isValid = computed(() => !!formData.value?.name)
71
-
72
- // 7. 方法
73
- async function handleSubmit() {
74
- loading.value = true
75
- try {
76
- emit('submit', formData.value!)
77
- } finally {
78
- loading.value = false
79
- }
80
- }
81
-
82
- // 8. 生命周期
83
- onMounted(() => {
84
- // ...
85
- })
86
- </script>
87
-
88
- <template>
89
- <!-- 模板 -->
90
- </template>
91
- ```
92
-
93
- ### 命名规范
94
-
95
- | 类型 | 风格 | 示例 |
96
- |------|------|------|
97
- | 组件文件 | `PascalCase.vue` | `UserProfile.vue`、`LoginView.vue` |
98
- | 页面组件 | `PascalCase` + View 后缀 | `DashboardView.vue`、`LoginView.vue` |
99
- | 组合式函数 | `use` + PascalCase | `useAuth.ts`、`useDictionary.ts` |
100
- | Store 文件 | `camelCase.ts` | `auth.ts`、`menu.ts` |
101
- | API 文件 | `camelCase.ts` | `user.ts`、`exam.ts` |
102
- | 类型文件 | `camelCase.ts` | `user.ts`、`common.ts` |
103
- | 工具文件 | `camelCase.ts` | `request.ts`、`format.ts` |
104
- | 变量/函数 | `camelCase` | `userName`、`handleSubmit` |
105
- | 类型/接口 | `PascalCase` | `UserInfo`、`ExamListParams` |
106
- | 常量 | `UPPER_SNAKE_CASE` | `MAX_PAGE_SIZE`、`API_TIMEOUT` |
107
- | 事件处理函数 | `handle` + 动作 | `handleLogin`、`handleDelete` |
108
- | 布尔变量 | `is/has/can/should` 前缀 | `isLoading`、`hasPermission` |
109
-
110
- ### 禁止魔法字符串
111
-
112
- 同一个字符串字面量在文件内出现 2 次及以上时,**必须**提取为常量。
113
-
114
- ```typescript
115
- // ❌ 魔法字符串散落多处,改一处漏一处
116
- storage.set('access_token', token)
117
- // ...
118
- const t = storage.get('access_token')
119
-
120
- // ✅ 提取为常量
121
- const ACCESS_TOKEN_KEY = 'access_token'
122
- storage.set(ACCESS_TOKEN_KEY, token)
123
- const t = storage.get(ACCESS_TOKEN_KEY)
124
-
125
- // ❌ 事件名、路由路径、storage key 等重复字面量
126
- emit('update:modelValue', value)
127
- router.push('/dashboard')
128
- localStorage.getItem('theme')
129
-
130
- // ✅ 跨文件共享的 key 放到专门的常量文件
131
- // constants/storage-keys.ts
132
- export const STORAGE_KEYS = {
133
- ACCESS_TOKEN: 'access_token',
134
- REFRESH_TOKEN: 'refresh_token',
135
- THEME: 'theme',
136
- } as const
137
- ```
138
-
139
- **判断标准:** 同一字符串在同一文件出现 ≥ 2 次 → 提取为常量。跨文件使用的 key → 提取到 `constants/` 目录共享。
140
-
141
- ### 禁止的写法
142
-
143
- ```typescript
144
- // ❌ 单字母变量(循环索引除外)
145
- const d = getData()
146
-
147
- // ✅ 有意义的名称
148
- const userData = getData()
149
-
150
- // ❌ 不带语义的布尔变量
151
- const active = ref(true)
152
-
153
- // ✅ 布尔变量带前缀
154
- const isActive = ref(true)
155
-
156
- // ❌ 在模板中使用复杂表达式
157
- <div v-if="list.filter(i => i.status === 'active').length > 0">
158
-
159
- // ✅ 用 computed 提取
160
- const hasActiveItems = computed(() => list.value.some(i => i.status === 'active'))
161
- ```
162
-
163
- ## 三、TypeScript 规范
164
-
165
- ### 强制规则
166
- - 所有 `.ts` 和 `.vue` 文件必须使用 TypeScript
167
- - **禁止**使用 `any`,除非与外部动态库交互且无法确定类型
168
- - 使用 `any` 时必须附注释说明原因
169
- - 类型导入使用 `import type { ... }`
170
-
171
- ### 类型标注
172
-
173
- ```typescript
174
- // ✅ Props 使用泛型定义
175
- const props = defineProps<{
176
- title: string
177
- count?: number
178
- items: UserInfo[]
179
- }>()
180
-
181
- // ✅ Emits 使用泛型定义
182
- const emit = defineEmits<{
183
- change: [value: string]
184
- submit: [data: FormData]
185
- }>()
186
-
187
- // ✅ ref 使用泛型
188
- const user = ref<UserInfo | null>(null)
189
- const list = ref<UserInfo[]>([])
190
-
191
- // ✅ computed 返回类型由推导,复杂时显式标注
192
- const filters = computed<FilterConfig[]>(() => [...])
193
-
194
- // ❌ 不标注类型
195
- const user = ref(null)
196
- const list = ref([])
197
- ```
198
-
199
- ### 类型定义
200
-
201
- ```typescript
202
- // ✅ 共享类型放 types/ 目录
203
- // types/user.ts
204
- export interface UserInfo {
205
- id: number
206
- username: string
207
- role: 'admin' | 'user'
208
- email?: string
209
- }
210
-
211
- export interface UserCreate {
212
- username: string
213
- password: string
214
- role: 'admin' | 'user'
215
- }
216
-
217
- // ✅ 组件内部类型放 script setup 里
218
- interface TableColumn {
219
- key: string
220
- label: string
221
- sortable?: boolean
222
- }
223
- ```
224
-
225
- ### 现代语法(强制)
226
-
227
- | 旧写法 | 新写法(强制) |
228
- |--------|--------------|
229
- | `Array<string>` | `string[]` |
230
- | 枚举 `enum Status {}` | 联合类型 `type Status = 'active' \| 'inactive'` |
231
-
232
- ## 五、路由规范
233
-
234
- ### 强制规则
235
-
236
- ```typescript
237
- import { createRouter, createWebHistory } from 'vue-router'
238
-
239
- const router = createRouter({
240
- history: createWebHistory(),
241
- routes: [
242
- {
243
- path: '/login',
244
- name: 'Login',
245
- component: () => import('@/views/LoginView.vue'),
246
- },
247
- {
248
- path: '/dashboard',
249
- name: 'Dashboard',
250
- component: () => import('@/views/DashboardView.vue'),
251
- meta: { requiresAuth: true },
252
- },
253
- ],
254
- })
255
- ```
256
-
257
- | 规则 | 说明 |
258
- |------|------|
259
- | 懒加载 | 所有路由组件必须使用 `() => import()` 懒加载 |
260
- | 路由守卫 | 认证检查放 `router.beforeEach`,不在组件中检查 |
261
- | meta 类型 | 使用 `RouteMeta` 类型扩展声明 meta 字段 |
262
- | 路径命名 | 使用 kebab-case:`/user-profile`,不用 camelCase |
263
-
264
- ### 路由守卫
265
-
266
- ```typescript
267
- // ✅ 全局前置守卫
268
- router.beforeEach((to) => {
269
- const authStore = useAuthStore()
270
-
271
- if (to.meta.requiresAuth && !authStore.isLoggedIn) {
272
- return { name: 'Login' }
273
- }
274
-
275
- if (to.meta.requiresAdmin && !authStore.isAdmin) {
276
- return { name: 'Dashboard' }
277
- }
278
- })
279
- ```
280
-
281
- ### 路由 Meta 类型声明
282
-
283
- ```typescript
284
- // types/router.d.ts
285
- import 'vue-router'
286
-
287
- declare module 'vue-router' {
288
- interface RouteMeta {
289
- requiresAuth?: boolean
290
- requiresAdmin?: boolean
291
- title?: string
292
- }
293
- }
294
- ```
295
-
296
- ## 七、组合式函数(Composables)
297
-
298
- ### 命名规范
299
-
300
- - 文件名和函数名必须以 `use` 开头
301
- - 返回值使用对象解构(不使用数组)
302
-
303
- ```typescript
304
- // composables/useList.ts
305
-
306
- // ✅ 正确
307
- export function useList<T>(fetchFn: () => Promise<T[]>) {
308
- const items = ref<T[]>([])
309
- const loading = ref(false)
310
-
311
- async function refresh() {
312
- loading.value = true
313
- try {
314
- items.value = await fetchFn()
315
- } finally {
316
- loading.value = false
317
- }
318
- }
319
-
320
- onMounted(refresh)
321
-
322
- return { items, loading, refresh }
323
- }
324
-
325
- // 使用
326
- const { items: users, loading, refresh } = useList(() => getUsers())
327
- ```
328
-
329
- ### 禁止的写法
330
-
331
- ```typescript
332
- // ❌ 不以 use 开头
333
- export function fetchList() { ... }
334
-
335
- // ❌ 返回数组
336
- export function useList() {
337
- return [items, loading, refresh]
338
- }
339
- ```
340
-
341
- ## 八、模板规范
342
-
343
- ### 指令使用
344
-
345
- ```vue
346
- <!-- ✅ v-if/v-else 在同级元素上 -->
347
- <div v-if="isLoading">加载中...</div>
348
- <div v-else>{{ data }}</div>
349
-
350
- <!-- ✅ v-for 必须带 key -->
351
- <div v-for="item in list" :key="item.id">{{ item.name }}</div>
352
-
353
- <!-- ❌ v-if 和 v-for 在同一元素上 -->
354
- <div v-for="item in list" v-if="item.active" :key="item.id">
355
-
356
- <!-- ✅ 用 computed 过滤 -->
357
- <div v-for="item in activeList" :key="item.id">
358
- ```
359
-
360
- ### 事件绑定
361
-
362
- ```vue
363
- <!-- ✅ 方法引用(无参数时) -->
364
- <button @click="handleSubmit">提交</button>
365
-
366
- <!-- ✅ 箭头函数(需要传参时) -->
367
- <button @click="() => handleDelete(item.id)">删除</button>
368
-
369
- <!-- ❌ 模板中写复杂逻辑 -->
370
- <button @click="loading = true; api.delete(id).then(() => refresh())">删除</button>
371
- ```
372
-
373
- ### 组件使用
374
-
375
- ```vue
376
- <!-- ✅ PascalCase 组件名 -->
377
- <UserCard :user="user" @click="handleSelect" />
378
-
379
- <!-- ❌ kebab-case 组件名 -->
380
- <user-card :user="user" />
381
-
382
- <!-- ✅ 布尔 prop 简写 -->
383
- <EButton loading disabled>提交</EButton>
384
-
385
- <!-- ✅ v-model 双向绑定 -->
386
- <EInput v-model="form.name" placeholder="请输入" />
387
- ```
388
-
389
- ## 九、样式规范
390
-
391
- ### 强制使用 Tailwind CSS
392
-
393
- ```vue
394
- <!-- ✅ 使用 Tailwind 工具类 -->
395
- <div class="flex items-center gap-4 p-6 rounded-lg bg-card">
396
-
397
- <!-- ❌ 使用内联样式 -->
398
- <div style="display: flex; padding: 24px;">
399
-
400
- <!-- ❌ 使用 scoped CSS(除非确实需要) -->
401
- <style scoped>
402
- .container { display: flex; }
403
- </style>
404
- ```
405
-
406
- ### 允许 scoped CSS 的场景
407
-
408
- - 第三方组件样式覆盖
409
- - 复杂动画定义
410
- - 无法用 Tailwind 实现的样式
411
-
412
- ```vue
413
- <!-- ✅ 覆盖第三方组件样式时可以用 scoped -->
414
- <style scoped>
415
- :deep(.el-input__inner) {
416
- border-radius: 8px;
417
- }
418
- </style>
419
- ```
420
-
421
- ### 响应式设计
422
-
423
- ```vue
424
- <!-- ✅ Mobile-first 响应式 -->
425
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
426
-
427
- <!-- ✅ 暗色模式使用 dark: 前缀 -->
428
- <div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
429
- ```
430
-
431
- ## 十、Vite 配置规范
432
-
433
- ### 强制配置
434
-
435
- ```typescript
436
- // vite.config.ts
437
- import { fileURLToPath, URL } from 'node:url'
438
- import { defineConfig } from 'vite'
439
- import vue from '@vitejs/plugin-vue'
440
- import tailwindcss from '@tailwindcss/vite'
441
-
442
- export default defineConfig({
443
- plugins: [vue(), tailwindcss()],
444
- resolve: {
445
- alias: {
446
- '@': fileURLToPath(new URL('./src', import.meta.url)),
447
- },
448
- },
449
- server: {
450
- port: 5173,
451
- proxy: {
452
- '/api': {
453
- target: 'http://127.0.0.1:8000',
454
- changeOrigin: true,
455
- },
456
- },
457
- },
458
- })
459
- ```
460
-
461
- | 规则 | 说明 |
462
- |------|------|
463
- | 路径别名 | 必须配置 `@` → `src/` |
464
- | API 代理 | 开发环境必须配置 `/api` 代理到后端 |
465
- | 端口 | 默认 5173,可在 `.env` 中覆盖 |
466
-
467
- ## 十一、错误处理
468
-
469
- ### 强制规则
470
-
471
- ```typescript
472
- // ✅ 异步操作必须有 try/catch
473
- async function handleSubmit() {
474
- loading.value = true
475
- try {
476
- await createUser(formData.value)
477
- message.success('创建成功')
478
- router.push('/users')
479
- } catch (err) {
480
- message.error('创建失败')
481
- } finally {
482
- loading.value = false
483
- }
484
- }
485
-
486
- // ❌ 没有错误处理
487
- async function handleSubmit() {
488
- await createUser(formData.value)
489
- router.push('/users')
490
- }
491
-
492
- // ❌ 空 catch
493
- try { ... } catch {}
494
-
495
- // ✅ 至少给用户反馈
496
- try { ... } catch { message.error('操作失败') }
497
- ```
498
-
499
- ### Loading 状态
500
-
501
- ```typescript
502
- // ✅ 所有异步操作必须有 loading 状态
503
- const loading = ref(false)
504
-
505
- async function fetchData() {
506
- loading.value = true
507
- try {
508
- data.value = await getData()
509
- } finally {
510
- loading.value = false
511
- }
512
- }
513
- ```
514
-
515
- ## 十二、导入规范
516
-
517
- ### 导入顺序(按以下顺序排列,组间空行)
518
-
519
- ```typescript
520
- // 1. Vue 核心
521
- import { ref, computed, onMounted, watch } from 'vue'
522
- import { useRouter, useRoute } from 'vue-router'
523
-
524
- // 2. 第三方库
525
- import { storeToRefs } from 'pinia'
526
- import axios from 'axios'
527
-
528
- // 3. 内部包(@dwydev/*)
529
- import { EButton, EInput } from '@dwydev/eui'
530
-
531
- // 4. 项目内模块
532
- import { useAuthStore } from '@/stores/auth'
533
- import { getUsers } from '@/api/user'
534
- import type { UserInfo } from '@/types/user'
535
- ```
536
-
537
- ### 强制规则
538
-
539
- ```typescript
540
- // ✅ 使用 @ 别名
541
- import { useAuthStore } from '@/stores/auth'
542
-
543
- // ❌ 使用相对路径(超过 2 层)
544
- import { useAuthStore } from '../../../stores/auth'
545
-
546
- // ✅ 类型导入使用 import type
547
- import type { UserInfo } from '@/types/user'
548
-
549
- // ❌ 类型和值混合导入
550
- import { UserInfo, getUsers } from '@/api/user'
551
- ```
552
-
553
- ## 十三、注释规范
554
-
555
- ### 强制规则
556
-
557
- 所有 `.ts` 和 `.vue` 文件必须遵循以下注释规则:
558
-
559
- 1. **文件顶部必须有概述注释** — 说明文件的功能和职责
560
- 2. **所有导出的方法/函数必须有中文注释** — 说明用途、参数、返回值
561
- 3. **所有导出的类必须有中文注释** — 说明类的职责
562
- 4. **所有导出的接口/类型必须有中文注释** — 说明类型含义,每个字段也需注释
563
- 5. **复杂的内部方法必须有中文注释** — 简单的 getter/setter 可省略
564
- 6. **注释使用中文** — 与代码保持一致的语言风格
565
-
566
- ### 文件顶部注释
567
-
568
- ```typescript
569
- /**
570
- * 用户认证相关的 API 请求模块
571
- * 提供登录、登出、刷新 token、获取用户信息等接口
572
- */
573
- import { request } from '@/utils/request'
574
- import type { LoginParams, UserInfo } from '@/types/user'
575
- ```
576
-
577
- ```vue
578
- <!--
579
- 用户信息卡片组件
580
- 展示用户头像、昵称、角色标签,支持点击跳转到用户详情页
581
- -->
582
- <script setup lang="ts">
583
- // ...
584
- </script>
585
- ```
586
-
587
- ### 方法/函数注释
588
-
589
- ```typescript
590
- /**
591
- * 用户登录
592
- * @param params 登录参数(用户名、密码)
593
- * @returns 登录成功后返回用户信息和 token
594
- */
595
- export async function login(params: LoginParams): Promise<LoginResult> {
596
- return request.post('/auth/login', params)
597
- }
598
-
599
- /**
600
- * 格式化金额显示
601
- * @param value 金额数值(单位:分)
602
- * @param currency 货币符号,默认 ¥
603
- * @returns 格式化后的金额字符串,如 ¥1,234.56
604
- */
605
- export function formatMoney(value: number, currency = '¥'): string {
606
- return `${currency}${(value / 100).toLocaleString()}`
607
- }
608
- ```
609
-
610
- ### 类注释
611
-
612
- ```typescript
613
- /**
614
- * HTTP 请求客户端
615
- * 封装 axios,提供拦截器、错误处理、token 自动刷新等能力
616
- */
617
- export class RequestClient {
618
- // ...
619
- }
620
- ```
621
-
622
- ### 接口/类型注释
623
-
624
- ```typescript
625
- /**
626
- * 用户基础信息
627
- */
628
- export interface UserInfo {
629
- /** 用户 ID */
630
- id: number
631
- /** 用户名(登录账号) */
632
- username: string
633
- /** 用户角色:admin=管理员,user=普通用户 */
634
- role: 'admin' | 'user'
635
- /** 邮箱地址(可选) */
636
- email?: string
637
- }
638
-
639
- /**
640
- * 分页查询参数
641
- */
642
- export interface PageParams {
643
- /** 当前页码,从 1 开始 */
644
- page: number
645
- /** 每页条数,默认 20 */
646
- pageSize: number
647
- }
648
- ```
649
-
650
- ### 组件 Props/Emits 注释
651
-
652
- ```vue
653
- <script setup lang="ts">
654
- /**
655
- * 数据表格组件的 Props 定义
656
- */
657
- interface Props {
658
- /** 表格数据源 */
659
- data: Record<string, unknown>[]
660
- /** 列配置 */
661
- columns: TableColumn[]
662
- /** 是否显示加载中 */
663
- loading?: boolean
664
- }
665
-
666
- const props = defineProps<Props>()
667
-
668
- /**
669
- * 组件事件定义
670
- */
671
- const emit = defineEmits<{
672
- /** 行点击事件,参数为当前行数据 */
673
- rowClick: [row: Record<string, unknown>]
674
- /** 选中行变化事件,参数为选中的行数组 */
675
- selectionChange: [rows: Record<string, unknown>[]]
676
- }>()
677
- </script>
678
- ```
679
-
680
- ### 组合式函数注释
681
-
682
- ```typescript
683
- /**
684
- * 列表数据管理 composable
685
- * 封装列表加载、刷新、loading 状态
686
- * @param fetchFn 数据获取函数
687
- * @returns 列表数据、加载状态、刷新方法
688
- */
689
- export function useList<T>(fetchFn: () => Promise<T[]>) {
690
- // ...
691
- }
692
- ```
693
-
694
- ### 可省略注释的场景
695
-
696
- - 一目了然的 getter/setter(如 `get name() { return this._name }`)
697
- - 测试文件中的 `describe` / `it` 块(用例名本身就是描述)
698
- - 组件内部的简单辅助函数(< 5 行且逻辑清晰)
699
- - 私有变量的简单赋值
700
-
701
- ### 禁止的写法
702
-
703
- ```typescript
704
- // ❌ 无文件头注释
705
- import { ref } from 'vue'
706
- export function login() { ... }
707
-
708
- // ❌ 方法无注释
709
- export async function deleteUser(id: number) {
710
- return request.delete(`/users/${id}`)
711
- }
712
-
713
- // ❌ 接口字段无说明
714
- export interface UserInfo {
715
- id: number
716
- username: string
717
- role: 'admin' | 'user'
718
- }
719
-
720
- // ❌ 英文注释(与代码风格不一致)
721
- /** Get user info by id */
722
- export async function getUser(id: number) { ... }
723
-
724
- // ❌ 废话注释(只复述代码,不说明 why)
725
- /** 设置 count 为 0 */
726
- count.value = 0
727
- ```
728
-
729
- ### 注释质量要求
730
-
731
- | 要求 | 说明 |
732
- |------|------|
733
- | 说明 why 而非 what | 代码已经说明 what,注释要解释为什么这样写 |
734
- | 语言一致性 | 全部使用中文,不混用中英文 |
735
- | 描述业务含义 | 避免"获取数据"这种泛泛说法,要写"获取用户列表" |
736
- | 标注边界条件 | 参数范围、特殊取值、空值处理要说明 |
737
- | 及时更新 | 代码修改后必须同步更新注释,禁止留下过时注释 |
738
-
739
- ## 十四、常见反模式(禁止)
740
-
741
- | 反模式 | 正确做法 |
742
- |--------|---------|
743
- | Options API | Composition API + `<script setup>` |
744
- | Pinia Options Store | Setup Store(箭头函数) |
745
- | `this.$refs` | `ref()` + 模板 ref |
746
- | `this.$emit` | `defineEmits<>()` |
747
- | `this.$router` | `useRouter()` |
748
- | `this.$route` | `useRoute()` |
749
- | Vuex | Pinia |
750
- | Mixins | Composables(`use*`) |
751
- | Event Bus | Pinia store 或 `provide/inject` |
752
- | `v-if` + `v-for` 同元素 | computed 过滤后 `v-for` |
753
- | scoped CSS 为主 | Tailwind CSS 为主 |
754
- | 组件中直接调 axios | 通过 `api/` 模块调用 |
755
- | `.value` 在模板中 | 模板自动解包,不需要 `.value` |
756
- | `reactive()` 用于简单值 | `ref()` 用于所有场景 |
757
- | 解构 store 不用 `storeToRefs` | 响应式值必须用 `storeToRefs` |
758
- | 手写 CSS 为主 | Tailwind 工具类为主 |
759
- | 测试文件放在 `src/` 内 | 放在独立 `tests/` 目录 |
760
- | 测试中用相对路径回溯 `../../src/` | 用 `@` 别名导入 |
761
-
762
- ## 十五、代码简约原则
763
-
764
- ### 核心思想
765
-
766
- 代码只写**必要的逻辑**,不写"以防万一"的冗余代码。信任 TypeScript 类型系统和框架保证,只在系统边界(用户输入、外部 API)做校验。
767
-
768
- ### 禁止的冗余模式
769
-
770
- ```typescript
771
- // ❌ 不必要的 fallback — 类型已保证
772
- const name = props.title ?? '' // props.title 声明为 string,不可能是 null
773
- const items = data.value || [] // ref<Item[]>([]) 初始就是数组
774
-
775
- // ✅ 直接使用
776
- const name = props.title
777
- const items = data.value
778
-
779
- // ❌ 不必要的可选链 — 值一定存在
780
- const id = user?.id // user 来自必传 prop,不可能是 undefined
781
-
782
- // ✅ 直接访问
783
- const id = user.id
784
-
785
- // ❌ 不必要的条件渲染守卫
786
- <div v-if="list"> // list 是 ref<Item[]>([]),永远为真值
787
- <div v-for="item in list" ...>
788
-
789
- // ✅ 直接渲染
790
- <div v-for="item in list" ...>
791
-
792
- // ❌ 不必要的空函数 fallback
793
- const onClick = props.onSubmit ?? (() => {})
794
-
795
- // ✅ 条件调用
796
- props.onSubmit?.()
797
-
798
- // ❌ 不必要的 try-catch 吞异常
799
- try {
800
- await api.getUsers()
801
- } catch {
802
- // 空 catch,bug 永远不会被发现
803
- }
804
-
805
- // ✅ 让异常暴露或给用户反馈
806
- try {
807
- await api.getUsers()
808
- } catch {
809
- toast.error('加载失败')
810
- }
811
- ```
812
-
813
- ### 判断标准
814
-
815
- 写每一行防御代码前问自己:**这个情况在当前上下文下真的会发生吗?**
816
-
817
- - 如果**会** → 写防御,加注释说明触发条件
818
- - 如果**不会** → 不写,信任上游保证
819
- - 如果**不确定** → 查看类型定义和调用链确认,不要"以防万一"
820
-
821
- ## 代码自检(写代码时强制执行)
822
-
823
- **每次生成或修改 Vue/TypeScript 代码后,必须逐条验证以下清单。任何一条未通过 → STOP,立即修正后再继续。**
824
-
825
- | # | 检查项 | 违规即 STOP |
826
- |---|--------|------------|
827
- | 1 | 使用 `<script setup lang="ts">`,不是 Options API | ✓ |
828
- | 2 | Props 用 `defineProps<{}>()`,Emits 用 `defineEmits<{}>()` | ✓ |
829
- | 3 | 无 `any` 类型(除非有注释说明原因) | ✓ |
830
- | 4 | `ref()` 复杂类型有泛型标注(`ref<T>` 而非 `ref(null)`) | ✓ |
831
- | 5 | Pinia 使用 Setup Store,不是 Options Store | ✓ |
832
- | 6 | Store 解构用 `storeToRefs`(响应式值),方法直接解构 | ✓ |
833
- | 7 | 类型导入用 `import type { ... }` | ✓ |
834
- | 8 | 路由组件使用 `() => import()` 懒加载 | ✓ |
835
- | 9 | API 调用通过 `api/` 模块,未在组件中直接 `axios.get` | ✓ |
836
- | 10 | 布尔变量有 `is/has/can/should` 前缀 | ✓ |
837
- | 11 | 样式以 Tailwind 为主,非必要不写 scoped CSS | ✓ |
838
- | 12 | 无 `v-if` + `v-for` 同元素 | ✓ |
839
- | 13 | 模板无复杂表达式,已提取为 `computed` | ✓ |
840
- | 14 | 枚举用联合类型 `type X = 'a' | 'b'`,不用 `enum` | ✓ |
841
- | 15 | 测试文件在 `tests/` 目录,不在 `src/` 内 | ✓ |
842
- | 16 | `tsconfig.app.json` 的 include 不包含 `tests/` | ✓ |
843
- | 17 | 文件顶部有中文概述注释,说明文件职责 | ✓ |
844
- | 18 | 所有导出的方法/函数有中文注释(含参数、返回值) | ✓ |
845
- | 19 | 所有导出的类/接口/类型有中文注释,接口字段逐个注释 | ✓ |
846
- | 20 | Props/Emits 定义有中文注释说明每个字段/事件 | ✓ |
847
-
848
- **不执行自检就提交代码 = 违规。**
849
-
850
- ---
851
-
852
- ## 错误处理(跨栈约束)
853
-
854
- ### 前端(Vue)
855
-
856
- 通过 ekit request 插件统一处理:
857
-
858
- ```typescript
859
- // 在 api/client.ts 配置拦截器后,业务代码不需要 try/catch
860
- // 401 → refreshToken 插件自动刷新,失败则跳登录
861
- // 422 → unwrap 插件抛出错误,页面级 catch 显示 message
862
- // 其他错误 → 全局错误提示
863
- ```
864
-
865
- **禁止:** 每个 API 调用都包 try/catch,应在页面级或 store 级统一处理。
866
-
867
- ---
868
-
869
- ## 环境变量管理
870
-
871
- ### 前端(Vite)
872
-
873
- ```bash
874
- # .env.development
875
- VITE_API_BASE_URL=http://localhost:8000/api
876
-
877
- # .env.production
878
- VITE_API_BASE_URL=/api
879
- ```
880
-
881
- 代码中通过 `import.meta.env.VITE_API_BASE_URL` 访问。**只有 `VITE_` 前缀的变量会暴露给客户端。**
882
-
883
- ### 强制规则
884
-
885
- | 规则 | 说明 |
886
- |------|------|
887
- | 每个项目必须有 `.env.example` | 列出所有变量名 + 注释说明,值用占位符 |
888
- | `.env` 文件禁止提交 git | 已在 git-security.md 约束 |
889
- | 禁止硬编码 | 数据库连接、密钥、API 地址等必须走环境变量 |
890
- | 禁止运行时读取 `.env` 文件 | 后端用 BaseSettings(启动时加载),前端用 Vite 编译注入 |