mta-mcp 1.0.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 (75) hide show
  1. package/README.md +818 -0
  2. package/agents/_TEMPLATE.md +153 -0
  3. package/agents/flutter.agent.md +222 -0
  4. package/agents/i18n.agent.md +78 -0
  5. package/agents/logicflow.agent.md +97 -0
  6. package/agents/vue3.agent.md +176 -0
  7. package/agents/wechat-miniprogram.agent.md +89 -0
  8. package/bin/mta.cjs +132 -0
  9. package/common/i18n.md +385 -0
  10. package/common/typescript-strict.md +186 -0
  11. package/dist/index.d.ts +64 -0
  12. package/dist/index.js +6493 -0
  13. package/dist/index.js.map +1 -0
  14. package/package.json +81 -0
  15. package/standards/README.md +194 -0
  16. package/standards/core/code-generation.md +421 -0
  17. package/standards/core/code-style.md +308 -0
  18. package/standards/core/dart-base.md +572 -0
  19. package/standards/core/mandatory-rules.md +103 -0
  20. package/standards/core/typescript-base.md +179 -0
  21. package/standards/frameworks/flutter-ui-system.md +497 -0
  22. package/standards/frameworks/flutter.md +1268 -0
  23. package/standards/frameworks/pinia.md +172 -0
  24. package/standards/frameworks/vue3-composition.md +779 -0
  25. package/standards/frameworks/wechat-miniprogram.md +2177 -0
  26. package/standards/libraries/element-plus.md +1128 -0
  27. package/standards/libraries/i18n.md +360 -0
  28. package/standards/libraries/logicflow.md +1007 -0
  29. package/standards/patterns/api-layer.md +187 -0
  30. package/standards/patterns/component-design.md +200 -0
  31. package/standards/patterns/design-system-restoration.md +570 -0
  32. package/standards/patterns/vue-api-mock-layer.md +958 -0
  33. package/standards/patterns/vue-css-nesting.md +604 -0
  34. package/standards/troubleshooting-cases/flutter/textfield-vertical-centering.md +107 -0
  35. package/standards/workflows/design-restoration-guide.md +164 -0
  36. package/standards/workflows/large-project-split.md +359 -0
  37. package/standards/workflows/problem-diagnosis.md +280 -0
  38. package/standards/workflows/textfield-centering-guide.md +157 -0
  39. package/templates/README.md +144 -0
  40. package/templates/common/types/_CONFIG.md +12 -0
  41. package/templates/common/types/api.ts +39 -0
  42. package/templates/common/types/common.ts +70 -0
  43. package/templates/config-templates/agents-section.md +9 -0
  44. package/templates/config-templates/custom-section.md +6 -0
  45. package/templates/config-templates/header.md +29 -0
  46. package/templates/config-templates/workflow-minimal.md +44 -0
  47. package/templates/copilot-instructions-mcp-optimized.md +158 -0
  48. package/templates/vue/api-layer/_CONFIG.md +145 -0
  49. package/templates/vue/api-layer/index.ts +58 -0
  50. package/templates/vue/api-layer/mock/index.ts +122 -0
  51. package/templates/vue/api-layer/modules/_template.ts +109 -0
  52. package/templates/vue/api-layer/modules/index.ts +16 -0
  53. package/templates/vue/api-layer/request.ts +279 -0
  54. package/templates/vue/api-layer/types.ts +80 -0
  55. package/troubleshooting/README.md +368 -0
  56. package/troubleshooting/USAGE_GUIDE.md +289 -0
  57. package/troubleshooting/flutter/clip-/351/230/264/345/275/261/350/243/201/345/211/252.md +244 -0
  58. package/troubleshooting/flutter/component-/351/200/232/347/224/250/345/214/226/346/217/220/345/217/226.md +269 -0
  59. package/troubleshooting/flutter/input-/345/255/227/346/256/265/347/274/272/345/244/261.md +240 -0
  60. package/troubleshooting/flutter/input-/350/276/271/346/241/206/351/227/256/351/242/230.md +236 -0
  61. package/troubleshooting/flutter/layout-/345/260/272/345/257/270/344/270/215/345/214/271/351/205/215.md +214 -0
  62. package/troubleshooting/flutter/shadow-/351/200/217/345/207/272/351/227/256/351/242/230.md +172 -0
  63. package/troubleshooting/flutter/sketch-/345/210/227/350/241/250item/345/214/272/345/237/237.md +212 -0
  64. package/troubleshooting/flutter/sketch-/345/233/276/346/240/207/345/260/272/345/257/270.md +135 -0
  65. package/troubleshooting/flutter/sketch-/345/256/214/346/225/264/346/217/220/345/217/226.md +201 -0
  66. package/troubleshooting/flutter/sketch-/345/261/236/346/200/247/346/234/252/344/275/277/347/224/250.md +139 -0
  67. package/troubleshooting/flutter/sketch-/350/203/214/346/231/257/345/261/202/351/253/230/345/272/246.md +264 -0
  68. package/troubleshooting/flutter/svg-/346/234/252/345/261/205/344/270/255.md +120 -0
  69. package/troubleshooting/flutter/svg-/351/242/234/350/211/262/345/274/202/345/270/270.md +117 -0
  70. package/troubleshooting/flutter/tabbar-/345/212/250/347/224/273/345/220/214/346/255/245.md +107 -0
  71. package/troubleshooting/flutter/withopacity-/345/274/203/347/224/250.md +81 -0
  72. package/troubleshooting/vue3/cascader-/350/257/257/346/233/277/346/215/242.md +130 -0
  73. package/troubleshooting/vue3/drawer-input-/346/240/267/345/274/217.md +181 -0
  74. package/troubleshooting/vue3/table-/347/274/226/350/276/221/345/217/226/346/266/210.md +148 -0
  75. package/troubleshooting/vue3/table-/350/276/271/346/241/206/351/227/256/351/242/230.md +178 -0
