@teamix-evo/skills 0.12.1 → 0.13.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/README.md +1 -1
- package/manifest.json +11 -28
- package/package.json +2 -2
- package/src/teamix-evo-code-opentrek/SKILL.md +13 -13
- package/src/teamix-evo-code-opentrek/api-layering.md +53 -44
- package/src/teamix-evo-code-opentrek/checklist.md +24 -24
- package/src/teamix-evo-code-opentrek/file-structure.md +55 -36
- package/src/teamix-evo-code-opentrek/forms-and-validation.md +17 -16
- package/src/teamix-evo-code-opentrek/reuse-first.md +6 -9
- package/src/teamix-evo-code-opentrek/testing.md +14 -14
- package/src/teamix-evo-code-uni-manager/SKILL.md +15 -15
- package/src/teamix-evo-code-uni-manager/api-layering.md +74 -58
- package/src/teamix-evo-code-uni-manager/checklist.md +28 -28
- package/src/teamix-evo-code-uni-manager/error-and-loading.md +2 -2
- package/src/teamix-evo-code-uni-manager/file-structure.md +77 -62
- package/src/teamix-evo-code-uni-manager/forms-and-validation.md +17 -15
- package/src/teamix-evo-code-uni-manager/reuse-first.md +7 -10
- package/src/teamix-evo-code-uni-manager/routing-and-codesplit.md +1 -1
- package/src/teamix-evo-code-uni-manager/testing.md +37 -37
- package/src/teamix-evo-design-opentrek/SKILL.md +17 -20
- package/src/teamix-evo-design-opentrek/boundaries.md +1 -1
- package/src/teamix-evo-design-opentrek/checklist.md +5 -5
- package/src/teamix-evo-design-opentrek/components.md +19 -19
- package/src/teamix-evo-design-opentrek/examples/standard-card-list.html +1 -1
- package/src/teamix-evo-design-opentrek/examples/standard-table-list.html +1 -1
- package/src/teamix-evo-design-opentrek/foundations.md +6 -6
- package/src/teamix-evo-design-opentrek/pages/dashboard-page/SKILL.md +18 -19
- package/src/teamix-evo-design-opentrek/pages/dashboard-page/patterns/dashboard-opentrek.md +6 -6
- package/src/teamix-evo-design-opentrek/pages/detail-page/SKILL.md +24 -25
- package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/api-doc-detail.md +3 -7
- package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/comparison-detail.md +3 -7
- package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/monitor-detail.md +3 -7
- package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/resource-detail.md +10 -10
- package/src/teamix-evo-design-opentrek/pages/form-page/SKILL.md +26 -27
- package/src/teamix-evo-design-opentrek/pages/list-page/SKILL.md +35 -36
- package/src/teamix-evo-design-opentrek/pages/list-page/_shared/item-card-spec.md +41 -32
- package/src/teamix-evo-design-opentrek/pages/list-page/patterns/card-list-opentrek.md +10 -10
- package/src/teamix-evo-design-opentrek/pages/list-page/patterns/card-list.md +23 -23
- package/src/teamix-evo-design-opentrek/pages/list-page/patterns/standard-list-opentrek.md +8 -8
- package/src/teamix-evo-design-opentrek/pages/list-page/patterns/standard-list.md +8 -8
- package/src/teamix-evo-design-opentrek/patterns/color-mapping.md +2 -2
- package/src/teamix-evo-design-opentrek/patterns/dashboard.md +1 -1
- package/src/teamix-evo-design-opentrek/patterns/detail-page.md +9 -9
- package/src/teamix-evo-design-opentrek/patterns/form-page.md +6 -6
- package/src/teamix-evo-design-opentrek/patterns/list-page.md +9 -9
- package/src/teamix-evo-design-opentrek/patterns/page-types.md +3 -3
- package/src/teamix-evo-design-opentrek/principles.md +541 -0
- package/src/teamix-evo-design-opentrek/rules/common-components.json +206 -76
- package/src/teamix-evo-design-opentrek/rules/component-specs.json +2 -2
- package/src/teamix-evo-design-opentrek/rules/page-frame.json +197 -193
- package/src/teamix-evo-design-opentrek/{generation-flow.md → workflow.md} +141 -22
- package/src/teamix-evo-design-uni-manager/SKILL.md +5 -5
- package/src/teamix-evo-design-uni-manager/boundaries.md +2 -2
- package/src/teamix-evo-design-uni-manager/brand.md +1 -1
- package/src/teamix-evo-design-uni-manager/checklist.md +2 -2
- package/src/teamix-evo-design-uni-manager/components.md +11 -11
- package/src/teamix-evo-design-uni-manager/foundations.md +7 -7
- package/src/teamix-evo-design-uni-manager/generation-flow.md +3 -3
- package/src/teamix-evo-manage/SKILL.md +111 -709
- package/src/teamix-evo-manage/init.md +98 -0
- package/src/teamix-evo-manage/migrate.md +100 -0
- package/src/teamix-evo-manage/rearchitect-capture-guide.md +174 -0
- package/src/teamix-evo-manage/rearchitect.md +373 -0
- package/src/teamix-evo-manage/update-component-staging.md +188 -0
- package/src/teamix-evo-manage/update-token-rename.md +126 -0
- package/src/teamix-evo-manage/update-token-treatment.md +116 -0
- package/src/teamix-evo-manage/update.md +213 -0
- package/src/teamix-evo-design-opentrek/brand.md +0 -154
- package/src/teamix-evo-design-opentrek/pages/list-page/_shared/search-combo-spec.md +0 -194
- package/src/teamix-evo-design-opentrek/philosophy.md +0 -98
- package/src/teamix-evo-design-opentrek/rules/README.md +0 -39
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_AGENT RUNTIME.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_AI GATEWAY.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_AI STUDIO.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_DEV-2.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_LOGO.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/_assets/OP_OPS.svg +0 -1
- package/src/teamix-evo-design-opentrek/rules/layout-rules.json +0 -218
- package/src/teamix-evo-design-opentrek/rules/page-header-spec.md +0 -123
- package/src/teamix-evo-design-opentrek/rules/sidebar-spec.md +0 -217
- package/src/teamix-evo-upgrade/SKILL.md +0 -431
- /package/src/teamix-evo-design-opentrek/{rules/boundaries.rules.json → boundaries.json} +0 -0
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> ⚠️ **Prerequisites**: 本文件是 [SKILL.md](./SKILL.md) Step 2 的强制读取项。任何涉及后端通信的代码必须先读本文件。
|
|
4
4
|
|
|
5
|
-
> **核心原则**:组件不直接 fetch
|
|
5
|
+
> **核心原则**:组件不直接 fetch。每个业务领域的代码聚合在 `src/features/<domain>/`,通过 hooks 暴露给组件。
|
|
6
6
|
>
|
|
7
|
-
> **uni-manager 增强**:多租户 / 多区域 / 跨云上下文(tenantId / regionId / cloudProvider)通过 **`src/lib/http.ts` 的 request interceptor 自动注入**,
|
|
7
|
+
> **uni-manager 增强**:多租户 / 多区域 / 跨云上下文(tenantId / regionId / cloudProvider)通过 **`src/lib/http.ts` 的 request interceptor 自动注入**,api 函数与 hook 不感知;只有"显式跨上下文"的少数 API 才在调用处显式覆盖。
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## 三层结构
|
|
11
|
+
## Feature-based 三层结构
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
┌────────────────────────────────────┐
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
│ 仅通过 hook
|
|
22
22
|
▼
|
|
23
23
|
┌────────────────────────────────────┐
|
|
24
|
-
│ src/hooks
|
|
25
|
-
│ - 包装请求生命周期
|
|
24
|
+
│ src/features/<domain>/hooks.ts │ Hook 层:状态管理 + 缓存(react-query 等)
|
|
25
|
+
│ - 包装请求生命周期(loading/error) │
|
|
26
26
|
│ - 处理缓存 / 重试 / 失效 │
|
|
27
27
|
│ - 不写具体的 URL / payload 拼装 │
|
|
28
28
|
│ - queryKey 含 tenantId / regionId │ ← uni-manager 强化:键值带上下文
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
│ 调用纯函数
|
|
31
31
|
▼
|
|
32
32
|
┌────────────────────────────────────┐
|
|
33
|
-
│ src/
|
|
33
|
+
│ src/features/<domain>/api.ts │ API 层:请求纯函数
|
|
34
34
|
│ - 输入参数 → 输出 Promise<T> │
|
|
35
35
|
│ - 拼 URL / 序列化 / 反序列化 │
|
|
36
36
|
│ - 不依赖 React │
|
|
@@ -46,38 +46,52 @@
|
|
|
46
46
|
└────────────────────────────────────┘
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
### 目录结构
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/features/
|
|
53
|
+
├── instance/ # 实例领域
|
|
54
|
+
│ ├── api.ts # 请求纯函数(listInstances, createInstance, ...)
|
|
55
|
+
│ ├── api.cloud.ts # 跨云查询(显式覆盖 interceptor,并发多 cloud)
|
|
56
|
+
│ ├── hooks.ts # react-query 封装(useInstanceList, useCreateInstance, ...)
|
|
57
|
+
│ ├── types.ts # 领域类型(Instance, InstanceStatus, InstanceListParams, ...)
|
|
58
|
+
│ ├── mocks.ts # 本地 mock 数据(开发阶段)
|
|
59
|
+
│ └── schema.ts # zod schema(CreateInstanceInputSchema, ...)
|
|
60
|
+
├── tenant/ # 租户管理
|
|
61
|
+
│ ├── api.ts
|
|
62
|
+
│ ├── hooks.ts
|
|
63
|
+
│ ├── types.ts
|
|
64
|
+
│ └── mocks.ts
|
|
65
|
+
├── region/ # 区域元数据
|
|
66
|
+
│ └── ...
|
|
67
|
+
└── audit-log/ # 操作日志
|
|
68
|
+
└── ...
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
新增一个业务领域 = 新增一个 `features/<domain>/` 文件夹,不会让任何一个目录膨胀。
|
|
72
|
+
|
|
49
73
|
---
|
|
50
74
|
|
|
51
|
-
## §1 · `
|
|
75
|
+
## §1 · `features/<domain>/api.ts` 约定
|
|
52
76
|
|
|
53
77
|
### 文件组织
|
|
54
78
|
|
|
55
|
-
按**领域**(domain
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
src/services/
|
|
59
|
-
├── instance.ts # 实例领域:list / get / create / start / stop / release
|
|
60
|
-
├── database.ts # 数据库领域
|
|
61
|
-
├── tenant.ts # 租户管理(meta,不是上下文)
|
|
62
|
-
├── region.ts # 区域元数据
|
|
63
|
-
├── audit-log.ts # 操作日志
|
|
64
|
-
└── index.ts # 统一 re-export(可选)
|
|
65
|
-
```
|
|
79
|
+
按**领域**(domain)拆为独立文件夹,每个领域的请求函数放 `api.ts`。
|
|
66
80
|
|
|
67
81
|
❌ 不要按动词拆:`getInstances.ts` / `createInstance.ts` —— 会爆文件数。
|
|
68
82
|
|
|
69
83
|
### 函数签名约定(uni-manager)
|
|
70
84
|
|
|
71
85
|
```ts
|
|
72
|
-
// src/
|
|
86
|
+
// src/features/instance/api.ts
|
|
73
87
|
import { http } from '@/lib/http';
|
|
74
88
|
import type {
|
|
75
89
|
Instance,
|
|
76
90
|
InstanceListParams,
|
|
77
91
|
CreateInstanceInput,
|
|
78
|
-
} from '
|
|
92
|
+
} from './types';
|
|
79
93
|
|
|
80
|
-
// ✅ 标准:
|
|
94
|
+
// ✅ 标准:api 不显式拼 tenantId / regionId / cloudProvider
|
|
81
95
|
// interceptor 会从当前 TenantContext 读取并加 header
|
|
82
96
|
export async function listInstances(
|
|
83
97
|
params: InstanceListParams,
|
|
@@ -120,37 +134,39 @@ export async function listInstancesAcrossTenants(
|
|
|
120
134
|
要点:
|
|
121
135
|
|
|
122
136
|
- **纯函数**(不依赖 React 上下文,可以在 SSR / Node 测试中单独跑)
|
|
123
|
-
-
|
|
124
|
-
- **错误不在
|
|
125
|
-
- **不写 console.log / 业务弹窗**
|
|
137
|
+
- **类型来自同目录 `./types`**,同一领域内用相对路径引用
|
|
138
|
+
- **错误不在 api 层处理** —— 抛出去给 hook / 全局 interceptor 处理(归一化在 `src/lib/http.ts`)
|
|
139
|
+
- **不写 console.log / 业务弹窗** —— 是数据通道,不是 UI 通道
|
|
126
140
|
- **不显式拼 tenantId / regionId** —— 让 interceptor 注入;显式覆盖只用于跨上下文场景
|
|
127
141
|
|
|
128
142
|
### 反模式
|
|
129
143
|
|
|
130
|
-
- ❌ `fetch('https://api.example.com/...')` 写死 host
|
|
131
|
-
- ❌ 每个
|
|
132
|
-
- ❌
|
|
133
|
-
- ❌ 在
|
|
134
|
-
- ❌ 一个
|
|
144
|
+
- ❌ `fetch('https://api.example.com/...')` 写死 host(应该用 `http` wrapper)
|
|
145
|
+
- ❌ 每个 api 函数加 `tenantId: string` 参数 —— 应该走 interceptor
|
|
146
|
+
- ❌ api 函数返回 `{ loading, data, error }`(那是 hook 该做的)
|
|
147
|
+
- ❌ 在 api 里 `import { toast } from 'sonner'` 弹通知(归到 hook 或全局 interceptor)
|
|
148
|
+
- ❌ 一个 api 函数同时拼 URL、做业务计算、改全局 store
|
|
135
149
|
|
|
136
150
|
---
|
|
137
151
|
|
|
138
|
-
## §2 · `
|
|
152
|
+
## §2 · `features/<domain>/hooks.ts` 约定
|
|
139
153
|
|
|
140
154
|
### 命名
|
|
141
155
|
|
|
142
156
|
- Query 类:`useInstanceList`、`useInstance(id)`、`useUserProfile`
|
|
143
157
|
- Mutation 类:`useCreateInstance`、`useReleaseInstance`、`useUpdateUser`
|
|
144
158
|
|
|
159
|
+
同一领域的 query + mutation hooks 聚合在一个 `hooks.ts` 里。
|
|
160
|
+
|
|
145
161
|
### 实现(以 `@tanstack/react-query` 为例 — uni-manager 增强 queryKey)
|
|
146
162
|
|
|
147
163
|
```ts
|
|
148
|
-
// src/hooks
|
|
164
|
+
// src/features/instance/hooks.ts
|
|
149
165
|
import { useQuery } from '@tanstack/react-query';
|
|
150
|
-
import { listInstances } from '
|
|
166
|
+
import { listInstances } from './api';
|
|
151
167
|
import { useTenant } from '@/contexts/TenantContext';
|
|
152
168
|
import { useRegion } from '@/contexts/RegionContext';
|
|
153
|
-
import type { InstanceListParams } from '
|
|
169
|
+
import type { InstanceListParams } from './types';
|
|
154
170
|
|
|
155
171
|
export function useInstanceList(params: InstanceListParams) {
|
|
156
172
|
const { tenantId } = useTenant();
|
|
@@ -165,9 +181,9 @@ export function useInstanceList(params: InstanceListParams) {
|
|
|
165
181
|
```
|
|
166
182
|
|
|
167
183
|
```ts
|
|
168
|
-
// src/hooks
|
|
184
|
+
// src/features/instance/hooks.ts (续)
|
|
169
185
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
170
|
-
import { createInstance } from '
|
|
186
|
+
import { createInstance } from './api';
|
|
171
187
|
import { useTenant } from '@/contexts/TenantContext';
|
|
172
188
|
|
|
173
189
|
export function useCreateInstance() {
|
|
@@ -184,11 +200,11 @@ export function useCreateInstance() {
|
|
|
184
200
|
|
|
185
201
|
### 反模式
|
|
186
202
|
|
|
187
|
-
- ❌ Hook 里再次拼 URL(违反"
|
|
203
|
+
- ❌ Hook 里再次拼 URL(违反"api 拼 URL"边界)
|
|
188
204
|
- ❌ queryKey 不包含 tenantId / regionId —— 切换租户后旧数据被复用
|
|
189
|
-
- ❌ 一个 hook
|
|
205
|
+
- ❌ 一个 hook 调多个领域的 api 把数据缝合 —— 缝合应该在 api 层(返回组合好的 DTO)
|
|
190
206
|
- ❌ Hook 里写业务跳转 / 路由切换 —— 由组件接 `mutation.onSuccess` callback 处理
|
|
191
|
-
- ❌ 在 hook 里手动加 `tenantId` 到
|
|
207
|
+
- ❌ 在 hook 里手动加 `tenantId` 到 api 入参 —— 走 interceptor
|
|
192
208
|
|
|
193
209
|
---
|
|
194
210
|
|
|
@@ -306,9 +322,9 @@ export function useTenant() {
|
|
|
306
322
|
|
|
307
323
|
### 反模式
|
|
308
324
|
|
|
309
|
-
- ❌ 在
|
|
310
|
-
- ❌ 多份 `http`
|
|
311
|
-
- ❌ 把 tenantId / regionId 放到每个
|
|
325
|
+
- ❌ 在 api 里再 `import axios` 直接用(应该用 `http`)
|
|
326
|
+
- ❌ 多份 `http` 实例(分包拆 fetch 应该用一个 instance + 不同 baseURL config)
|
|
327
|
+
- ❌ 把 tenantId / regionId 放到每个 api 的 url query 里(应该走 header)
|
|
312
328
|
- ❌ 把 tenantId 当 React state 在每个组件里 prop drilling
|
|
313
329
|
- ❌ tenantId 切换后不清 query cache —— 要么 queryKey 含 tenantId(推荐),要么显式 `qc.clear()`
|
|
314
330
|
|
|
@@ -317,16 +333,16 @@ export function useTenant() {
|
|
|
317
333
|
## §4 · 类型分层
|
|
318
334
|
|
|
319
335
|
```
|
|
320
|
-
src/types
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
└── api.ts # 通用 ApiResponse<T>、Pagination<T>
|
|
336
|
+
src/features/instance/types.ts # Instance、InstanceStatus、InstanceListParams、CreateInstanceInput
|
|
337
|
+
src/features/tenant/types.ts # Tenant、TenantMeta、...
|
|
338
|
+
src/features/region/types.ts # Region、CloudProvider、...
|
|
339
|
+
src/types/api.ts # 通用 ApiResponse<T>、Pagination<T>(跨领域共享)
|
|
325
340
|
```
|
|
326
341
|
|
|
327
|
-
- **Domain
|
|
328
|
-
-
|
|
329
|
-
-
|
|
342
|
+
- **Domain 类型**(如 `Instance`)放 `src/features/<domain>/types.ts`,同领域内用相对路径 `./types` 引用
|
|
343
|
+
- 组件消费时用 `@/features/instance/types` 引入
|
|
344
|
+
- **跨领域通用类型**(如 `ApiResponse<T>`、`Pagination<T>`)放 `src/types/api.ts`
|
|
345
|
+
- **API 入参 / 出参类型**:与 domain 类型分开命名(`InstanceListParams` vs `Instance`),但放同一个 `types.ts`
|
|
330
346
|
|
|
331
347
|
---
|
|
332
348
|
|
|
@@ -334,12 +350,12 @@ src/types/
|
|
|
334
350
|
|
|
335
351
|
某些场景**允许**绕过本规范,但需要在代码注释里说明原因:
|
|
336
352
|
|
|
337
|
-
| 场景
|
|
338
|
-
|
|
|
339
|
-
| 静态资源 fetch
|
|
340
|
-
| 第三方 SDK(地图、支付)有自己的客户端
|
|
341
|
-
| 跨租户管理员视图(显式覆盖 header)
|
|
342
|
-
| 一次性的诊断 / 调试代码
|
|
353
|
+
| 场景 | 允许 |
|
|
354
|
+
| ---------------------------------------- | ------------------------------------------------- |
|
|
355
|
+
| 静态资源 fetch(读 `public/` 下的 json) | 组件直接 `fetch` 可,不用进 features |
|
|
356
|
+
| 第三方 SDK(地图、支付)有自己的客户端 | 包装层放 `src/lib/<sdk>.ts`,不强制走 features |
|
|
357
|
+
| 跨租户管理员视图(显式覆盖 header) | api 接 `contextOverride` 参数(见 §1 示例) |
|
|
358
|
+
| 一次性的诊断 / 调试代码 | 临时 fetch 可,但 PR 合并前必须清理或归位 |
|
|
343
359
|
|
|
344
360
|
---
|
|
345
361
|
|
|
@@ -362,9 +378,9 @@ src/types/
|
|
|
362
378
|
```
|
|
363
379
|
## 数据层改动
|
|
364
380
|
|
|
365
|
-
- src/
|
|
366
|
-
- src/
|
|
367
|
-
- src/hooks
|
|
381
|
+
- src/features/instance/types.ts: 新增 `InstanceListParams`、`InstanceStatus`
|
|
382
|
+
- src/features/instance/api.ts: 新增 `listInstances`、`releaseInstance`(纯函数,使用 `@/lib/http`,不显式拼 tenantId)
|
|
383
|
+
- src/features/instance/hooks.ts: 新增 `useInstanceList`(useQuery, key=['instances', tenantId, regionId, params])
|
|
368
384
|
- src/pages/instances/index.tsx: 仅消费 `useInstanceList`,不直接 fetch;URL 同步 tenantId / regionId
|
|
369
385
|
- src/lib/http.ts: 已有 interceptor 注入 X-Tenant-Id / X-Region-Id,无需修改
|
|
370
386
|
```
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
## 1. 复用判定 ✓
|
|
10
10
|
|
|
11
|
-
- [ ] 新建 UI 组件前,查过 `@teamix-evo/ui`
|
|
11
|
+
- [ ] 新建 UI 组件前,查过 `@teamix-evo/ui` 注册表(`.teamix-evo/meta/ui/manifest.json` + `<id>.md`)
|
|
12
12
|
- [ ] 新建 UI 组件前,查过 `biz-ui/uni-manager`(um-topbar 等)+ §2 概念占位拼装表
|
|
13
13
|
- [ ] 新建 UI 组件前,grep 过本项目 `src/components/`
|
|
14
|
-
- [ ] 新建工具函数前,grep 过 `src/utils/` `src/lib/` `src/
|
|
14
|
+
- [ ] 新建工具函数前,grep 过 `src/utils/` `src/lib/` `src/features/`
|
|
15
15
|
- [ ] 没有重复实现 ui 包已经提供的能力(Button、Input、Dialog、DataTable 等)
|
|
16
16
|
- [ ] 没有 fork ui 包源码改样式(改样式应当走 design token)
|
|
17
17
|
- [ ] **组件兜底铁律**:找不到现成实物时优先 ui 原子件组合,未自撸基础件
|
|
@@ -21,24 +21,24 @@
|
|
|
21
21
|
|
|
22
22
|
- [ ] 组件**没有**直接调 `fetch` / `axios`
|
|
23
23
|
- [ ] 组件**没有**直接读 `import.meta.env` / `process.env`
|
|
24
|
-
- [ ] 所有后端调用都落在 `src/
|
|
25
|
-
- [ ]
|
|
26
|
-
- [ ]
|
|
27
|
-
- [ ]
|
|
28
|
-
- [ ] 数据 hook 在 `src/hooks
|
|
24
|
+
- [ ] 所有后端调用都落在 `src/features/<domain>/api.ts`,且是纯函数
|
|
25
|
+
- [ ] API 函数**不依赖 React**(没有 `useXxx`、没有 `useState`)
|
|
26
|
+
- [ ] API 函数**不弹通知 / 不跳路由**
|
|
27
|
+
- [ ] API 函数**不显式拼 `tenantId` / `regionId` / `cloudProvider`** —— 走 interceptor 注入
|
|
28
|
+
- [ ] 数据 hook 在 `src/features/<domain>/hooks.ts`,使用统一的数据库(react-query / swr,不混用)
|
|
29
29
|
- [ ] 数据 hook 的 `queryKey` 含 `tenantId` / `regionId`(确保切换上下文时 cache 自动失效)
|
|
30
30
|
- [ ] 全局只有一份 http 实例,在 `src/lib/http.ts`
|
|
31
31
|
- [ ] `lib/http.ts` interceptor 已注入 `X-Tenant-Id` / `X-Region-Id` / `X-Cloud-Provider`
|
|
32
32
|
|
|
33
33
|
## 3. 目录归位 ✓
|
|
34
34
|
|
|
35
|
-
- [ ]
|
|
36
|
-
- [ ]
|
|
35
|
+
- [ ] 业务领域代码(api/hooks/types/mocks)放在 `src/features/<domain>/`
|
|
36
|
+
- [ ] 新文件位于约定的顶层目录(`pages/`、`components/`、`features/`、`stores/`、`utils/`、`lib/`、`contexts/`)
|
|
37
|
+
- [ ] **没有**新增同义层(如 `views/`、`services/`、`helpers/` 与现有目录并存)
|
|
37
38
|
- [ ] 仅在单个页面使用的组件 / hook 放在 `src/pages/<id>/_components/` 或 `_hooks/`(下划线前缀)
|
|
38
|
-
- [ ]
|
|
39
|
+
- [ ] 跨页面复用的组件升到 `src/components/`;跨领域共享的 hook 放 `src/hooks/`(极少数,如 useDebounce / useDangerConfirm)
|
|
39
40
|
- [ ] 跨云 / 跨租户专属组件放在 `src/components/cloud/`(CloudBadge / RegionBadge / ContextSwitcher)
|
|
40
41
|
- [ ] 危险操作组件放在 `src/components/danger/` 或 `src/hooks/useDangerConfirm.ts`
|
|
41
|
-
- [ ] React Hook 在 `hooks/`,不在 `utils/`
|
|
42
42
|
- [ ] 类型声明文件没有运行时代码(纯类型)
|
|
43
43
|
|
|
44
44
|
## 4. 命名 ✓
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
- [ ] 跨目录 import 走 `@/*` 别名
|
|
55
55
|
- [ ] **未发生反向依赖**:
|
|
56
56
|
- `components/` 没有 import `pages/`
|
|
57
|
-
- `
|
|
58
|
-
- `hooks
|
|
59
|
-
- `utils/` `lib/`
|
|
57
|
+
- `features/<domain>/api.ts` 没有 import `hooks` `components/` `pages/`
|
|
58
|
+
- `features/<domain>/hooks.ts` 没有 import `pages/`
|
|
59
|
+
- `utils/` `lib/` 没有 import 业务层
|
|
60
60
|
- [ ] 页面私有目录(`_components/` `_hooks/`)**没有**被其他页面 import
|
|
61
61
|
|
|
62
62
|
## 6. 类型 ✓
|
|
63
63
|
|
|
64
|
-
- [ ] Domain 类型放 `src/
|
|
65
|
-
- [ ]
|
|
64
|
+
- [ ] Domain 类型放 `src/features/<domain>/types.ts`,不在 api / component 文件就地定义
|
|
65
|
+
- [ ] API 函数有显式的入参 / 返回 `Promise<T>` 类型
|
|
66
66
|
- [ ] 没有 `any`
|
|
67
67
|
- [ ] Hook 的返回类型由 `react-query` / `swr` 自动推断
|
|
68
68
|
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
|
|
75
75
|
## 8. 副作用与可测性 ✓
|
|
76
76
|
|
|
77
|
-
- [ ]
|
|
77
|
+
- [ ] API 函数是纯函数(同样输入 → 同样输出,无隐式全局读写)
|
|
78
78
|
- [ ] 工具函数(`src/utils/`)是纯函数
|
|
79
79
|
- [ ] 没有在模块顶层(import 时)发请求 / 弹通知 / 改全局状态
|
|
80
80
|
- [ ] 异步操作有错误处理路径
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
(若改动涉及表单 — 详见 [`forms-and-validation.md`](forms-and-validation.md))
|
|
85
85
|
|
|
86
86
|
- [ ] 表单状态用 `react-hook-form`,**未用** `useState` 拼字段
|
|
87
|
-
- [ ] 校验用 `zod` schema,schema 落 `src/
|
|
87
|
+
- [ ] 校验用 `zod` schema,schema 落 `src/features/<domain>/schema.ts`
|
|
88
88
|
- [ ] 类型从 schema 推导(`z.infer<...>`)
|
|
89
89
|
- [ ] 错误信息写在 schema 里
|
|
90
90
|
- [ ] 提交走 mutation hook
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
|
|
124
124
|
(详见 [`testing.md`](testing.md))
|
|
125
125
|
|
|
126
|
-
- [ ]
|
|
126
|
+
- [ ] 新增纯函数(`src/utils/`、`src/features/*/api.ts`)有同名 `*.test.ts`(**必测**)
|
|
127
127
|
- [ ] 新增 zod schema 有 `parse` 成功 / 失败用例(**必测**)
|
|
128
128
|
- [ ] 关键业务路径(创建实例 / 切租户 / 删除资源)有组件 / hook 测试(**必测**)
|
|
129
129
|
- [ ] 测试文件就近放在 `<source>.test.ts(x)`
|
|
@@ -151,11 +151,11 @@
|
|
|
151
151
|
|
|
152
152
|
1. **组件直接 fetch / axios** —— 数据层未分层
|
|
153
153
|
2. **新建 Button / Input / Dialog 等基础组件** —— 重造 ui 包能力
|
|
154
|
-
3. **
|
|
155
|
-
4. **
|
|
156
|
-
5. **`src/views/` 与 `src/pages/`
|
|
154
|
+
3. **features/*/api.ts 函数依赖 React** —— 边界穿透
|
|
155
|
+
4. **api 函数显式拼 tenantId / regionId 在 url query** —— 应走 header interceptor
|
|
156
|
+
5. **`src/views/` 与 `src/pages/` 并存(或 `src/services/` 与 `src/features/`)** —— 同义层污染
|
|
157
157
|
6. **响应里没有复用决策日志** —— 流程未跑通
|
|
158
|
-
7. **反向依赖**(
|
|
158
|
+
7. **反向依赖**(api 引 hook、component 引 page)—— 依赖图破坏
|
|
159
159
|
8. **多份 http 实例** —— 鉴权 / 上下文 header 无法统一
|
|
160
160
|
9. **没有全局 ErrorBoundary** —— 异常直接白屏给用户
|
|
161
161
|
10. **表单用 `useState` 拼字段** —— 弃 react-hook-form + zod
|
|
@@ -177,15 +177,15 @@
|
|
|
177
177
|
## 编码合规自检(uni-manager)
|
|
178
178
|
|
|
179
179
|
- 复用判定: ✅ 复用 ui Button / DataTable + biz-ui/uni-manager um-topbar;🆕 新写 InstanceStatusBadge(业务状态映射)
|
|
180
|
-
- 数据层: ✅
|
|
181
|
-
- 目录: ✅ 页面在 pages/instances/;✅ 跨云组件在 components/cloud/CloudBadge.tsx
|
|
180
|
+
- 数据层: ✅ features/instance/api.ts 纯函数;✅ features/instance/hooks.ts queryKey 含 tenantId / regionId;✅ 组件未 fetch;✅ http.ts interceptor 已注入上下文
|
|
181
|
+
- 目录: ✅ 页面在 pages/instances/;✅ 领域代码在 features/instance/;✅ 跨云组件在 components/cloud/CloudBadge.tsx
|
|
182
182
|
- 命名: ✅ kebab 目录 / Pascal 组件 / camel hook
|
|
183
183
|
- 边界: ✅ 全部走 @/\*;✅ 无反向依赖
|
|
184
|
-
- 类型: ✅
|
|
185
|
-
-
|
|
184
|
+
- 类型: ✅ features/instance/types.ts 集中声明
|
|
185
|
+
- 表单(若涉及): ✅ useForm + zodResolver;✅ schema 在 features/instance/schema.ts;✅ 删除走 useDangerConfirm(输入名称)
|
|
186
186
|
- 错误/加载: ✅ 三态全处理;✅ ErrorBoundary 已在 App 根;✅ 跨云 404 fallback 已加
|
|
187
187
|
- 路由: ✅ React.lazy + Suspense;✅ tenantId/regionId 进 URL;✅ um-topbar 切换改写 URL
|
|
188
|
-
- 测试: ✅ instance.test.ts msw handler 验证 X-Tenant-Id;✅ schema parse 失败用例已加
|
|
188
|
+
- 测试: ✅ features/instance/api.test.ts msw handler 验证 X-Tenant-Id;✅ schema parse 失败用例已加
|
|
189
189
|
- design 协作: ✅ UM1/UM2/UM3 一致性三件套已自检
|
|
190
190
|
- 红线: ✅ 全过(含 uni-manager UM 4 条红线)
|
|
191
191
|
|
|
@@ -352,9 +352,9 @@ function InstanceDetailPage() {
|
|
|
352
352
|
| `alert('保存失败')` | 阻断用户、丑、不可样式化 | `toast.error()` |
|
|
353
353
|
| ErrorBoundary 不上报 | 错过线上 bug | `onError` 接 `reportError` |
|
|
354
354
|
| reportError 不带 tenant/region 上下文 | 排障无法定位 | `monitor.ts` 自动注入 active context |
|
|
355
|
-
|
|
|
355
|
+
| api 里 `try/catch` 吞错 | 上层失去判断依据 | 让 error 抛上来,hook / boundary 决定 |
|
|
356
356
|
| 跨云资源 404 用通用 ErrorPanel 兜底 | 用户看不出是上下文问题 | 用 `CloudResourceNotFound` + cloud/region badge |
|
|
357
|
-
| 跨云聚合查询全或全无 | 一个 region 失败,其他可用结果丢失 |
|
|
357
|
+
| 跨云聚合查询全或全无 | 一个 region 失败,其他可用结果丢失 | api 层返结构化结果,UI 显示部分成功 + 失败提示 |
|
|
358
358
|
|
|
359
359
|
---
|
|
360
360
|
|