@lovrabet/cli 1.3.4 → 1.4.1-beta.2

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 (88) hide show
  1. package/lib/ai-setup/config.js +1 -1
  2. package/lib/api/api-doc-ui.js +1 -1
  3. package/lib/api/api-doc.js +1 -1
  4. package/lib/api/api-pull-ui.js +1 -1
  5. package/lib/api/fetch-model-list.js +1 -1
  6. package/lib/api/generate-api-file.js +1 -1
  7. package/lib/api/main.js +1 -1
  8. package/lib/api/pull-silent.js +1 -1
  9. package/lib/app-menu/app-menu-sync-ui.js +1 -1
  10. package/lib/app-menu/create-menu.js +1 -1
  11. package/lib/app-menu/get-local-pages.js +1 -1
  12. package/lib/app-menu/get-online-menu-list.js +1 -1
  13. package/lib/app-menu/update-menu.js +1 -0
  14. package/lib/app-menu/use-get-online-menu-list.js +1 -1
  15. package/lib/app-menu/utils.js +1 -1
  16. package/lib/app-menu/valid-url.js +1 -1
  17. package/lib/app-menu-update-cdn/current-content.js +1 -1
  18. package/lib/app-menu-update-cdn/input-cdn-asset.js +1 -1
  19. package/lib/app-menu-update-cdn/main.js +1 -1
  20. package/lib/app-menu-update-cdn/update-menu.js +1 -0
  21. package/lib/auth/auth-server-ui.js +1 -1
  22. package/lib/auth/auth-server.js +1 -1
  23. package/lib/auth/constant.js +1 -1
  24. package/lib/auth/get-cookie.js +1 -1
  25. package/lib/auth/is-session-valid.js +1 -1
  26. package/lib/auth/logout.js +1 -1
  27. package/lib/cli-flags.js +1 -1
  28. package/lib/cli.js +1 -1
  29. package/lib/cmd/build-watch.js +1 -1
  30. package/lib/cmd/build.js +1 -1
  31. package/lib/cmd/logs.js +1 -1
  32. package/lib/cmd/preview.js +1 -1
  33. package/lib/cmd/start-execa.js +1 -1
  34. package/lib/cmd/start.js +1 -1
  35. package/lib/config/config-help.js +1 -1
  36. package/lib/config/main.js +1 -1
  37. package/lib/constant/domain.js +1 -1
  38. package/lib/constant/env.js +1 -1
  39. package/lib/create-app/enhanced-guided-create.js +1 -1
  40. package/lib/create-app/format-elapsed.js +1 -1
  41. package/lib/create-app/main.js +1 -1
  42. package/lib/create-app/non-interactive.js +1 -1
  43. package/lib/create-app/task-finished.js +1 -1
  44. package/lib/create-app/task-loading.js +1 -1
  45. package/lib/create-app/task-running.js +1 -1
  46. package/lib/create-app/task-time.js +1 -1
  47. package/lib/create-app/use-copy-project-template.js +1 -1
  48. package/lib/create-app/use-format-code.js +1 -1
  49. package/lib/create-app/use-install-dependencies.js +1 -1
  50. package/lib/help.js +1 -1
  51. package/lib/i18n/I18nProvider.js +1 -1
  52. package/lib/i18n/hooks.js +1 -1
  53. package/lib/i18n/index.js +1 -1
  54. package/lib/i18n/locales/en-US.js +1 -1
  55. package/lib/i18n/locales/zh-CN.js +1 -1
  56. package/lib/init/main.js +1 -1
  57. package/lib/mcp/McpInstallUI.js +1 -1
  58. package/lib/mcp/claude.js +1 -1
  59. package/lib/mcp/cursor.js +1 -1
  60. package/lib/mcp/main.js +1 -1
  61. package/lib/mcp/mcp-install-non-interactive.js +1 -1
  62. package/lib/mcp/mcp-installer.js +1 -1
  63. package/lib/skills/main.js +1 -1
  64. package/lib/skills/npx-skills-add.js +1 -0
  65. package/lib/ui/IDESelector.js +1 -1
  66. package/lib/ui/useIDESelection.js +1 -1
  67. package/lib/utils/ai_config.js +1 -1
  68. package/lib/utils/cdn-config.js +1 -1
  69. package/lib/utils/check-sdk-version.js +1 -1
  70. package/lib/utils/cli-version-check.js +1 -1
  71. package/lib/utils/config.js +1 -1
  72. package/lib/utils/copy-directory.js +1 -1
  73. package/lib/utils/file-utils.js +1 -1
  74. package/lib/utils/guides-cdn.js +1 -1
  75. package/lib/utils/http-client.js +1 -1
  76. package/lib/utils/logger.js +1 -1
  77. package/lib/utils/rules-cdn.js +1 -1
  78. package/lib/utils/sleep.js +1 -1
  79. package/lib/utils/template-replacer.js +1 -1
  80. package/package.json +5 -5
  81. package/sidecar/flags.json +10 -0
  82. package/templates/rules/lovrabet_rules.mdc.tpl +891 -0
  83. package/templates/skill/SKILL.md.tpl +120 -0
  84. package/lib/ai-setup/AISetupUI.js +0 -1
  85. package/lib/app-menu-update-cdn/update-menu-cdn-url.js +0 -1
  86. package/lib/skills/SkillInstallUI.js +0 -1
  87. package/lib/skills/skill-install-non-interactive.js +0 -1
  88. package/lib/skills/skill-installer.js +0 -1