@@ -0,0 +1,958 @@
1
+ # Vue API + Mock 层封装模式
2
+
3
+ > 适用场景:Vue 3 + TypeScript 项目(兼容 Element Plus / Ant Design Vue / Naive UI 等)
4
+ > 核心特性:API 集中管理、Mock 无缝切换、wrap 错误处理、文件上传下载
5
+ > 设计原则:**灵活可扩展**,模块按需添加,不限定具体业务
6
+
7
+ ---
8
+
9
+ ## 一、目录结构
10
+
11
+ ```
12
+ src/api/
13
+ ├── index.ts # 统一导出入口
14
+ ├── request.ts # Axios 核心封装(必需)
15
+ ├── mock/ # Mock 系统(可选)
16
+ │ ├── index.ts # Mock 开关与工具函数
17
+ │ └── [module].ts # 按业务模块创建,如 user.ts, order.ts
18
+ └── modules/ # 业务 API 模块
19
+ ├── index.ts # 模块统一导出
20
+ └── [module].ts # 按业务模块创建,如 user.ts, order.ts
21
+ ```
22
+
23
+ **模块命名建议**(根据项目实际需求选择):
24
+
25
+ | 通用模块 | 电商类 | 管理系统 | 社交类 |
26
+ |---------|--------|----------|--------|
27
+ | `user.ts` | `product.ts` | `auth.ts` | `post.ts` |
28
+ | `common.ts` | `order.ts` | `permission.ts` | `comment.ts` |
29
+ | `upload.ts` | `cart.ts` | `log.ts` | `message.ts` |
30
+
31
+ ---
32
+
33
+ ## 二、核心文件模板
34
+
35
+ ### 2.1 request.ts - Axios 核心封装
36
+
37
+ ```typescript
38
+ /**
39
+ * Axios 请求封装
40
+ * 集成 Mock 系统、错误处理、Token 管理
41
+ *
42
+ * 💡 自定义点:
43
+ * - API_TIMEOUT: 超时时间
44
+ * - NO_TOKEN_URLS: 无需 Token 的白名单
45
+ * - TOKEN_KEY/USER_KEY: 存储键名
46
+ * - ERROR_CODE_MAP: 错误码映射
47
+ * - 响应拦截器中的业务状态码判断
48
+ */
49
+ import axios from 'axios'
50
+ import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
51
+ // import { ElMessage } from 'element-plus' // Element Plus
52
+ // import { message } from 'ant-design-vue' // Ant Design Vue
53
+ // import { useMessage } from 'naive-ui' // Naive UI
54
+ import router from '@/router'
55
+ import type { ApiResponse } from '@/types/api'
56
+
57
+ // ========== 常量配置(按项目调整)==========
58
+
59
+ const API_TIMEOUT = 30000
60
+
61
+ // 无需 Token 的接口白名单(按项目调整)
62
+ const NO_TOKEN_URLS = [
63
+ '/login',
64
+ '/register',
65
+ '/sms-code',
66
+ '/captcha',
67
+ ]
68
+
69
+ // ========== Token 管理(按项目调整键名)==========
70
+
71
+ const TOKEN_KEY = 'app_token'
72
+ const USER_KEY = 'app_user'
73
+
74
+ export const getToken = (): string | null => localStorage.getItem(TOKEN_KEY)
75
+ export const setToken = (token: string): void => localStorage.setItem(TOKEN_KEY, token)
76
+ export const clearToken = (): void => {
77
+ localStorage.removeItem(TOKEN_KEY)
78
+ localStorage.removeItem(USER_KEY)
79
+ }
80
+
81
+ export const getUserInfo = <T = any>(): T | null => {
82
+ const userStr = localStorage.getItem(USER_KEY)
83
+ if (!userStr) return null
84
+ try {
85
+ return JSON.parse(userStr)
86
+ } catch {
87
+ return null
88
+ }
89
+ }
90
+
91
+ export const setUserInfo = (user: any): void => {
92
+ localStorage.setItem(USER_KEY, JSON.stringify(user))
93
+ }
94
+
95
+ // ========== 消息提示(按 UI 框架调整)==========
96
+
97
+ // 封装消息提示,便于切换 UI 框架
98
+ const showMessage = {
99
+ success: (msg: string) => console.log('✅', msg), // 替换为 UI 框架方法
100
+ error: (msg: string) => console.error('❌', msg),
101
+ warning: (msg: string) => console.warn('⚠️', msg),
102
+ }
103
+
104
+ // Element Plus 示例:
105
+ // import { ElMessage } from 'element-plus'
106
+ // const showMessage = {
107
+ // success: (msg: string) => ElMessage.success(msg),
108
+ // error: (msg: string) => ElMessage.error(msg),
109
+ // warning: (msg: string) => ElMessage.warning(msg),
110
+ // }
111
+
112
+ // ========== 错误处理 ==========
113
+
114
+ const ERROR_CODE_MAP: Record<number, string> = {
115
+ 400: '请求参数错误',
116
+ 401: '登录已过期,请重新登录',
117
+ 403: '没有操作权限',
118
+ 404: '请求的资源不存在',
119
+ 500: '服务器内部错误',
120
+ 502: '网关错误',
121
+ 503: '服务暂不可用',
122
+ 504: '网关超时',
123
+ }
124
+
125
+ const handleRequestError = (error: any): Promise<never> => {
126
+ console.error('请求错误:', error)
127
+
128
+ if (error.code === 'ERR_NETWORK') {
129
+ showMessage.error('网络连接失败,请检查网络设置')
130
+ } else if (error.code === 'ECONNABORTED') {
131
+ showMessage.error('请求超时,请稍后重试')
132
+ }
133
+
134
+ return Promise.reject(error)
135
+ }
136
+
137
+ const handleResponseError = (error: any): Promise<never> => {
138
+ const status = error?.response?.status
139
+ const data = error?.response?.data
140
+ const message = data?.message || ERROR_CODE_MAP[status] || '请求失败,请稍后重试'
141
+
142
+ if (status === 401) {
143
+ clearToken()
144
+ showMessage.warning(message)
145
+ router.push({ name: 'login' }) // 按项目调整登录路由
146
+ return Promise.reject(error)
147
+ }
148
+
149
+ showMessage.error(message)
150
+ return Promise.reject(error)
151
+ }
152
+
153
+ // ========== Axios 实例 ==========
154
+
155
+ const instance: AxiosInstance = axios.create({
156
+ baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
157
+ timeout: API_TIMEOUT,
158
+ headers: { 'Content-Type': 'application/json' },
159
+ })
160
+
161
+ // 请求拦截器
162
+ instance.interceptors.request.use(
163
+ (config: InternalAxiosRequestConfig) => {
164
+ const needToken = !NO_TOKEN_URLS.some(url => config.url?.includes(url))
165
+
166
+ if (needToken) {
167
+ const token = getToken()
168
+ if (token) {
169
+ // 按后端要求调整 Token 传递方式
170
+ config.headers.Authorization = `Bearer ${token}`
171
+ // config.headers.token = token // 备选方式
172
+ }
173
+ }
174
+
175
+ return config
176
+ },
177
+ handleRequestError
178
+ )
179
+
180
+ // 响应拦截器(按后端响应结构调整)
181
+ instance.interceptors.response.use(
182
+ (response: AxiosResponse) => {
183
+ const res = response.data
184
+
185
+ // 文件流直接返回
186
+ if (response.config.responseType === 'blob') {
187
+ return res
188
+ }
189
+
190
+ // 业务状态码判断(按后端约定调整)
191
+ // 常见格式:{ code: 0, data: {}, message: '' }
192
+ // 或:{ code: 200, data: {}, msg: '' }
193
+ // 或:{ success: true, data: {}, message: '' }
194
+ if (res.code === 0 || res.code === 200 || res.success === true) {
195
+ return res
196
+ }
197
+
198
+ // 401 Token 过期
199
+ if (res.code === 401) {
200
+ clearToken()
201
+ showMessage.warning(res.message || '登录已过期')
202
+ router.push({ name: 'login' })
203
+ return Promise.reject(new Error(res.message))
204
+ }
205
+
206
+ // 其他业务错误
207
+ showMessage.error(res.message || res.msg || '请求失败')
208
+ return Promise.reject(new Error(res.message || res.msg))
209
+ },
210
+ handleResponseError
211
+ )
212
+
213
+ // ========== 请求方法 ==========
214
+
215
+ export async function get<T = any>(
216
+ url: string,
217
+ params?: Record<string, any>,
218
+ config?: AxiosRequestConfig
219
+ ): Promise<ApiResponse<T>> {
220
+ return instance.get(url, { params, ...config })
221
+ }
222
+
223
+ export async function post<T = any>(
224
+ url: string,
225
+ data?: any,
226
+ config?: AxiosRequestConfig
227
+ ): Promise<ApiResponse<T>> {
228
+ return instance.post(url, data, config)
229
+ }
230
+
231
+ export async function put<T = any>(
232
+ url: string,
233
+ data?: any,
234
+ config?: AxiosRequestConfig
235
+ ): Promise<ApiResponse<T>> {
236
+ return instance.put(url, data, config)
237
+ }
238
+
239
+ export async function del<T = any>(
240
+ url: string,
241
+ params?: Record<string, any>,
242
+ config?: AxiosRequestConfig
243
+ ): Promise<ApiResponse<T>> {
244
+ return instance.delete(url, { params, ...config })
245
+ }
246
+
247
+ // ========== 工具函数 ==========
248
+
249
+ /**
250
+ * 包装异步请求,返回 [data, error] 元组
251
+ * 借鉴 Go 语言错误处理风格,避免 try/catch
252
+ *
253
+ * @example
254
+ * const [res, err] = await wrap(api.getList(params))
255
+ * if (err) {
256
+ * console.error('请求失败:', err)
257
+ * return
258
+ * }
259
+ * // 使用 res.data
260
+ */
261
+ export async function wrap<T>(
262
+ promise: Promise<T>
263
+ ): Promise<[T, null] | [null, Error]> {
264
+ try {
265
+ const data = await promise
266
+ return [data, null]
267
+ } catch (error) {
268
+ return [null, error as Error]
269
+ }
270
+ }
271
+
272
+ /**
273
+ * 下载文件
274
+ */
275
+ export async function downloadFile(
276
+ url: string,
277
+ params?: Record<string, any>,
278
+ filename?: string
279
+ ): Promise<void> {
280
+ const response = await instance.get(url, {
281
+ params,
282
+ responseType: 'blob',
283
+ })
284
+
285
+ const blob = new Blob([response as any])
286
+ const link = document.createElement('a')
287
+ link.href = URL.createObjectURL(blob)
288
+ link.download = filename || 'download'
289
+ link.click()
290
+ URL.revokeObjectURL(link.href)
291
+ }
292
+
293
+ /**
294
+ * 上传文件
295
+ */
296
+ export async function uploadFile<T = any>(
297
+ url: string,
298
+ file: File,
299
+ fieldName = 'file',
300
+ extraData?: Record<string, any>
301
+ ): Promise<ApiResponse<T>> {
302
+ const formData = new FormData()
303
+ formData.append(fieldName, file)
304
+
305
+ if (extraData) {
306
+ Object.entries(extraData).forEach(([key, value]) => {
307
+ formData.append(key, value)
308
+ })
309
+ }
310
+
311
+ return instance.post(url, formData, {
312
+ headers: { 'Content-Type': 'multipart/form-data' },
313
+ })
314
+ }
315
+
316
+ export default instance
317
+ ```
318
+
319
+ ### 2.2 mock/index.ts - Mock 系统核心
320
+
321
+ ```typescript
322
+ // Mock 系统入口
323
+ import type { ApiResponse, PageData } from '@/types/common'
324
+
325
+ export const MOCK_ENABLED = import.meta.env.VITE_MOCK_ENABLED === 'true'
326
+ export const MOCK_DELAY = 300
327
+
328
+ export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
329
+ export const delay = sleep
330
+
331
+ /**
332
+ * 生成分页响应数据
333
+ */
334
+ export function generatePageResponse<T>(
335
+ list: T[],
336
+ page: number,
337
+ pageSize: number,
338
+ total?: number
339
+ ): ApiResponse<PageData<T>> {
340
+ const start = (page - 1) * pageSize
341
+ const end = start + pageSize
342
+ const data = list.slice(start, end)
343
+ return {
344
+ code: 0,
345
+ data: { list: data, total: total ?? list.length, page, pageSize },
346
+ message: 'success',
347
+ }
348
+ }
349
+
350
+ /**
351
+ * 生成成功响应
352
+ */
353
+ export function successResponse<T>(data: T): ApiResponse<T> {
354
+ return { code: 0, data, message: 'success' }
355
+ }
356
+
357
+ /**
358
+ * 生成错误响应
359
+ */
360
+ export function errorResponse(message: string, code = -1) {
361
+ return { code, data: null, message }
362
+ }
363
+
364
+ /**
365
+ * 生成随机 ID
366
+ */
367
+ export function randomId(): string {
368
+ return Date.now().toString(36) + Math.random().toString(36).substr(2, 9)
369
+ }
370
+
371
+ /**
372
+ * 生成随机日期(近 n 天内)
373
+ */
374
+ export function randomDate(days = 30): string {
375
+ const date = new Date()
376
+ date.setDate(date.getDate() - Math.floor(Math.random() * days))
377
+ return date.toISOString().split('T')[0] + ' ' + date.toTimeString().split(' ')[0]
378
+ }
379
+
380
+ /**
381
+ * 从数组中随机选择
382
+ */
383
+ export function randomPick<T>(arr: T[]): T {
384
+ return arr[Math.floor(Math.random() * arr.length)]
385
+ }
386
+
387
+ /**
388
+ * 生成随机金额
389
+ */
390
+ export function randomAmount(min = 100, max = 10000): number {
391
+ return Number((Math.random() * (max - min) + min).toFixed(2))
392
+ }
393
+
394
+ // 导出各模块 Mock
395
+ export * from './auth'
396
+ // export * from './customer'
397
+ // export * from './finance'
398
+ // export * from './system'
399
+ // export * from './transaction'
400
+ ```
401
+
402
+ ### 2.3 API 模块示例 - modules/[module].ts
403
+
404
+ ```typescript
405
+ /**
406
+ * [模块名] API
407
+ *
408
+ * 💡 创建新模块时复制此模板,替换:
409
+ * - 模块名称和描述
410
+ * - URLs 对象中的接口地址
411
+ * - 类型定义
412
+ * - 具体的 API 函数
413
+ */
414
+ import { post, get, put, del } from '../request'
415
+ import { MOCK_ENABLED } from '../mock'
416
+ // import * as mockModule from '../mock/[module]' // 对应的 Mock
417
+ import type { ApiResponse, PageResponse, PageParams } from '@/types/api'
418
+
419
+ // ========== 接口地址(按实际后端调整)==========
420
+
421
+ const URLs = {
422
+ list: '/xxx/list',
423
+ detail: '/xxx/detail',
424
+ create: '/xxx/create',
425
+ update: '/xxx/update',
426
+ delete: '/xxx/delete',
427
+ }
428
+
429
+ // ========== 类型定义(按实际业务调整)==========
430
+
431
+ export interface ItemData {
432
+ id: string
433
+ name: string
434
+ status: string
435
+ createdAt: string
436
+ // ... 其他字段
437
+ }
438
+
439
+ export interface CreateParams {
440
+ name: string
441
+ // ... 其他参数
442
+ }
443
+
444
+ export interface ListParams extends PageParams {
445
+ keyword?: string
446
+ status?: string
447
+ // ... 其他筛选条件
448
+ }
449
+
450
+ // ========== 接口实现 ==========
451
+
452
+ /** 获取列表 */
453
+ export async function getList(params: ListParams): Promise<PageResponse<ItemData>> {
454
+ // if (MOCK_ENABLED) return mockModule.mockGetList(params)
455
+ return get(URLs.list, params)
456
+ }
457
+
458
+ /** 获取详情 */
459
+ export async function getDetail(id: string): Promise<ApiResponse<ItemData>> {
460
+ // if (MOCK_ENABLED) return mockModule.mockGetDetail(id)
461
+ return get(URLs.detail, { id })
462
+ }
463
+
464
+ /** 创建 */
465
+ export async function create(data: CreateParams): Promise<ApiResponse<ItemData>> {
466
+ // if (MOCK_ENABLED) return mockModule.mockCreate(data)
467
+ return post(URLs.create, data)
468
+ }
469
+
470
+ /** 更新 */
471
+ export async function update(id: string, data: Partial<CreateParams>): Promise<ApiResponse<ItemData>> {
472
+ // if (MOCK_ENABLED) return mockModule.mockUpdate(id, data)
473
+ return put(`${URLs.update}/${id}`, data)
474
+ }
475
+
476
+ /** 删除 */
477
+ export async function remove(id: string): Promise<ApiResponse<null>> {
478
+ // if (MOCK_ENABLED) return mockModule.mockDelete(id)
479
+ return del(URLs.delete, { id })
480
+ }
481
+ ```
482
+
483
+ ### 2.4 modules/index.ts - 统一导出
484
+
485
+ ```typescript
486
+ /**
487
+ * API 模块统一导出
488
+ *
489
+ * 💡 添加新模块时:
490
+ * 1. 创建 modules/[module].ts
491
+ * 2. 在此处添加导入和导出
492
+ */
493
+
494
+ // 示例:导出各业务模块
495
+ // export * as userApi from './user'
496
+ // export * as orderApi from './order'
497
+ // export * as productApi from './product'
498
+
499
+ // 按项目实际模块调整...
500
+ ```
501
+
502
+ ### 2.5 index.ts - API 统一入口
503
+
504
+ ```typescript
505
+ /**
506
+ * API 统一入口
507
+ *
508
+ * 使用方式:
509
+ *
510
+ * 1. 直接导入模块使用
511
+ * import { userApi } from '@/api'
512
+ * const res = await userApi.getList(params)
513
+ *
514
+ * 2. 使用 wrap 函数处理错误(推荐)
515
+ * import { wrap, userApi } from '@/api'
516
+ * const [res, err] = await wrap(userApi.getList(params))
517
+ * if (err) return
518
+ *
519
+ * 3. 直接使用请求方法
520
+ * import { get, post } from '@/api'
521
+ * const res = await post('/custom/url', data)
522
+ */
523
+
524
+ // 导出基础请求方法
525
+ export { get, post, put, del, wrap, downloadFile, uploadFile } from './request'
526
+ export { default as axios } from './request'
527
+
528
+ // 导出 Token 管理方法
529
+ export { getToken, setToken, clearToken, getUserInfo, setUserInfo } from './request'
530
+
531
+ // 导出所有 API 模块
532
+ export * from './modules'
533
+
534
+ // 导出 Mock 配置
535
+ export { MOCK_ENABLED } from './mock'
536
+ ```
537
+
538
+ ---
539
+
540
+ ## 三、类型定义模板
541
+
542
+ ### 3.1 types/api.ts
543
+
544
+ ```typescript
545
+ /**
546
+ * API 通用类型定义
547
+ *
548
+ * 💡 按后端实际响应结构调整字段名
549
+ */
550
+
551
+ /**
552
+ * API 响应基础结构
553
+ * 常见格式:
554
+ * - { code: 0, data: T, message: '' }
555
+ * - { code: 200, data: T, msg: '' }
556
+ * - { success: true, data: T, message: '' }
557
+ */
558
+ export interface ApiResponse<T = any> {
559
+ code: number
560
+ data: T
561
+ message: string
562
+ // msg?: string // 备选字段名
563
+ // success?: boolean // 备选判断字段
564
+ }
565
+
566
+ /**
567
+ * 分页请求参数
568
+ * 常见字段名:page/pageNum/current, pageSize/size/limit
569
+ */
570
+ export interface PageParams {
571
+ page: number
572
+ pageSize: number
573
+ // pageNum?: number // 备选字段名
574
+ // current?: number // 备选字段名
575
+ // size?: number // 备选字段名
576
+ }
577
+
578
+ /**
579
+ * 分页响应数据
580
+ * 常见字段名:list/records/items, total/totalCount
581
+ */
582
+ export interface PageData<T> {
583
+ list: T[]
584
+ total: number
585
+ page: number
586
+ pageSize: number
587
+ // records?: T[] // 备选字段名
588
+ // items?: T[] // 备选字段名
589
+ }
590
+
591
+ /**
592
+ * 分页响应(完整)
593
+ */
594
+ export type PageResponse<T> = ApiResponse<PageData<T>>
595
+
596
+ /**
597
+ * 通用 ID 类型(按项目调整)
598
+ */
599
+ export type ID = string | number
600
+
601
+ /**
602
+ * 通用状态枚举示例
603
+ */
604
+ export type Status = 'active' | 'inactive' | 'pending' | 'deleted'
605
+ ```
606
+
607
+ ---
608
+
609
+ ## 四、环境变量配置
610
+
611
+ ### .env.development
612
+
613
+ ```bash
614
+ VITE_API_BASE_URL=/api
615
+ VITE_MOCK_ENABLED=true
616
+ ```
617
+
618
+ ### .env.production
619
+
620
+ ```bash
621
+ VITE_API_BASE_URL=https://api.example.com
622
+ VITE_MOCK_ENABLED=false
623
+ ```
624
+
625
+ ---
626
+
627
+ ## 五、使用示例
628
+
629
+ ### 5.1 基础调用
630
+
631
+ ```typescript
632
+ import { userApi } from '@/api'
633
+
634
+ const handleSubmit = async () => {
635
+ try {
636
+ const res = await userApi.create(formData)
637
+ if (res.code === 0) {
638
+ // 成功处理
639
+ }
640
+ } catch (err) {
641
+ console.error(err)
642
+ }
643
+ }
644
+ ```
645
+
646
+ ### 5.2 使用 wrap 函数(推荐)
647
+
648
+ ```typescript
649
+ import { wrap, userApi } from '@/api'
650
+
651
+ const handleSubmit = async () => {
652
+ const [res, err] = await wrap(userApi.create(formData))
653
+
654
+ if (err) {
655
+ console.error('操作失败:', err)
656
+ return
657
+ }
658
+
659
+ // 成功处理
660
+ router.push('/list')
661
+ }
662
+ ```
663
+
664
+ ### 5.3 列表页面通用模式
665
+
666
+ ```typescript
667
+ import { wrap } from '@/api'
668
+ import { xxxApi } from '@/api' // 替换为实际模块
669
+
670
+ // 状态
671
+ const loading = ref(false)
672
+ const list = ref<ItemData[]>([])
673
+ const total = ref(0)
674
+ const currentPage = ref(1)
675
+ const pageSize = ref(10)
676
+ const searchParams = ref({})
677
+
678
+ // 获取列表
679
+ const fetchList = async () => {
680
+ loading.value = true
681
+
682
+ const [res, err] = await wrap(xxxApi.getList({
683
+ page: currentPage.value,
684
+ pageSize: pageSize.value,
685
+ ...searchParams.value
686
+ }))
687
+
688
+ loading.value = false
689
+
690
+ if (err) return
691
+
692
+ list.value = res.data.list
693
+ total.value = res.data.total
694
+ }
695
+
696
+ // 删除
697
+ const handleDelete = async (id: string) => {
698
+ const [, err] = await wrap(xxxApi.remove(id))
699
+ if (err) return
700
+
701
+ // 刷新列表
702
+ fetchList()
703
+ }
704
+ ```
705
+
706
+ ---
707
+
708
+ ## 六、Mock 数据编写规范
709
+
710
+ ### 6.1 基本结构
711
+
712
+ ```typescript
713
+ // mock/[module].ts - Mock 模块模板
714
+ import { successResponse, errorResponse, generatePageResponse, randomId, delay } from './index'
715
+ import type { PageParams } from '@/types/api'
716
+
717
+ // 模拟数据类型
718
+ interface MockItem {
719
+ id: string
720
+ name: string
721
+ status: string
722
+ createdAt: string
723
+ }
724
+
725
+ // 数据缓存
726
+ let dataCache: MockItem[] | null = null
727
+
728
+ // 生成单条数据
729
+ function generateItem(index: number): MockItem {
730
+ return {
731
+ id: randomId(),
732
+ name: `项目${index}`,
733
+ status: randomPick(['active', 'inactive']),
734
+ createdAt: randomDate(30)
735
+ }
736
+ }
737
+
738
+ // 获取数据(带缓存)
739
+ function getData(): MockItem[] {
740
+ if (!dataCache) {
741
+ dataCache = Array.from({ length: 50 }, (_, i) => generateItem(i + 1))
742
+ }
743
+ return dataCache
744
+ }
745
+
746
+ /** 模拟获取列表 */
747
+ export async function mockGetList(params: PageParams & { keyword?: string }) {
748
+ await delay(300)
749
+
750
+ let data = getData()
751
+
752
+ // 筛选(按实际需求调整)
753
+ if (params.keyword) {
754
+ data = data.filter(item => item.name.includes(params.keyword!))
755
+ }
756
+
757
+ return generatePageResponse(data, params.page || 1, params.pageSize || 10)
758
+ }
759
+
760
+ /** 模拟获取详情 */
761
+ export async function mockGetDetail(id: string) {
762
+ await delay(200)
763
+
764
+ const item = getData().find(d => d.id === id)
765
+ if (!item) return errorResponse('数据不存在')
766
+
767
+ return successResponse(item)
768
+ }
769
+
770
+ /** 模拟创建 */
771
+ export async function mockCreate(data: Partial<MockItem>) {
772
+ await delay(300)
773
+
774
+ const newItem = { ...generateItem(getData().length + 1), ...data }
775
+ getData().unshift(newItem)
776
+
777
+ return successResponse(newItem)
778
+ }
779
+
780
+ /** 模拟更新 */
781
+ export async function mockUpdate(id: string, data: Partial<MockItem>) {
782
+ await delay(300)
783
+
784
+ const list = getData()
785
+ const index = list.findIndex(d => d.id === id)
786
+ if (index === -1) return errorResponse('数据不存在')
787
+
788
+ list[index] = { ...list[index], ...data }
789
+ return successResponse(list[index])
790
+ }
791
+
792
+ /** 模拟删除 */
793
+ export async function mockDelete(id: string) {
794
+ await delay(200)
795
+
796
+ const list = getData()
797
+ const index = list.findIndex(d => d.id === id)
798
+ if (index === -1) return errorResponse('数据不存在')
799
+
800
+ list.splice(index, 1)
801
+ return successResponse(null)
802
+ }
803
+ ```
804
+
805
+ ### 6.2 登录认证 Mock 示例
806
+
807
+ ```typescript
808
+ // mock/auth.ts
809
+ import { successResponse, errorResponse, randomId, delay } from './index'
810
+
811
+ // 模拟用户(按项目调整)
812
+ const mockUsers: Record<string, { password: string; user: any }> = {
813
+ admin: {
814
+ password: '123456',
815
+ user: { id: '1', username: 'admin', name: '管理员', roles: ['admin'] }
816
+ },
817
+ user: {
818
+ password: '123456',
819
+ user: { id: '2', username: 'user', name: '普通用户', roles: ['user'] }
820
+ }
821
+ }
822
+
823
+ const tokenStore = new Map<string, any>()
824
+
825
+ export async function mockLogin(username: string, password: string) {
826
+ await delay(500)
827
+
828
+ const userData = mockUsers[username]
829
+ if (!userData || userData.password !== password) {
830
+ return errorResponse('用户名或密码错误')
831
+ }
832
+
833
+ const token = `mock_${randomId()}`
834
+ tokenStore.set(token, userData.user)
835
+
836
+ return successResponse({ token, user: userData.user })
837
+ }
838
+
839
+ export async function mockGetUserInfo(token: string) {
840
+ await delay(200)
841
+
842
+ const user = tokenStore.get(token)
843
+ if (!user) return errorResponse('Token 无效', 401)
844
+
845
+ return successResponse(user)
846
+ }
847
+ ```
848
+
849
+ ---
850
+
851
+ ## 七、最佳实践
852
+
853
+ ### 7.1 API 模块组织
854
+
855
+ - 每个业务模块一个文件,按实际需求命名
856
+ - URLs 集中定义在文件顶部
857
+ - 类型定义紧跟 URLs
858
+ - Mock 判断在函数内部
859
+
860
+ ### 7.2 错误处理
861
+
862
+ - 使用 `wrap` 函数统一处理
863
+ - 避免重复的 try/catch
864
+ - 错误信息由拦截器统一展示
865
+
866
+ ### 7.3 Mock 开发
867
+
868
+ - Mock 函数与真实 API 返回结构一致
869
+ - 使用缓存避免重复生成数据
870
+ - 支持基本的筛选和分页
871
+ - 登录 Mock 要模拟 Token 存储
872
+
873
+ ### 7.4 类型安全
874
+
875
+ - 所有 API 函数都有返回类型
876
+ - 参数使用 interface 定义
877
+ - 避免使用 any
878
+
879
+ ### 7.5 适配不同后端
880
+
881
+ **响应结构适配**:
882
+ ```typescript
883
+ // 后端 A:{ code: 0, data, message }
884
+ // 后端 B:{ code: 200, data, msg }
885
+ // 后端 C:{ success: true, data, message }
886
+
887
+ // 在响应拦截器中统一处理
888
+ if (res.code === 0 || res.code === 200 || res.success === true) {
889
+ return res
890
+ }
891
+ ```
892
+
893
+ **分页参数适配**:
894
+ ```typescript
895
+ // 封装转换函数
896
+ function toBackendPageParams(params: PageParams) {
897
+ return {
898
+ pageNum: params.page, // 或 current
899
+ pageSize: params.pageSize // 或 size
900
+ }
901
+ }
902
+ ```
903
+
904
+ ---
905
+
906
+ ## 八、快速接入清单
907
+
908
+ 新项目接入此模式时,按以下步骤操作:
909
+
910
+ ### 8.1 使用模板(推荐)
911
+
912
+ 直接复制模板目录 `templates/vue/api-layer/` 到项目 `src/api/`:
913
+
914
+ ```bash
915
+ # 模板位置
916
+ copilot-prompts/templates/vue/api-layer/
917
+ ├── _CONFIG.md # 配置说明(无需复制)
918
+ ├── request.ts # ← 复制
919
+ ├── types.ts # ← 复制
920
+ ├── index.ts # ← 复制
921
+ ├── mock/index.ts # ← 复制
922
+ └── modules/
923
+ ├── index.ts # ← 复制
924
+ └── _template.ts # ← 复制后重命名为业务模块
925
+ ```
926
+
927
+ 详细配置说明见:[templates/vue/api-layer/_CONFIG.md](../../../templates/vue/api-layer/_CONFIG.md)
928
+
929
+ ### 8.2 手动创建
930
+
931
+ 如需从零开始,确保创建以下文件:
932
+
933
+ - [ ] `src/api/request.ts` - 核心封装
934
+ - [ ] `src/api/types.ts` - 类型定义
935
+ - [ ] `src/api/mock/index.ts` - Mock 工具
936
+ - [ ] `src/api/modules/index.ts` - 模块导出
937
+ - [ ] `src/api/index.ts` - 统一入口
938
+
939
+ ### 8.3 按项目调整
940
+
941
+ - [ ] `request.ts` 中的消息提示方法(适配 UI 框架)
942
+ - [ ] `request.ts` 中的 Token 传递方式(按后端要求)
943
+ - [ ] `request.ts` 中的业务状态码判断(按后端约定)
944
+ - [ ] `types/api.ts` 中的响应结构(按后端格式)
945
+ - [ ] `.env` 中的环境变量
946
+
947
+ ### 8.3 创建业务模块
948
+
949
+ - [ ] 根据业务需求创建 `modules/xxx.ts`
950
+ - [ ] 对应创建 `mock/xxx.ts`(如需要)
951
+ - [ ] 在 `modules/index.ts` 中导出
952
+
953
+ ---
954
+
955
+ **适用技术栈**: Vue 3 + TypeScript + 任意 UI 框架 + Vite/Webpack
956
+ **维护者**: MTA工作室
957
+ **创建日期**: 2025-12-25
958
+ **版本**: v1.1(通用化版本)