@gadmin2n/schematics 0.0.95 → 0.0.97

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 (43) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/DESIGN.md +348 -0
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/PRODUCT.md +75 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +5 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +24 -1
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.ts +32 -3
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.service.ts +28 -6
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-dsl-validate.spec.ts +220 -0
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-dsl-validate.ts +129 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.dto.ts +1 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.service.ts +4 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +46 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +27 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +6 -0
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/node-types.ts +43 -4
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/validate.ts +109 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/validate.test.ts +205 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +1 -1
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/promptGenerator.ts +19 -3
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/header.tsx +55 -56
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/layout.tsx +7 -3
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/logo.tsx +7 -1
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +179 -160
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +34 -31
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +24 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +24 -2
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +24 -2
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +173 -1
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +27 -2
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +29 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasConfigRegistry.tsx +2 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +98 -3
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableConfigModal.tsx +192 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/utils/tableCodeUtils.ts +338 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +66 -51
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +7 -1
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.tsx +66 -26
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +7 -1
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +9 -2
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useWorkflowAgent.ts +30 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +9 -2
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.ts +27 -0
  43. package/package.json +1 -1
@@ -0,0 +1,348 @@
1
+ ---
2
+ name: Gadmin2 (gadmin-test instance)
3
+ description: Internal lowcode admin generator — calm, dense, information-first
4
+ colors:
5
+ blueprint-blue: "#1677FF"
6
+ signal-vermilion: "#fa541c"
7
+ reading-ink: "#262626"
8
+ reading-ink-soft: "#595959"
9
+ reading-ink-mute: "#8c8c8c"
10
+ surface: "#FFFFFF"
11
+ surface-mist: "#FAFAFA"
12
+ surface-quiet: "#F7F8F9"
13
+ workbench-slate: "#F0F2F5"
14
+ drawer-fog: "#F5F5F5"
15
+ rule: "#D9D9D9"
16
+ rule-soft: "#E8E9EA"
17
+ hold-amber: "#FFFBE6"
18
+ typography:
19
+ display:
20
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
21
+ fontSize: "38px"
22
+ fontWeight: 600
23
+ lineHeight: 1.21
24
+ letterSpacing: "-0.01em"
25
+ headline:
26
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
27
+ fontSize: "30px"
28
+ fontWeight: 600
29
+ lineHeight: 1.26
30
+ letterSpacing: "normal"
31
+ title:
32
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
33
+ fontSize: "20px"
34
+ fontWeight: 600
35
+ lineHeight: 1.4
36
+ letterSpacing: "normal"
37
+ body:
38
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
39
+ fontSize: "14px"
40
+ fontWeight: 400
41
+ lineHeight: 1.5
42
+ letterSpacing: "normal"
43
+ label:
44
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
45
+ fontSize: "12px"
46
+ fontWeight: 400
47
+ lineHeight: 1.66
48
+ letterSpacing: "normal"
49
+ rounded:
50
+ sm: "2px"
51
+ md: "6px"
52
+ lg: "8px"
53
+ spacing:
54
+ xs: "4px"
55
+ sm: "8px"
56
+ md: "16px"
57
+ lg: "24px"
58
+ xl: "32px"
59
+ components:
60
+ button-primary:
61
+ backgroundColor: "{colors.blueprint-blue}"
62
+ textColor: "{colors.surface}"
63
+ rounded: "{rounded.md}"
64
+ padding: "4px 15px"
65
+ typography: "{typography.body}"
66
+ button-default:
67
+ backgroundColor: "{colors.surface}"
68
+ textColor: "{colors.reading-ink}"
69
+ rounded: "{rounded.md}"
70
+ padding: "4px 15px"
71
+ typography: "{typography.body}"
72
+ button-text:
73
+ backgroundColor: "transparent"
74
+ textColor: "{colors.reading-ink}"
75
+ rounded: "{rounded.md}"
76
+ padding: "4px 15px"
77
+ typography: "{typography.body}"
78
+ input-default:
79
+ backgroundColor: "{colors.surface}"
80
+ textColor: "{colors.reading-ink}"
81
+ rounded: "{rounded.md}"
82
+ padding: "4px 11px"
83
+ typography: "{typography.body}"
84
+ card:
85
+ backgroundColor: "{colors.surface}"
86
+ textColor: "{colors.reading-ink}"
87
+ rounded: "{rounded.lg}"
88
+ padding: "24px"
89
+ card-header:
90
+ backgroundColor: "{colors.surface-mist}"
91
+ textColor: "{colors.reading-ink}"
92
+ rounded: "{rounded.lg}"
93
+ padding: "16px 24px"
94
+ typography: "{typography.title}"
95
+ table-cell:
96
+ backgroundColor: "{colors.surface}"
97
+ textColor: "{colors.reading-ink}"
98
+ padding: "12px 16px"
99
+ typography: "{typography.body}"
100
+ table-header-sorted:
101
+ backgroundColor: "{colors.surface-mist}"
102
+ textColor: "{colors.reading-ink}"
103
+ padding: "12px 16px"
104
+ typography: "{typography.label}"
105
+ modal:
106
+ backgroundColor: "{colors.surface}"
107
+ textColor: "{colors.reading-ink}"
108
+ rounded: "{rounded.lg}"
109
+ padding: "20px 24px"
110
+ app-header:
111
+ backgroundColor: "{colors.surface}"
112
+ textColor: "{colors.reading-ink}"
113
+ height: "64px"
114
+ padding: "0 24px"
115
+ layout:
116
+ backgroundColor: "{colors.workbench-slate}"
117
+ textColor: "{colors.reading-ink}"
118
+ ---
119
+
120
+ # Design System: Gadmin2
121
+
122
+ ## 1. Overview
123
+
124
+ **Creative North Star: "The Quiet Workbench"**
125
+
126
+ Gadmin2 是内部使用的低代码管理后台生成器,它生成的页面服务于一线运营和业务分析师——他们一天里要打开十几个不同的资源页面,处理订单、工单、活动、权限审计、工作流。设计的姿态是「**安静的工作台**」:屏幕上的主角永远是表格、表单、图表本身,包裹它们的卡片、按钮、标题越透明越好;视觉系统的工作不是「让人惊艳」,而是「让人能在不被打扰的状态下连续工作四个小时」。
127
+
128
+ 这个系统直接继承 Ant Design v5 + RefineThemes.Blue 的取舍:克制的蓝色为唯一品牌信号、原生系统字体栈、6px 圆角、近乎平面的阴影策略。它**刻意**不做品牌特征化——不引入定制字体、不堆叠彩色徽章、不堆装饰图形。专业感来自取舍,不来自堆砌。
129
+
130
+ 它明确拒绝四种气质:传统重型企业系统(渐变顶栏 + 装饰图标 + 视觉负担)、通用 B2B SaaS 模板(渐变 hero + 大数字指标块 + 千篇一律的卡片网格)、免费后台模板(彩色侧边栏 + 状态徽章铺满 + 装饰性 side-stripe)、毛玻璃/炫技工具风(blur + 渐变文字 + 为「酷」的深色主题)。这些都和「内部工具应该让人专注于工作」这件事相悖。
131
+
132
+ **Key Characteristics:**
133
+
134
+ - 数据优先于装饰:屏幕的主角是表格、表单、图表
135
+ - 安静的密度:靠间距和字重承载信息层级,不靠颜色和分隔线
136
+ - 单一品牌信号:Blueprint Blue 是 chrome 上唯一的彩色,覆盖 ≤10% 屏幕
137
+ - 平面默认:阴影只在 Modal、Dropdown、Tooltip 等真正浮起的元素出现
138
+ - 原生字体:用户 OS 提供的字体栈承担可读性,不引入品牌字体
139
+
140
+ ## 2. Colors
141
+
142
+ 调色板的字符是「冷静的灰阶 + 一抹工程蓝」。所有颜色按角色——而不是色相——组织。
143
+
144
+ ### Primary
145
+
146
+ - **Blueprint Blue** (`#1677FF`):唯一品牌信号色。出现在主操作按钮(Create / Save)、链接、当前选中导航项、表单焦点描边、关键数据图表的强调线上。任何屏幕上 ≤10%,超过就是滥用。它的稀有性本身就是它的价值。
147
+
148
+ ### Secondary
149
+
150
+ - **Signal Vermilion** (`#FA541C`):错误与破坏性操作的专属颜色。用于错误提示文字、删除按钮、必填校验信息。**不**用于「警告」或「待处理」状态——那些状态用图标 + 文字 + amber 底色表达,不进入主色板。
151
+
152
+ ### Neutral (Ink Ramp)
153
+
154
+ 文字三档,由强到弱:
155
+
156
+ - **Reading Ink** (`#262626`,等价 `rgba(0,0,0,0.85)`):正文主色,列表标题、表格主字段、表单标签。
157
+ - **Reading Ink Soft** (`#595959`,等价 `rgba(0,0,0,0.65)`):次要描述、表格副字段、说明文字。
158
+ - **Reading Ink Mute** (`#8C8C8C`,等价 `rgba(0,0,0,0.45)`):分页统计、占位提示、时间戳元数据。
159
+
160
+ ### Neutral (Surface Ramp)
161
+
162
+ 容器四档,由「外」到「内」:
163
+
164
+ - **Workbench Slate** (`#F0F2F5`):最外层 layout 背景,舞台之外。
165
+ - **Drawer Fog** (`#F5F5F5`):Show 详情页 / Drawer 容器,承载多个白色块。
166
+ - **Surface Quiet** (`#F7F8F9`):默认容器底(Refine 默认 colorBgContainer)。
167
+ - **Surface** (`#FFFFFF`):所有「真正承载内容」的表面——表格、表单输入、Modal、Card body。
168
+ - **Surface Mist** (`#FAFAFA`):Card 头、表格当前排序列高亮,比 Surface 暗一档作为分组提示。
169
+
170
+ ### Neutral (Rules)
171
+
172
+ - **Rule** (`#D9D9D9`):Modal header/footer 分隔线、Input 描边。1px。
173
+ - **Rule Soft** (`#E8E9EA`):表格内部分隔、低优先级的视觉切分。
174
+
175
+ ### Tertiary (Holds)
176
+
177
+ - **Hold Amber** (`#FFFBE6`):单元格级的注意力锚点(如 cost-center 高亮、待审条目)。**仅作为底色**,不作为字色或描边色。
178
+
179
+ ### Named Rules
180
+
181
+ **The One Voice Rule.** Blueprint Blue 是 chrome 上唯一的品牌色。任何屏幕的彩色覆盖率 ≤10%。如果一个屏幕里同时有蓝色按钮、蓝色标签、蓝色图标、蓝色进度条——你已经在堆砌,回去删掉两个。
182
+
183
+ **The Ink Ramp Rule.** 所有文字颜色必须从 Reading Ink / Soft / Mute 三档里选。**禁止**新增灰色(不要 #666、#999、#aaa、#bbb、#ccc);这三档已经覆盖了主/次/弱三种语义。淡灰文字「显得高级」是设计反模式,不通过 review。
184
+
185
+ **The Status Color Reserve Rule.** 颜色不是一线状态信号。状态用图标 + 文字标签表达,颜色(绿/黄/红)只作为辅助。色盲用户与黑白打印场景必须能区分。
186
+
187
+ ## 3. Typography
188
+
189
+ **Display Font**: 原生系统字体栈(`-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`)
190
+ **Body Font**: 同上
191
+ **Label/Mono Font**: 不区分(mono 仅在 SQL Modal、JSON Editor 等技术上下文里出现,使用 Monaco/Consolas 默认)
192
+
193
+ **Character**: 字体系统刻意不做选型。它依赖每个用户 OS 自带的字体——macOS 上是 SF Pro,Windows 上是 Segoe UI,Linux 上是 Roboto——以保证「在用户最熟悉的字体下读他们最熟悉的工作」。这是工具,不是品牌。
194
+
195
+ ### Hierarchy
196
+
197
+ | 角色 | 大小 / 行高 | 字重 | 用途 |
198
+ |------|------------|------|------|
199
+ | **Display** (`38px / 1.21`) | 600 | Dashboard 大数字指标(KPI 单值) |
200
+ | **Headline** (`30px / 1.26`) | 600 | 页面级标题(极少使用,多数页面没有大标题) |
201
+ | **Title** (`20px / 1.4`) | 600 | Section heading、Drawer 标题、Modal 标题 |
202
+ | **Body** (`14px / 1.5`) | 400 | 默认正文,所有表格、表单、按钮文字。最大行长 65–75ch(说明性段落) |
203
+ | **Label** (`12px / 1.66`) | 400 | Caption、辅助文字、分页统计、时间戳 |
204
+
205
+ 正文 body **不**使用 16px——Ant Design v5 的默认正文是 14px,这与「高密度信息工作」的姿态一致。Form labelFontSize 也固定 14px。
206
+
207
+ ### Named Rules
208
+
209
+ **The Native Type Rule.** **禁止**引入品牌字体(不引入 Inter / Noto Sans / Plus Jakarta / 任何 Google Fonts)。系统字体栈是唯一选择。理由:①包体最小、②在用户母语 OS 下显示最清晰、③它本身就是「不为品牌强行表达」的姿态。
210
+
211
+ **The No Display-By-Default Rule.** Display 38px 仅用于 Dashboard 单值大数字。**不**用于页面标题、Hero 文字、营销宣传——这是工具不是着陆页。多数 CRUD 页面的最高级别只有 Title 20px。
212
+
213
+ **The Letter-Spacing Off Rule.** 不使用 letter-spacing 自定义。**禁止**小字大写带 tracking 的「ABOUT / PROCESS」式 eyebrow——它是 SaaS 着陆页的语法,与内部管理工具无关。
214
+
215
+ ## 4. Elevation
216
+
217
+ **Flat-by-default**。系统的默认状态是平面:表格、表单、Card body 都没有阴影;层级靠浅灰底(Surface Ramp 四档)切分。阴影只用于「真正浮起、覆盖在内容之上」的元素——Modal、Dropdown、Popover、Tooltip、Notification——这是 Ant Design v5 出厂浮层默认值,不在业务层覆盖。
218
+
219
+ 页面分组主要靠 Drawer Fog (`#F5F5F5`) 容器 + 多个 Surface (`#FFFFFF`) 白块的组合,不靠 box-shadow。
220
+
221
+ ### Shadow Vocabulary (Ant 默认浮层)
222
+
223
+ - **Popup**(`box-shadow: 0 6px 16px 0 rgba(0,0,0,0.08), 0 3px 6px -4px rgba(0,0,0,0.12), 0 9px 28px 8px rgba(0,0,0,0.05)`):Dropdown / Select Panel / Popover / Date Picker。
224
+ - **Dialog**(`box-shadow: 0 6px 16px 0 rgba(0,0,0,0.08), 0 3px 6px -4px rgba(0,0,0,0.12), 0 9px 28px 8px rgba(0,0,0,0.05)`):Modal、Drawer。
225
+
226
+ 业务层不在 inline style 里写新的 box-shadow。
227
+
228
+ ### Named Rules
229
+
230
+ **The Flat-By-Default Rule.** 表面默认平面。**禁止**给 Card / Section / Panel 加 box-shadow 营造「卡片浮起」效果——那是免费后台模板风。如果需要分组,用 Surface Ramp 的浅灰底层切,不用阴影。
231
+
232
+ **The Floating-Means-Floating Rule.** 阴影只表达「真正浮在 DOM 流之上」的元素:Modal、Drawer、Dropdown、Popover、Tooltip、Notification。Card 不浮起、Section 不浮起、Tab Pane 不浮起。
233
+
234
+ ## 5. Components
235
+
236
+ 每个组件先一句性格描述,再说明形态、颜色、状态、行为。
237
+
238
+ ### Buttons
239
+
240
+ - **性格**:克制、高密度。padding 比常规 SaaS 紧——小按钮是常态,因为屏幕里通常并排 4–6 个操作。
241
+ - **Shape**:6px 圆角(`rounded.md`)。**不**做 pill 形(borderRadius=999)按钮。
242
+ - **Primary**(`button-primary`):Blueprint Blue 底 + 白字 + `4px 15px` padding。仅用于页面的「主操作」(Create、Save、Submit、Confirm)。一个屏幕原则上只有 1 个 primary。
243
+ - **Default**(`button-default`):白底 + Reading Ink 字 + Rule 描边。次操作、辅助操作。
244
+ - **Text / Link**:透明底 + Reading Ink Soft 字。表格行操作、低优先级链接(Edit / View / Detail)。**禁止**用 Primary 按钮做行操作。
245
+ - **Danger**:仅删除使用 Signal Vermilion。**必须**配合 `<Popconfirm>` 二次确认(来自 web-page 规范)。
246
+ - **Hover**:Ant 默认(10% 加深),不另行覆盖。
247
+ - **Focus**:Ant 默认 outline。键盘可达性必须保留——**禁止** `outline: none` 不补焦点环。
248
+
249
+ ### Inputs / Fields
250
+
251
+ - **性格**:界面的工作面,必须比按钮更安静。
252
+ - **Shape**:6px 圆角(`rounded.md`)+ 1px Rule 描边。
253
+ - **Background**:`Surface` 白底,**不**透明继承父容器底色——避免在灰色容器里输入框「沉下去」。
254
+ - **Focus**:Blueprint Blue 描边 + 2px 外发光(Ant 默认)。
255
+ - **Disabled**:Reading Ink Mute 字 + Surface Quiet 底,光标 `not-allowed`。
256
+ - **Placeholder**:Reading Ink Mute——但确保对比度 ≥4.5:1。
257
+ - **AutoComplete > Select > Input**:对于「值来自数据库的可枚举软枚举」字段(GPU 型号、BG、供应商类型),优先使用 AutoComplete + 后端 `/xxx-enums` 接口;前端**不**维护静态 options 列表。
258
+
259
+ ### Tables
260
+
261
+ - **性格**:信息密度的舞台。表格是 90% 业务页面的主体。
262
+ - **Cell padding**:`12px 11.25px 11px 16px`(已被全局覆盖)。比 Ant 默认稍紧。
263
+ - **whitespace**:`nowrap`(已全局)——长字段截断 + tooltip 优于换行。
264
+ - **Header**:默认 Surface 白底;**当前排序列**头变 Surface Mist 高亮。
265
+ - **Hover row**:Ant 默认浅灰底;**audit-log-table** 类只读表关闭 hover 高亮。
266
+ - **Pagination**:左侧统计文字 Reading Ink Mute / 14px。
267
+ - **空状态**:必须有明确文案(不只是「No Data」),优先指向「为什么没有 + 下一步」。
268
+ - **行操作**:放最右侧的固定列。优先使用 Ant Link 按钮密集排列,不重新发明操作菜单。
269
+
270
+ ### Cards
271
+
272
+ - **性格**:分组容器,**不**是装饰。
273
+ - **Shape**:8px 圆角(`rounded.lg`,Ant 默认 borderRadiusLG)。
274
+ - **Background**:Surface 白底。Header 区 Surface Mist 与 body 视觉切分。
275
+ - **Shadow**:无(见 Flat-By-Default Rule)。
276
+ - **Border**:无。靠浅灰底层级。
277
+ - **Padding**:`24px`。
278
+ - **嵌套禁令**:**禁止**卡片嵌套卡片。如果你想在 Card 里加分块,用 1px Rule Soft 横分隔线 + Title 20px 即可。
279
+
280
+ ### Modal / Drawer
281
+
282
+ - **性格**:临时遮罩,必须能秒级关闭。
283
+ - **Modal padding**:`20px 24px` header / body / footer 三段,1px Rule 分隔。
284
+ - **Drawer**:右侧滑出 / 全屏滑出,`16px` 容器内边距 + Drawer Fog 灰底切分多个 Surface 白块。
285
+ - **关闭方式**:必须支持 Esc + 点击遮罩 + 右上 X,三者全启用。
286
+ - **Form 提交**:成功后 Modal 自动关闭并刷新列表;失败保留输入数据 + 错误提示在表单字段下,**不**用 toast 替代字段级错误。
287
+
288
+ ### Navigation (Sider)
289
+
290
+ - **性格**:稳定地图,**不**是注意力争夺战。
291
+ - **Style**:Surface Quiet 底 + 折叠/展开动画 ≤200ms。
292
+ - **Item padding**:`0 16px`。
293
+ - **Active**:Blueprint Blue 字色 + 浅蓝底(Ant 默认)。**禁止**给整行加左侧 4px 蓝条作为 active 指示——那是 AdminLTE 习气。
294
+ - **Hover**:浅灰底,无颜色变化。
295
+
296
+ ### App Header
297
+
298
+ - **Height**:`64px`。
299
+ - **Background**:Surface 白底。
300
+ - **Position**:`sticky / top: 0 / z-index: 999`。
301
+ - **Internals**:右对齐——业务切换 Select、语言切换、用户头像 + 下拉。**没有**搜索框、**没有**通知钟铛——保留给真正需要的时候。
302
+
303
+ ### Tags / Status
304
+
305
+ - **Shape**:极小圆角(`rounded.sm` 或 0)。
306
+ - **Color rule**:状态色(绿成功 / 黄警告 / 红错误 / 蓝进行中)必须配 `<Icon>` + 文字。**禁止**裸彩色圆点 / 实心方块代替状态——色盲用户读不到。
307
+ - **Density**:表格内一行可以并排 2–3 个 tag,单元格 padding 不加宽。
308
+
309
+ ### Forms
310
+
311
+ - **Layout**:单列(窄屏)→ 双列 grid(宽屏 ≥768px)。绝不堆四列。
312
+ - **Label**:14px / Reading Ink / `400`。**不**加冒号、**不**加红色星号外的额外标记。
313
+ - **Required**:必填用 Ant 默认红星,**不**自己加「\*必填」文字提示。
314
+ - **Submit / Cancel**:底部右对齐,Cancel 在左、Submit / Save 在右。
315
+ - **External help**:用 Alert 组件放在表单顶部,**不**在每个字段下面塞说明 paragraph——挤垮密度。
316
+
317
+ ## 6. Do's and Don'ts
318
+
319
+ 具体、强硬。每条都引用 PRODUCT.md 里的反参考,让视觉规范替战略守门。
320
+
321
+ ### Do
322
+
323
+ - **Do** 把 chrome 当作工具:每加一段视觉装饰前问自己「删掉它页面会变难用吗?不会就删」。
324
+ - **Do** 把 Blueprint Blue 当作奇货:一屏 ≤10% 覆盖;Save、链接、当前态——挑一个用。
325
+ - **Do** 让所有正文/次要文字从 Reading Ink / Soft / Mute 三档里选;新增灰色去 review 一定挂。
326
+ - **Do** 状态搭配图标 + 文字:彩色圆点单独使用是 bug。
327
+ - **Do** 用 Surface Ramp 的四档浅灰切分组,不要用 box-shadow 装「卡片浮起」。
328
+ - **Do** 表格 nowrap + tooltip 截断;表格不换行,业务列表不堆富文本。
329
+ - **Do** 表单错误显示在字段下方;toast 只用于「全表单级别」失败。
330
+ - **Do** 破坏性操作 (`Delete`) 必须配合 `<Popconfirm>` 二次确认。
331
+ - **Do** 内部页面坚持 4.5:1 对比度(正文);Reading Ink Mute (`#8c8c8c`) 用作正文是不通过的。
332
+ - **Do** 遵循 `prefers-reduced-motion`——所有动画必须有 instant fallback。
333
+
334
+ ### Don't
335
+
336
+ - **Don't** 加 `border-left: 3px ...` 作为列表/卡片的颜色强调(PRODUCT.md 反参考:「免费后台模板风的装饰性 side-stripe」)。
337
+ - **Don't** 用 `background-clip: text` + 渐变做标题(PRODUCT.md 反参考:「毛玻璃 / 炫技工具风」的渐变文字)。
338
+ - **Don't** 用「大数字 + 上箭头百分比 + 渐变背景」的指标卡模板(PRODUCT.md 反参考:「通用 B2B SaaS 模板」的 hero metric template)。
339
+ - **Don't** 堆叠相同尺寸的 icon + 标题 + 描述卡片网格(PRODUCT.md 反参考:「通用 B2B SaaS 模板」的 identical card grid)。
340
+ - **Don't** 在每个区块上方加小字大写带 letter-spacing 的「ABOUT / PRICING」式 eyebrow——这是 SaaS 着陆页语法,不是后台。
341
+ - **Don't** 给 Card / Section / Panel 加 box-shadow(违反 Flat-By-Default Rule)。
342
+ - **Don't** 嵌套 Card:`<Card>` 里再放 `<Card>` 永远是错的,用 1px 分隔线 + Title 即可。
343
+ - **Don't** 引入品牌字体(违反 Native Type Rule)。
344
+ - **Don't** 给整行 nav 加左侧 4px 彩条作为 active 指示(AdminLTE 习气,PRODUCT.md 反参考)。
345
+ - **Don't** 用毛玻璃 / blur 装饰 Header / Sider / Modal——内部工具不需要炫技。
346
+ - **Don't** 用「Oops! / 哎呀 / 出错啦」这类粉饰错误信息——直说「保存失败:第 3 行的 email 格式不合法」。
347
+ - **Don't** 给已存在 Ant 默认样式的元素 inline 覆盖 `style={{ borderRadius / boxShadow / ... }}`——如果默认值有问题,去改 ConfigProvider 或全局 CSS,不要在业务组件里散乱覆盖。
348
+ - **Don't** 用 `outline: none` 移除焦点环不补——内部工具的键盘用户最不能丢。
@@ -0,0 +1,75 @@
1
+ # Product
2
+
3
+ ## Register
4
+
5
+ product
6
+
7
+ ## Users
8
+
9
+ 内部运营人员与业务分析师。他们坐在工位前的台式机/双屏环境里,每天处理订单、工单、活动、权限审计、工作流状态等结构化数据。
10
+
11
+ 主要工作模式:
12
+ - 在 CRUD 列表页里做大量的搜索、过滤、批量更新与导入导出。
13
+ - 在 Dashboard 上读图表追指标,发现异常后下钻到明细页定位问题。
14
+ - 跨业务(多 game / 多 studio)切换上下文,一天内进出十几个不同的资源页面。
15
+
16
+ 次级使用者是构建 gadmin2 实例的内部工程师(写 `config/prisma/*.prisma` 和 `config/ui/*.ts`),但他们的体验依赖于「生成出来的页面默认就好用」,所以设计上以一线运营/分析师为第一受众。
17
+
18
+ ## Product Purpose
19
+
20
+ gadmin2 是一个低代码生成工具:给它一份 Prisma schema + 一份 UI 配置,它就生成全栈的管理后台(Refine + Ant Design v5 + ECharts 前端,NestJS + Prisma 后端)。
21
+
22
+ `gadmin-test` 是 gadmin2 模板的实例项目,承担两件事:
23
+ 1. 让真实业务(订单、工作流、活动、权限、Canvas 仪表盘等)跑在最新一版生成器之上。
24
+ 2. 在这里迭代新特性,再通过 `sync-to-template.sh` 反哺回上游模板。
25
+
26
+ 成功的标准不是「页面好看」,而是:让运营在最少的认知负担下完成 90% 的数据操作;让工程师可以放心地只写配置,不去手改样式;让生成的默认页面已经达到可发布质量。
27
+
28
+ ## Brand Personality
29
+
30
+ **冷静 / 精准 / 专家感(calm / precise / expert)。**
31
+
32
+ 气质上的三句话指导:
33
+
34
+ - **安静的密度**:同屏可以承载大量数据,但视觉上不焦躁。用留白、间距和字重区分层级,不用色彩和分隔线堆叠层级。
35
+ - **信息优先于 chrome**:屏幕主角永远是表格、图表、表单本身;包裹它们的卡片、标题、按钮越透明越好。
36
+ - **像同事一样说话**:标签、错误、空状态用直白、可执行的语言。不卖萌、不夸张、不掩饰边界条件——内部工具的用户是专家,他们要的是事实。
37
+
38
+ 参考与各自的「为什么」:
39
+
40
+ - **Linear**:克制的色板、锐利的字体节奏、近乎零的装饰。教会我们「专业感来自取舍,不是来自堆砌」。
41
+ - **Stripe Dashboard**:冷静的留白、只在关键处使用品牌色作为强调、表格和过滤栏的细节考究。教会我们「数据密集的页面也可以很安静」。
42
+
43
+ ## Anti-references
44
+
45
+ 明确「**不要长成这样**」——四类全部命中:
46
+
47
+ - **传统重型企业系统(Salesforce / SAP)**:渐变顶栏、密集装饰图标、彩色 tab 条、信息层级靠颜色硬切。视觉负担大于信息密度。
48
+ - **通用 B2B SaaS 模板**:渐变 hero 卡片、上箭头大数字指标块、千篇一律的 icon-heading-text 三件套卡片网格。所谓 SaaS 套娃。
49
+ - **免费后台模板风(Bootstrap / AdminLTE)**:彩色侧边栏、装饰性的 `border-left` 侧条作为状态强调、状态徽章铺满表格行。看起来「热闹」,本质是把混乱伪装成层级。
50
+ - **毛玻璃 / 炫技工具风**:blur 背景、渐变文字、为「酷」而做的深色主题。我们的用户不在咖啡馆刷设计灵感,他们在写工单。
51
+
52
+ 具体的禁忌(在生成的代码里要 grep 不到):
53
+ - 不用 `border-left: 3px ...` 作为列表/卡片的颜色强调。
54
+ - 不用 `background-clip: text` + 渐变做标题。
55
+ - Dashboard 不用「大数字 + 上箭头百分比 + 渐变背景」的指标卡模板。
56
+ - 不堆叠相同尺寸的 icon + 标题 + 描述卡片网格。
57
+ - 不在每个区块上方加小字大写带 letter-spacing 的「ABOUT / PRICING」式 eyebrow。
58
+
59
+ ## Design Principles
60
+
61
+ 1. **数据优先于装饰** — 任何屏幕,先问「数据看清楚了吗」,再问「漂亮吗」。删掉一个分隔线、一个图标、一段说明文字而页面没有变难用,就该删。
62
+ 2. **冷静的信息密度** — 同屏可以塞很多东西,但不能让人焦虑。靠间距节奏、字重对比和层级排布承载密度,不靠颜色或边框。Cards 是惰性答案,能用一条横分隔线解决就别套卡片。
63
+ 3. **靠清晰建立信任** — 标签、错误信息、空状态、操作确认都用直白可执行的句子。不写「Oops!」「出了点小问题」之类粉饰;写「保存失败:第 3 行的 `email` 格式不合法」。
64
+ 4. **配置即设计** — 因为页面由 prisma + ui 配置生成,所有视觉规范必须沉淀到 token、组件和默认值里,让一个新模型 `yarn generate` 出来就已经是合格的页面,工程师不需要写任何 CSS。
65
+ 5. **保留专家流速** — 内部用户每天要重复几百次同样的操作。键盘快捷键、批量选择、复制 ID、可分享链接的过滤状态、自动聚焦的输入框,都比「视觉打磨」优先级更高。任何「为新人友好」的改动都不能减慢老用户。
66
+
67
+ ## Accessibility & Inclusion
68
+
69
+ 无正式合规要求(纯内部工具)。但坚持务实可读基线,作为质量底线而非合规底线:
70
+
71
+ - **对比度**:正文 ≥ 4.5:1;大字(≥18px 或 bold ≥14px)≥ 3:1;占位符不放过 4.5:1。淡灰文字「显得高级」是设计反模式,本项目里不通过 review。
72
+ - **状态不只靠颜色**:成功 / 警告 / 错误 / 待处理这些状态必须搭配图标或文字标签,不能只用绿/黄/红圆点。色盲用户和黑白打印场景都要能区分。
73
+ - **键盘可达**:所有交互(行操作、过滤器、Drawer/Modal 关闭、表格分页)都要能用 Tab + Enter 完成。`tabindex` 顺序符合视觉顺序。
74
+ - **尊重系统偏好**:所有动画必须有 `@media (prefers-reduced-motion: reduce)` 退化(一般退化为瞬切或 crossfade)。
75
+ - **不做的事**:不强制 ARIA full coverage、不做读屏器适配、不做 RTL 布局。这些对内部运营场景投入产出不划算,等业务真的需要时再补。
@@ -130,6 +130,11 @@ model WorkflowNodeType {
130
130
  configSchema Json @map("config_schema")
131
131
  inputSchema Json @map("input_schema")
132
132
  outputSchema Json @map("output_schema")
133
+ // 节点出口句柄列表(前端 ReactFlow Handle id):
134
+ // null = 动态(如 switch,从 config.cases 推出)
135
+ // [] = 单出口无具名 handle(如 ACTION 类)
136
+ // ['true','false'] = 多出口枚举(如 if_else)
137
+ outputs Json?
133
138
  isBuiltin Boolean @default(true) @map("is_builtin")
134
139
  creator String @default("") @db.VarChar(128) @map("creator")
135
140
  createdAt DateTime @default(now()) @map("created_at")
@@ -1,4 +1,4 @@
1
- import { PrismaClient } from '@prisma/client';
1
+ import { Prisma, PrismaClient } from '@prisma/client';
2
2
 
3
3
  export const NODE_TYPES = [
4
4
  // ─── TRIGGER ───────────────────────────────────────────────────────────────
@@ -27,6 +27,7 @@ export const NODE_TYPES = [
27
27
  type: 'object',
28
28
  properties: { triggeredAt: { type: 'string' } },
29
29
  },
30
+ outputs: [],
30
31
  },
31
32
  {
32
33
  type: 'webhook_trigger',
@@ -64,6 +65,7 @@ export const NODE_TYPES = [
64
65
  },
65
66
  },
66
67
  outputSchema: { type: 'object', properties: { body: { type: 'object' } } },
68
+ outputs: [],
67
69
  },
68
70
  {
69
71
  type: 'event_trigger',
@@ -93,6 +95,7 @@ export const NODE_TYPES = [
93
95
  type: 'object',
94
96
  properties: { payload: { type: 'object' } },
95
97
  },
98
+ outputs: [],
96
99
  },
97
100
  {
98
101
  type: 'manual_trigger',
@@ -112,6 +115,7 @@ export const NODE_TYPES = [
112
115
  },
113
116
  inputSchema: { type: 'object', properties: { data: { type: 'object' } } },
114
117
  outputSchema: { type: 'object', properties: { data: { type: 'object' } } },
118
+ outputs: [],
115
119
  },
116
120
 
117
121
  // ─── ACTION ────────────────────────────────────────────────────────────────
@@ -172,6 +176,7 @@ export const NODE_TYPES = [
172
176
  headers: { type: 'object' },
173
177
  },
174
178
  },
