draftgo-cli 3.0.1 → 3.0.33

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -124
  3. package/package.json +21 -8
  4. package/resources/skill/SKILL.md +62 -89
  5. package/resources/skill/init/SKILL.md +18 -67
  6. package/resources/skill/manifest.json +27 -0
  7. package/resources/skill/pull/SKILL.md +18 -44
  8. package/resources/skill/push/SKILL.md +30 -247
  9. package/resources/skill/references/aihub.md +86 -0
  10. package/resources/skill/references/api-endpoints.md +178 -0
  11. package/resources/skill/references/api.json +20248 -0
  12. package/resources/skill/{quickref → references}/app-api.md +44 -14
  13. package/resources/skill/{core → references}/architecture.md +6 -26
  14. package/resources/skill/references/chat-sdk.md +201 -0
  15. package/resources/skill/references/custom-services.md +308 -0
  16. package/resources/skill/references/data.md +298 -0
  17. package/resources/skill/references/db-relations.md +227 -0
  18. package/resources/skill/references/frontend.md +788 -0
  19. package/resources/skill/references/modules.md +66 -0
  20. package/resources/skill/references/parallel.md +48 -0
  21. package/resources/skill/{specs → references}/runtime.md +31 -1
  22. package/resources/skill/{specs → references}/security.md +3 -3
  23. package/resources/skill/references/ui-protocol.md +99 -0
  24. package/resources/skill/scripts/draftgo_delete.py +0 -2
  25. package/resources/skill/scripts/draftgo_init.py +15 -3
  26. package/resources/skill/scripts/draftgo_pull.py +154 -87
  27. package/resources/skill/scripts/draftgo_push.py +440 -183
  28. package/resources/skill/story/SKILL.md +13 -23
  29. package/src/cli.js +22 -7
  30. package/src/commandRegistry.js +34 -0
  31. package/src/commands/api.js +204 -0
  32. package/src/commands/autoPush.js +41 -0
  33. package/src/commands/check.js +27 -17
  34. package/src/commands/delete.js +6 -4
  35. package/src/commands/deploy.js +31 -0
  36. package/src/commands/help.js +41 -28
  37. package/src/commands/init.js +34 -20
  38. package/src/commands/local.js +9 -3
  39. package/src/commands/map.js +18 -7
  40. package/src/commands/sync.js +11 -4
  41. package/src/commands/update.js +39 -52
  42. package/src/commands/verifyUi.js +199 -0
  43. package/src/index.js +13 -46
  44. package/src/localdev/compose.js +48 -197
  45. package/src/localdev/index.js +116 -216
  46. package/src/localdev/mysqlClient.js +12 -9
  47. package/src/localdev/services.js +163 -0
  48. package/src/platforms.js +3 -3
  49. package/src/projectConfig.js +12 -2
  50. package/src/projectMap.js +240 -68
  51. package/src/skill.js +113 -29
  52. package/src/updateCheck.js +37 -15
  53. package/resources/skill/core/modules.md +0 -54
  54. package/resources/skill/practices/anti-patterns.md +0 -70
  55. package/resources/skill/practices/best-practices.md +0 -41
  56. package/resources/skill/practices/dev-declaration.md +0 -94
  57. package/resources/skill/quickref/api-endpoints.md +0 -130
  58. package/resources/skill/quickref/api.json +0 -17675
  59. package/resources/skill/quickref/dg-components.md +0 -198
  60. package/resources/skill/rules/dev-workflow.md +0 -652
  61. package/resources/skill/rules/frontend.md +0 -210
  62. package/resources/skill/rules/parallel.md +0 -263
  63. package/resources/skill/specs/data.md +0 -108
  64. package/resources/skill/specs/ui-protocol.md +0 -68
  65. package/src/commands/doctor.js +0 -54
  66. package/src/commands/new.js +0 -183
  67. package/src/commands/projectScript.js +0 -37
  68. /package/resources/skill/{rules → references}/debugging-syntax.md +0 -0