@@ -0,0 +1,891 @@
1
+ ---
2
+ globs: ["*.ts", "*.tsx"]
3
+ alwaysApply: false
4
+ ---
5
+ # Lovrabet Development Rules
6
+
7
+ You are a Lovrabet platform development assistant. Help developers use Lovrabet SDK and MCP correctly.
8
+
9
+ ## Core Rules
10
+
11
+ ### 1. TypeScript SDK Usage (CRITICAL)
12
+
13
+ The Lovrabet SDK has three main APIs. Use them correctly:
14
+
15
+ #### Filter API - Advanced Data Queries
16
+
17
+ ```typescript
18
+ // ✅ Correct structure
19
+ const result = await client.models.<modelName>.filter({
20
+ where: {
21
+ status: { $eq: 'active' }, // MUST use operators
22
+ age: { $gte: 18, $lte: 65 },
23
+ },
24
+ select: ['id', 'name'], // NOT 'fields'
25
+ orderBy: [{ createTime: 'desc' }], // NOT 'sort'
26
+ currentPage: 1, // NOT 'page'
27
+ pageSize: 20, // NOT 'limit'
28
+ });
29
+
30
+ // Returns
31
+ console.log(result.tableData); // Data list
32
+ console.log(result.total); // Total count
33
+ ```
34
+
35
+ #### SQL API - Custom Queries
36
+
37
+ ```typescript
38
+ // Object parameter format
39
+ const data = await client.sql.execute({
40
+ sqlCode: 'fc8e7777-06e3847d',
41
+ params: { userId: '123' }
42
+ });
43
+
44
+ // Application layer checks business status
45
+ if (data.execSuccess && data.execResult) {
46
+ data.execResult.forEach(row => console.log(row));
47
+ } else {
48
+ console.error('SQL execution failed');
49
+ }
50
+
51
+ // With type safety
52
+ interface PageStat {
53
+ creation_date: string;
54
+ page_count: number;
55
+ }
56
+
57
+ const result = await client.sql.execute<PageStat>({
58
+ sqlCode: 'fc8e7777-06e3847d'
59
+ });
60
+
61
+ if (result.execSuccess && result.execResult) {
62
+ result.execResult.forEach(stat => {
63
+ console.log(stat.creation_date); // TypeScript autocomplete
64
+ console.log(stat.page_count);
65
+ });
66
+ }
67
+ ```
68
+
69
+ **返回值结构**:
70
+ - SDK 返回:`{ execSuccess: boolean, execResult?: T[] }`(业务数据层)
71
+ - HTTP 错误:抛出 `LovrabetError` 异常
72
+ - 应用层职责:检查 `execSuccess` 状态,处理业务结果
73
+
74
+ #### BFF API - Backend Functions
75
+
76
+ ```typescript
77
+ const dashboard = await client.bff.execute({
78
+ scriptName: 'getUserDashboard',
79
+ params: { userId: '123' }
80
+ });
81
+
82
+ // With type safety
83
+ interface DashboardData {
84
+ userCount: number;
85
+ orderCount: number;
86
+ totalAmount: number;
87
+ }
88
+
89
+ const dashboard = await client.bff.execute<DashboardData>({
90
+ scriptName: 'getUserDashboard',
91
+ params: { userId: '123' }
92
+ });
93
+
94
+ console.log(dashboard.userCount); // TypeScript autocomplete
95
+
96
+ // Complete error handling
97
+ try {
98
+ const result = await client.bff.execute({
99
+ scriptName: 'someEndpoint',
100
+ params: { /* ... */ }
101
+ });
102
+ // Use result directly (it's already the business data)
103
+ } catch (error) {
104
+ if (error instanceof LovrabetError) {
105
+ console.error('BFF call failed:', error.message);
106
+ }
107
+ }
108
+ ```
109
+
110
+ **返回值结构**:
111
+ - SDK 返回:业务数据对象(直接是业务数据层,不是包装器)
112
+ - HTTP 错误:抛出 `LovrabetError` 异常
113
+ - 应用层职责:直接使用返回的业务数据
114
+
115
+ **Common Errors to Avoid:**
116
+ - `where: { status: 'active' }` → Use `where: { status: { $eq: 'active' } }`
117
+ - `fields: ['id']` → Use `select: ['id']`
118
+ - `orderBy: 'createTime desc'` → Use `orderBy: [{ createTime: 'desc' }]`
119
+ - `where: { name: 'LIKE %keyword%' }` → Use `where: { name: { $contain: 'keyword' } }`
120
+ - SQL: Wrong format → Use `client.sql.execute({ sqlCode, params })`
121
+ - BFF: Wrong format → Use `client.bff.execute({ scriptName, params })`
122
+ - Filter: `getList()` → Use `filter()` (getList is deprecated)
123
+
124
+ ### 2. MCP SQL Workflow (STRICT ORDER)
125
+
126
+ When creating custom SQL, follow this 5-step workflow:
127
+
128
+ ```
129
+ Step 1 → Step 2 → Step 3 → Step 4 → Step 5
130
+ Query → Generate → Validate → Save → Test
131
+ ```
132
+
133
+ 1. **Query**: Use `list_sql_queries` to check if SQL exists
134
+ 2. **Generate**: Use `get_dataset_detail` to get table structure FIRST
135
+ 3. **Validate**: Use `validate_sql_content` to verify syntax
136
+ 4. **Save**: Use `save_or_update_custom_sql` to save
137
+ 5. **Test**: Use `execute_custom_sql` to test execution
138
+
139
+ **NEVER skip any step!**
140
+
141
+ ### 3. AntD UI Standards (No AI Style)
142
+
143
+ **Prohibited:**
144
+ - emoji: ✨🚀💡🎯📊🔥🎉
145
+ - AI-style text: "太棒了!", "让我们开始吧!"
146
+ - Fancy colors: #FF00FF, gradients
147
+ - Exclamation marks in buttons
148
+ - Cute terms: "小伙伴", "亲", "宝宝"
149
+
150
+ **Required:**
151
+ - Use AntD token colors
152
+ - Professional text: "保存", "取消", "操作成功"
153
+ - @ant-design/icons for icons (not emoji)
154
+
155
+ ### 4. Code Comments
156
+
157
+ Always add dataset/table comments before SDK calls:
158
+
159
+ ```typescript
160
+ // 数据集: 用户信息 | 数据表: users
161
+ const users = await client.models.users.filter({
162
+ where: { status: { $eq: 'active' } },
163
+ });
164
+ ```
165
+
166
+ ### 5. API Integration (Optional)
167
+
168
+ Use `lovrabet api pull` to generate TypeScript types and API code:
169
+
170
+ ```bash
171
+ # Pull API definitions for all datasets
172
+ lovrabet api pull
173
+
174
+ # Pull specific datasets
175
+ lovrabet api pull --datasetcode customer,order
176
+
177
+ # Specify output directory
178
+ lovrabet api pull --output ./src/api
179
+ ```
180
+
181
+ **Generated files structure:**
182
+ ```
183
+ src/api/
184
+ ├── lovrabet/
185
+ │ ├── index.ts # Export entry
186
+ │ ├── customer.ts # Customer dataset API
187
+ │ ├── order.ts # Order dataset API
188
+ │ └── types.ts # TypeScript type definitions
189
+ ```
190
+
191
+ **Usage:**
192
+ ```typescript
193
+ // Import generated API and types
194
+ import { customerApi } from '@/api/lovrabet';
195
+ import type { Customer } from '@/api/lovrabet/types';
196
+
197
+ // Use with type safety
198
+ const customers = await customerApi.filter({
199
+ where: { status: { $eq: 'active' } },
200
+ currentPage: 1,
201
+ pageSize: 20,
202
+ });
203
+
204
+ customers.tableData.forEach((customer: Customer) => {
205
+ console.log(customer.name); // TypeScript autocomplete
206
+ });
207
+ ```
208
+
209
+
210
+ ## 页面开发规范
211
+
212
+ ### 1. 页面顶部注释(强制)
213
+
214
+ **所有通过 CLI 生成的页面必须在文件顶部包含以下注释**:
215
+
216
+ ```typescript
217
+ /**
218
+ * Generated by Lovrabet CLI v{CliVersion}
219
+ * Created: {CreatedAt}
220
+ * Page: {PageName}
221
+ */
222
+ ```
223
+
224
+ **注释格式说明**:
225
+ - `CliVersion`:生成该页面的 CLI 版本号,便于追踪和兼容性检查
226
+ - `CreatedAt`:页面创建时间,便于维护和追溯
227
+ - `PageName`:页面路径(如 `user-profile`),用于菜单和路由
228
+
229
+ ### 2. displayName(强制)
230
+
231
+ **所有页面组件导出时必须设置 `displayName`**,值为简洁的项目支持语言名称:
232
+
233
+ ```tsx
234
+ export default function UserProfile() {
235
+ // ...
236
+ }
237
+
238
+ UserProfile.displayName = '用户详情';
239
+ ```
240
+
241
+ **规则**:
242
+ - `displayName` 使用项目支持的语言(中文、英文等),简洁明了
243
+ - 推荐 2-4 个字的业务名称,如"用户列表"、"订单详情"、"User List"
244
+
245
+ ### 3. 保留注释要求
246
+
247
+ **禁止删除或修改页面顶部的生成注释**。如需追加修改记录,使用 `@modified` 标签:
248
+
249
+ ```typescript
250
+ // ✅ 正确:保留原始注释,追加自己的说明
251
+ /**
252
+ * Generated by Lovrabet CLI v1.2.5-beta.2
253
+ * Created: 01-18-2026, 21:33:08
254
+ * Page: user-profile
255
+ *
256
+ * @modified 2025-01-20 添加批量删除功能
257
+ */
258
+
259
+ // ❌ 错误:删除原始注释
260
+ ```
261
+
262
+
263
+ ## SDK 使用规范
264
+
265
+ ### 1. SDK 初始化
266
+
267
+ ```typescript
268
+ import { createClient } from '@lovrabet/sdk';
269
+
270
+ // 正确:使用 createClient 函数
271
+ const client = createClient({
272
+ appCode: '<appcode>',
273
+ accessKey: process.env.LOVRABET_ACCESS_KEY, // 服务端
274
+ models: [
275
+ { tableName: 'users', datasetCode: 'abc123def456', alias: 'users' },
276
+ { tableName: 'orders', datasetCode: 'xyz789ghi012', alias: 'orders' },
277
+ ],
278
+ });
279
+
280
+ // 错误:使用 new 关键字
281
+ const client = new LovrabetClient({ ... });
282
+
283
+ // 错误:使用不存在的 mode 参数
284
+ const client = createClient({ mode: 'webapi', ... });
285
+ ```
286
+
287
+ ### 2. SDK 返回值行为(核心原则!)
288
+
289
+ **SDK 设计原则:始终返回 data(业务数据层),应用层自行处理**
290
+
291
+ #### 核心原则
292
+
293
+ - **成功时**:SDK 方法返回 API 响应中的 `data` 字段内容(业务数据层),而不是完整的响应包装器
294
+ - **失败时**:抛出 `LovrabetError` 异常(HTTP 级别错误)
295
+ - **业务状态检查**:应用层负责检查业务逻辑状态(如 `execSuccess`),提取业务结果(如 `execResult`)
296
+
297
+ #### SDK 内部处理机制
298
+
299
+ ```typescript
300
+ // SDK 内部:processResponse 处理响应
301
+ export const processResponse = async <ResponseData>(response: Response) => {
302
+ const result = await response.json();
303
+
304
+ if (!result.success) {
305
+ throw new LovrabetError(errorMessage, { ... });
306
+ }
307
+
308
+ return result.data; // ✅ 只返回 data,不是完整的 response
309
+ };
310
+ ```
311
+
312
+ #### 使用示例
313
+
314
+ ```typescript
315
+ // ✅ 正确:Filter API - 直接使用返回的数据
316
+ try {
317
+ const result = await client.models.users.filter({
318
+ where: { status: { $eq: 'active' } },
319
+ });
320
+ console.log(result.tableData); // 直接使用,result 就是 data
321
+ console.log(result.total);
322
+ } catch (error) {
323
+ if (error instanceof LovrabetError) {
324
+ console.error('HTTP 请求失败:', error.message);
325
+ }
326
+ }
327
+
328
+ // ✅ 正确:SQL API - 应用层检查业务状态
329
+ try {
330
+ const data = await client.sql.execute({
331
+ sqlCode: 'fc8e7777-06e3847d',
332
+ });
333
+
334
+ // 应用层负责检查业务逻辑状态
335
+ if (data.execSuccess && data.execResult) {
336
+ data.execResult.forEach(row => console.log(row));
337
+ } else {
338
+ console.error('SQL 查询失败');
339
+ }
340
+ } catch (error) {
341
+ if (error instanceof LovrabetError) {
342
+ console.error('HTTP 请求失败:', error.message);
343
+ }
344
+ }
345
+
346
+ // ✅ 正确:BFF API - 直接使用返回的业务数据
347
+ try {
348
+ const dashboard = await client.bff.execute({
349
+ scriptName: 'getUserDashboard',
350
+ params: { userId: '123' },
351
+ });
352
+ console.log(dashboard.userCount); // 直接使用业务数据
353
+ } catch (error) {
354
+ if (error instanceof LovrabetError) {
355
+ console.error('HTTP 请求失败:', error.message);
356
+ }
357
+ }
358
+
359
+ // ❌ 错误:假设 SDK 返回完整响应对象
360
+ const response = await client.sql.execute({ sqlCode: 'xxx' });
361
+ if (response.success && response.data?.execSuccess) {
362
+ // SDK 不会返回这种嵌套结构
363
+ }
364
+ ```
365
+
366
+ ### 3. Filter 查询构建规范
367
+
368
+ #### 支持的条件操作符
369
+
370
+ | 操作符 | 说明 | 示例 |
371
+ |--------|------|------|
372
+ | `$eq` | 等于 | `{ status: { $eq: 'active' } }` |
373
+ | `$ne` | 不等于 | `{ status: { $ne: 'deleted' } }` |
374
+ | `$gte` | 大于等于 | `{ age: { $gte: 18 } }` |
375
+ | `$lte` | 小于等于 | `{ age: { $lte: 65 } }` |
376
+ | `$gt` | 大于 | `{ price: { $gt: 100 } }` |
377
+ | `$lt` | 小于 | `{ price: { $lt: 1000 } }` |
378
+ | `$in` | 在集合内 | `{ type: { $in: ['A', 'B'] } }` |
379
+ | `$contain` | 包含(模糊) | `{ name: { $contain: 'keyword' } }` |
380
+ | `$startWith` | 以...开头 | `{ email: { $startWith: 'admin' } }` |
381
+ | `$endWith` | 以...结尾 | `{ domain: { $endWith: '.com' } }` |
382
+ | `$and` | 且(所有满足) | `{ $and: [条件1, 条件2] }` |
383
+ | `$or` | 或(任一满足) | `{ $or: [条件1, 条件2] }` |
384
+
385
+ #### 逻辑操作符示例
386
+
387
+ ```typescript
388
+ // ✅ $and - 所有条件都要满足
389
+ where: {
390
+ $and: [
391
+ { age: { $gte: 18 } },
392
+ { status: { $eq: 'active' } }
393
+ ]
394
+ }
395
+
396
+ // ✅ $or - 任一条件满足即可
397
+ where: {
398
+ $or: [
399
+ { status: { $eq: 'pending' } },
400
+ { status: { $eq: 'processing' } }
401
+ ]
402
+ }
403
+
404
+ // ✅ 嵌套组合
405
+ where: {
406
+ $and: [
407
+ { age: { $gte: 18 } },
408
+ {
409
+ $or: [
410
+ { country: { $eq: '中国' } },
411
+ { country: { $eq: '美国' } }
412
+ ]
413
+ }
414
+ ]
415
+ }
416
+ ```
417
+
418
+ #### 常见错误
419
+
420
+ | 错误写法 | 正确写法 |
421
+ |---------|---------|
422
+ | `where: { status: 'active' }` | `where: { status: { $eq: 'active' } }` |
423
+ | `fields: ['id']` | `select: ['id']` |
424
+ | `orderBy: 'createTime desc'` | `orderBy: [{ createTime: 'desc' }]` |
425
+ | `where: { name: 'LIKE %kw%' }` | `where: { name: { $contain: 'kw' } }` |
426
+ | `page: 1, limit: 20` | `currentPage: 1, pageSize: 20` |
427
+
428
+ ### 4. 性能优化(禁止循环查询)
429
+
430
+ **禁止在循环中调用单条查询 API**:
431
+
432
+ ```typescript
433
+ // ❌ 错误:N+1 查询
434
+ for (const order of orders) {
435
+ const user = await userDS.findOne({ id: order.user_id });
436
+ order.username = user?.username;
437
+ }
438
+
439
+ // ✅ 正确:批量查询
440
+ const userIds = [...new Set(orders.map(o => o.user_id))];
441
+ const userResult = await userDS.filter({
442
+ where: { id: { $in: userIds } }
443
+ });
444
+ const userMap = Object.fromEntries(
445
+ (userResult.tableData || []).map(u => [u.id, u])
446
+ );
447
+ for (const order of orders) {
448
+ order.username = userMap[order.user_id]?.username;
449
+ }
450
+ ```
451
+
452
+ **批量写入使用自定义 SQL**:
453
+
454
+ ```typescript
455
+ // ❌ 错误:循环写入
456
+ for (const item of items) {
457
+ await ds.create(item);
458
+ }
459
+
460
+ // ✅ 正确:使用自定义 SQL
461
+ await context.client.sql.execute({
462
+ sqlCode: 'batch-insert-sql-code',
463
+ params: { items: JSON.stringify(items) }
464
+ });
465
+ ```
466
+
467
+ #### 文案示例
468
+
469
+ ```typescript
470
+ // ✅ 正确:简洁专业
471
+ const messages = {
472
+ success: '操作成功',
473
+ error: '操作失败',
474
+ confirm: '确定要删除这条记录吗?',
475
+ cancel: '取消',
476
+ submit: '提交',
477
+ save: '保存',
478
+ loading: '加载中...',
479
+ noData: '暂无数据',
480
+ };
481
+
482
+ // ❌ 错误:AI 味道
483
+ const messages = {
484
+ success: '🎉 太棒了!操作成功!',
485
+ error: '哎呀,出错了 😢',
486
+ confirm: '你真的要删除吗?确定吗?',
487
+ cancel: '再想想吧~',
488
+ submit: '提交表单 ✨',
489
+ save: '保存更改 💾',
490
+ loading: '马上就好~请稍等 ⏳',
491
+ noData: '空空如也~还没有数据哦',
492
+ };
493
+ ```
494
+
495
+ ### 颜色规范
496
+
497
+ #### 使用 AntD Token
498
+
499
+ ```typescript
500
+ // ✅ 正确:使用 AntD token
501
+ import { theme } from 'antd';
502
+
503
+ const { token } = theme.useToken();
504
+
505
+ const styles = {
506
+ primary: token.colorPrimary,
507
+ success: token.colorSuccess,
508
+ error: token.colorError,
509
+ warning: token.colorWarning,
510
+ text: token.colorText,
511
+ border: token.colorBorder,
512
+ };
513
+
514
+ // ❌ 错误:自定义花哨颜色
515
+ const styles = {
516
+ primary: '#FF00FF', // 霓虹粉
517
+ success: '#00FF00', // 亮绿色
518
+ accent: '#FFD700', // 金色
519
+ gradient: 'linear-gradient(45deg, #ff6b6b, #feca57)', // 渐变
520
+ };
521
+ ```
522
+
523
+ ### 组件使用规范
524
+
525
+ #### 按钮
526
+
527
+ ```typescript
528
+ // ✅ 正确:简洁明了
529
+ <Button type="primary" onClick={handleSave}>
530
+ 保存
531
+ </Button>
532
+ <Button danger onClick={handleDelete}>
533
+ 删除
534
+ </Button>
535
+
536
+ // ❌ 错误:花哨风格
537
+ <Button type="primary" onClick={handleSave}>
538
+ 💾 保存更改
539
+ </Button>
540
+ <Button danger onClick={handleDelete}>
541
+ 🗑️ 哎呀,要删除吗?
542
+ </Button>
543
+ ```
544
+
545
+ #### 表格
546
+
547
+ ```typescript
548
+ // ✅ 正确:使用 ProTable 或 Table + Pagination
549
+ import { Table } from 'antd';
550
+
551
+ <Table
552
+ columns={columns}
553
+ dataSource={data}
554
+ pagination={{
555
+ current: currentPage,
556
+ pageSize: pageSize,
557
+ total: total,
558
+ showSizeChanger: true,
559
+ showQuickJumper: true,
560
+ showTotal: (total) => `共 ${total} 条`,
561
+ }}
562
+ />
563
+
564
+ // ❌ 错误:花哨的空状态
565
+ <Table
566
+ columns={columns}
567
+ dataSource={data}
568
+ locale={{
569
+ emptyText: (
570
+ <div>
571
+ <span>🎭 </span>
572
+ <span>还没有数据哦~</span>
573
+ </div>
574
+ ),
575
+ }}
576
+ />
577
+ ```
578
+
579
+ #### 消息提示
580
+
581
+ ```typescript
582
+ // ✅ 正确:简洁专业
583
+ import { message } from 'antd';
584
+
585
+ message.success('保存成功');
586
+ message.error('操作失败,请重试');
587
+ message.warning('请填写必填项');
588
+ message.info('正在处理...');
589
+
590
+ // ❌ 错误:AI 味道
591
+ message.success('🎉 太棒了!保存成功!');
592
+ message.error('😱 哎呀出错了!请再试一次');
593
+ message.warning('⚠️ 嘿!别忘了填写必填项哦');
594
+ message.info('🔔 正在为你处理中,请稍等~');
595
+ ```
596
+
597
+ #### Modal 确认
598
+
599
+ ```typescript
600
+ // ✅ 正确:专业简洁
601
+ import { Modal } from 'antd';
602
+
603
+ Modal.confirm({
604
+ title: '确认删除',
605
+ content: '确定要删除这条记录吗?',
606
+ okText: '确定',
607
+ cancelText: '取消',
608
+ onOk: handleDelete,
609
+ });
610
+
611
+ // ❌ 错误:AI 味道
612
+ Modal.confirm({
613
+ title: '🤔 确认要删除吗?',
614
+ content: '删除后可就找不回来了哦!你确定要删除这条记录吗?',
615
+ okText: '是的,删除!',
616
+ cancelText: '再想想~',
617
+ icon: <ExclamationCircleOutlined style={{ color: '#FFD700' }} />,
618
+ });
619
+ ```
620
+
621
+ ### 表单规范
622
+
623
+ ```typescript
624
+ // ✅ 正确:简洁清晰的错误提示
625
+ <Form>
626
+ <Form.Item
627
+ name="username"
628
+ label="用户名"
629
+ rules={[
630
+ { required: true, message: '请输入用户名' },
631
+ { min: 3, message: '用户名至少3个字符' },
632
+ ]}
633
+ >
634
+ <Input placeholder="请输入用户名" />
635
+ </Form.Item>
636
+ </Form>
637
+
638
+ // ❌ 错误:花哨的错误提示
639
+ <Form>
640
+ <Form.Item
641
+ name="username"
642
+ label="👤 用户名"
643
+ rules={[
644
+ { required: true, message: '哎呀!别忘了填写用户名哦~' },
645
+ { min: 3, message: '用户名太短啦,至少要3个字符呢!' },
646
+ ]}
647
+ >
648
+ <Input placeholder="输入用户名试试看~ ✨" />
649
+ </Form.Item>
650
+ </Form>
651
+ ```
652
+
653
+ ### 空状态规范
654
+
655
+ ```typescript
656
+ // ✅ 正确:简洁专业
657
+ import { Empty } from 'antd';
658
+
659
+ <Empty
660
+ description="暂无数据"
661
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
662
+ />
663
+
664
+ // 带操作按钮
665
+ <Empty
666
+ description="暂无数据"
667
+ image={Empty.PRESENTED_IMAGE_SIMPLE}>
668
+ <Button type="primary">创建数据</Button>
669
+ </Empty>
670
+
671
+ // ❌ 错误:AI 味道
672
+ <Empty
673
+ description={
674
+ <div>
675
+ <div>🎭 还没有数据哦~</div>
676
+ <div style={{ fontSize: 12, color: '#999' }}>
677
+ 快来创建第一条数据吧!✨
678
+ </div>
679
+ </div>
680
+ }
681
+ />
682
+ ```
683
+
684
+ ### 图标使用规范
685
+
686
+ ```typescript
687
+ // ✅ 正确:使用 @ant-design/icons
688
+ import {
689
+ UserOutlined,
690
+ DeleteOutlined,
691
+ EditOutlined,
692
+ SearchOutlined,
693
+ } from '@ant-design/icons';
694
+
695
+ // 按钮图标(可选,保持简洁)
696
+ <Button icon={<PlusOutlined />}>新建</Button>
697
+
698
+ // ❌ 错误:使用 emoji 作为图标
699
+ <Button>🆕 新建</Button>
700
+ <Button>✏️ 编辑</Button>
701
+ <Button>🗑️ 删除</Button>
702
+ ```
703
+
704
+ ### 检查清单
705
+
706
+ 在编写 UI 代码时,确保:
707
+
708
+ - [ ] 没有 emoji
709
+ - [ ] 没有感叹号
710
+ - [ ] 文案简洁专业
711
+ - [ ] 使用 AntD token 颜色
712
+ - [ ] 使用 @ant-design/icons 而非 emoji
713
+ - [ ] 按钮文案为动词(保存/删除/编辑)
714
+ - [ ] 提示文案中性(操作成功/操作失败)
715
+ - [ ] 没有过度动画效果
716
+
717
+
718
+
719
+ ## 禁止事项
720
+
721
+ ### 页面相关
722
+ 1. **不要删除页面顶部注释** - CLI 生成的注释必须保留,用于版本追踪和维护
723
+ 2. **不要修改页面顶部注释内容** - 如需追加说明,使用 `@modified` 标签
724
+ 3. **不要使用不规范的组件命名** - 组件名必须使用 PascalCase
725
+
726
+ ### SDK 相关
727
+ 1. **不要臆测 API** - 使用不存在的方法或参数
728
+ 2. **不要臆测字段** - 未通过 MCP 确认字段是否存在
729
+ 3. **不要混淆认证模式** - 在错误的模式使用功能(如 OpenAPI 模式使用 delete)
730
+ 4. **不要忽略错误处理** - 所有 SDK 调用必须 try-catch
731
+ 5. **不要假设返回值结构** - 注意不同方法返回值的差异
732
+ 6. **不要在循环中调用 API** - 使用 Promise.all 批量处理
733
+ 7. **不要在每次渲染时调用 API** - 使用 useEffect 控制
734
+ 8. **不要使用 getList()** - 列表查询必须使用 filter()
735
+
736
+ ### Filter 查询相关
737
+ 9. **不要忘记使用操作符** - where 条件必须使用 `$eq`、`$gte` 等操作符
738
+ 10. **不要使用错误的参数名** - 是 `select` 不是 `fields`,是 `orderBy` 不是 `sortList`
739
+ 11. **不要使用 SQL 语法** - 不支持 `LIKE`、`>=` 等,必须用 `$contain`、`$gte` 等
740
+ 12. **不要忽略参数格式** - `orderBy` 和 `select` 必须是数组,不能是字符串
741
+
742
+ ### SQL 相关
743
+ 13. **不要跳过 MCP SQL 工作流步骤** - 必须按顺序:查询→生成→验证→保存→测试
744
+ 14. **不要忘记检查 execSuccess** - `client.sql.execute()` 返回的数据必须检查 `execSuccess` 状态(应用层职责)
745
+ 15. **不要假设 sql.execute 返回数组** - 返回的是 `{ execSuccess, execResult }` 对象(业务数据层)
746
+ 16. **不要假设SQL code存在 = SQL正确** - 必须验证SQL内容中的表名和字段名与实际数据库结构匹配
747
+ 17. **不要仅检查SQL code是否存在** - 必须获取SQL内容,对比表名和字段名,修复SQL内容本身
748
+ 18. **不要在自定义 SQL 中使用 INSERT/UPDATE/DELETE** - 只允许 SELECT 查询
749
+ 19. **不要假设 SDK 返回完整响应对象** - SDK 返回的是 `data`(业务数据层),不是 `{ success, data }` 结构
750
+
751
+ ### UI 相关
752
+ 20. **不要使用 emoji** - 包括按钮、标题、提示文案等所有地方
753
+ 21. **不要使用 AI 味道的文案** - 如"太棒了!"、"让我们开始吧!"、"试试看!"
754
+ 22. **不要使用花哨的颜色** - 只使用 AntD token 颜色
755
+ 23. **不要使用感叹号** - 标题和按钮文案中禁止使用 "!"
756
+ 24. **不要使用自定义动画** - 只使用 AntD 内置动画
757
+ 25. **不要使用非专业称呼** - 如"小伙伴"、"亲"、"宝宝" 等
758
+
759
+
760
+
761
+ ## 快速参考
762
+
763
+ ### SDK 方法返回值
764
+
765
+ | 方法 | 返回结构 | 参数格式 | 认证限制 |
766
+ |------|----------|----------|----------|
767
+ | `filter()` | `{ tableData, total }` | `filter(params)` | OpenAPI + WebAPI |
768
+ | `getOne()` | 单个对象 | `getOne(id)` 或 `getOne({ id })` | OpenAPI + WebAPI |
769
+ | `create/update()` | 操作结果对象 | `create(data)` / `update(id, data)` | OpenAPI + WebAPI |
770
+ | `delete()` | 删除结果 | `delete(id)` | 仅 WebAPI |
771
+ | `getSelectOptions()` | `[{ label, value }]` | `getSelectOptions(params)` | 仅 WebAPI |
772
+ | `sql.execute()` | `{ execSuccess, execResult }` | `execute({ sqlCode, params })` | OpenAPI + WebAPI |
773
+ | `bff.execute()` | 业务数据对象 | `execute({ scriptName, params })` | OpenAPI + WebAPI |
774
+ | `user.getList()` | `User[]` | `getList()` | 仅 WebAPI |
775
+
776
+ **注意**:
777
+ - 所有方法返回的都是 `data`(业务数据层),不是完整的响应对象
778
+ - SQL 和 BFF 的返回值需要应用层检查业务状态(如 `execSuccess`)
779
+
780
+ ### 记住
781
+
782
+ #### 页面开发
783
+ - **保留顶部注释**:CLI 生成的注释包含版本、时间、页面名,禁止删除
784
+ - **组件命名 PascalCase**:`UserProfile` 而非 `userProfile`
785
+ - **追加修改说明**:使用 `@modified` 标签记录手动修改
786
+
787
+ #### SDK 核心
788
+ - **返回值原则**:SDK 始终返回 `data`(业务数据层),不是完整的响应包装器
789
+ - **成功时**:返回 API 响应的 `data` 字段内容
790
+ - **失败时**:抛出 `LovrabetError` 异常(HTTP 级别错误)
791
+ - **业务状态检查**:应用层负责检查业务逻辑状态(如 `execSuccess`)
792
+ - **所有调用**:必须用 `try-catch` 包裹
793
+ - **先获取元数据**:使用 MCP 确认字段存在
794
+
795
+ #### Filter 查询
796
+ - **列表查询用 filter**:必须使用 `filter()` 方法,禁止使用 `getList()`
797
+ - **注意参数名**:`select`(不是 `fields`),`orderBy`(不是 `sortList`),`currentPage`/`pageSize`(不是 `page`/`limit`)
798
+ - **必须使用操作符**:`where: { status: { $eq: 'active' } }`,不是 `where: { status: 'active' }`
799
+ - **参数必须是数组**:`select: ['id', 'name']`,不是 `select: 'id,name'`
800
+
801
+ #### SQL API
802
+ - **使用方式**:`client.sql.execute({ sqlCode, params })` - 对象参数格式
803
+ - **返回值检查**:应用层必须检查 `execSuccess` 状态
804
+
805
+ #### BFF API
806
+ - **使用方式**:`client.bff.execute({ scriptName, params })` - 直接客户端访问
807
+ - **返回值**:直接是业务数据对象,无需检查 `execSuccess`
808
+
809
+ #### MCP SQL 流程
810
+ - **严格执行 5 步流程**:查询→生成→验证→保存→测试
811
+ - **SQL验证必须系统化**:创建或使用SQL时,必须对比SQL内容与实际表结构,验证表名和字段名
812
+ - **SQL code存在 ≠ SQL正确**:必须获取SQL内容,验证表名、字段名、JOIN条件是否正确
813
+
814
+ #### UI 规范
815
+ - **禁止 emoji**:所有地方都不使用 emoji
816
+ - **文案简洁专业**:"保存"而不是"保存吧!","操作成功"而不是"太棒了!"
817
+ - **使用 AntD token**:颜色使用 `token.colorPrimary` 等,不自定义花哨颜色
818
+
819
+
820
+
821
+ ## 示例代码说明
822
+
823
+ **重要**:本文档中所有代码示例使用的是**占位符**(用 `<>` 包裹),而非真实字段名或值。
824
+
825
+ ### 占位符说明
826
+
827
+ - `<appcode>` - 应用代码,需替换为实际的 appcode
828
+ - `<modelName>` - 模型名称,如 `users`、`orders` 等
829
+ - `<dataset-code>` - 数据集代码
830
+ - `<table-name>` - 表名
831
+ - `<table-alias>` - 表别名(SQL 中使用)
832
+ - `<id>` - 记录 ID
833
+ - `<field>`, `<field1>`, `<field2>` - 字段名称
834
+ - `<fieldName>` - 字段名称(用于排序等)
835
+ - `<field-name>` - 具体字段名
836
+ - `<wrong-field>`, `<correct-field>` - 错误/正确的字段名(SQL 验证示例)
837
+ - `<wrong-pk>`, `<correct-pk>` - 错误/正确的主键字段名
838
+ - `<wrong-table>`, `<correct-table>` - 错误/正确的表名
839
+ - `<value>`, `<value1>`, `<value2>` - 字段值
840
+ - `<sql-code>` - SQL 代码
841
+ - `<sql-name>` - SQL 名称
842
+ - `<value-field>` - 用作选项值的字段
843
+ - `<label-field>` - 用作显示文本的字段
844
+
845
+ ### 如何使用
846
+
847
+ 在实际编写代码前,**必须**:
848
+
849
+ 1. 使用 MCP 工具获取数据集详情
850
+ 2. 查看实际的字段列表和字段类型
851
+ 3. 用真实的字段名替换占位符
852
+ 4. 确保字段类型匹配
853
+
854
+ **切记**:不要直接复制示例代码中的占位符到实际项目中!
855
+
856
+
857
+ **学习资源**:
858
+ - [官方文档](https://open.lovrabet.com/docs/lovrabet-sdk/intro)
859
+ - [API 参考](https://open.lovrabet.com/docs/lovrabet-sdk/api-reference)
860
+ - [Filter API](https://open.lovrabet.com/docs/lovrabet-sdk/filter-api)
861
+
862
+ ---
863
+
864
+ ## 更新日志
865
+
866
+ ### v2.1.0 (2025-01-24)
867
+ - ✨ **完善 SDK 返回值行为说明** - 明确 SDK 返回 `data`(业务数据层),应用层负责检查业务状态
868
+ - ✨ **补充 SQL API 详细说明** - 明确返回值结构和应用层职责,添加类型安全示例
869
+ - ✨ **补充 BFF API 详细说明** - 添加完整的使用示例和错误处理
870
+ - ✨ **补充 Filter API 逻辑操作符** - 添加 `$and`、`$or` 嵌套组合示例
871
+ - ✨ **新增 API 集成指南** - 添加 `lovrabet api pull` 使用说明
872
+ - 📝 更新"快速参考"和"记住"部分,明确 SDK 返回值结构和使用方式
873
+ - 🧹 **清理向后兼容说明** - 移除所有向后兼容和别名用法说明,避免大模型产生幻觉
874
+
875
+ ### v2.0.0 (2025-01-17)
876
+ - ✨ **新增 SDK Filter 查询构建规范(强制)** - 详细说明 filter 的正确用法,列举 10 种常见错误
877
+ - ✨ **新增 MCP SQL 创建工作流(强制顺序)** - 明确 5 步流程顺序:查询→生成→验证→保存→测试
878
+ - ✨ **新增 AntD UI 开发规范(禁止 AI 风格)** - 禁止 emoji、AI 味道文案、花哨颜色等
879
+ - 📝 重组禁止事项为 4 大类(SDK / Filter / SQL / UI),共 24 条
880
+ - 📝 更新"记住"部分,分 4 大类总结关键要点
881
+
882
+ ### v1.0.0 (2025-11-15)
883
+ - 📋 完整的 SDK 使用规范
884
+ - 🔧 MCP Tools 使用指南
885
+ - ⚡ 性能优化建议
886
+ - 🛡️ 错误处理最佳实践
887
+
888
+ ---
889
+
890
+ > update:2025-01-24
891
+