179
+ outputs: [],
175
180
  },
176
181
  {
177
182
  type: 'db_query',
@@ -210,6 +215,7 @@ export const NODE_TYPES = [
210
215
  type: 'object',
211
216
  properties: { rows: { type: 'array' }, rowCount: { type: 'number' } },
212
217
  },
218
+ outputs: [],
213
219
  },
214
220
  {
215
221
  type: 'db_execute',
@@ -259,6 +265,7 @@ export const NODE_TYPES = [
259
265
  insertedId: { type: 'string' },
260
266
  },
261
267
  },
268
+ outputs: [],
262
269
  },
263
270
  {
264
271
  type: 'send_notification',
@@ -300,6 +307,7 @@ export const NODE_TYPES = [
300
307
  type: 'object',
301
308
  properties: { sent: { type: 'boolean' }, messageId: { type: 'string' } },
302
309
  },
310
+ outputs: [],
303
311
  },
304
312
  {
305
313
  type: 'code',
@@ -331,6 +339,7 @@ export const NODE_TYPES = [
331
339
  type: 'object',
332
340
  properties: { result: { type: 'object' } },
333
341
  },
342
+ outputs: [],
334
343
  },
335
344
 
336
345
  // ─── DATA PROCESSING ───────────────────────────────────────────────────────