@@ -0,0 +1,788 @@
1
+ ---
2
+ name: draftgo-frontend-rules
3
+ description: DraftGo frontend page development rules.
4
+ version: 2.0.0
5
+ ---
6
+
7
+ # DraftGo 前端开发规范
8
+
9
+ > 根 `SKILL.md` 是完整核心规则源;本文件只展开前端实现方法。若表述冲突,以根 `SKILL.md` 为准,不额外叠加固定验收动作。
10
+
11
+ > 本文件只规定前端**操作性规则**,已剥离的内容见:
12
+ > 架构原理 → `{{SKILL_DIR}}/references/architecture.md`
13
+ > App API / Token / 路由 / 运行时 → `{{SKILL_DIR}}/references/runtime.md` · `{{SKILL_DIR}}/references/app-api.md`
14
+ > 动态 DB / filters → `{{SKILL_DIR}}/references/data.md`
15
+ > 开发禁区 → `{{SKILL_DIR}}/references/security.md`
16
+
17
+ ---
18
+
19
+ ## 页面开发强制规则
20
+
21
+ ### 真实闭环(强制)
22
+
23
+ - 页面必须有可访问 route,从真实入口可达(导航/首页/后台菜单/相关按钮至少一处)
24
+ - 按钮、表单、搜索、筛选、分页、提交、删除等交互默认真实有效
25
+ - 涉及异步数据、提交、搜索、筛选或接口调用时,必须覆盖相关加载态/空态/错误态/成功态,不能只堆静态卡片
26
+ - 展示可维护内容时主动判断是否需要后台管理页 + 同一份真实数据
27
+ - 业务模块尽量不要复制大量同构工作台;列表、详情、编辑、管理页优先按任务差异设计结构、密度和操作区
28
+ - 无法实现真实闭环时停止开发,向用户说明阻塞点;确认为平台限制、可复用经验或连续排查失败时再写入 `.draftgo/lessons/`
29
+
30
+ ### 操作型页面布局(强制)
31
+
32
+ 后台管理、表格、列表等操作型页面用**工作台布局**,不用文档流堆叠:
33
+ 强烈建议直接使用antd的组件
34
+ ```css
35
+ .workbench-page { min-height:100vh; display:flex; flex-direction:column; overflow:hidden; }
36
+ .workbench-toolbar { flex-shrink:0; }
37
+ .workbench-body { flex:1; min-height:0; display:flex; flex-direction:column; }
38
+ .workbench-data { flex:1; min-height:0; overflow:auto; }
39
+ .workbench-footer { flex-shrink:0; }
40
+ ```
41
+
42
+ - 分页/操作栏/保存栏固定在工作区底部,不随数据量上浮
43
+ - 滚动发生在数据区,不是整页
44
+
45
+ ### 表格数据区与分页钉底
46
+
47
+ 当页面主体是表格、日志列表、后台数据管理页时,必须先做固定高度的 flex column 容器,再放表格。不要让分页跟随表格内容高度往下跑,也不要让整页滚动。
48
+
49
+ 推荐骨架:
50
+
51
+ ```css
52
+ /* 整页表格页 */
53
+ .table-page {
54
+ height: 100vh;
55
+ min-height: 0;
56
+ display: flex;
57
+ flex-direction: column;
58
+ overflow: hidden;
59
+ }
60
+
61
+ /* 如果页面外已有顶部导航,用实际导航高度替换 56px */
62
+ .table-page.with-top-nav {
63
+ height: calc(100vh - 56px);
64
+ }
65
+
66
+ .table-toolbar {
67
+ flex-shrink: 0;
68
+ }
69
+
70
+ .table-shell {
71
+ flex: 1;
72
+ min-height: 0;
73
+ display: flex;
74
+ flex-direction: column;
75
+ overflow: hidden;
76
+ }
77
+
78
+ .table-data {
79
+ flex: 1;
80
+ min-height: 0;
81
+ overflow: auto;
82
+ }
83
+
84
+ .table-pagination {
85
+ flex-shrink: 0;
86
+ margin-top: auto;
87
+ }
88
+ ```
89
+
90
+ antd Table 推荐结构:
91
+
92
+ ```javascript
93
+ h('div', { className: 'table-page' },
94
+ toolbar && h('div', { className: 'table-toolbar' }, toolbar),
95
+ h('div', { className: 'table-shell' },
96
+ h('div', { className: 'table-data' },
97
+ h(antd.Table, {
98
+ rowKey: 'id',
99
+ columns,
100
+ dataSource: items,
101
+ loading,
102
+ pagination: false,
103
+ scroll: { x: 1100 },
104
+ onChange: handleTableChange,
105
+ })
106
+ ),
107
+ h('div', { className: 'table-pagination' },
108
+ h(antd.Pagination, {
109
+ current: page,
110
+ pageSize,
111
+ total,
112
+ showSizeChanger: true,
113
+ onChange: loadPage,
114
+ })
115
+ )
116
+ )
117
+ )
118
+ ```
119
+
120
+ 如果表格列很多,横向滚动条必须固定在分页上方,而不是藏在长数据列表底部。做法是让 antd Table 自己生成 `.ant-table-body` 滚动区:外层 `.table-data` 只负责 flex 占位和 `overflow:hidden`,Table 设置 `pagination:false`、`scroll:{ x: 1100, y: '100%' }`,再用 CSS 让 `.ant-table-body` 填满剩余高度。
121
+
122
+ ```css
123
+ .table-data {
124
+ flex: 1;
125
+ min-height: 0;
126
+ overflow: hidden;
127
+ display: flex;
128
+ flex-direction: column;
129
+ }
130
+
131
+ .table-data .ant-table-wrapper,
132
+ .table-data .ant-spin-nested-loading,
133
+ .table-data .ant-spin-container,
134
+ .table-data .ant-table,
135
+ .table-data .ant-table-container {
136
+ flex: 1;
137
+ min-height: 0;
138
+ min-width: 0;
139
+ display: flex;
140
+ flex-direction: column;
141
+ }
142
+
143
+ .table-data .ant-table-header { flex-shrink: 0; }
144
+ .table-data .ant-table-body {
145
+ flex: 1 1 0;
146
+ min-height: 0;
147
+ max-height: none !important;
148
+ overflow: auto !important;
149
+ }
150
+ ```
151
+
152
+ ```javascript
153
+ h(antd.Table, {
154
+ rowKey: 'id',
155
+ columns,
156
+ dataSource: items,
157
+ loading,
158
+ pagination: false,
159
+ scroll: { x: 1100, y: '100%' },
160
+ })
161
+ ```
162
+
163
+ 硬性验收:
164
+
165
+ - 最外层容器高度明确:整页用 `height:100vh`;有顶部导航时用 `calc(100vh - 顶部导航高度)`;嵌在已固定高度的工作区时用 `height:100%; min-height:0`。
166
+ - 布局必须是 `display:flex; flex-direction:column`。
167
+ - 顶部筛选区 / toolbar 若存在,使用自适应高度并 `flex-shrink:0`。
168
+ - 中间表格区域必须是 `flex:1; min-height:0; overflow:auto`。
169
+ - 分页区域必须在容器最底部,使用 `flex-shrink:0; margin-top:auto`。
170
+ - 禁止分页随数据内容高度下移;数据很多时只滚动 `.table-data`。
171
+ - 表格列多时使用 antd `scroll.x` 或内部横滚,禁止撑出整页横向滚动条;需要随时可横向滑动时,横向滚动条必须固定在分页上方。
172
+ - 不要同时让 `body`、页面容器、表格容器三层都滚动;最多保留表格数据区滚动。
173
+
174
+ ### 文本与图标换行控制(强制)
175
+
176
+ 按钮、标签、导航项、状态徽章等**小范围整体区域**内的文本和图标,**禁止自动换行**:
177
+
178
+ ```css
179
+ /* ✅ 不换行,整体在同行显示 */
180
+ .btn-content { white-space: nowrap; }
181
+
182
+ /* ❌ 允许换行 → 宽度不足时文本/图标散落,视觉参差不齐 */
183
+ .btn-content { white-space: normal; } /* 默认即 normal,必须显式覆盖 */
184
+ ```
185
+
186
+ **常见中招场景**:
187
+ - 按钮内文字 + 图标(`提交` + `→` 换行变两行)
188
+ - 操作行内的多个 icon+文字 组合(不同按钮高度不一)
189
+ - 标签/徽章内文字换行导致错位
190
+ - 表单字段名 + 输入框 并排时字段名换行
191
+
192
+ **判断标准**:这块内容在视觉上是一个整体(属于同一个操作、同一个标签、同一个单元),就不应该因为容器宽度被拆开。整行拆行是可以的,但**同组内的子元素不能拆行**。
193
+
194
+ ---
195
+
196
+ ### 移动端与多端适配(按页面目标与影响范围)
197
+
198
+ 移动端适配是产品与受众判断,不是所有页面的固定验收门。公众页面、用户可能从手机访问的业务页面、已有响应式断点,或本次修改涉及布局/CSS/导航/弹窗时,应认真处理窄屏;明确的桌面工作台、仅改文案/数据逻辑、后端或脚本任务,不要机械增加移动端检查。需要窄屏布局时,可以先保证小屏可用,再在宽屏增强信息密度。
199
+
200
+ ---
201
+
202
+ #### 1. 容器宽度:禁止固定像素,用流式布局
203
+
204
+ 内容容器宽度必须随可用空间伸缩,永远不要写死像素宽度:
205
+
206
+ ```css
207
+ /* ✅ */
208
+ .container { width: 100%; max-width: 960px; margin: 0 auto; padding: 0 16px; }
209
+
210
+ /* ❌ 窄屏必然横向溢出 */
211
+ .container { width: 1200px; }
212
+ ```
213
+
214
+ Tailwind:`w-full max-w-4xl mx-auto px-4`
215
+
216
+ ---
217
+
218
+ #### 2. 并排布局:默认纵向堆叠,宽屏才横排
219
+
220
+ 任何"左右并排"的布局,首先决定窄屏下如何堆叠,再决定多宽时切换为横排:
221
+
222
+ ```css
223
+ /* 窄屏纵向,768px+ 横排 */
224
+ .layout { display: flex; flex-direction: column; gap: 16px; }
225
+ @media (min-width: 768px) {
226
+ .layout { flex-direction: row; }
227
+ .sidebar { width: 240px; flex-shrink: 0; }
228
+ .main { flex: 1; min-width: 0; } /* min-width:0 防长内容撑破父容器 */
229
+ }
230
+ ```
231
+
232
+ Tailwind:`flex flex-col md:flex-row gap-4`,主内容区加 `flex-1 min-w-0`。
233
+
234
+ 多列卡片 / 统计块,让列数随宽度自动伸缩:
235
+
236
+ ```css
237
+ .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
238
+ ```
239
+
240
+ Tailwind 固定列数降级写法:`grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4`
241
+
242
+ ---
243
+
244
+ #### 3. 可点击区域:触控友好
245
+
246
+ 手指触摸区域不能太小,按钮、链接、导航项应结合页面密度提供足够内边距和间距,相邻元素避免误触:
247
+
248
+ ```css
249
+ .btn { padding: 8px 16px; display: inline-flex; align-items: center; }
250
+ .nav-item { display: flex; align-items: center; padding: 8px 12px; }
251
+ ```
252
+
253
+ ---
254
+
255
+ #### 4. 文字:正文不缩,标题宽屏再放大
256
+
257
+ 正文 16px 在任何屏幕都合适,不需要响应式。大标题在手机上不要过大,宽屏再逐步放大:
258
+
259
+ ```css
260
+ .title { font-size: 20px; }
261
+ @media (min-width: 1024px) { .title { font-size: 28px; } }
262
+ ```
263
+
264
+ Tailwind:`text-xl lg:text-3xl`
265
+
266
+ ---
267
+
268
+ #### 5. 超宽内容(表格、代码):容器内横滚,不让整页横滚
269
+
270
+ 表格列多时,不能让整个页面出现横向滚动条,必须在容器层限制:
271
+
272
+ ```html
273
+ <div style="overflow-x: auto;">
274
+ <table style="min-width: 640px; width: 100%;">...</table>
275
+ </div>
276
+ ```
277
+
278
+ antd Table 加 `scroll={{ x: 'max-content' }}`,无需额外包裹。
279
+
280
+ ---
281
+
282
+ #### 6. 间距:小屏收紧,大屏舒展
283
+
284
+ ```css
285
+ .page { padding: 16px; }
286
+ @media (min-width: 768px) { .page { padding: 24px 32px; } }
287
+ ```
288
+
289
+ Tailwind:`p-4 md:p-6 lg:p-8`
290
+
291
+ ---
292
+
293
+ #### 7. 弹窗与抽屉:宽度跟屏幕走
294
+
295
+ ```css
296
+ .modal { width: min(90vw, 520px); } /* 窄屏自动收缩 */
297
+ .modal-body { max-height: 60vh; overflow-y: auto; } /* 内容多时内部滚动 */
298
+ ```
299
+
300
+ antd Modal:`width="min(90vw, 520px)"`;抽屉窄屏优先 `placement="bottom"`。
301
+
302
+ ---
303
+
304
+ #### 按需验收参考
305
+
306
+ | 参考宽度 | 适用时的观察项 |
307
+ |---|---|
308
+ | **390px** | 面向手机用户时,观察横向溢出、核心操作和文字截断 |
309
+ | **768px** | 涉及平板或断点切换时,观察重叠和挤压 |
310
+ | **1440px** | 桌面工作台或宽屏页面,观察信息密度和空间利用 |
311
+
312
+ 不要为了收尾机械遍历全部宽度。需要浏览器证据时优先运行 `draftgo verify-ui <url> --mobile-check auto`:默认单个 `390x844` 视口,检查空白页、横向溢出、console/page error 和可选关键元素,仅失败时截图。只有必须依赖用户现有登录会话、浏览器扩展或真实桌面状态时才考虑 computer use。
313
+
314
+ ---
315
+
316
+ ### 导航与系统页边界(强制)
317
+
318
+ - 顶部导航已有内置资源时,通常先读 `.draftgo/navigations/index.json` 和对应 HTML;需要新导航时优先复制/复刻一份再改造,而不是直接改坏内置导航。
319
+ - 导航栏通常要同时考虑未登录 / 已登录 / 管理员三种状态,以及收起 / 展开状态;普通用户不显示管理后台入口,管理员额外显示管理后台入口。
320
+ - 管理端侧边栏通常基于现有内置侧边栏修改,新增业务管理路由优先追加或局部调整;删除系统内置页面入口前先确认影响。
321
+ - 业务页面内可以做局部二级导航或侧边栏,但必须考虑外部导航已存在且可单独配置,避免重复渲染全局导航。
322
+ - 系统内置页面通常不改;确需修改登录、设置、权限、用户、系统配置等页面时,先说明影响、验证方式和保留的管理员能力。
323
+ - 页面风格不要照搬管理侧内置页面;业务前台按业务用户和品牌语境设计,管理侧按操作效率和信息密度设计。
324
+
325
+ ### 新增页面绑定(强制)
326
+
327
+ - 公开页 → 顶部/侧边导航、首页入口、相关按钮之一
328
+ - 后台页 → 后台导航、管理菜单或现有后台入口
329
+ - 多页面功能:列表/详情/新建/编辑/管理必须互相走通
330
+ - 用户明确要求"隐藏页/草稿页"才可不绑定,但必须在 changelog / Task 说明原因
331
+
332
+ ### `page_1_root.html` 首页特例(强制)
333
+
334
+ - 仍是默认内置首页 → 通常按官网介绍、品牌门面和入口聚合重新实现,不沿用内置文案和结构;只有用户明确要“进入即使用”的首页,才把首页当实际工作台
335
+ - 已被用户改过 → 只做要求范围内的修改,不强制重做
336
+ - 判断依据:看 HTML 内容是否保留 DraftGo 默认文案/系统介绍/未定制品牌,不要只凭文件名判断
337
+
338
+ ---
339
+
340
+ ## 必须 / 禁止
341
+
342
+ ### 必须
343
+ - 完整 HTML 文档结构(`<html><head><body>`)
344
+ - 静态资源用本地路径(见下方资源清单)
345
+ - 配色优先使用系统主题语义 token `var(--dg-*)`,并同时适配浅色 / 深色模式
346
+ - 弹窗用 `App.confirm()` / `App.toast()`,禁止 `window.alert/confirm/prompt`
347
+ - 弹窗/抽屉优先做成无外层滚动条,滚动控制在内容区
348
+ - 页面初始渲染展示默认状态(骨架屏),不能因请求延迟空白
349
+
350
+ ### 禁止
351
+ - 需要 CDN 时优先使用国内镜像引入;已有本地资源时优先使用 `/assets/` 本地路径
352
+ - ❌ `window.alert/confirm/prompt`
353
+ - ❌ `App()` 写法 → ✅ `const App = window.parent?.App`
354
+ - ❌ `window.location.search` 读参数 → ✅ `window.__DG_ROUTE_CONTEXT__.query`
355
+ - ❌ 页面内 `window.location.href=...` 跳转 → ✅ `window.parent.location.href=...`
356
+ - ❌ TSX/React 版组件写进数据库 HTML(数据库页面不支持 React)
357
+ - ❌ 硬编码颜色值 → ✅ `var(--dg-*)`
358
+ - ❌ 在业务页面实现全局浮窗/客服/统计脚本 → ✅ `frontend_global_*` 固定槽位
359
+ - ❌ 在业务页面重写 Toast 组件 → ✅ 使用 `App.toast()`,视觉、位置、大小、透明度、时长通过 `frontend_global_toast_*` 系统配置统一调整
360
+
361
+ ---
362
+
363
+ ## 本地静态资源清单
364
+
365
+ | 路径 | 说明 |
366
+ |---|---|
367
+ | `/assets/tailwindcss.js` | Tailwind CSS 运行时 |
368
+ | `/assets/antd/reset.css` | Ant Design 5.29.3 reset CSS(antd 必须) |
369
+ | `/assets/react/react.min.js` | React 18 UMD → `window.React` |
370
+ | `/assets/react-dom/react-dom.min.js` | ReactDOM 18 UMD → `window.ReactDOM` |
371
+ | `/assets/dayjs/dayjs.min.js` | dayjs → antd DatePicker 依赖 |
372
+ | `/assets/antd/antd.min.js` | Ant Design 5.29.3 UMD → `window.antd` |
373
+ | `/assets/fontawesome/css/all.min.css` | FontAwesome 6 |
374
+ | `/assets/icons/{name}.svg` | 内置精选 SVG 图标库(kebab-case 命名) |
375
+ | `/assets/icons/manifest.json` | 图标库映射清单 |
376
+ | `/assets/fonts/inter.css` | Inter 字体 |
377
+ | `/assets/fonts/lexend.css` | Lexend 字体 |
378
+ | `/assets/fonts/plus-jakarta-sans.css` | Plus Jakarta Sans |
379
+ | `/assets/fonts/plus-jakarta-sans-jetbrains-mono.css` | Plus Jakarta Sans + JetBrains Mono |
380
+ | `/assets/vendor/marked/marked.min.js` | Markdown 解析 |
381
+ | `/assets/vendor/highlightjs/highlight.min.js` | 代码语法高亮(highlight.js) |
382
+ | `/assets/vendor/prism/prism.min.js` | 轻量代码高亮(Prism.js) |
383
+ | `/assets/vendor/dompurify/purify.min.js` | HTML 净化,渲染用户输入前必须过一遍 |
384
+ | `/assets/vendor/html2canvas/html2canvas.min.js` | html2canvas 1.4.1,页面截图 / DOM 导出图片 |
385
+ | `/assets/draftgo-chat.js` | DraftGo Chat SDK 完整版,注册 `<dg-chat>` 与 `DraftGoChat` |
386
+ | `/assets/vendor/gsap/gsap.min.js` | GSAP 核心(先于其他 GSAP 插件加载) |
387
+ | `/assets/vendor/gsap/ScrollTrigger.min.js` | GSAP ScrollTrigger |
388
+ | `/assets/vendor/gsap/ScrollToPlugin.min.js` | GSAP ScrollTo |
389
+ | `/assets/vendor/gsap/Draggable.min.js` | GSAP Draggable |
390
+ | `/assets/vendor/gsap/Flip.min.js` | GSAP Flip |
391
+ | `/assets/vendor/gsap/SplitText.min.js` | GSAP SplitText |
392
+ | `/assets/vendor/gsap/MorphSVGPlugin.min.js` | GSAP MorphSVG |
393
+ | `/assets/vendor/gsap/MotionPathPlugin.min.js` | GSAP MotionPath |
394
+ | `/assets/vendor/gsap/DrawSVGPlugin.min.js` | GSAP DrawSVG |
395
+ | `/assets/vendor/gsap/Observer.min.js` | GSAP Observer(统一手势/滚动监听) |
396
+ | `/assets/vendor/gsap/EasePack.min.js` | GSAP 扩展缓动(Bounce/Elastic 等) |
397
+ | `/assets/vendor/gsap/CustomEase.min.js` | GSAP 自定义缓动曲线 |
398
+ | `/assets/vendor/gsap/TextPlugin.min.js` | GSAP 打字机效果 |
399
+
400
+ **图标用法**:
401
+ ```html
402
+ <!-- 跟随文字颜色(推荐) -->
403
+ <span aria-hidden="true" style="width:16px;height:16px;display:inline-block;background:currentColor;-webkit-mask:url('/assets/icons/fingerprint.svg') center/contain no-repeat;mask:url('/assets/icons/fingerprint.svg') center/contain no-repeat;"></span>
404
+ <!-- 保留原色 -->
405
+ <img src="/assets/icons/fingerprint.svg" alt="" width="16" height="16">
406
+ ```
407
+
408
+ AI 模型 / Provider 品牌图标可以使用 LobeHub 静态图标的 npmmirror 镜像 CDN。优先固定版本号,避免 `latest` 变更导致图标漂移;只在模型、供应商、AI 服务品牌标识场景使用,不替代通用 UI 图标。
409
+
410
+ ```html
411
+ <!-- SVG,保留品牌原色 -->
412
+ <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/1.91.0/files/icons/openai.svg" alt="OpenAI" width="18" height="18">
413
+ <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/1.91.0/files/icons/anthropic.svg" alt="Anthropic" width="18" height="18">
414
+ <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/1.91.0/files/icons/deepseek-color.svg" alt="DeepSeek" width="18" height="18">
415
+ ```
416
+
417
+ ---
418
+
419
+ ## Ant Design 使用规范
420
+
421
+ Ant Design 5.29.3 已内置为 UMD 包(`window.antd`),可优先选择使用。
422
+
423
+ > `Segmented` 的默认样式视觉厚重、组件库感过强,禁止原样用于成品界面;优先根据语义改用 `Tabs`、`Radio.Group` 等更合适的控件,确需使用时必须通过 `ConfigProvider`、design token 或组件公开 API 调整为与页面一致的轻量样式。
424
+
425
+ ### ① 依赖加载顺序(缺一不可,顺序不可乱)
426
+
427
+ ```html
428
+ <link href="/assets/antd/reset.css" rel="stylesheet">
429
+ <script src="/assets/react/react.min.js"></script>
430
+ <script src="/assets/react-dom/react-dom.min.js"></script>
431
+ <script src="/assets/dayjs/dayjs.min.js"></script>
432
+ <script src="/assets/antd/antd.min.js"></script>
433
+ ```
434
+
435
+ > 资源版本固定为 5.29.3。只加载 `/assets/antd/reset.css` 和 UMD JS;组件样式由 UMD 运行时注入,禁止额外加载或引用 `/assets/antd/antd.min.css`,也禁止混用其他版本的 Ant Design CSS。
436
+
437
+ ### ② 强烈建议:跟随 DraftGo 系统主题
438
+
439
+ 用 `antd.ConfigProvider` 包裹根组件,读取 `App.theme` 和 CSS 变量 `--dg-accent`,实现 dark/light 自动切换和主题色联动:
440
+
441
+ ```javascript
442
+ const App = window.parent?.App;
443
+
444
+ function getAntdThemeConfig() {
445
+ const isDark = App?.theme === 'dark';
446
+ const accent = getComputedStyle(document.documentElement)
447
+ .getPropertyValue('--dg-accent').trim() || '#27272a';
448
+ return {
449
+ algorithm: isDark ? antd.theme.darkAlgorithm : antd.theme.defaultAlgorithm,
450
+ token: { colorPrimary: accent }
451
+ };
452
+ }
453
+
454
+ const Root = () => {
455
+ const [themeConfig, setThemeConfig] = React.useState(getAntdThemeConfig);
456
+ React.useEffect(() => {
457
+ const ob = new MutationObserver(() => setThemeConfig(getAntdThemeConfig()));
458
+ ob.observe(document.documentElement, {
459
+ attributes: true, attributeFilter: ['style', 'class', 'data-theme']
460
+ });
461
+ return () => ob.disconnect();
462
+ }, []);
463
+ return React.createElement(antd.ConfigProvider, { theme: themeConfig },
464
+ React.createElement(YourApp)
465
+ );
466
+ };
467
+ ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(Root));
468
+ ```
469
+
470
+ ### ③ 组件状态样式的单一所有权
471
+
472
+ 交互组件的 hover、focus、disabled、error 等状态,在同一视觉属性上只能由一个层级负责。组件库已经管理内部 DOM 和状态样式时,业务页不得再以 CSS 介入同一状态的边框、轮廓、阴影、背景或颜色;否则 CSS 层叠会把两套状态效果同时呈现,或在组件升级后产生不可预期的覆盖。
473
+
474
+ - 使用 AntD 等组件库时,组件库是状态样式的所有者。主题联动和样式调整走 `ConfigProvider`、design token 或组件公开的配置 API,不为其内部结构补写全局或通用选择器。
475
+ - 自建组件时,业务页才是状态样式的所有者;状态规则必须收敛到该组件的专有类或页面根容器内,不能影响其他组件或页面。
476
+ - 改动前先确认所有权;同一状态属性不得同时由 token/API 和 CSS 覆盖定义。若必须接管第三方组件样式,先移除或禁用原有对应效果,再用有作用域的规则完整接管。
477
+
478
+ ### ④ 强烈建议:弹窗和 Toast 走 DraftGo 系统
479
+
480
+ | antd 原生 | 改用 |
481
+ |-----------|------|
482
+ | `Modal.confirm(...)` | `await App.confirm(msg, title)` |
483
+ | `Modal.info / warning(...)` | `App.showModal(msg, title)` |
484
+ | `message.success(...)` | `App.showSuccess(msg)` |
485
+ | `message.error(...)` | `App.showError(msg)` |
486
+ | `message.info / warning(...)` | `App.showInfo / showWarning(msg)` |
487
+
488
+ Tooltip(hover 浮层)无对应 App API,保留 antd 原生。
489
+
490
+ ### ⑤ 禁止 ESM 方式加载 antd
491
+
492
+ ```html
493
+ <!-- ❌ 禁止 —— antd ESM 版 Button 是 object 而非 React 组件 -->
494
+ <script type="module">
495
+ import * as antd from 'https://cdn.jsdelivr.net/npm/antd@6.x/+esm';
496
+ </script>
497
+ ```
498
+
499
+ ---
500
+
501
+ ## 空状态(Empty State)规范
502
+
503
+ **触发:页面需要展示"暂无数据"、"列表为空"等空状态**
504
+
505
+ ### ❌ 禁止写法(图标偏左 bug)
506
+
507
+ ```javascript
508
+ // 错误:display:block 使 <i> 变为块级元素,text-align:center 对块级元素无效 → 图标偏左
509
+ h('div', { style: { textAlign: 'center' } },
510
+ h('i', { className: 'fa-solid fa-plug', style: { display: 'block' } }),
511
+ '暂无数据'
512
+ )
513
+ ```
514
+
515
+ ### ✅ antd 页面:用 `antd.Empty`(首选)
516
+
517
+ ```javascript
518
+ const { Empty } = antd;
519
+
520
+ // Table 的 locale.emptyText
521
+ locale={{ emptyText: React.createElement(Empty, {
522
+ image: Empty.PRESENTED_IMAGE_SIMPLE,
523
+ description: '暂无数据'
524
+ }) }}
525
+
526
+ // 独立空状态区域
527
+ React.createElement(Empty, {
528
+ image: Empty.PRESENTED_IMAGE_SIMPLE,
529
+ description: '暂无 API,点击右上角「新增」开始注册'
530
+ })
531
+ ```
532
+
533
+ ### ✅ 非 antd 页面 / 自定义样式:flexbox 容器(禁止用 text-align 居中图标)
534
+
535
+ ```html
536
+ <style>
537
+ .dg-empty {
538
+ display: flex; flex-direction: column;
539
+ align-items: center; justify-content: center;
540
+ padding: 48px 24px; gap: 10px;
541
+ color: var(--dg-text-muted);
542
+ }
543
+ .dg-empty-icon { font-size: 28px; opacity: .4; }
544
+ .dg-empty-text { font-size: 13px; }
545
+ </style>
546
+
547
+ <div class="dg-empty">
548
+ <i class="fa-solid fa-plug dg-empty-icon"></i>
549
+ <span class="dg-empty-text">暂无数据</span>
550
+ </div>
551
+ ```
552
+
553
+ ```javascript
554
+ // React h() 写法
555
+ h('div', { style: { display:'flex', flexDirection:'column', alignItems:'center',
556
+ justifyContent:'center', padding:'48px 24px', gap:10, color:'var(--dg-text-muted)' } },
557
+ h('i', { className: 'fa-solid fa-plug', style: { fontSize:28, opacity:.4 } }),
558
+ h('span', { style: { fontSize:13 } }, '暂无数据')
559
+ )
560
+ ```
561
+
562
+ > **核心原则**:图标居中必须用 `align-items: center`(flexbox),不能依赖 `text-align: center` + `display: block`。
563
+
564
+ ---
565
+
566
+ ## GSAP 动效规范
567
+
568
+ **做页面时,尽可能引入 GSAP 提升体验感**。GSAP 是 DraftGo 内置动效库,适合入场动画、滚动交互、状态切换、数字滚动等所有需要"动起来"的场景。
569
+
570
+ 加载规则:
571
+ - 核心 `gsap.min.js` 必须首先加载,其余插件按需选加
572
+ - 使用插件前必须调用 `gsap.registerPlugin(...)`
573
+ - GSAP 优先使用 `/assets/vendor/gsap/` 本地路径;确需 CDN 时优先使用国内镜像引入
574
+
575
+ ```html
576
+ <script src="/assets/vendor/gsap/gsap.min.js"></script>
577
+ <script src="/assets/vendor/gsap/ScrollTrigger.min.js"></script>
578
+ <script>
579
+ gsap.registerPlugin(ScrollTrigger);
580
+ </script>
581
+ ```
582
+
583
+ ### 常用模式速查
584
+
585
+ **页面入场(卡片/列表逐个淡入上移)**
586
+ ```javascript
587
+ gsap.from('.card', {
588
+ y: 30, opacity: 0, duration: 0.5, stagger: 0.08,
589
+ ease: 'power2.out', clearProps: 'all'
590
+ });
591
+ ```
592
+
593
+ **滚动触发(元素进入视口时播放)**
594
+ ```javascript
595
+ gsap.from('.section', {
596
+ scrollTrigger: { trigger: '.section', start: 'top 80%' },
597
+ y: 40, opacity: 0, duration: 0.6, ease: 'power3.out'
598
+ });
599
+ ```
600
+
601
+ **Timeline 编排(多步骤有序动画)**
602
+ ```javascript
603
+ const tl = gsap.timeline({ defaults: { ease: 'power2.out', duration: 0.4 } });
604
+ tl.from('.title', { y: -20, opacity: 0 })
605
+ .from('.subtitle', { y: -10, opacity: 0 }, '-=0.2')
606
+ .from('.btn', { scale: 0.9, opacity: 0 }, '-=0.1');
607
+ ```
608
+
609
+ **数字滚动(计数器)**
610
+ ```javascript
611
+ gsap.to({ val: 0 }, {
612
+ val: 9527, duration: 1.5, ease: 'power1.out',
613
+ onUpdate() { el.textContent = Math.round(this.targets()[0].val).toLocaleString(); }
614
+ });
615
+ ```
616
+
617
+ **按钮 hover 微交互**
618
+ ```javascript
619
+ document.querySelectorAll('.btn').forEach(btn => {
620
+ btn.addEventListener('mouseenter', () => gsap.to(btn, { scale: 1.04, duration: 0.2 }));
621
+ btn.addEventListener('mouseleave', () => gsap.to(btn, { scale: 1, duration: 0.2 }));
622
+ });
623
+ ```
624
+
625
+ **SplitText 逐字入场**
626
+ ```javascript
627
+ gsap.registerPlugin(SplitText);
628
+ const split = new SplitText('.headline', { type: 'chars' });
629
+ gsap.from(split.chars, { opacity: 0, y: 20, stagger: 0.04, duration: 0.5, ease: 'back.out(1.7)' });
630
+ ```
631
+
632
+ ### 注意事项
633
+
634
+ - 动画时长:微交互 0.15–0.25s,元素入场 0.4–0.6s,页面级过渡 0.6–1s
635
+ - `clearProps: 'all'` 适合只播一次的入场动画,避免 inline style 污染后续样式
636
+ - 页面卸载时清理:`ScrollTrigger.getAll().forEach(t => t.kill())`
637
+ - 不要同一元素混用 CSS transition 和 GSAP tween(会产生竞争)
638
+ - 不要为动效而动效;动画必须服务于体验,不能影响可用性
639
+
640
+ ---
641
+
642
+ ## 颜色 Token(强制)
643
+
644
+ | Token | 用途 |
645
+ |---|---|
646
+ | `--dg-bg-base` / `--dg-bg-page` / `--dg-bg-surface` | 背景层级 |
647
+ | `--dg-text-primary` / `--dg-text-secondary` / `--dg-text-muted` | 文字层级 |
648
+ | `--dg-accent` / `--dg-accent-hover` / `--dg-accent-subtle` | 主题色 |
649
+ | `--dg-border` / `--dg-success` / `--dg-error` / `--dg-warning` | 功能色 |
650
+
651
+ 配色决策顺序:
652
+
653
+ - 默认优先采用系统主题 token,让页面继承当前站点的 `App.theme` 与 `App.colorScheme`
654
+ - 页面与系统 token 明显不搭配、品牌要求、用户明确指定或图表需要区分多系列时,允许使用自主配色
655
+ - 自主配色必须提供浅色与深色两套变量或覆盖,不能只在当前主题下看起来正常
656
+ - 自主配色仍优先落成页面局部 CSS 变量,再由组件引用,避免把 hex / rgb 散落在样式里
657
+ - 所有正文、按钮、输入框、状态提示的文字对比度必须达到 WCAG AA(正文 4.5:1)
658
+
659
+ 例外:品牌色、图表色。硬编码时必须同时提供 `[data-theme="dark"]` 覆盖;如浅色主题也偏离系统 token,应同时提供 `[data-theme="light"]` 或默认变量。
660
+
661
+ 主题机制:壳层注入时已把 CSS 变量写入 `<head>`,页面无需初始化,直接用 `var(--dg-*)` 即可。
662
+
663
+ ```javascript
664
+ // 读取(仅需 JS 分支时才读)
665
+ const theme = App?.theme; // 'light' | 'dark'
666
+ const scheme = App?.colorScheme; // 'dark-gray-white' | 'deep-blue-white' | ...
667
+
668
+ // 切换
669
+ App.applyTheme('dark');
670
+ App.setColorScheme('deep-blue-white');
671
+ ```
672
+
673
+ ---
674
+
675
+ ## 导航栏开发
676
+
677
+ | 属性 | 说明 |
678
+ |---|---|
679
+ | `data-nav-position="top\|side"` | 根元素必填 |
680
+ | `data-nav-width="220px"` | 侧边栏展开宽度(可选,默认 260px) |
681
+ | `data-nav-collapsed-width="64px"` | 收起宽度(可选,默认 72px) |
682
+
683
+ ```html
684
+ <aside data-nav-position="side" data-nav-width="220px">
685
+ <a href="/dashboard" data-page-route="/dashboard">仪表盘</a>
686
+ </aside>
687
+ ```
688
+
689
+ 导航链接推荐用 `data-page-route` 实现路由感知跳转,避免硬编 `onclick`。
690
+
691
+ 导航栏设计建议:
692
+ - 未登录态可突出登录 / 注册 / 介绍入口
693
+ - 已登录态显示常用业务入口和个人入口
694
+ - 管理员态额外显示后台入口,但不要让普通用户看到管理入口
695
+ - 收起状态保留最小可识别导航,不要只剩装饰图标
696
+
697
+ ---
698
+
699
+ ## AIHub 页面 SDK
700
+
701
+ 新页面需要 AI 对话 UI 时,加载完整版脚本并使用原生 `<dg-chat>`:
702
+
703
+ ```html
704
+ <script src="/assets/draftgo-chat.js"></script>
705
+ <dg-chat protocol="draftgo-agent" agent-id="AGENT_ID"></dg-chat>
706
+ ```
707
+
708
+ 需要动态创建时使用 `DraftGoChat.create()`:
709
+
710
+ ```javascript
711
+ const chat = DraftGoChat.create('#chat-host', {
712
+ protocol: 'draftgo-agent',
713
+ agentId,
714
+ surface: 'inline',
715
+ view: 'conversation'
716
+ });
717
+ ```
718
+
719
+ 完整配置、协议、事件、历史、扩展与安全约束见 `references/chat-sdk.md`。
720
+
721
+ `DraftGoAI` 是同一脚本提供的旧代码兼容门面,只用于没有对话 UI 的轻量调用或图片生成:
722
+
723
+ ```javascript
724
+ // 聊天(默认流式;onDelta(delta, full) 每个增量触发,Promise resolve 完整文本)
725
+ const text = await DraftGoAI.chat(agentId, '你好', (delta, full) => render(full));
726
+
727
+ // 多轮:传 sessionId 复用会话(→ body.session_id);Agent 开启“持续对话”后服务端续写历史。不传 = 无状态单轮
728
+ await DraftGoAI.chat(agentId, '接着上一条', handler, { sessionId: threadKey });
729
+
730
+ // 图片生成(必须用 /images 接口,不要用 /chat)
731
+ const result = await DraftGoAI.images(agentId, '生成主图', { size: '1024x1024', n: 1 });
732
+
733
+ // 用户选模型
734
+ const { user_selectable, models } = await DraftGoAI.getSelectableModels(agentId);
735
+ await DraftGoAI.chat(agentId, '你好', handler, { model: selectedModel, sessionId: threadKey });
736
+ ```
737
+
738
+ - 使用 `DraftGoAI` 前同样必须先加载 `/assets/draftgo-chat.js`;壳层不会默认注入。
739
+ - `DraftGoAI.chat` 内部创建隐藏 `<dg-chat>`,始终流式。新页面的可见对话、附件、模型选择器、推理、历史和重生成直接使用 `<dg-chat>`。
740
+ - Agent 的能力与 `spec` 字段(工具/子智能体/记忆/结构化输出/多模态/持续对话/ttft failover)见 `references/aihub.md`。
741
+
742
+ ---
743
+
744
+ ## 退出登录
745
+
746
+ ```javascript
747
+ window.parent.location.href = '/login'; // ✅ 完整刷新,导航栏重新加载
748
+ // App.navigate('/login') ❌ 导航栏不会更新
749
+ ```
750
+
751
+ ---
752
+
753
+ ## 加载体验
754
+
755
+ - 初始渲染必须先展示骨架屏,再异步填数据
756
+ - 禁止因请求失败导致整页空白
757
+ - 空态必须有明确文案 + 后续操作入口,不能留空或只有 loading
758
+ - 若父级是 flex 工作区,空态容器应 `flex:1` 承接剩余空间,避免高度塌陷
759
+ - 弹窗/抽屉内容区建议隐藏外层滚动条,把滚动控制在内容区:`overflow:hidden` + 内部 `overflow:auto; min-height:0`,避免双滚动条和粗糙滚动体验
760
+
761
+ ## 选择器与表单体验
762
+
763
+ - 涉及下拉选项样式时,优先做自定义弹层菜单,确保触发态、浮层、选中态和禁用态都与页面风格一致
764
+ - 若确实使用原生控件,只能作为退化方案,不作为首选风格;不要把系统默认下拉样式当作最终交付
765
+
766
+ ---
767
+
768
+ ## 平台能力调用快速参考
769
+
770
+ ```javascript
771
+ const App = window.parent?.App;
772
+
773
+ // 响应信封:{ code: 200, data: <载荷>, message: "success" }
774
+ const res = await App.get('pages'); // 全量列表;表格/后台列表建议显式传 page + page_size
775
+ if (res.code !== 200) { App.showError(res.message); return; }
776
+ const items = res.data.items;
777
+
778
+ // 非后台管理页面读取动态 DB 业务列表:
779
+ // 当前用户是管理员时带 scope=mine,避免业务视角拿到全量 owner 数据。
780
+ const params = { page: 1, page_size: 20 };
781
+ if (App.isAdmin && !App.getCurrentRoute?.()?.startsWith('/admin')) params.scope = 'mine';
782
+ const dbRes = await App.get('db/order', params);
783
+
784
+ // URL 参数(三阶回落)
785
+ const q = (window.__DG_ROUTE_CONTEXT__ || window.__DG_GET_ROUTE_CONTEXT__?.() || App?.getCurrentRouteContext?.() || { query: {} }).query;
786
+ ```
787
+
788
+ 完整 API 表 → `{{SKILL_DIR}}/references/app-api.md`