@deepstorm/cli 0.11.0 → 0.11.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.
Files changed (123) hide show
  1. package/README.md +8 -8
  2. package/dist/build-registry.js +11 -5
  3. package/dist/cli.js +839 -585
  4. package/dist/mcp/code-hosting/github.json +14 -1
  5. package/dist/mcp/docs-reference/context7.json +1 -10
  6. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
  7. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
  8. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
  9. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
  10. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
  11. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
  12. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
  13. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
  14. package/dist/skills/atoll-ops/SKILL.md +4 -0
  15. package/dist/skills/reef-commit/SKILL.md +9 -5
  16. package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
  17. package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
  18. package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
  19. package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
  20. package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
  21. package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
  22. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
  23. package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
  24. package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
  25. package/dist/skills/reef-harden/EXAMPLES.md +12 -10
  26. package/dist/skills/reef-harden/SKILL.md +6 -0
  27. package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
  28. package/dist/skills/reef-pr/SKILL.md +7 -0
  29. package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
  30. package/dist/skills/reef-scope/SKILL.md +6 -6
  31. package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
  32. package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
  33. package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
  34. package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
  35. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
  36. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
  37. package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
  38. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
  39. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
  40. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
  41. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
  42. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
  43. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
  44. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
  45. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
  46. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
  47. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
  48. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
  49. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
  50. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
  51. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
  52. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
  53. package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
  54. package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
  55. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
  56. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
  57. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
  58. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
  59. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
  60. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
  61. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
  62. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
  63. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
  64. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
  65. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
  66. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
  67. package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
  68. package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
  69. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
  70. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
  71. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
  72. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
  73. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
  74. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
  75. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
  76. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
  77. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
  78. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
  79. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
  80. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
  81. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
  82. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
  83. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
  84. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
  85. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
  86. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
  87. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
  88. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
  89. package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
  90. package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
  91. package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
  92. package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
  93. package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
  94. package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
  95. package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
  96. package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
  97. package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
  98. package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
  99. package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
  100. package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
  101. package/dist/skills/reef-testcase/SKILL.md +19 -19
  102. package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
  103. package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
  104. package/dist/skills/sweep-explore/SKILL.md +129 -100
  105. package/dist/skills/sweep-explore/references/explore-flow-template.md +9 -9
  106. package/dist/skills/sweep-init/SKILL.md +4 -0
  107. package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
  108. package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
  109. package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
  110. package/dist/skills/sweep-record/SKILL.md +38 -15
  111. package/dist/skills/sweep-run/SKILL.md +59 -50
  112. package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
  113. package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
  114. package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
  115. package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
  116. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
  117. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
  118. package/dist/skills/tide-discuss/references/checklists.md +32 -32
  119. package/dist/skills/tide-discuss/references/data-format.md +85 -77
  120. package/dist/skills/tide-discuss/references/prd-template.md +21 -19
  121. package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
  122. package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
  123. package/package.json +4 -3
@@ -4,92 +4,94 @@
4
4
 