@@ -363,6 +372,7 @@ export const NODE_TYPES = [
363
372
  },
364
373
  inputSchema: { type: 'object', properties: { data: { type: 'object' } } },
365
374
  outputSchema: { type: 'object', properties: { data: { type: 'object' } } },
375
+ outputs: [],
366
376
  },
367
377
 
368
378
  // ─── CONDITION ─────────────────────────────────────────────────────────────
@@ -405,6 +415,7 @@ export const NODE_TYPES = [
405
415
  type: 'object',
406
416
  properties: { branch: { type: 'string', enum: ['true', 'false'] } },
407
417
  },
418
+ outputs: ['true', 'false'],
408
419
  },
409
420
  {
410
421
  type: 'switch',
@@ -439,6 +450,7 @@ export const NODE_TYPES = [
439
450
  type: 'object',
440
451
  properties: { matchedCase: { type: 'string' } },
441
452
  },
453
+ outputs: null,
442
454
  },
443
455
 
444
456
  // ─── FLOW CONTROL ──────────────────────────────────────────────────────────
@@ -481,6 +493,7 @@ export const NODE_TYPES = [
481
493
  results: { type: 'array' },
482
494
  },
483
495
  },
496
+ outputs: [],
484
497
  },
485
498
  {
486
499
  type: 'delay',
@@ -520,6 +533,7 @@ export const NODE_TYPES = [
520
533
  resumeReason: { type: 'string' },
521
534
  },
522
535
  },
