draftgo-cli 3.0.1 → 3.0.29

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