@deepstorm/cli 0.10.2 → 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.
- package/README.md +8 -8
- package/dist/build-registry.js +11 -5
- package/dist/cli.js +1256 -634
- package/dist/mcp/code-hosting/github.json +14 -1
- package/dist/mcp/docs-reference/context7.json +1 -10
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
- package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
- package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
- package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
- package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
- package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
- package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
- package/dist/registry.json +10 -0
- package/dist/skills/atoll-ops/SKILL.md +4 -0
- package/dist/skills/reef-commit/SKILL.md +9 -5
- package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
- package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
- package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
- package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
- package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
- package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
- package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
- package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
- package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
- package/dist/skills/reef-harden/EXAMPLES.md +12 -10
- package/dist/skills/reef-harden/SKILL.md +6 -0
- package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
- package/dist/skills/reef-pr/SKILL.md +7 -0
- package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
- package/dist/skills/reef-scope/SKILL.md +6 -6
- package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
- package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
- package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
- package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
- package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
- package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
- package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
- package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
- package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
- package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
- package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
- package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
- package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
- package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
- package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
- package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
- package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
- package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
- package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
- package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
- package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
- package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
- package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
- package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
- package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
- package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
- package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
- package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
- package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
- package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
- package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
- package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
- package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
- package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
- package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
- package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
- package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
- package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
- package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
- package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
- package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
- package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
- package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
- package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
- package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
- package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
- package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
- package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
- package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
- package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
- package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
- package/dist/skills/reef-testcase/SKILL.md +19 -19
- package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
- package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
- package/dist/skills/sweep-explore/SKILL.md +824 -0
- package/dist/skills/sweep-explore/references/explore-flow-template.md +61 -0
- package/dist/skills/sweep-init/SKILL.md +4 -0
- package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
- package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
- package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
- package/dist/skills/sweep-record/SKILL.md +358 -0
- package/dist/skills/sweep-run/SKILL.md +59 -50
- package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
- package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
- package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
- package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
- package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
- package/dist/skills/tide-discuss/references/checklists.md +32 -32
- package/dist/skills/tide-discuss/references/data-format.md +85 -77
- package/dist/skills/tide-discuss/references/prd-template.md +21 -19
- package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
- package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
- package/package.json +5 -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 () => {
|
|
70
|
-
|
|
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
|
|
116
|
-
Object.entries(params).map(([k, v]) => [k, String(v)])
|
|
117
|
-
|
|
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:
|
|
175
|
+
element: (
|
|
176
|
+
<LazyLoad>
|
|
177
|
+
<Dashboard />
|
|
178
|
+
</LazyLoad>
|
|
179
|
+
),
|
|
174
180
|
},
|
|
175
181
|
{
|
|
176
182
|
path: '/users',
|
|
177
|
-
element:
|
|
183
|
+
element: (
|
|
184
|
+
<LazyLoad>
|
|
185
|
+
<UserList />
|
|
186
|
+
</LazyLoad>
|
|
187
|
+
),
|
|
178
188
|
},
|
|
179
189
|
{
|
|
180
190
|
path: '/users/new',
|
|
181
|
-
element:
|
|
191
|
+
element: (
|
|
192
|
+
<LazyLoad>
|
|
193
|
+
<CreateUser />
|
|
194
|
+
</LazyLoad>
|
|
195
|
+
),
|
|
182
196
|
},
|
|
183
197
|
{
|
|
184
198
|
path: '/users/:id',
|
|
185
|
-
element:
|
|
199
|
+
element: (
|
|
200
|
+
<LazyLoad>
|
|
201
|
+
<UserDetail />
|
|
202
|
+
</LazyLoad>
|
|
203
|
+
),
|
|
186
204
|
},
|
|
187
205
|
{
|
|
188
206
|
path: '/settings',
|
|
189
|
-
element:
|
|
207
|
+
element: (
|
|
208
|
+
<LazyLoad>
|
|
209
|
+
<Settings />
|
|
210
|
+
</LazyLoad>
|
|
211
|
+
),
|
|
190
212
|
},
|
|
191
213
|
{
|
|
192
214
|
path: '/404',
|
|
193
|
-
element:
|
|
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
|
-
| 新建组件
|
|
12
|
-
| 新建表单
|
|
13
|
-
| 读数据
|
|
14
|
-
| 写数据
|
|
15
|
-
| Hook 职责 | 只负责业务逻辑和数据获取,不管理 UI 渲染
|
|
16
|
-
| 路由
|
|
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
|
-
| 函数组件
|
|
42
|
+
| 规则 | 要求 |
|
|
43
|
+
| ----------------- | ----------------------------------------------------- |
|
|
44
|
+
| 函数组件 | 禁止 Class 组件,使用 `React.FC` 或显式 Props 类型 |
|
|
45
45
|
| `interface` Props | 使用 `interface` 定义 Props,使用 `type` 定义联合类型 |
|
|
46
|
-
| 状态提升
|
|
47
|
-
| 默认导出
|
|
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 => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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`
|
|
124
|
-
| 表单受控/非受控混淆
|
|
125
|
-
| 状态更新异步
|
|
126
|
-
| `useCallback` 滥用
|
|
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
|
-
|
|
108
|
-
|
|
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
|
```
|