536
+ outputs: [],
523
537
  },
524
538
  {
525
539
  type: 'parallel',
@@ -558,6 +572,7 @@ export const NODE_TYPES = [
558
572
  type: 'object',
559
573
  properties: { results: { type: 'array' } },
560
574
  },
575
+ outputs: [],
561
576
  },
562
577
  {
563
578
  type: 'error_handler',
@@ -601,6 +616,7 @@ export const NODE_TYPES = [
601
616
  type: 'object',
602
617
  properties: { error: { type: 'object' }, handled: { type: 'boolean' } },
603
618
  },
619
+ outputs: [],
604
620
  },
605
621
 
606
622
  // ─── APPROVAL ──────────────────────────────────────────────────────────────
@@ -662,6 +678,7 @@ export const NODE_TYPES = [
662
678
  comment: { type: 'string' },
663
679
  },
664
680
  },
681
+ outputs: [],
665
682
  },
666
683
 
667
684
  // ─── SUB WORKFLOW ──────────────────────────────────────────────────────────
@@ -699,12 +716,16 @@ export const NODE_TYPES = [
699
716
  type: 'object',
700
717
  properties: { result: { type: 'object' }, status: { type: 'string' } },
701
718
  },
719
+ outputs: [],
702
720
  },
703
721
  ];