5
5
  ```tsx
6
6
  // hooks/useUsers.ts
7
- import { useState, useEffect, useCallback } from 'react'
8
- import type { User, PageResponse } from '../shared/types/user'
7
+ import { useState, useEffect, useCallback } from 'react';
8
+ import type { User, PageResponse } from '../shared/types/user';
9
9
 
10
10
  interface UseUsersOptions {
11
- page?: number
12
- size?: number
13
- search?: string
14
- role?: string
11
+ page?: number;
12
+ size?: number;
13
+ search?: string;
14
+ role?: string;
15
15
  }
16
16
 
17
17
  interface UseUsersResult {
18
- users: User[]
19
- total: number
20
- loading: boolean
21
- error: Error | null
22
- refresh: () => void
18
+ users: User[];
19
+ total: number;
20
+ loading: boolean;
21
+ error: Error | null;
22
+ refresh: () => void;
23
23
  }
24
24
 
25
25
  // 使用 URLSearchParams 构建查询参数
26
26
  function buildQuery(params: UseUsersOptions): string {
27
- const searchParams = new URLSearchParams()
28
- if (params.page !== undefined) searchParams.set('page', String(params.page))
29
- if (params.size !== undefined) searchParams.set('size', String(params.size))
30
- if (params.search) searchParams.set('search', params.search)
31
- if (params.role) searchParams.set('role', params.role)
32
- const qs = searchParams.toString()
33
- return qs ? `?${qs}` : ''
27
+ const searchParams = new URLSearchParams();
28
+ if (params.page !== undefined) searchParams.set('page', String(params.page));
29
+ if (params.size !== undefined) searchParams.set('size', String(params.size));
30
+ if (params.search) searchParams.set('search', params.search);
31
+ if (params.role) searchParams.set('role', params.role);
32
+ const qs = searchParams.toString();
33
+ return qs ? `?${qs}` : '';
34
34
  }
35
35
 
36
36
  export function useUsers(options: UseUsersOptions = {}): UseUsersResult {
37
- const [users, setUsers] = useState<User[]>([])
38
- const [total, setTotal] = useState(0)
39
- const [loading, setLoading] = useState(true)
40
- const [error, setError] = useState<Error | null>(null)
41
- const [refreshKey, setRefreshKey] = useState(0)
37
+ const [users, setUsers] = useState<User[]>([]);
38
+ const [total, setTotal] = useState(0);
39
+ const [loading, setLoading] = useState(true);
40
+ const [error, setError] = useState<Error | null>(null);
41
+ const [refreshKey, setRefreshKey] = useState(0);
42
42
 
43
- const refresh = useCallback(() => setRefreshKey(k => k + 1), [])
43
+ const refresh = useCallback(() => setRefreshKey((k) => k + 1), []);
44
44
 
45
45
  useEffect(() => {
46
- let cancelled = false
47
- setLoading(true)
48
- setError(null)
46
+ let cancelled = false;
47
+ setLoading(true);
48
+ setError(null);
49
49
 
50
50
  fetch(`/api/v1/users${buildQuery(options)}`)
51
- .then(res => {
52
- if (!res.ok) throw new Error(`HTTP ${res.status}`)
53
- return res.json() as Promise<PageResponse<User>>
51
+ .then((res) => {
52
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
53
+ return res.json() as Promise<PageResponse<User>>;
54
54
  })
55
- .then(data => {
55
+ .then((data) => {
56
56
  if (!cancelled) {
57
- setUsers(data.content)
58
- setTotal(data.totalElements)
59
- setLoading(false)
57
+ setUsers(data.content);
58
+ setTotal(data.totalElements);
59
+ setLoading(false);
60
60
  }
61
61
  })
62
- .catch(err => {
62
+ .catch((err) => {
63
63
  if (!cancelled) {
64
- setError(err)
65
- setLoading(false)
64
+ setError(err);
65
+ setLoading(false);
66
66
  }
67
- })
67
+ });
68
68
 
69
- return () => { cancelled = true }
70
- }, [options.page, options.size, options.search, options.role, refreshKey])
69
+ return () => {
70
+ cancelled = true;
71
+ };
72
+ }, [options.page, options.size, options.search, options.role, refreshKey]);
71
73
 
72
- return { users, total, loading, error, refresh }
74
+ return { users, total, loading, error, refresh };
73
75
  }
74
76
 
75
77
  // 写操作 Hook
76
78
  export function useCreateUser() {
77
- const [loading, setLoading] = useState(false)
79
+ const [loading, setLoading] = useState(false);
78
80
 
79
81
  const create = async (data: CreateUserRequest): Promise<User> => {
80
- setLoading(true)
82
+ setLoading(true);
81
83
  const res = await fetch('/api/v1/users', {
82
84
  method: 'POST',
83
85
  headers: { 'Content-Type': 'application/json' },
84
86
  body: JSON.stringify(data),
85
- })
86
- if (!res.ok) throw new Error(`HTTP ${res.status}`)
87
- const user = await res.json()
88
- setLoading(false)
89
- return user
90
- }
91
-
92
- return { create, loading }
87
+ });
88
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
89
+ const user = await res.json();
90
+ setLoading(false);
91
+ return user;
92
+ };
93
+
94
+ return { create, loading };
93
95
  }
94
96
  ```
