@doubao-apps/create 0.0.33 → 0.0.34
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/dist/421.js +4 -4
- package/dist/cli.js +74 -66
- package/dist/index.d.ts +1 -2
- package/dist/template-empty/README.md +1 -1
- package/dist/template-empty/package.json +2 -2
- package/dist/template-starter/README.md +48 -169
- package/dist/template-starter/package.json +2 -2
- package/dist/template-starter/src/app.config.ts +2 -3
- package/dist/template-starter/src/pages/home/index.scss +38 -172
- package/dist/template-starter/src/pages/home/index.tsx +20 -78
- package/dist/template-starter/src/widgets/hello-world/index.scss +23 -0
- package/dist/template-starter/src/widgets/hello-world/index.tsx +29 -0
- package/package.json +6 -2
- package/dist/template-empty/AGENTS.md +0 -299
- package/dist/template-empty/references/examples/common-patterns.md +0 -589
- package/dist/template-empty/references/examples/component-basics.md +0 -526
- package/dist/template-empty/references/guides/best-practices.md +0 -571
- package/dist/template-empty/references/guides/component-development.md +0 -891
- package/dist/template-empty/references/guides/performance-optimization.md +0 -402
- package/dist/template-empty/references/guides/troubleshooting.md +0 -287
- package/dist/template-empty/references/reference/components-quick-ref.md +0 -103
- package/dist/template-empty/references/reference/framework-api-quick-ref.md +0 -550
- package/dist/template-empty/references/reference/open-api/README.md +0 -8
- package/dist/template-empty/references/reference/open-api.md +0 -11
- package/dist/template-empty/references/rules/dos-and-donts.md +0 -467
- package/dist/template-starter/AGENTS.md +0 -299
- package/dist/template-starter/debug-scene/render-home-page-full.json +0 -6
- package/dist/template-starter/debug-scene/render-home-page.json +0 -5
- package/dist/template-starter/debug-scene/render-react-lynx-page-full.json +0 -6
- package/dist/template-starter/references/examples/common-patterns.md +0 -589
- package/dist/template-starter/references/examples/component-basics.md +0 -526
- package/dist/template-starter/references/guides/best-practices.md +0 -571
- package/dist/template-starter/references/guides/component-development.md +0 -891
- package/dist/template-starter/references/guides/performance-optimization.md +0 -402
- package/dist/template-starter/references/guides/troubleshooting.md +0 -287
- package/dist/template-starter/references/reference/components-quick-ref.md +0 -103
- package/dist/template-starter/references/reference/framework-api-quick-ref.md +0 -550
- package/dist/template-starter/references/reference/open-api/README.md +0 -8
- package/dist/template-starter/references/reference/open-api.md +0 -11
- package/dist/template-starter/references/rules/dos-and-donts.md +0 -467
- package/dist/template-starter/src/pages/applet/index.scss +0 -279
- package/dist/template-starter/src/pages/applet/index.tsx +0 -78
- package/dist/template-starter/src/pages/lynx/index.scss +0 -249
- package/dist/template-starter/src/pages/lynx/index.tsx +0 -78
- package/dist/template-starter/src/pages/react-lynx/index.scss +0 -335
- package/dist/template-starter/src/pages/react-lynx/index.tsx +0 -132
- package/dist/template-starter/src/widgets/weather-card/index.scss +0 -272
- package/dist/template-starter/src/widgets/weather-card/index.tsx +0 -193
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
# 组件基础示例
|
|
2
|
-
|
|
3
|
-
Page(页面)和 Widget(卡片)组件的基础用法对比。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 📋 核心差异
|
|
8
|
-
|
|
9
|
-
| 特性 | Page | Widget |
|
|
10
|
-
|------|------|--------|
|
|
11
|
-
| **使用场景** | 全屏页面、九宫格、浮窗 | 聊天流中的卡片 |
|
|
12
|
-
| **定义方式** | `definePage()` | `defineWidget()` |
|
|
13
|
-
| **输入数据** | URL 参数、页面配置 | AI 生成的结构化数据 |
|
|
14
|
-
| **显示位置** | 独立页面/窗口 | 聊天对话中 |
|
|
15
|
-
| **交互方式** | 完整的页面交互 | 卡片内交互 + 消息发送 |
|
|
16
|
-
| **生命周期** | `onCreated`, `onMounted`, `onShow`, `onHide`, `onDestroy`,`onError` | `onCreated`, `onMounted`, `onShow`, `onHide`, `onDestroy`,`onError`,`onForeground`, `onBackground` |
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 🎯 Page 基础示例
|
|
21
|
-
|
|
22
|
-
### 用户资料页面
|
|
23
|
-
|
|
24
|
-
**文件结构**:
|
|
25
|
-
```
|
|
26
|
-
src/pages/user-profile/
|
|
27
|
-
├── index.tsx # 组件逻辑
|
|
28
|
-
└── index.scss # 组件样式
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### app.config.ts
|
|
32
|
-
|
|
33
|
-
`app.config.ts` 需要配置 `appId` 和 `name`。这里额外配置 `pages` 是为了补充标题和描述,并确保
|
|
34
|
-
`user-profile` 作为页面 ID 使用。
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import { defineAppConfig } from '@doubao-apps/kit';
|
|
38
|
-
|
|
39
|
-
export default defineAppConfig({
|
|
40
|
-
appId: 'com.example.my-doubao-app',
|
|
41
|
-
name: '我的豆包应用',
|
|
42
|
-
pages: [
|
|
43
|
-
{
|
|
44
|
-
entry: 'pages/user-profile/index',
|
|
45
|
-
id: 'user-profile',
|
|
46
|
-
title: '用户资料',
|
|
47
|
-
description: '显示用户个人信息和统计数据'
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### index.tsx
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
import { definePage, useState, useEffect } from '@doubao-apps/framework';
|
|
57
|
-
import './index.scss';
|
|
58
|
-
|
|
59
|
-
interface UserProfile {
|
|
60
|
-
id: string;
|
|
61
|
-
name: string;
|
|
62
|
-
avatar: string;
|
|
63
|
-
email: string;
|
|
64
|
-
stats: {
|
|
65
|
-
posts: number;
|
|
66
|
-
followers: number;
|
|
67
|
-
following: number;
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function UserProfilePage() {
|
|
72
|
-
const [user, setUser] = useState<UserProfile | null>(null);
|
|
73
|
-
const [loading, setLoading] = useState(true);
|
|
74
|
-
const [error, setError] = useState<string | null>(null);
|
|
75
|
-
|
|
76
|
-
// 数据获取
|
|
77
|
-
const fetchUserProfile = async () => {
|
|
78
|
-
try {
|
|
79
|
-
setLoading(true);
|
|
80
|
-
setError(null);
|
|
81
|
-
|
|
82
|
-
// 模拟 API 请求
|
|
83
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
84
|
-
|
|
85
|
-
const mockData: UserProfile = {
|
|
86
|
-
id: '12345',
|
|
87
|
-
name: '张三',
|
|
88
|
-
avatar: 'https://via.placeholder.com/150',
|
|
89
|
-
email: 'zhangsan@example.com',
|
|
90
|
-
stats: {
|
|
91
|
-
posts: 42,
|
|
92
|
-
followers: 1234,
|
|
93
|
-
following: 567
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
setUser(mockData);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
setError('加载失败,请重试');
|
|
100
|
-
} finally {
|
|
101
|
-
setLoading(false);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// 编辑按钮
|
|
106
|
-
const handleEdit = () => {
|
|
107
|
-
console.log('编辑用户资料');
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// 渲染
|
|
111
|
-
if (loading) {
|
|
112
|
-
return (
|
|
113
|
-
<view className="profile-container">
|
|
114
|
-
<view className="loading">加载中...</view>
|
|
115
|
-
</view>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (error) {
|
|
120
|
-
return (
|
|
121
|
-
<view className="profile-container">
|
|
122
|
-
<view className="error">{error}</view>
|
|
123
|
-
<button onClick={fetchUserProfile}>重试</button>
|
|
124
|
-
</view>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!user) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<view className="profile-container">
|
|
134
|
-
{/* 头部 */}
|
|
135
|
-
<view className="profile-header">
|
|
136
|
-
<image className="avatar" src={user.avatar} />
|
|
137
|
-
<text className="name">{user.name}</text>
|
|
138
|
-
<text className="email">{user.email}</text>
|
|
139
|
-
<button className="edit-btn" onClick={handleEdit}>
|
|
140
|
-
编辑资料
|
|
141
|
-
</button>
|
|
142
|
-
</view>
|
|
143
|
-
|
|
144
|
-
{/* 数据统计 */}
|
|
145
|
-
<view className="profile-stats">
|
|
146
|
-
<view className="stat-item">
|
|
147
|
-
<text className="stat-value">{user.stats.posts}</text>
|
|
148
|
-
<text className="stat-label">帖子</text>
|
|
149
|
-
</view>
|
|
150
|
-
<view className="stat-item">
|
|
151
|
-
<text className="stat-value">{user.stats.followers}</text>
|
|
152
|
-
<text className="stat-label">关注者</text>
|
|
153
|
-
</view>
|
|
154
|
-
<view className="stat-item">
|
|
155
|
-
<text className="stat-value">{user.stats.following}</text>
|
|
156
|
-
<text className="stat-label">关注中</text>
|
|
157
|
-
</view>
|
|
158
|
-
</view>
|
|
159
|
-
</view>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export default definePage({
|
|
164
|
-
onShow() {
|
|
165
|
-
console.log('页面显示');
|
|
166
|
-
},
|
|
167
|
-
onHide() {
|
|
168
|
-
console.log('页面隐藏');
|
|
169
|
-
},
|
|
170
|
-
render() {
|
|
171
|
-
return <UserProfilePage />;
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### index.scss
|
|
177
|
-
|
|
178
|
-
```scss
|
|
179
|
-
.profile-container {
|
|
180
|
-
padding: 32px;
|
|
181
|
-
background-color: #f5f5f5;
|
|
182
|
-
|
|
183
|
-
.loading,
|
|
184
|
-
.error {
|
|
185
|
-
text-align: center;
|
|
186
|
-
padding: 40px;
|
|
187
|
-
font-size: 28px;
|
|
188
|
-
color: #666;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.profile-header {
|
|
192
|
-
background-color: #fff;
|
|
193
|
-
border-radius: 16px;
|
|
194
|
-
padding: 40px;
|
|
195
|
-
text-align: center;
|
|
196
|
-
margin-bottom: 32px;
|
|
197
|
-
|
|
198
|
-
.avatar {
|
|
199
|
-
width: 160px;
|
|
200
|
-
height: 160px;
|
|
201
|
-
border-radius: 80px;
|
|
202
|
-
margin-bottom: 24px;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.name {
|
|
206
|
-
font-size: 36px;
|
|
207
|
-
font-weight: bold;
|
|
208
|
-
color: #333;
|
|
209
|
-
margin-bottom: 12px;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.email {
|
|
213
|
-
font-size: 28px;
|
|
214
|
-
color: #666;
|
|
215
|
-
margin-bottom: 24px;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
.edit-btn {
|
|
219
|
-
padding: 16px 48px;
|
|
220
|
-
background-color: #1890ff;
|
|
221
|
-
color: #fff;
|
|
222
|
-
border-radius: 8px;
|
|
223
|
-
font-size: 28px;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.profile-stats {
|
|
228
|
-
display: flex;
|
|
229
|
-
justify-content: space-around;
|
|
230
|
-
background-color: #fff;
|
|
231
|
-
border-radius: 16px;
|
|
232
|
-
padding: 32px;
|
|
233
|
-
|
|
234
|
-
.stat-item {
|
|
235
|
-
text-align: center;
|
|
236
|
-
|
|
237
|
-
.stat-value {
|
|
238
|
-
font-size: 36px;
|
|
239
|
-
font-weight: bold;
|
|
240
|
-
color: #333;
|
|
241
|
-
margin-bottom: 8px;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.stat-label {
|
|
245
|
-
font-size: 24px;
|
|
246
|
-
color: #999;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## 🎴 Widget 基础示例
|
|
256
|
-
|
|
257
|
-
### 天气卡片
|
|
258
|
-
|
|
259
|
-
**文件结构**:
|
|
260
|
-
```
|
|
261
|
-
src/widgets/weather-card/
|
|
262
|
-
├── index.tsx # 组件逻辑
|
|
263
|
-
└── index.scss # 组件样式
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### index.tsx
|
|
267
|
-
|
|
268
|
-
```tsx
|
|
269
|
-
import { defineWidget, getViewData, useState, useEffect } from '@doubao-apps/framework';
|
|
270
|
-
import './index.scss';
|
|
271
|
-
|
|
272
|
-
interface WeatherData {
|
|
273
|
-
city: string;
|
|
274
|
-
temperature: number;
|
|
275
|
-
condition: string;
|
|
276
|
-
humidity: number;
|
|
277
|
-
windSpeed: number;
|
|
278
|
-
icon: string;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
interface WeatherWidgetData {
|
|
282
|
-
city: string;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function WeatherCard({ city }: WeatherWidgetData) {
|
|
286
|
-
const [weather, setWeather] = useState<WeatherData | null>(null);
|
|
287
|
-
const [loading, setLoading] = useState(true);
|
|
288
|
-
const [error, setError] = useState<string | null>(null);
|
|
289
|
-
|
|
290
|
-
// 获取天气数据
|
|
291
|
-
const fetchWeather = async () => {
|
|
292
|
-
try {
|
|
293
|
-
setLoading(true);
|
|
294
|
-
setError(null);
|
|
295
|
-
|
|
296
|
-
// 模拟 API 请求
|
|
297
|
-
await new Promise(resolve => setTimeout(resolve, 800));
|
|
298
|
-
|
|
299
|
-
const mockData: WeatherData = {
|
|
300
|
-
city: city,
|
|
301
|
-
temperature: 22,
|
|
302
|
-
condition: '晴天',
|
|
303
|
-
humidity: 65,
|
|
304
|
-
windSpeed: 12,
|
|
305
|
-
icon: '☀️'
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
setWeather(mockData);
|
|
309
|
-
} catch (err) {
|
|
310
|
-
setError('获取天气失败');
|
|
311
|
-
} finally {
|
|
312
|
-
setLoading(false);
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
useEffect(() => {
|
|
317
|
-
fetchWeather();
|
|
318
|
-
}, [city]);
|
|
319
|
-
|
|
320
|
-
// 刷新按钮
|
|
321
|
-
const handleRefresh = () => {
|
|
322
|
-
fetchWeather();
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// 渲染
|
|
326
|
-
if (loading) {
|
|
327
|
-
return (
|
|
328
|
-
<view className="weather-card">
|
|
329
|
-
<text className="loading">加载中...</text>
|
|
330
|
-
</view>
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (error || !weather) {
|
|
335
|
-
return (
|
|
336
|
-
<view className="weather-card">
|
|
337
|
-
<text className="error">{error || '暂无数据'}</text>
|
|
338
|
-
<button onClick={handleRefresh}>重试</button>
|
|
339
|
-
</view>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return (
|
|
344
|
-
<view className="weather-card">
|
|
345
|
-
{/* 头部 */}
|
|
346
|
-
<view className="weather-header">
|
|
347
|
-
<text className="city">{weather.city}</text>
|
|
348
|
-
<button className="refresh-btn" onClick={handleRefresh}>
|
|
349
|
-
刷新
|
|
350
|
-
</button>
|
|
351
|
-
</view>
|
|
352
|
-
|
|
353
|
-
{/* 主要信息 */}
|
|
354
|
-
<view className="weather-main">
|
|
355
|
-
<text className="icon">{weather.icon}</text>
|
|
356
|
-
<view className="temperature-section">
|
|
357
|
-
<text className="temperature">{weather.temperature}°C</text>
|
|
358
|
-
<text className="condition">{weather.condition}</text>
|
|
359
|
-
</view>
|
|
360
|
-
</view>
|
|
361
|
-
|
|
362
|
-
{/* 详细信息 */}
|
|
363
|
-
<view className="weather-details">
|
|
364
|
-
<view className="detail-item">
|
|
365
|
-
<text className="detail-label">湿度</text>
|
|
366
|
-
<text className="detail-value">{weather.humidity}%</text>
|
|
367
|
-
</view>
|
|
368
|
-
<view className="detail-item">
|
|
369
|
-
<text className="detail-label">风速</text>
|
|
370
|
-
<text className="detail-value">{weather.windSpeed} km/h</text>
|
|
371
|
-
</view>
|
|
372
|
-
</view>
|
|
373
|
-
</view>
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
export default defineWidget({
|
|
378
|
-
onMounted() {
|
|
379
|
-
console.log('卡片挂载');
|
|
380
|
-
},
|
|
381
|
-
onDestroy() {
|
|
382
|
-
console.log('卡片销毁');
|
|
383
|
-
},
|
|
384
|
-
render() {
|
|
385
|
-
const viewData = getViewData<WeatherWidgetData>();
|
|
386
|
-
return <WeatherCard city={viewData.city} />;
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### index.scss
|
|
392
|
-
|
|
393
|
-
```scss
|
|
394
|
-
.weather-card {
|
|
395
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
396
|
-
border-radius: 16px;
|
|
397
|
-
padding: 32px;
|
|
398
|
-
color: #fff;
|
|
399
|
-
|
|
400
|
-
.loading,
|
|
401
|
-
.error {
|
|
402
|
-
text-align: center;
|
|
403
|
-
padding: 32px;
|
|
404
|
-
font-size: 28px;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.weather-header {
|
|
408
|
-
display: flex;
|
|
409
|
-
justify-content: space-between;
|
|
410
|
-
align-items: center;
|
|
411
|
-
margin-bottom: 32px;
|
|
412
|
-
|
|
413
|
-
.city {
|
|
414
|
-
font-size: 32px;
|
|
415
|
-
font-weight: bold;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
.refresh-btn {
|
|
419
|
-
padding: 12px 24px;
|
|
420
|
-
background-color: rgba(255, 255, 255, 0.2);
|
|
421
|
-
color: #fff;
|
|
422
|
-
border-radius: 8px;
|
|
423
|
-
font-size: 24px;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
.weather-main {
|
|
428
|
-
display: flex;
|
|
429
|
-
align-items: center;
|
|
430
|
-
margin-bottom: 32px;
|
|
431
|
-
|
|
432
|
-
.icon {
|
|
433
|
-
font-size: 120px;
|
|
434
|
-
margin-right: 32px;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
.temperature-section {
|
|
438
|
-
.temperature {
|
|
439
|
-
font-size: 72px;
|
|
440
|
-
font-weight: bold;
|
|
441
|
-
margin-bottom: 8px;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
.condition {
|
|
445
|
-
font-size: 28px;
|
|
446
|
-
opacity: 0.9;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
.weather-details {
|
|
452
|
-
display: flex;
|
|
453
|
-
justify-content: space-around;
|
|
454
|
-
padding-top: 24px;
|
|
455
|
-
border-top: 1px solid rgba(255, 255, 255, 0.3);
|
|
456
|
-
|
|
457
|
-
.detail-item {
|
|
458
|
-
text-align: center;
|
|
459
|
-
|
|
460
|
-
.detail-label {
|
|
461
|
-
font-size: 24px;
|
|
462
|
-
opacity: 0.8;
|
|
463
|
-
margin-bottom: 8px;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
.detail-value {
|
|
467
|
-
font-size: 32px;
|
|
468
|
-
font-weight: bold;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
---
|
|
476
|
-
|
|
477
|
-
## 🔑 关键要点
|
|
478
|
-
|
|
479
|
-
### Page 开发要点
|
|
480
|
-
|
|
481
|
-
1. **完整的页面生命周期**
|
|
482
|
-
- `onShow()` - 页面显示时调用
|
|
483
|
-
- `onHide()` - 页面隐藏时调用
|
|
484
|
-
- `onDestroy()` - 页面销毁时调用
|
|
485
|
-
|
|
486
|
-
2. **页面配置(可选 pages)**
|
|
487
|
-
- 一级目录不写进 `pages` 也会自动发现,默认 `id` 是目录名
|
|
488
|
-
- 需要 `title`、`description`、固定首页或多级目录时再配置
|
|
489
|
-
- 多级目录需要显式写 `entry`
|
|
490
|
-
|
|
491
|
-
3. **状态管理**
|
|
492
|
-
- 使用 React Hooks(useState, useEffect)
|
|
493
|
-
- 处理加载、错误、数据状态
|
|
494
|
-
|
|
495
|
-
4. **用户交互**
|
|
496
|
-
- 按钮点击、表单提交
|
|
497
|
-
- 页面跳转、数据更新
|
|
498
|
-
|
|
499
|
-
### Widget 开发要点
|
|
500
|
-
|
|
501
|
-
1. **卡片生命周期**
|
|
502
|
-
- `onShow()` / `onHide()` - 卡片显示/隐藏
|
|
503
|
-
- `onForeground()` / `onBackground()` - 应用前后台切换
|
|
504
|
-
- `onMounted()` / `onDestroy()` - 挂载/销毁
|
|
505
|
-
|
|
506
|
-
2. **ViewData 定义**
|
|
507
|
-
- 使用 TypeScript 类型定义 `getViewData<T>()` 的 viewData 结构
|
|
508
|
-
- 对可选字段和空数据做兜底处理
|
|
509
|
-
- 避免直接使用 `any`
|
|
510
|
-
|
|
511
|
-
3. **卡片类型 (boxType)**
|
|
512
|
-
- `inbox` - 普通卡片
|
|
513
|
-
- `full_box` - 全宽卡片
|
|
514
|
-
|
|
515
|
-
4. **消息交互**
|
|
516
|
-
- 接收 AI 传入的数据
|
|
517
|
-
- 可以发送消息回 Bot
|
|
518
|
-
- 支持卡片内交互
|
|
519
|
-
|
|
520
|
-
---
|
|
521
|
-
|
|
522
|
-
## 📚 延伸阅读
|
|
523
|
-
|
|
524
|
-
- **组件开发完整指南** → [../guides/component-development.md](../guides/component-development.md)
|
|
525
|
-
- **常用开发模式** → [./common-patterns.md](./common-patterns.md)
|
|
526
|
-
- **API 参考** → [../reference/framework-api-quick-ref.md](../reference/framework-api-quick-ref.md)
|