704
722
 
705
723
  export async function seedNodeTypes(prisma: PrismaClient) {
706
724
  console.log('Seeding workflow node types...');
707
725
  for (const nodeType of NODE_TYPES) {
726
+ // Json? null requires Prisma.DbNull (SQL NULL) — literal null is rejected at type level.
727
+ const outputsValue =
728
+ nodeType.outputs === null ? Prisma.DbNull : nodeType.outputs;
708
729
  await prisma.workflowNodeType.upsert({
709
730
  where: { type: nodeType.type },
710
731
  update: {
@@ -715,6 +736,7 @@ export async function seedNodeTypes(prisma: PrismaClient) {
715
736
  configSchema: nodeType.configSchema,
716
737
  inputSchema: nodeType.inputSchema,
717
738
  outputSchema: nodeType.outputSchema,
739
+ outputs: outputsValue,
718
740
  },
719
741
  create: {
720
742
  type: nodeType.type,
@@ -725,6 +747,7 @@ export async function seedNodeTypes(prisma: PrismaClient) {
725
747
  configSchema: nodeType.configSchema,
726
748
  inputSchema: nodeType.inputSchema,
727
749
  outputSchema: nodeType.outputSchema,
750
+ outputs: outputsValue,
728
751
  },
729
752
  });
730
753
  }