@brushes/schema-to-view 1.0.4 → 1.0.5
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brushes/schema-to-view",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "根据 UI 截图/设计稿动态生成低代码平台 JSON schema。当用户提供 UI 图片要求生成页面、将设计稿转为低代码页面、根据截图创建页面 JSON、图片转 Schema 时使用此 skill。触发条件:UI 截图 + 低代码、设计稿转代码、图片生成页面、schema 生成",
|
|
5
5
|
"keywords": ["pi-package"],
|
|
6
6
|
"pi": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npm install -g @mcpcn/image-vision-mcp)",
|
|
5
|
+
"Bash(npm list -g @mcpcn/image-vision-mcp --json 2>/dev/null | grep -A5 '\"bin\"')",
|
|
6
|
+
"Bash(claude mcp --help 2>&1)",
|
|
7
|
+
"Bash(claude mcp add image-vision-mcp -- /Users/qin/.nvm/versions/node/v20.19.4/bin/image-vision-mcp)",
|
|
8
|
+
"Bash(claude mcp list)",
|
|
9
|
+
"Bash(claude mcp get image-vision-mcp 2>&1)",
|
|
10
|
+
"mcp__image-vision-mcp__image_understanding",
|
|
11
|
+
"mcp__image-vision-mcp__ocr_extract_text",
|
|
12
|
+
"Bash(python3 -c \"import json; d=json.load\\(open\\('/Users/qin/.claude.json'\\)\\); print\\(json.dumps\\(d.get\\('mcpServers', d.get\\('mcp', 'NOT FOUND'\\)\\), indent=2\\)\\)\" 2>/dev/null || echo \"No top-level mcp key\")",
|
|
13
|
+
"Bash(claude mcp add -s user zai-mcp-server --env Z_AI_API_KEY=8f58762d9666441d8727664d2f5388c4.IeHdqWPfh24GJACk -- npx -y \"@z_ai/mcp-server\")",
|
|
14
|
+
"Bash(claude mcp list -s user)",
|
|
15
|
+
"Bash(Z_AI_API_KEY=8f58762d9666441d8727664d2f5388c4.IeHdqWPfh24GJACk npx -y @z_ai/mcp-server)",
|
|
16
|
+
"mcp__zai-mcp-server__analyze_image",
|
|
17
|
+
"Bash(python3 -c \"import json; data=json.load\\(open\\('/Users/qin/Desktop/react-lowcode/web-lowcode/pageModels/fixtures.json'\\)\\); print\\(f'Valid JSON: {len\\(data\\)} nodes'\\); [print\\(f' {k}: {v[\\\\\"displayName\\\\\"]} \\({v[\\\\\"type\\\\\"][\\\\\"resolvedName\\\\\"]}\\) parent={v.get\\(\\\\\"parent\\\\\",\\\\\"ROOT\\\\\"\\)}'\\) for k,v in data.items\\(\\)]\")",
|
|
18
|
+
"Bash(python3 -c \"\ns = open\\('/Users/qin/Desktop/react-lowcode/web-lowcode/pageModels/fixtures.json'\\).read\\(\\)\nprint\\(f'Total chars: {len\\(s\\)}'\\)\nprint\\(f'Around error \\(7647\\):'\\)\nprint\\(repr\\(s[7620:7700]\\)\\)\n\")",
|
|
19
|
+
"Bash(python3 -c \"import json; json.load\\(open\\('/Users/qin/Desktop/react-lowcode/web-lowcode/pageModels/furniture.json'\\)\\); print\\('JSON 格式正确'\\)\")",
|
|
20
|
+
"Bash(python3 -m json.tool)",
|
|
21
|
+
"Bash(git -C /Users/qin/Desktop/react-lowcode/web-lowcode log --oneline -20 --all)",
|
|
22
|
+
"Bash(git -C /Users/qin/Desktop/react-lowcode/web-lowcode log --format=\"%H %s\" -10)",
|
|
23
|
+
"Bash(git -C /Users/qin/Desktop/react-lowcode/web-lowcode diff HEAD -- .gitignore apps/editor/config/dev.ts apps/editor/src/pages/container/components/Topbar.tsx apps/main/config/dev.ts | head -200)",
|
|
24
|
+
"Bash(git -C /Users/qin/Desktop/react-lowcode/web-lowcode log --all --diff-filter=AMR --name-only --format=\"%H %s\" -20 -- \"*.ts\" \"*.tsx\" \"*.json\" | head -200)",
|
|
25
|
+
"Bash(grep -l \"image\\\\|schema\\\\|图片\\\\|生成\\\\|fixture\\\\|pageModel\" /Users/qin/.claude/projects/-Users-qin-Desktop-react-lowcode-web-lowcode/*.jsonl 2>/dev/null)",
|
|
26
|
+
"Bash(python3 -c \"\nimport json\nwith open\\('/Users/qin/Desktop/react-lowcode/web-lowcode/pageModels/furniture.json'\\) as f:\n data = json.load\\(f\\)\nprint\\(f'furniture.json nodes: {len\\(data\\)}'\\)\nfor k, v in data.items\\(\\):\n display = v.get\\('displayName', '?'\\)\n resolved = v['type']['resolvedName']\n parent = v.get\\('parent', 'ROOT'\\)\n print\\(f' {k}: {display} \\({resolved}\\) parent={parent}'\\)\n\")",
|
|
27
|
+
"Read(//Users/qin/.claude/**)",
|
|
28
|
+
"WebSearch",
|
|
29
|
+
"Bash(git check-ignore .claude/skills/lowcode-gen/SKILL.md && echo \"IGNORED\" || echo \"NOT IGNORED\")",
|
|
30
|
+
"Bash(git check-ignore .claude/settings.local.json && echo \"settings IGNORED\" || echo \"settings NOT IGNORED\")",
|
|
31
|
+
"Skill(schema-to-view)"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lowcode-gen
|
|
3
|
+
description: >
|
|
4
|
+
为 web-lowcode 低代码平台生成新组件代码。当用户需要添加新的低代码组件、
|
|
5
|
+
创建组件配置面板、或注册组件到编辑器的数据源时需要此 skill。触发条件:
|
|
6
|
+
新建组件、添加可视化组件、生成低代码组件、创建 setting 面板、注册编辑器组件。
|
|
7
|
+
argument-hint: "[组件名] [分类: basic|service|operate|layout]"
|
|
8
|
+
allowed-tools: Bash(ls mkdir) Read Write Edit Glob Grep
|
|
9
|
+
paths: "packages/component-ui/**,packages/component-setting/**,apps/editor/src/pages/left/dataSource-component/**"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# 低代码组件代码生成器
|
|
13
|
+
|
|
14
|
+
为 web-lowcode 低代码平台生成一个新的可视化组件,遵循项目既定架构模式。
|
|
15
|
+
|
|
16
|
+
## 项目架构速览
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
web-lowcode/
|
|
20
|
+
├── packages/
|
|
21
|
+
│ ├── component-ui/src/ # UI 组件实现
|
|
22
|
+
│ │ ├── basic/ # 基础组件 (Text, Image, Button...)
|
|
23
|
+
│ │ ├── service/ # 业务组件 (Navigator, CardLR...)
|
|
24
|
+
│ │ ├── operate/ # 操作组件 (Login, Buy, Save...)
|
|
25
|
+
│ │ └── components/ # 复合组件 (Banner, Tab, Drawer...)
|
|
26
|
+
│ ├── component-setting/src/
|
|
27
|
+
│ │ ├── component-setting/ # 配置面板组件(每组件一个目录)
|
|
28
|
+
│ │ └── common/ # 通用配置工具(margin, padding, api 等)
|
|
29
|
+
│ └── component-store/src/ # 状态管理 hooks
|
|
30
|
+
├── apps/editor/src/pages/left/
|
|
31
|
+
│ └── dataSource-component/ # 组件注册(按分类的 tsx 文件)
|
|
32
|
+
└── ...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 组件生成流程
|
|
36
|
+
|
|
37
|
+
生成一个新组件需要创建/修改 **3 个文件**:
|
|
38
|
+
|
|
39
|
+
| 步骤 | 位置 | 作用 |
|
|
40
|
+
|------|------|------|
|
|
41
|
+
| 1. UI 组件 | `packages/component-ui/src/{category}/{name}/index.tsx` | 组件实现 |
|
|
42
|
+
| 2. 配置面板 | `packages/component-setting/src/component-setting/{name}/index.tsx` | 编辑器右侧属性面板 |
|
|
43
|
+
| 3. 注册组件 | `apps/editor/src/pages/left/dataSource-component/{category}.tsx` | 注册到编辑器 |
|
|
44
|
+
|
|
45
|
+
### 步骤 1:创建 UI 组件
|
|
46
|
+
|
|
47
|
+
在 `packages/component-ui/src/{category}/` 下创建组件文件。
|
|
48
|
+
|
|
49
|
+
**核心模式:**
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { useMemo } from "react";
|
|
53
|
+
import { get } from "lodash";
|
|
54
|
+
import {
|
|
55
|
+
useModuleContext,
|
|
56
|
+
useModuleRootContext,
|
|
57
|
+
} from "@brushes/component-core";
|
|
58
|
+
import { HOCCodeWrapComponent } from "@brushes/core-transform";
|
|
59
|
+
|
|
60
|
+
// 1. 定义 Props 接口
|
|
61
|
+
type XxxProps = {
|
|
62
|
+
module?: string;
|
|
63
|
+
storeKey?: string;
|
|
64
|
+
code?: string;
|
|
65
|
+
className?: string;
|
|
66
|
+
padding?: React.CSSProperties;
|
|
67
|
+
margin?: React.CSSProperties;
|
|
68
|
+
// ... 组件特有 props
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 2. 实现纯组件
|
|
72
|
+
const XxxInner: React.FC<XxxProps & { dataInfo: object }> = ({
|
|
73
|
+
storeKey = "_skuInfo",
|
|
74
|
+
code,
|
|
75
|
+
dataInfo,
|
|
76
|
+
padding = {},
|
|
77
|
+
margin = {},
|
|
78
|
+
className,
|
|
79
|
+
...restProps
|
|
80
|
+
}) => {
|
|
81
|
+
// 从 dataInfo 中根据 code 路径取值
|
|
82
|
+
const value = useMemo(() => {
|
|
83
|
+
if (code && dataInfo) {
|
|
84
|
+
return get(dataInfo, code) ?? restProps.someDefault;
|
|
85
|
+
}
|
|
86
|
+
return restProps.someDefault;
|
|
87
|
+
}, [code, dataInfo]);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className={className} style={{ ...padding, ...margin }}>
|
|
91
|
+
{value}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// 3. 模块 store 包装
|
|
97
|
+
const WrapXxx: React.FC<XxxProps> = ({ storeKey = "_skuInfo", ...resetProps }) => {
|
|
98
|
+
const _skuInfo = useModuleContext((s) => s.moduleStore[storeKey]) || {};
|
|
99
|
+
return <XxxInner dataInfo={_skuInfo} {...resetProps} />;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 4. 根 store 包装
|
|
103
|
+
const WrapRootXxx: React.FC<XxxProps> = ({ storeKey = "", ...resetProps }) => {
|
|
104
|
+
const _skuInfo = useModuleRootContext((s) => s.rootStore[storeKey]) || {};
|
|
105
|
+
return <XxxInner dataInfo={_skuInfo} {...resetProps} />;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// 5. 按 module 分发
|
|
109
|
+
const XxxJsx: React.FC<XxxProps> = ({ module = "moduleStore", ...resetProps }) => {
|
|
110
|
+
if (module === "moduleStore") return <WrapXxx {...resetProps} />;
|
|
111
|
+
return <WrapRootXxx {...resetProps} />;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// 6. HOC 包装导出
|
|
115
|
+
export const Xxx = HOCCodeWrapComponent(XxxJsx);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**导出注册 - 在 `packages/component-ui/src/{category}/index.ts` 中添加:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
export * from "./xxx";
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 步骤 2:创建设置面板
|
|
125
|
+
|
|
126
|
+
在 `packages/component-setting/src/component-setting/{name}/index.tsx` 创建。
|
|
127
|
+
|
|
128
|
+
**核心模式:**
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import type { FieldType } from '@brushes/form';
|
|
132
|
+
import { basicSettings, formConfigType } from '@brushes/core-transform';
|
|
133
|
+
import { actionField, marginField, paddingField } from "../../common";
|
|
134
|
+
|
|
135
|
+
// 样式配置字段
|
|
136
|
+
const styleFields: FieldType[] = [
|
|
137
|
+
{
|
|
138
|
+
label: '文本内容',
|
|
139
|
+
name: 'text',
|
|
140
|
+
type: 'text',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: 'store类型',
|
|
144
|
+
name: 'module',
|
|
145
|
+
type: 'select',
|
|
146
|
+
extraProps: {
|
|
147
|
+
options: [
|
|
148
|
+
{ label: '全局store', value: 'rootStore' },
|
|
149
|
+
{ label: '非全局store', value: 'moduleStore' },
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
// ...更多字段
|
|
154
|
+
...marginField,
|
|
155
|
+
...paddingField,
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
// 数据配置字段
|
|
159
|
+
const dataFields: FieldType[] = [
|
|
160
|
+
// 数据相关配置
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
// 组装表单配置
|
|
164
|
+
const baseFormField: formConfigType[] = [
|
|
165
|
+
{ title: '样式', formFields: styleFields },
|
|
166
|
+
{ title: '数据', formFields: dataFields },
|
|
167
|
+
{ title: '逻辑', formFields: actionField },
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
export const XxxSettings = basicSettings(baseFormField);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**FieldType 字段类型:**
|
|
174
|
+
- `text` — 文本输入
|
|
175
|
+
- `number` — 数字输入
|
|
176
|
+
- `color` — 颜色选择器
|
|
177
|
+
- `select` — 下拉选择(需 `extraProps.options`)
|
|
178
|
+
- `radio` — 单选按钮
|
|
179
|
+
- `switch` — 开关
|
|
180
|
+
- `formList` — 可动态增减的列表(需 `extraProps.innerForm`)
|
|
181
|
+
- `textarea` — 多行文本
|
|
182
|
+
|
|
183
|
+
**导出注册 - 在 `packages/component-setting/src/component-setting/index.ts` 中添加:**
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
export { XxxSettings } from './xxx';
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 步骤 3:注册到编辑器数据源
|
|
190
|
+
|
|
191
|
+
在 `apps/editor/src/pages/left/dataSource-component/{category}.tsx` 中:
|
|
192
|
+
|
|
193
|
+
1. **顶部添加 Settings 导入**
|
|
194
|
+
2. **在数组中添加 TypeComponent 条目**
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
{
|
|
198
|
+
name: '组件显示名',
|
|
199
|
+
icon: 'icon-text',
|
|
200
|
+
Component: BasicComponent['Xxx'], // 或 Materials['Xxx'], ServiceComponent['Xxx'], Operate['Xxx']
|
|
201
|
+
setting: {
|
|
202
|
+
props: {
|
|
203
|
+
// 默认属性值
|
|
204
|
+
text: '默认文本',
|
|
205
|
+
module: 'moduleStore',
|
|
206
|
+
},
|
|
207
|
+
related: {
|
|
208
|
+
settings: XxxSettings,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**组件引用来源对照(与 import 对应):**
|
|
215
|
+
|
|
216
|
+
| DataSource 文件 | 组件引用变量 | 对应包路径 |
|
|
217
|
+
|----------------|-------------|-----------|
|
|
218
|
+
| `basic.tsx` | `BasicComponent` | `@brushes/lowcode-component-ui/basic` |
|
|
219
|
+
| `basic.tsx` | `Materials` | `@brushes/lowcode-component-ui/components` |
|
|
220
|
+
| `service.tsx` | `ServiceComponent` | `@brushes/lowcode-component-ui/service` |
|
|
221
|
+
| `operate.tsx` | `Operate` | `@brushes/lowcode-component-ui/operate` |
|
|
222
|
+
|
|
223
|
+
## 组件分类指南
|
|
224
|
+
|
|
225
|
+
| 分类 | 目录 | DataSource 文件 | 示例 |
|
|
226
|
+
|------|------|-----------------|------|
|
|
227
|
+
| 基础组件 | `component-ui/src/basic/` | `basic.tsx` | Text, Image, Button, Divider, Form |
|
|
228
|
+
| 业务组件 | `component-ui/src/service/` | `service.tsx` | Navigator, CardLR, Search, Map |
|
|
229
|
+
| 操作组件 | `component-ui/src/operate/` | `operate.tsx` | Login, Logout, Buy, Save |
|
|
230
|
+
| 复合组件 | `component-ui/src/components/` | `basic.tsx` | Banner, Tab, RichText, Drawer |
|
|
231
|
+
| 布局组件 | — | `layout.tsx` | OutContainer, Container |
|
|
232
|
+
|
|
233
|
+
## 常用公共字段(import from `../../common`)
|
|
234
|
+
|
|
235
|
+
- `marginField` — 外边距配置(marginTop/Right/Bottom/Left)
|
|
236
|
+
- `paddingField` — 内边距配置(paddingTop/Right/Bottom/Left)
|
|
237
|
+
- `actionField` — 交互逻辑配置
|
|
238
|
+
- `SelectCube` — 魔方选择器(图片+链接)
|
|
239
|
+
- `apiField` — API 配置字段
|
|
240
|
+
|
|
241
|
+
## 注意事项
|
|
242
|
+
|
|
243
|
+
1. 所有 UI 组件必须用 `HOCCodeWrapComponent` 包装
|
|
244
|
+
2. 设置面板必须用 `basicSettings(baseFormField)` 包装
|
|
245
|
+
3. DataSource 文件结尾会自动调用 `combine(array)` 注册组件
|
|
246
|
+
4. 组件的 `module` 属性控制数据来源:`moduleStore` vs `rootStore`
|
|
247
|
+
5. 数据绑定通过 `storeKey` + `code` 实现,用 lodash `get` 取值
|
|
248
|
+
6. 生成代码后,检查所有 import 路径是否正确
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# 低代码组件模板参考
|
|
2
|
+
|
|
3
|
+
## 模板 1:简单展示组件(如 Badge、Tag、Label)
|
|
4
|
+
|
|
5
|
+
适用场景:纯展示,不需要复杂数据绑定的组件。
|
|
6
|
+
|
|
7
|
+
### UI 组件
|
|
8
|
+
```typescript
|
|
9
|
+
// packages/component-ui/src/basic/badge/index.tsx
|
|
10
|
+
import { HOCCodeWrapComponent } from "@brushes/core-transform";
|
|
11
|
+
|
|
12
|
+
interface BadgeProps {
|
|
13
|
+
text?: string;
|
|
14
|
+
color?: string;
|
|
15
|
+
background?: string;
|
|
16
|
+
borderRadius?: number;
|
|
17
|
+
fontSize?: number;
|
|
18
|
+
padding?: React.CSSProperties;
|
|
19
|
+
margin?: React.CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const BadgeJsx: React.FC<BadgeProps> = ({
|
|
23
|
+
text = '',
|
|
24
|
+
color = '#fff',
|
|
25
|
+
background = '#ff4d4f',
|
|
26
|
+
borderRadius = 4,
|
|
27
|
+
fontSize = 12,
|
|
28
|
+
padding = {},
|
|
29
|
+
margin = {},
|
|
30
|
+
}) => {
|
|
31
|
+
return (
|
|
32
|
+
<span
|
|
33
|
+
style={{
|
|
34
|
+
display: 'inline-block',
|
|
35
|
+
color,
|
|
36
|
+
background,
|
|
37
|
+
borderRadius,
|
|
38
|
+
fontSize,
|
|
39
|
+
padding: '2px 8px',
|
|
40
|
+
...padding,
|
|
41
|
+
...margin,
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
{text}
|
|
45
|
+
</span>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Badge = HOCCodeWrapComponent(BadgeJsx);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 设置面板
|
|
53
|
+
```typescript
|
|
54
|
+
// packages/component-setting/src/component-setting/badge/index.tsx
|
|
55
|
+
import type { FieldType } from '@brushes/form';
|
|
56
|
+
import { basicSettings, formConfigType } from '@brushes/core-transform';
|
|
57
|
+
import { paddingField, marginField } from "../../common";
|
|
58
|
+
|
|
59
|
+
const styleFields: FieldType[] = [
|
|
60
|
+
{ label: '文本内容', name: 'text', type: 'text' },
|
|
61
|
+
{ label: '文本颜色', name: 'color', type: 'color' },
|
|
62
|
+
{ label: '背景色', name: 'background', type: 'color' },
|
|
63
|
+
{ label: '圆角', name: 'borderRadius', type: 'number' },
|
|
64
|
+
{ label: '字体大小', name: 'fontSize', type: 'number' },
|
|
65
|
+
...marginField,
|
|
66
|
+
...paddingField,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const baseFormField: formConfigType[] = [
|
|
70
|
+
{ title: '样式', formFields: styleFields },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
export const BadgeSettings = basicSettings(baseFormField);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 模板 2:数据绑定展示组件(如 Price、Stock、Title)
|
|
79
|
+
|
|
80
|
+
适用场景:需要从 store 中读取数据并支持数据转换的组件。
|
|
81
|
+
|
|
82
|
+
### UI 组件
|
|
83
|
+
```typescript
|
|
84
|
+
// packages/component-ui/src/basic/price/index.tsx
|
|
85
|
+
import { useMemo } from "react";
|
|
86
|
+
import { get } from "lodash";
|
|
87
|
+
import { useModuleContext, useModuleRootContext } from "@brushes/component-core";
|
|
88
|
+
import { HOCCodeWrapComponent } from "@brushes/core-transform";
|
|
89
|
+
import { fixPrice } from "@brushes/component-tool";
|
|
90
|
+
|
|
91
|
+
interface PriceProps {
|
|
92
|
+
module?: string;
|
|
93
|
+
storeKey?: string;
|
|
94
|
+
code?: string;
|
|
95
|
+
precision?: number;
|
|
96
|
+
fontSize?: number;
|
|
97
|
+
color?: string;
|
|
98
|
+
padding?: React.CSSProperties;
|
|
99
|
+
margin?: React.CSSProperties;
|
|
100
|
+
[key: string]: any;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const PriceInner: React.FC<PriceProps & { dataInfo: object }> = ({
|
|
104
|
+
code,
|
|
105
|
+
precision = 2,
|
|
106
|
+
fontSize = 14,
|
|
107
|
+
color = '#333',
|
|
108
|
+
dataInfo,
|
|
109
|
+
padding = {},
|
|
110
|
+
margin = {},
|
|
111
|
+
}) => {
|
|
112
|
+
const value = useMemo(() => {
|
|
113
|
+
if (code && dataInfo) {
|
|
114
|
+
const result = get(dataInfo, code);
|
|
115
|
+
return result != null ? fixPrice(+result, precision) : '--';
|
|
116
|
+
}
|
|
117
|
+
return '--';
|
|
118
|
+
}, [code, dataInfo, precision]);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<span style={{ fontSize, color, fontWeight: 'bold', ...padding, ...margin }}>
|
|
122
|
+
¥{value}
|
|
123
|
+
</span>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const WrapPrice: React.FC<PriceProps> = ({ storeKey = "_skuInfo", ...resetProps }) => {
|
|
128
|
+
const _skuInfo = useModuleContext((s) => s.moduleStore[storeKey]) || {};
|
|
129
|
+
return <PriceInner dataInfo={_skuInfo} {...resetProps} />;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const WrapRootPrice: React.FC<PriceProps> = ({ storeKey = "", ...resetProps }) => {
|
|
133
|
+
const _skuInfo = useModuleRootContext((s) => s.rootStore[storeKey]) || {};
|
|
134
|
+
return <PriceInner dataInfo={_skuInfo} {...resetProps} />;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const PriceJsx: React.FC<PriceProps> = ({ module = "moduleStore", ...resetProps }) => {
|
|
138
|
+
if (module === "moduleStore") return <WrapPrice {...resetProps} />;
|
|
139
|
+
return <WrapRootPrice {...resetProps} />;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const Price = HOCCodeWrapComponent(PriceJsx);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 模板 3:布局/容器组件
|
|
148
|
+
|
|
149
|
+
适用场景:可包含子元素的布局容器,类似 Container、OutContainer。
|
|
150
|
+
|
|
151
|
+
这类组件不使用 HOCCodeWrapComponent,而是通过 @craftjs 的 Element 机制。
|
|
152
|
+
|
|
153
|
+
### 注册方式
|
|
154
|
+
在 dataSource 文件中设置 `isCanvas: true`:
|
|
155
|
+
```typescript
|
|
156
|
+
{
|
|
157
|
+
name: '自定义容器',
|
|
158
|
+
icon: 'icon-rectangle',
|
|
159
|
+
isCanvas: true,
|
|
160
|
+
Component: LayoutComponent['CustomContainer'],
|
|
161
|
+
setting: {
|
|
162
|
+
props: {
|
|
163
|
+
padding: { paddingTop: 5, paddingBottom: 5 },
|
|
164
|
+
background: '#fff',
|
|
165
|
+
},
|
|
166
|
+
related: {
|
|
167
|
+
settings: CustomContainerSettings,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 模板 4:带 API 数据源的组件
|
|
176
|
+
|
|
177
|
+
适用场景:需要从 API 拉取数据的组件,或作为 API 数据的容器。
|
|
178
|
+
|
|
179
|
+
参照 `ApiComponent`(`component-ui/src/basic/api/index.tsx`),通常作为容器包裹其他组件,配合 `linkedNodes` 使用。
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 模板 5:操作类组件
|
|
184
|
+
|
|
185
|
+
适用场景:触发业务操作的组件(加入购物车、购买、保存等)。
|
|
186
|
+
|
|
187
|
+
这些组件通常调用 `@brushes/component-store-web` 中的操作 hooks。
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// 参考 packages/component-ui/src/operate/ 下的现有实现
|
|
191
|
+
import { useModuleContext } from "@brushes/component-core";
|
|
192
|
+
import { HOCCodeWrapComponent } from "@brushes/core-transform";
|
|
193
|
+
|
|
194
|
+
// 操作组件通常依赖特定的业务 hook
|
|
195
|
+
const BuyJsx: React.FC<BuyProps> = (props) => {
|
|
196
|
+
const { setModuleStore } = useModuleContext((s) => s);
|
|
197
|
+
// 业务逻辑...
|
|
198
|
+
return <Button onClick={...}>立即购买</Button>;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const Buy = HOCCodeWrapComponent(BuyJsx);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## FieldType 完整参考
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
interface FieldType {
|
|
210
|
+
label: string; // 字段标签
|
|
211
|
+
name: string; // 对应 props key
|
|
212
|
+
type: 'text' | 'number' | 'color' | 'select' | 'radio' | 'switch' | 'formList' | 'textarea';
|
|
213
|
+
extraProps?: {
|
|
214
|
+
options?: Array<{ label: string; value: any }>; // select/radio 选项
|
|
215
|
+
innerForm?: FieldType[]; // formList 的子表单
|
|
216
|
+
dependencies?: string[]; // 联动依赖字段
|
|
217
|
+
};
|
|
218
|
+
calIsVisible?: (form: FormInstance) => boolean; // 条件显示
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 现有 common 公共组件一览
|
|
223
|
+
|
|
224
|
+
| 导出名 | 用途 |
|
|
225
|
+
|--------|------|
|
|
226
|
+
| `marginField` | 外边距配置字段数组 |
|
|
227
|
+
| `paddingField` | 内边距配置字段数组 |
|
|
228
|
+
| `actionField` | 交互逻辑配置 |
|
|
229
|
+
| `SelectCube` | 魔方选择器(图片 + 链接) |
|
|
230
|
+
| `SelectPictureOrVideo` | 图片/视频选择器 |
|
|
231
|
+
| `SelectLink` | 链接选择器 |
|
|
232
|
+
| `apiField` | API 接口配置 |
|
|
233
|
+
| `columnField` | 列配置 |
|
|
234
|
+
| `borderWidthHeightRadiusField` | 边框/宽/高/圆角配置 |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: schema-to-view
|
|
3
3
|
description: >
|
|
4
4
|
根据 Pc / Web UI 截图/设计稿动态生成低代码平台 Web JSON schema。当用户提供Pc/ Web UI 图片要求生成页面、
|
|
5
5
|
将设计稿转为低代码页面、根据截图创建页面 JSON、图片转 Schema 时使用此 skill。
|
|
@@ -18,7 +18,7 @@ scope:
|
|
|
18
18
|
- 已有 Schema 的属性修改
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# schema-to-view:UI 截图 → 低代码 JSON Schema
|
|
22
22
|
|
|
23
23
|
将 Pc / Web UI 设计稿/截图转换为 web-lowcode 平台可用的 JSON schema 文件。
|
|
24
24
|
|
|
File without changes
|