95
97
 
@@ -98,28 +100,28 @@ export function useCreateUser() {
98
100
  ```tsx
99
101
  // services/userService.ts
100
102
  // 将 API 调用统一到 Service 层,Hook 只负责状态管理
101
- import type { User, CreateUserRequest, UpdateUserRequest, PageResponse } from '../shared/types/user'
103
+ import type { User, CreateUserRequest, UpdateUserRequest, PageResponse } from '../shared/types/user';
102
104
 
103
- const BASE_URL = '/api/v1/users'
105
+ const BASE_URL = '/api/v1/users';
104
106
 
105
107
  async function handleResponse<T>(res: Response): Promise<T> {
106
108
  if (!res.ok) {
107
- const error = await res.json().catch(() => ({ message: `HTTP ${res.status}` }))
108
- throw new Error(error.message)
109
+ const error = await res.json().catch(() => ({ message: `HTTP ${res.status}` }));
110
+ throw new Error(error.message);
109
111
  }
110
- return res.json()
112
+ return res.json();
111
113
  }
112
114
 
113
115
  export const userService = {
114
116
  list(params?: Record<string, string | number>): Promise<PageResponse<User>> {
115
- const qs = params ? '?' + new URLSearchParams(
116
- Object.entries(params).map(([k, v]) => [k, String(v)])
117
- ).toString() : ''
118
- return fetch(`${BASE_URL}${qs}`).then(handleResponse)
117
+ const qs = params
118
+ ? '?' + new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString()
119
+ : '';
120
+ return fetch(`${BASE_URL}${qs}`).then(handleResponse);
119
121
  },
120
122
 
121
123
  getById(id: string): Promise<User> {
122
- return fetch(`${BASE_URL}/${id}`).then(handleResponse)
124
+ return fetch(`${BASE_URL}/${id}`).then(handleResponse);
123
125
  },
124
126
 
125
127
  create(data: CreateUserRequest): Promise<User> {
@@ -127,7 +129,7 @@ export const userService = {
127
129
  method: 'POST',
128
130
  headers: { 'Content-Type': 'application/json' },
129
131
  body: JSON.stringify(data),
130
- }).then(handleResponse)
132
+ }).then(handleResponse);
131
133
  },
132
134
 
133
135
  update(id: string, data: UpdateUserRequest): Promise<User> {
@@ -135,77 +137,101 @@ export const userService = {
135
137
  method: 'PUT',
136
138
  headers: { 'Content-Type': 'application/json' },
137
139
  body: JSON.stringify(data),
138
- }).then(handleResponse)
140
+ }).then(handleResponse);
139
141
  },
140
142
 
141
143
  delete(id: string): Promise<void> {
142
- return fetch(`${BASE_URL}/${id}`, { method: 'DELETE' }).then(handleResponse)
144
+ return fetch(`${BASE_URL}/${id}`, { method: 'DELETE' }).then(handleResponse);
143
145
  },
144
- }
146
+ };
145
147
  ```
146
148
 
147
149
  ## 路由配置
148
150
 
149
151
  ```tsx
150
152
  // routes/index.tsx
151
- import { createBrowserRouter, Navigate } from 'react-router-dom'
152
- import { lazy, Suspense } from 'react'
153
- import { Spin } from 'antd'
153
+ import { createBrowserRouter, Navigate } from 'react-router-dom';
154
+ import { lazy, Suspense } from 'react';
155
+ import { Spin } from 'antd';
154
156
 
155
157
  // 懒加载页面组件
156
- const Dashboard = lazy(() => import('../pages/Dashboard'))
157
- const UserList = lazy(() => import('../pages/UserList'))
158
- const UserDetail = lazy(() => import('../pages/UserDetail'))
159
- const CreateUser = lazy(() => import('../pages/CreateUser'))
160
- const Settings = lazy(() => import('../pages/Settings'))
161
- const NotFound = lazy(() => import('../pages/NotFound'))
158
+ const Dashboard = lazy(() => import('../pages/Dashboard'));
159
+ const UserList = lazy(() => import('../pages/UserList'));
160
+ const UserDetail = lazy(() => import('../pages/UserDetail'));
161
+ const CreateUser = lazy(() => import('../pages/CreateUser'));
162
+ const Settings = lazy(() => import('../pages/Settings'));
163
+ const NotFound = lazy(() => import('../pages/NotFound'));
162
164
 
163
165
  // 通用懒加载包装
164
166
  const LazyLoad: React.FC<{ children: React.ReactNode }> = ({ children }) => (
165
167
  <Suspense fallback={<Spin style={{ display: 'flex', justifyContent: 'center', marginTop: 120 }} />}>
166
168
  {children}
167
169
  </Suspense>
168
- )
170
+ );
169
171
 
170
172
  export const router = createBrowserRouter([
171
173
  {
172
174
  path: '/',
173
- element: <LazyLoad><Dashboard /></LazyLoad>,
175
+ element: (
176
+ <LazyLoad>
177
+ <Dashboard />
178
+ </LazyLoad>
179
+ ),
174
180
  },
175
181
  {
176
182
  path: '/users',
177
- element: <LazyLoad><UserList /></LazyLoad>,
183
+ element: (
184
+ <LazyLoad>
185
+ <UserList />
186
+ </LazyLoad>
187
+ ),
178
188
  },
179
189
  {
180
190
  path: '/users/new',
181
- element: <LazyLoad><CreateUser /></LazyLoad>,
191
+ element: (
192
+ <LazyLoad>
193
+ <CreateUser />
194
+ </LazyLoad>
195
+ ),
182
196
  },
183
197
  {
184
198
  path: '/users/:id',
185
- element: <LazyLoad><UserDetail /></LazyLoad>,
199
+ element: (
200
+ <LazyLoad>
201
+ <UserDetail />
202
+ </LazyLoad>
203
+ ),
186
204
  },
187
205
  {
188
206
  path: '/settings',
189
- element: <LazyLoad><Settings /></LazyLoad>,
207
+ element: (
208
+ <LazyLoad>
209
+ <Settings />
210
+ </LazyLoad>
211
+ ),
190
212
  },
191
213
  {
192
214
  path: '/404',
193
- element: <LazyLoad><NotFound /></LazyLoad>,
215
+ element: (
216
+ <LazyLoad>
217
+ <NotFound />
218
+ </LazyLoad>
219
+ ),
194
220
  },
195
221
  {
196
222
  path: '*',
197
223
  element: <Navigate to="/404" replace />,
198
224
  },
199
- ])
225
+ ]);
200
226
  ```
201
227
 
202
228
  ## 主应用入口
203
229
 
204
230
  ```tsx
205
231
  // App.tsx
206
- import { RouterProvider } from 'react-router-dom'
207
- import { ConfigProvider } from 'antd'
208
- import { router } from './routes'
232
+ import { RouterProvider } from 'react-router-dom';
233
+ import { ConfigProvider } from 'antd';
234
+ import { router } from './routes';
209
235
 
210
236
  const App: React.FC = () => {
211
237
  return (
@@ -219,8 +245,8 @@ const App: React.FC = () => {
219
245
  >
220
246
  <RouterProvider router={router} />
221
247
  </ConfigProvider>
222
- )
223
- }
248
+ );
249
+ };
224
250
 
225
- export default App
251
+ export default App;
226
252
  ```
@@ -6,22 +6,22 @@
6
6
 
7
7
  ## 速查
8
8
 
9
- | 场景 | 决策 |
10
- | --- | --- |
11
- | 新建组件 | 函数组件 + TypeScript + `interface` Props |
12
- | 新建表单 | Ant Design `Form` + `useState` / `useReducer` |
13
- | 读数据 | 自定义 Hook(`useUsers`)封装 fetch/axios |
14
- | 写数据 | 自定义 Hook + `useState` loading/error 三态 |
15
- | Hook 职责 | 只负责业务逻辑和数据获取,不管理 UI 渲染 |
16
- | 路由 | `React.lazy()` + `Suspense` 懒加载 |
9
+ | 场景 | 决策 |
10
+ | --------- | --------------------------------------------- |
11
+ | 新建组件 | 函数组件 + TypeScript + `interface` Props |
12
+ | 新建表单 | Ant Design `Form` + `useState` / `useReducer` |
13
+ | 读数据 | 自定义 Hook(`useUsers`)封装 fetch/axios |
14
+ | 写数据 | 自定义 Hook + `useState` loading/error 三态 |
15
+ | Hook 职责 | 只负责业务逻辑和数据获取,不管理 UI 渲染 |
16
+ | 路由 | `React.lazy()` + `Suspense` 懒加载 |
17
17
 
18
18
  ## 新建组件
19
19
 
20
20
  ```tsx
21
21
  interface UserTableProps {
22
- users: User[]
23
- loading?: boolean
24
- onSelect?: (user: User) => void
22
+ users: User[];
23
+ loading?: boolean;
24
+ onSelect?: (user: User) => void;
25
25
  }
26
26
 
27
27
  const UserTable: React.FC<UserTableProps> = ({ users, loading, onSelect }) => {
@@ -35,16 +35,16 @@ const UserTable: React.FC<UserTableProps> = ({ users, loading, onSelect }) => {
35
35
  onClick: () => onSelect?.(record),
36
36
  })}
37
37
  />
38
- )
39
- }
38
+ );
39
+ };
40
40
  ```
41
41
 
42
- | 规则 | 要求 |
43
- | --- | --- |
44
- | 函数组件 | 禁止 Class 组件,使用 `React.FC` 或显式 Props 类型 |
42
+ | 规则 | 要求 |
43
+ | ----------------- | ----------------------------------------------------- |
44
+ | 函数组件 | 禁止 Class 组件,使用 `React.FC` 或显式 Props 类型 |
45
45
  | `interface` Props | 使用 `interface` 定义 Props,使用 `type` 定义联合类型 |
46
- | 状态提升 | 组件间共享状态提升到最近公共父组件 |
47
- | 默认导出 | 页面级组件使用 `export default`,通用组件使用具名导出 |
46
+ | 状态提升 | 组件间共享状态提升到最近公共父组件 |
47
+ | 默认导出 | 页面级组件使用 `export default`,通用组件使用具名导出 |
48
48
 
49
49
  ## Hooks 规则
50
50
 
@@ -55,18 +55,24 @@ const UserTable: React.FC<UserTableProps> = ({ users, loading, onSelect }) => {
55
55
  ```tsx
56
56
  // ✅ 正确:自定义 Hook 分离数据获取逻辑
57
57
  function useUsers() {
58
- const [users, setUsers] = useState<User[]>([])
59
- const [loading, setLoading] = useState(true)
60
- const [error, setError] = useState<Error | null>(null)
58
+ const [users, setUsers] = useState<User[]>([]);
59
+ const [loading, setLoading] = useState(true);
60
+ const [error, setError] = useState<Error | null>(null);
61
61
 
62
62
  useEffect(() => {
63
63
  fetch('/api/v1/users')
64
- .then(res => res.json())
65
- .then(data => { setUsers(data); setLoading(false) })
66
- .catch(err => { setError(err); setLoading(false) })
67
- }, [])
68
-
69
- return { users, loading, error }
64
+ .then((res) => res.json())
65
+ .then((data) => {
66
+ setUsers(data);
67
+ setLoading(false);
68
+ })
69
+ .catch((err) => {
70
+ setError(err);
71
+ setLoading(false);
72
+ });
73
+ }, []);
74
+
75
+ return { users, loading, error };
70
76
  }
71
77
  ```
72
78
 
@@ -91,13 +97,13 @@ function useUsers() {
91
97
  - 权限通过 Route 组件的 wrapper 或 middleware 模式控制
92
98
 
93
99
  ```tsx
94
- const UserList = React.lazy(() => import('./pages/UserList'))
95
- const UserDetail = React.lazy(() => import('./pages/UserDetail'))
100
+ const UserList = React.lazy(() => import('./pages/UserList'));
101
+ const UserDetail = React.lazy(() => import('./pages/UserDetail'));
96
102
 
97
103
  const routes = [
98
104
  { path: '/users', element: <UserList /> },
99
105
  { path: '/users/:id', element: <UserDetail /> },
100
- ]
106
+ ];
101
107
  ```
102
108
 
103
109
  ## 代码风格
@@ -109,18 +115,18 @@ const routes = [
109
115
 
110
116
  ## 注释规则
111
117
 
112
- | 文件类型 | 注释要求 |
113
- |---------|---------|
114
- | **类型定义** | 复杂类型加行内注释说明字段含义 |
115
- | **API Hook** | 简要说明该 Hook 的功能和返回值 |
116
- | **组件** | 关键组件加注释说明用途;复杂逻辑行内注释 |
118
+ | 文件类型 | 注释要求 |
119
+ | ------------ | ---------------------------------------- |
120
+ | **类型定义** | 复杂类型加行内注释说明字段含义 |
121
+ | **API Hook** | 简要说明该 Hook 的功能和返回值 |
122
+ | **组件** | 关键组件加注释说明用途;复杂逻辑行内注释 |
117
123
 
118
124
  ## 常见坑
119
125
 
120
- | 场景 | 问题 | 正确做法 |
121
- |------|------|---------|
122
- | `useEffect` 依赖遗漏 | 闭包内使用了 state 或 props 但未加入依赖数组 | 穷举所有依赖,或使用 `useCallback` 稳定引用 |
123
- | 列表 `key` | 使用 index 作为 key 导致渲染异常 | 使用唯一 ID(`rowKey` / 数据 id) |
124
- | 表单受控/非受控混淆 | 组件同时接收 `value` 和 `defaultValue` | 受控组件用 `value` + `onChange`,非受控用 `defaultValue` |
125
- | 状态更新异步 | 连续 `setState` 导致只生效最后一次 | 使用函数形式 `setCount(prev => prev + 1)` |
126
- | `useCallback` 滥用 | 对所有函数都用 `useCallback` | 仅对传给子组件或 `useEffect` 依赖的函数使用 |
126
+ | 场景 | 问题 | 正确做法 |
127
+ | -------------------- | -------------------------------------------- | -------------------------------------------------------- |
128
+ | `useEffect` 依赖遗漏 | 闭包内使用了 state 或 props 但未加入依赖数组 | 穷举所有依赖,或使用 `useCallback` 稳定引用 |
129
+ | 列表 `key` | 使用 index 作为 key 导致渲染异常 | 使用唯一 ID(`rowKey` / 数据 id) |
130
+ | 表单受控/非受控混淆 | 组件同时接收 `value` 和 `defaultValue` | 受控组件用 `value` + `onChange`,非受控用 `defaultValue` |
131
+ | 状态更新异步 | 连续 `setState` 导致只生效最后一次 | 使用函数形式 `setCount(prev => prev + 1)` |
132
+ | `useCallback` 滥用 | 对所有函数都用 `useCallback` | 仅对传给子组件或 `useEffect` 依赖的函数使用 |
@@ -58,10 +58,7 @@
58
58
  <a-divider />
59
59
 
60
60
  <!-- ✅ 多属性 -->
61
- <a-date-picker
62
- v-model:value="date"
63
- format="YYYY-MM-DD"
64
- />
61
+ <a-date-picker v-model:value="date" format="YYYY-MM-DD" />
65
62
  </template>
66
63
  ```
67
64
 
@@ -73,13 +70,7 @@
73
70
  <div>{{ loading ? '加载中...' : users.length + ' 条记录' }}</div>
74
71
 
75
72
  <!-- ✅ 复杂条件使用多个 v-if 换行 -->
76
- <a-alert
77
- v-if="error"
78
- type="error"
79
- :message="error.message"
80
- closable
81
- @close="clearError"
82
- />
73
+ <a-alert v-if="error" type="error" :message="error.message" closable @close="clearError" />
83
74
  <a-table v-else-if="!loading" :data-source="users" />
84
75
  </template>
85
76
  ```
@@ -89,26 +80,21 @@
89
80
  ```vue
90
81
  <script setup lang="ts">
91
82
  // ✅ ref/computed 连续排列,按逻辑分组
92
- const users = ref<User[]>([])
93
- const loading = ref(true)
94
- const search = ref('')
83
+ const users = ref<User[]>([]);
84
+ const loading = ref(true);
85
+ const search = ref('');
95
86
 
96
- const filteredUsers = computed(() =>
97
- users.value.filter(u => u.username.includes(search.value))
98
- )
87
+ const filteredUsers = computed(() => users.value.filter((u) => u.username.includes(search.value)));
99
88
 
100
89
  // ✅ watch 短依赖行内
101
90
  watch(search, () => {
102
- fetchUsers(search.value)
103
- })
91
+ fetchUsers(search.value);
92
+ });
104
93
 
105
94
  // ✅ 多源 watch 换行
106
- watch(
107
- [search, role, status],
108
- ([newSearch, newRole, newStatus]) => {
109
- fetchFilteredUsers({ search: newSearch, role: newRole, status: newStatus })
110
- }
111
- )
95
+ watch([search, role, status], ([newSearch, newRole, newStatus]) => {
96
+ fetchFilteredUsers({ search: newSearch, role: newRole, status: newStatus });
97
+ });
112
98
  </script>
113
99
  ```
114
100
 
@@ -120,22 +106,17 @@ watch(
120
106
  <a-button @click="isOpen = true">打开</a-button>
121
107
 
122
108
  <!-- ✅ 多行函数体:提取到 script setup 中 -->
123
- <a-modal
124
- title="确认删除"
125
- :open="isOpen"
126
- @ok="handleDelete"
127
- @cancel="isOpen = false"
128
- >
109
+ <a-modal title="确认删除" :open="isOpen" @ok="handleDelete" @cancel="isOpen = false">
129
110
  <p>确定要删除该用户吗?</p>
130
111
  </a-modal>
131
112
  </template>
132
113
 
133
114
  <script setup lang="ts">
134
115
  async function handleDelete() {
135
- await deleteUser(id)
136
- message.success('删除成功')
137
- isOpen.value = false
138
- onDeleted?.()
116
+ await deleteUser(id);
117
+ message.success('删除成功');
118
+ isOpen.value = false;
119
+ onDeleted?.();
139
120
  }
140
121
  </script>
141
122
  ```
@@ -145,18 +126,18 @@ async function handleDelete() {
145
126
  ```typescript
146
127
  // ✅ 每个属性一行
147
128
  interface UserFormProps {
148
- user?: User
149
- onSubmit: (values: CreateUserRequest) => Promise<void>
150
- onCancel: () => void
129
+ user?: User;
130
+ onSubmit: (values: CreateUserRequest) => Promise<void>;
131
+ onCancel: () => void;
151
132
  }
152
133
 
153
134
  // ✅ emits 命名元组每个事件一行
154
135
  const emit = defineEmits<{
155
- success: [userId: string]
156
- close: []
157
- error: [message: string]
158
- }>()
136
+ success: [userId: string];
137
+ close: [];
138
+ error: [message: string];
139
+ }>();
159
140
 
160
141
  // ✅ 联合类型换行
161
- type UserStatus = 'active' | 'inactive' | 'locked' | 'pending'
142
+ type UserStatus = 'active' | 'inactive' | 'locked' | 'pending';
162
143
  ```