@doubao-apps/ai 0.0.26 → 0.0.27

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 (91) hide show
  1. package/README.md +21 -0
  2. package/dist/994.js +3 -3
  3. package/dist/contexts/doubao-apps-dev/.ai/examples/common-patterns.md +600 -0
  4. package/dist/contexts/doubao-apps-dev/.ai/examples/component-basics.md +509 -0
  5. package/dist/contexts/doubao-apps-dev/.ai/guides/best-practices.md +571 -0
  6. package/dist/contexts/doubao-apps-dev/.ai/guides/component-development.md +857 -0
  7. package/dist/contexts/doubao-apps-dev/.ai/guides/performance-optimization.md +404 -0
  8. package/dist/contexts/doubao-apps-dev/.ai/guides/system-prompt.md +331 -0
  9. package/dist/contexts/doubao-apps-dev/.ai/guides/troubleshooting.md +291 -0
  10. package/dist/contexts/doubao-apps-dev/.ai/reference/components-quick-ref.md +103 -0
  11. package/dist/contexts/doubao-apps-dev/.ai/reference/framework-api-quick-ref.md +537 -0
  12. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/01-/345/237/272/347/241/200-/350/264/246/345/217/267-/347/263/273/347/273/237.md +699 -0
  13. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/02-storage.md → contexts/doubao-apps-dev/.ai/reference/open-api/02-/345/255/230/345/202/250.md} +13 -21
  14. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/03-router.md → contexts/doubao-apps-dev/.ai/reference/open-api/03-/350/267/257/347/224/261.md} +9 -9
  15. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/04-ui- → contexts/doubao-apps-dev/.ai/reference/open-api/04-/347/225/214/351/235/242-}/344/272/244/344/272/222.md +12 -12
  16. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/05-/347/225/214/351/235/242-/350/276/223/345/205/245.md +42 -0
  17. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/06-/347/275/221/347/273/234.md +148 -0
  18. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/07-media.md → contexts/doubao-apps-dev/.ai/reference/open-api/07-/345/252/222/344/275/223.md} +8 -8
  19. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/08-open- → contexts/doubao-apps-dev/.ai/reference/open-api/08-/345/274/200/346/224/276/350/203/275/345/212/233-}/344/270/232/345/212/241/350/203/275/345/212/233.md +83 -14
  20. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/09-device-bluetooth.md → contexts/doubao-apps-dev/.ai/reference/open-api/09-/350/256/276/345/244/207-/350/223/235/347/211/231.md} +22 -46
  21. package/dist/{skills/doubao-apps-dev/.ai/reference/open-api/10-device-wi-fi.md → contexts/doubao-apps-dev/.ai/reference/open-api/10-/350/256/276/345/244/207-wi-fi.md} +9 -9
  22. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/11-/350/256/276/345/244/207-/345/212/240/351/200/237/345/272/246/350/256/241.md +104 -0
  23. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/12-/350/256/276/345/244/207-ibeacon.md +148 -0
  24. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/13-/350/256/276/345/244/207-/347/275/227/347/233/230.md +82 -0
  25. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/14-/350/256/276/345/244/207-/350/256/276/345/244/207/346/226/271/345/220/221.md +70 -0
  26. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/15-/350/256/276/345/244/207-/351/231/200/350/236/272/344/273/252.md +104 -0
  27. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/16-ui-/350/276/223/345/205/245.md +65 -0
  28. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/17-/350/256/276/345/244/207-/347/275/221/347/273/234.md +164 -0
  29. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/18-/350/256/276/345/244/207-/347/237/255/344/277/241.md +62 -0
  30. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/19-/350/256/276/345/244/207-/346/227/240/351/232/234/347/242/215.md +43 -0
  31. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/20-/350/256/276/345/244/207-/347/224/265/346/261/240.md +83 -0
  32. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/21-/350/256/276/345/244/207-/346/227/245/345/216/206.md +215 -0
  33. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/22-/350/256/276/345/244/207-/345/211/252/350/264/264/346/235/277.md +70 -0
  34. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/23-/350/256/276/345/244/207-/350/201/224/347/263/273/344/272/272.md +270 -0
  35. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/24-/350/256/276/345/244/207-/345/212/240/345/257/206.md +56 -0
  36. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/25-/350/256/276/345/244/207-/347/224/265/350/257/235.md +41 -0
  37. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/26-/350/256/276/345/244/207-/346/211/253/347/240/201.md +100 -0
  38. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/27-/350/256/276/345/244/207-/345/261/217/345/271/225.md +173 -0
  39. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/28-/350/256/276/345/244/207-/351/234/207/345/212/250.md +66 -0
  40. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api/README.md +36 -0
  41. package/dist/contexts/doubao-apps-dev/.ai/reference/open-api.md +326 -0
  42. package/dist/contexts/doubao-apps-dev/.ai/rules/dos-and-donts.md +390 -0
  43. package/dist/{skills → contexts}/doubao-apps-dev/AGENTS.md +54 -31
  44. package/dist/manifest.json +45 -0
  45. package/dist/skills/doubao-apps-dev/.ai/examples/common-patterns.md +53 -43
  46. package/dist/skills/doubao-apps-dev/.ai/examples/component-basics.md +0 -17
  47. package/dist/skills/doubao-apps-dev/.ai/guides/component-development.md +96 -214
  48. package/dist/skills/doubao-apps-dev/.ai/guides/system-prompt.md +14 -14
  49. package/dist/skills/doubao-apps-dev/.ai/reference/framework-api-quick-ref.md +46 -85
  50. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/01-/345/237/272/347/241/200-/350/264/246/345/217/267-/347/263/273/347/273/237.md +13 -13
  51. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/02-/345/255/230/345/202/250.md +409 -0
  52. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/03-/350/267/257/347/224/261.md +165 -0
  53. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/04-/347/225/214/351/235/242-/344/272/244/344/272/222.md +432 -0
  54. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/05-/347/225/214/351/235/242-/350/276/223/345/205/245.md +42 -0
  55. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/06-/347/275/221/347/273/234.md +148 -0
  56. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/07-/345/252/222/344/275/223.md +346 -0
  57. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/08-/345/274/200/346/224/276/350/203/275/345/212/233-/344/270/232/345/212/241/350/203/275/345/212/233.md +546 -0
  58. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/09-/350/256/276/345/244/207-/350/223/235/347/211/231.md +961 -0
  59. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/10-/350/256/276/345/244/207-wi-fi.md +277 -0
  60. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/11-/350/256/276/345/244/207-/345/212/240/351/200/237/345/272/246/350/256/241.md +104 -0
  61. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/12-/350/256/276/345/244/207-ibeacon.md +148 -0
  62. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/13-/350/256/276/345/244/207-/347/275/227/347/233/230.md +82 -0
  63. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/14-/350/256/276/345/244/207-/350/256/276/345/244/207/346/226/271/345/220/221.md +70 -0
  64. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/15-/350/256/276/345/244/207-/351/231/200/350/236/272/344/273/252.md +104 -0
  65. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/16-ui-/350/276/223/345/205/245.md +65 -0
  66. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/17-/350/256/276/345/244/207-/347/275/221/347/273/234.md +164 -0
  67. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/18-/350/256/276/345/244/207-/347/237/255/344/277/241.md +62 -0
  68. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/19-/350/256/276/345/244/207-/346/227/240/351/232/234/347/242/215.md +43 -0
  69. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/20-/350/256/276/345/244/207-/347/224/265/346/261/240.md +83 -0
  70. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/21-/350/256/276/345/244/207-/346/227/245/345/216/206.md +215 -0
  71. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/22-/350/256/276/345/244/207-/345/211/252/350/264/264/346/235/277.md +70 -0
  72. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/23-/350/256/276/345/244/207-/350/201/224/347/263/273/344/272/272.md +270 -0
  73. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/24-/350/256/276/345/244/207-/345/212/240/345/257/206.md +56 -0
  74. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/25-/350/256/276/345/244/207-/347/224/265/350/257/235.md +41 -0
  75. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/26-/350/256/276/345/244/207-/346/211/253/347/240/201.md +100 -0
  76. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/27-/350/256/276/345/244/207-/345/261/217/345/271/225.md +173 -0
  77. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/28-/350/256/276/345/244/207-/351/234/207/345/212/250.md +66 -0
  78. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/README.md +27 -11
  79. package/dist/skills/doubao-apps-dev/.ai/reference/open-api.md +282 -166
  80. package/dist/skills/doubao-apps-dev/.ai/rules/dos-and-donts.md +50 -37
  81. package/dist/skills/doubao-apps-dev/SKILL.md +55 -27
  82. package/dist/skills/douyin-to-doubao/SKILL.md +2 -2
  83. package/dist/skills/h5-to-doubao/SKILL.md +2 -2
  84. package/dist/skills/uniapp-to-doubao/SKILL.md +2 -2
  85. package/dist/skills/weixin-to-doubao/SKILL.md +2 -2
  86. package/package.json +4 -2
  87. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/05-ui-/350/276/223/345/205/245.md +0 -95
  88. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/06-network.md +0 -298
  89. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/11-device-/344/274/240/346/204/237/345/231/250.md +0 -372
  90. package/dist/skills/doubao-apps-dev/.ai/reference/open-api/12-device-/346/234/254/345/234/260/350/203/275/345/212/233.md +0 -1005
  91. /package/dist/{skills → contexts}/doubao-apps-dev/CLAUDE.md +0 -0
@@ -0,0 +1,857 @@
1
+ # 组件开发指南
2
+
3
+ Page(页面)和 Widget(卡片)组件的完整开发指南。
4
+
5
+ ---
6
+
7
+ ## 🎯 Page 开发指南
8
+
9
+ ### 基本结构
10
+
11
+ ```tsx
12
+ import { definePage, getViewData, useState, useEffect } from '@doubao-apps/framework';
13
+ import './index.scss';
14
+
15
+ export default definePage({
16
+ // 1. AI 元数据配置
17
+ aiMeta: {
18
+ id: 'my-page', // 页面唯一标识
19
+ title: '我的页面', // 页面标题
20
+ description: '页面功能描述' // 页面描述
21
+ },
22
+
23
+ // 2. 生命周期钩子
24
+ onShow() {
25
+ // 页面显示时调用(包括从其他页面返回)
26
+ console.log('页面显示');
27
+ },
28
+
29
+ onHide() {
30
+ // 页面隐藏时调用
31
+ console.log('页面隐藏');
32
+ },
33
+
34
+ onDestroy() {
35
+ // 页面销毁时调用
36
+ console.log('页面销毁');
37
+ },
38
+
39
+ // 3. 渲染函数
40
+ render() {
41
+ // 使用 getViewData() 和 getViewContext() 获取数据和上下文
42
+ const input = getViewData();
43
+
44
+ return <MyPageComponent {...input} />;
45
+ }
46
+ });
47
+ ```
48
+
49
+ ### 页面布局示例
50
+
51
+ **1. 默认页面布局**
52
+
53
+ ```tsx
54
+ export default definePage({
55
+ aiMeta: {
56
+ id: 'full-page',
57
+ title: '全屏页面'
58
+ },
59
+ render() {
60
+ return (
61
+ <view className="full-page">
62
+ <view className="header">头部</view>
63
+ <view className="content">内容区域</view>
64
+ <view className="footer">底部</view>
65
+ </view>
66
+ );
67
+ }
68
+ });
69
+ ```
70
+
71
+ **2. 九宫格布局示例**
72
+
73
+ ```tsx
74
+ export default definePage({
75
+ aiMeta: {
76
+ id: 'grid-page',
77
+ title: '九宫格'
78
+ },
79
+ render() {
80
+ const items = [
81
+ { id: '1', title: '应用1', icon: '🎯' },
82
+ { id: '2', title: '应用2', icon: '📱' },
83
+ { id: '3', title: '应用3', icon: '⚡' }
84
+ ];
85
+
86
+ return (
87
+ <view className="grid-container">
88
+ {items.map(item => (
89
+ <view key={item.id} className="grid-item">
90
+ <text className="icon">{item.icon}</text>
91
+ <text className="title">{item.title}</text>
92
+ </view>
93
+ ))}
94
+ </view>
95
+ );
96
+ }
97
+ });
98
+ ```
99
+
100
+ ### 页面参数接收
101
+
102
+ ```tsx
103
+ interface PageInput {
104
+ userId: string;
105
+ tab?: string;
106
+ }
107
+
108
+ export default definePage({
109
+ aiMeta: {
110
+ id: 'user-detail',
111
+ title: '用户详情'
112
+ },
113
+ render() {
114
+ const input = getViewData<PageInput>();
115
+ const { userId, tab = 'profile' } = input;
116
+
117
+ return (
118
+ <view>
119
+ <text>用户 ID: {userId}</text>
120
+ <text>当前标签: {tab}</text>
121
+ </view>
122
+ );
123
+ }
124
+ });
125
+ ```
126
+
127
+ ### 页面导航
128
+
129
+ ```tsx
130
+ import { navigateTo, redirectTo, reLaunch } from '@doubao-apps/framework/api';
131
+
132
+ // 保留当前页面,打开新页面
133
+ navigateTo({
134
+ url: 'user-detail?userId=123&tab=profile'
135
+ });
136
+
137
+ // 替换当前页面
138
+ redirectTo({
139
+ url: 'settings'
140
+ });
141
+
142
+ // 关闭所有页面并回到首页
143
+ reLaunch({
144
+ url: 'home'
145
+ });
146
+ ```
147
+
148
+ `url` 在常规页面跳转里填写页面的 `pageId`,与该页面的 `aiMeta.id` 一致;需要传参时可直接在后面拼接 query string。
149
+
150
+ 页面内可继续通过 `getViewData<PageInput>()` 读取 URL 中传入的参数。
151
+
152
+ ### 常用模式
153
+
154
+ **1. 数据加载**
155
+
156
+ ```tsx
157
+ import { useEffect, useState } from '@doubao-apps/framework';
158
+ import { request } from '@doubao-apps/framework/api';
159
+
160
+ function MyPage({ userId }: { userId: string }) {
161
+ const [data, setData] = useState(null);
162
+ const [loading, setLoading] = useState(true);
163
+ const [error, setError] = useState(null);
164
+
165
+ useEffect(() => {
166
+ const fetchData = async () => {
167
+ try {
168
+ setLoading(true);
169
+ const result = await request({
170
+ url: `/api/users/${userId}`,
171
+ method: 'GET'
172
+ });
173
+ setData(result.data);
174
+ } catch (err) {
175
+ setError(err.message);
176
+ } finally {
177
+ setLoading(false);
178
+ }
179
+ };
180
+
181
+ fetchData();
182
+ }, [userId]);
183
+
184
+ if (loading) return <text>加载中...</text>;
185
+ if (error) return <text>错误: {error}</text>;
186
+ if (!data) return <text>无数据</text>;
187
+
188
+ return <view>{/* 渲染数据 */}</view>;
189
+ }
190
+ ```
191
+
192
+ **2. 表单提交**
193
+
194
+ ```tsx
195
+ import { useState } from '@doubao-apps/framework';
196
+ import { navigateBack, request, showToast } from '@doubao-apps/framework/api';
197
+
198
+ function FormPage() {
199
+ const [formData, setFormData] = useState({
200
+ name: '',
201
+ email: ''
202
+ });
203
+ const [submitting, setSubmitting] = useState(false);
204
+
205
+ const handleSubmit = async () => {
206
+ try {
207
+ setSubmitting(true);
208
+ await request({
209
+ url: '/api/submit',
210
+ method: 'POST',
211
+ data: formData
212
+ });
213
+ showToast({ message: '提交成功' });
214
+ navigateBack(); // 返回上一页
215
+ } catch (err) {
216
+ showToast({ message: '提交失败' });
217
+ } finally {
218
+ setSubmitting(false);
219
+ }
220
+ };
221
+
222
+ return (
223
+ <view>
224
+ <input
225
+ value={formData.name}
226
+ onInput={e => setFormData({ ...formData, name: e.detail.value })}
227
+ placeholder="姓名"
228
+ />
229
+ <input
230
+ value={formData.email}
231
+ onInput={e => setFormData({ ...formData, email: e.detail.value })}
232
+ placeholder="邮箱"
233
+ />
234
+ <button onClick={handleSubmit} disabled={submitting}>
235
+ {submitting ? '提交中...' : '提交'}
236
+ </button>
237
+ </view>
238
+ );
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 🎴 Widget 开发指南
245
+
246
+ ### Metadata 配置
247
+
248
+ ```ts
249
+ import { defineAppConfig } from '@doubao-apps/kit';
250
+
251
+ export default defineAppConfig({
252
+ widgets: {
253
+ 'widgets/my-widget': {
254
+ id: 'my-widget', // 卡片唯一标识
255
+ name: '我的卡片', // 卡片名称
256
+ description: '卡片功能描述', // 卡片描述
257
+ boxType: 'inbox', // 卡片类型: inbox | full_box
258
+ border: true,
259
+ keywords: ['demo', 'widget'],
260
+ titleType: 'normal'
261
+ }
262
+ }
263
+ });
264
+ ```
265
+
266
+ ### 基本结构
267
+
268
+ ```tsx
269
+ import { defineWidget, getViewData } from '@doubao-apps/framework';
270
+ import './index.scss';
271
+
272
+ interface MyWidgetData {
273
+ title: string;
274
+ content?: string;
275
+ }
276
+
277
+ export default defineWidget({
278
+ onShow() {
279
+ console.log('卡片显示');
280
+ },
281
+
282
+ onHide() {
283
+ console.log('卡片隐藏');
284
+ },
285
+
286
+ onForeground() {
287
+ console.log('应用回到前台');
288
+ },
289
+
290
+ onBackground() {
291
+ console.log('应用进入后台');
292
+ },
293
+
294
+ onMounted() {
295
+ // 卡片挂载时调用
296
+ console.log('卡片挂载');
297
+ },
298
+
299
+ onDestroy() {
300
+ console.log('卡片销毁');
301
+ },
302
+
303
+ render() {
304
+ const input = getViewData<MyWidgetData>();
305
+ return <MyWidgetComponent {...input} />;
306
+ }
307
+ });
308
+ ```
309
+
310
+ ### 卡片类型 (boxType)
311
+
312
+ **1. inbox - 普通卡片**
313
+
314
+ 默认卡片类型,适用于绝大多数场景(信息展示、结果回显、轻量交互)。
315
+
316
+ 推荐使用场景:
317
+ - 内容相对简短:标题/摘要/列表/状态提示等
318
+ - 不需要占用整行宽度,希望保持对话流的紧凑阅读体验
319
+ - 交互以按钮、简单表单项为主
320
+
321
+ ```ts
322
+ // src/app.config.ts
323
+ import { defineAppConfig } from '@doubao-apps/kit';
324
+
325
+ export default defineAppConfig({
326
+ widgets: {
327
+ 'widgets/info-card': {
328
+ id: 'info-card',
329
+ name: '信息卡片',
330
+ boxType: 'inbox'
331
+ }
332
+ }
333
+ });
334
+ ```
335
+
336
+ ```tsx
337
+ export default defineWidget({
338
+ render() {
339
+ const input = getViewData<{ title: string; content: string }>();
340
+
341
+ return (
342
+ <view className="info-card">
343
+ <text className="title">{input.title}</text>
344
+ <text className="content">{input.content}</text>
345
+ </view>
346
+ );
347
+ }
348
+ });
349
+ ```
350
+
351
+ **2. full_box - 全宽卡片**
352
+
353
+ 适用于需要更大展示空间的卡片(例如图表、图片墙、复杂表单等)。
354
+
355
+ ```ts
356
+ // src/app.config.ts
357
+ import { defineAppConfig } from '@doubao-apps/kit';
358
+
359
+ export default defineAppConfig({
360
+ widgets: {
361
+ 'widgets/user-action-card': {
362
+ id: 'user-action-card',
363
+ name: '全宽卡片示例',
364
+ boxType: 'full_box'
365
+ }
366
+ }
367
+ });
368
+ ```
369
+
370
+ ```tsx
371
+ export default defineWidget({
372
+ render() {
373
+ const input = getViewData<{ action: string; result: string }>();
374
+ return (
375
+ <view className="user-action-card">
376
+ <text>操作: {input.action}</text>
377
+ <text>结果: {input.result}</text>
378
+ </view>
379
+ );
380
+ }
381
+ });
382
+ ```
383
+
384
+ ### 输入数据类型
385
+
386
+ Widget 输入数据通过 `getViewData<T>()` 的泛型参数和 TypeScript 接口表达。
387
+
388
+ ```tsx
389
+ interface ProductCardData {
390
+ name: string;
391
+ price: number;
392
+ images?: string[];
393
+ category: '电子' | '服装' | '食品' | '图书';
394
+ inStock: boolean;
395
+ specs?: {
396
+ color?: string;
397
+ size?: string;
398
+ };
399
+ }
400
+
401
+ export default defineWidget({
402
+ render() {
403
+ const input = getViewData<ProductCardData>();
404
+ return (
405
+ <view className="product-card">
406
+ <text className="name">{input.name}</text>
407
+ <text className="price">¥{input.price}</text>
408
+ {input.images && (
409
+ <view className="images">
410
+ {input.images.map((img, idx) => (
411
+ <image key={idx} src={img} />
412
+ ))}
413
+ </view>
414
+ )}
415
+ <text className="category">{input.category}</text>
416
+ {!input.inStock && <text className="out-of-stock">缺货</text>}
417
+ </view>
418
+ );
419
+ }
420
+ });
421
+ ```
422
+
423
+ ### 常用模式
424
+
425
+ **1. 数据获取和展示**
426
+
427
+ ```tsx
428
+ import { useEffect, useState } from '@doubao-apps/framework';
429
+ import { request } from '@doubao-apps/framework/api';
430
+
431
+ function WeatherWidget({ city }: { city: string }) {
432
+ const [weather, setWeather] = useState(null);
433
+ const [loading, setLoading] = useState(true);
434
+
435
+ useEffect(() => {
436
+ const fetchWeather = async () => {
437
+ try {
438
+ const response = await request({
439
+ url: `/api/weather?city=${city}`,
440
+ method: 'GET'
441
+ });
442
+ setWeather(response.data);
443
+ } catch (err) {
444
+ console.error('获取天气失败:', err);
445
+ } finally {
446
+ setLoading(false);
447
+ }
448
+ };
449
+
450
+ fetchWeather();
451
+ }, [city]);
452
+
453
+ if (loading) return <text>加载中...</text>;
454
+ if (!weather) return <text>无数据</text>;
455
+
456
+ return (
457
+ <view className="weather-widget">
458
+ <text className="city">{weather.city}</text>
459
+ <text className="temp">{weather.temperature}°C</text>
460
+ <text className="condition">{weather.condition}</text>
461
+ </view>
462
+ );
463
+ }
464
+ ```
465
+
466
+ **2. 用户交互**
467
+
468
+ ```tsx
469
+ import { sendQueryMessage } from '@doubao-apps/framework/api';
470
+
471
+ function InteractiveWidget({ initialCount }: { initialCount: number }) {
472
+ const [count, setCount] = useState(initialCount);
473
+
474
+ const handleIncrement = () => {
475
+ setCount(count + 1);
476
+ // 发送一条用户消息到当前会话
477
+ sendQueryMessage({
478
+ content: `计数增加到 ${count + 1}`,
479
+ type: 'text'
480
+ });
481
+ };
482
+
483
+ return (
484
+ <view className="interactive-widget">
485
+ <text>当前计数: {count}</text>
486
+ <button onClick={handleIncrement}>增加</button>
487
+ </view>
488
+ );
489
+ }
490
+ ```
491
+
492
+ **3. 打开页面**
493
+
494
+ ```tsx
495
+ import { navigateTo } from '@doubao-apps/framework/api';
496
+
497
+ function ActionWidget({ itemId }: { itemId: string }) {
498
+ const handleViewDetail = () => {
499
+ navigateTo({
500
+ url: `item-detail?id=${itemId}`
501
+ });
502
+ };
503
+
504
+ return (
505
+ <view>
506
+ <button onClick={handleViewDetail}>查看详情</button>
507
+ </view>
508
+ );
509
+ }
510
+ ```
511
+
512
+ ---
513
+
514
+ ## 🎨 样式开发
515
+
516
+ ### SCSS 文件组织
517
+
518
+ ```scss
519
+ // 样式变量
520
+ $primary-color: #1890ff;
521
+ $text-color: #333;
522
+ $bg-color: #f5f5f5;
523
+ $border-radius: 8px;
524
+ $spacing: 16px;
525
+
526
+ // 容器样式
527
+ .container {
528
+ padding: $spacing;
529
+ background-color: $bg-color;
530
+
531
+ .header {
532
+ font-size: 36px;
533
+ color: $text-color;
534
+ margin-bottom: $spacing;
535
+ }
536
+
537
+ .content {
538
+ background-color: #fff;
539
+ border-radius: $border-radius;
540
+ padding: $spacing;
541
+ }
542
+ }
543
+
544
+ // 响应式布局
545
+ .responsive-layout {
546
+ display: flex;
547
+ flex-wrap: wrap;
548
+ gap: $spacing;
549
+
550
+ .item {
551
+ flex: 1 1 calc(50% - #{$spacing});
552
+ min-width: 200px;
553
+ }
554
+ }
555
+ ```
556
+
557
+ ### 常用布局模式
558
+
559
+ **1. Flex 布局**
560
+
561
+ ```scss
562
+ .flex-container {
563
+ display: flex;
564
+ flex-direction: row; // row | column
565
+ justify-content: center; // flex-start | center | flex-end | space-between
566
+ align-items: center; // flex-start | center | flex-end | stretch
567
+ gap: 16px;
568
+ }
569
+ ```
570
+
571
+ **2. Grid 布局**
572
+
573
+ ```scss
574
+ .grid-container {
575
+ display: grid;
576
+ grid-template-columns: repeat(3, 1fr);
577
+ gap: 16px;
578
+ padding: 16px;
579
+ }
580
+ ```
581
+
582
+ **3. 卡片布局**
583
+
584
+ ```scss
585
+ .card {
586
+ background-color: #fff;
587
+ border-radius: 8px;
588
+ padding: 24px;
589
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
590
+ margin-bottom: 16px;
591
+ }
592
+ ```
593
+
594
+ ---
595
+
596
+ ## 🔌 View Context APIs
597
+
598
+ 在 Page 和 Widget 开发中,有时需要访问视图的上下文信息和数据。Framework 提供了两个核心 API:
599
+
600
+ ### `getViewContext()`
601
+
602
+ 获取当前视图的上下文信息,包含页面环境、配置和语言设置。
603
+
604
+ **类型定义**:
605
+ ```typescript
606
+ import type { AIViewContext } from '@doubao-apps/framework';
607
+ import { getViewContext } from '@doubao-apps/framework';
608
+
609
+ // AIViewContext 的字段以实际类型定义为准,这里仅展示常用字段:
610
+ // - viewId / pageName / lang / appSettings / replyFor
611
+ ```
612
+
613
+ **使用场景**:
614
+
615
+ 1. **多语言支持**:
616
+ ```tsx
617
+ import { getViewContext } from '@doubao-apps/framework';
618
+
619
+ function GreetingWidget() {
620
+ const context = getViewContext();
621
+
622
+ const greeting = context.lang === 'zh-CN' ? '你好' : 'Hello';
623
+
624
+ return <text>{greeting}!</text>;
625
+ }
626
+ ```
627
+
628
+ 2. **访问应用配置**:
629
+ ```tsx
630
+ function ThemeAwareComponent() {
631
+ const context = getViewContext();
632
+ const theme = context.appSettings.theme || 'light';
633
+
634
+ return <view className={`theme-${theme}`}>...</view>;
635
+ }
636
+ ```
637
+
638
+ 3. **调试和日志**:
639
+ ```tsx
640
+ function MyPage() {
641
+ const context = getViewContext();
642
+
643
+ useEffect(() => {
644
+ console.log('当前页面:', context.pageName);
645
+ console.log('View ID:', context.viewId);
646
+ }, []);
647
+
648
+ return <view>...</view>;
649
+ }
650
+ ```
651
+
652
+ ### `getViewData<T>()`
653
+
654
+ 获取当前视图的数据,支持泛型类型定义,确保类型安全。
655
+
656
+ **类型定义**:
657
+ ```typescript
658
+ import { getViewData } from '@doubao-apps/framework';
659
+
660
+ function getViewData<T extends Record<string, any>>(): T
661
+ ```
662
+
663
+ **使用场景**:
664
+
665
+ 1. **在非 render 函数中获取数据**:
666
+ ```tsx
667
+ import { defineWidget, getViewData } from '@doubao-apps/framework';
668
+
669
+ interface TodoData {
670
+ tasks: string[];
671
+ }
672
+
673
+ export default defineWidget({
674
+ aiMeta: { ... },
675
+
676
+ onMounted() {
677
+ // 在生命周期钩子中访问数据
678
+ const data = getViewData<TodoData>();
679
+ console.log('任务列表:', data.tasks);
680
+ },
681
+
682
+ render() {
683
+ // render 函数中使用 getViewData() 获取数据
684
+ const input = getViewData<TodoData>();
685
+ return <view>{input.tasks.length} 个任务</view>;
686
+ }
687
+ });
688
+ ```
689
+
690
+ 2. **在自定义函数中访问数据**:
691
+ ```tsx
692
+ interface WeatherData {
693
+ city: string;
694
+ temperature: number;
695
+ }
696
+
697
+ function WeatherWidget() {
698
+ const loadWeatherIcon = () => {
699
+ const data = getViewData<WeatherData>();
700
+ return data.temperature > 25 ? '☀️' : '❄️';
701
+ };
702
+
703
+ return (
704
+ <view>
705
+ <text>{loadWeatherIcon()}</text>
706
+ </view>
707
+ );
708
+ }
709
+ ```
710
+
711
+ **注意事项**:
712
+ - ✅ **推荐**:在 render 函数中使用 `getViewData()` 获取数据
713
+ - ✅ **推荐**:在生命周期钩子和自定义函数中使用 `getViewData()` 获取数据
714
+ - 🔒 **类型安全**:始终定义明确的 TypeScript 接口并传递给泛型参数
715
+
716
+ ### 使用 `getViewData()` 和 `getViewContext()`
717
+
718
+ render 函数不再接收 input 和 context 参数,请使用以下方法获取数据和上下文:
719
+
720
+ **推荐做法**:
721
+ ```tsx
722
+ // ✅ 推荐:render 函数中使用 getViewData()
723
+ render() {
724
+ const input = getViewData<WeatherData>();
725
+ return <text>{input.city}: {input.temperature}°C</text>;
726
+ }
727
+
728
+ // ✅ 推荐:生命周期钩子中使用 getViewData()
729
+ onMounted() {
730
+ const data = getViewData<WeatherData>();
731
+ // 处理数据...
732
+ }
733
+
734
+ // ✅ 推荐:同时获取数据和上下文
735
+ render() {
736
+ const input = getViewData<WeatherData>();
737
+ const context = getViewContext();
738
+ return <text>{input.city} - {context.lang}</text>;
739
+ }
740
+ ```
741
+
742
+ ---
743
+
744
+ ## 🔧 开发最佳实践
745
+
746
+ ### 1. TypeScript 类型定义
747
+
748
+ ```tsx
749
+ // 定义 props 类型
750
+ interface ComponentProps {
751
+ title: string;
752
+ count?: number;
753
+ onAction: (value: string) => void;
754
+ }
755
+
756
+ // 定义 state 类型
757
+ interface ComponentState {
758
+ loading: boolean;
759
+ data: DataType | null;
760
+ error: string | null;
761
+ }
762
+
763
+ function MyComponent({ title, count = 0, onAction }: ComponentProps) {
764
+ const [state, setState] = useState<ComponentState>({
765
+ loading: false,
766
+ data: null,
767
+ error: null
768
+ });
769
+
770
+ return <view>{/* 组件内容 */}</view>;
771
+ }
772
+ ```
773
+
774
+ ### 2. 错误处理
775
+
776
+ ```tsx
777
+ try {
778
+ // 异步操作
779
+ const result = await someAsyncOperation();
780
+ setData(result);
781
+ } catch (err) {
782
+ // 记录错误
783
+ console.error('操作失败:', err);
784
+
785
+ // 显示用户友好的错误消息
786
+ setError('操作失败,请重试');
787
+
788
+ // 可选:上报错误
789
+ reportAppLog({
790
+ level: 'error',
791
+ message: err.message,
792
+ context: { operation: 'someAsyncOperation' }
793
+ });
794
+ }
795
+ ```
796
+
797
+ ### 3. 性能优化
798
+
799
+ ```tsx
800
+ import { useMemo, useCallback } from '@doubao-apps/framework';
801
+
802
+ function OptimizedComponent({ data, onSelect }) {
803
+ // 缓存计算结果
804
+ const processedData = useMemo(() => {
805
+ return data.map(item => ({
806
+ ...item,
807
+ computed: expensiveComputation(item)
808
+ }));
809
+ }, [data]);
810
+
811
+ // 缓存回调函数
812
+ const handleClick = useCallback((id: string) => {
813
+ onSelect(id);
814
+ }, [onSelect]);
815
+
816
+ return (
817
+ <view>
818
+ {processedData.map(item => (
819
+ <view key={item.id} onClick={() => handleClick(item.id)}>
820
+ {item.computed}
821
+ </view>
822
+ ))}
823
+ </view>
824
+ );
825
+ }
826
+ ```
827
+
828
+ ### 4. 生命周期管理
829
+
830
+ ```tsx
831
+ useEffect(() => {
832
+ // 订阅
833
+ const subscription = subscribeEvent('data-update', handleDataUpdate);
834
+
835
+ // 定时器
836
+ const timer = setInterval(() => {
837
+ fetchData();
838
+ }, 5000);
839
+
840
+ // 清理函数
841
+ return () => {
842
+ unsubscribeEvent(subscription);
843
+ clearInterval(timer);
844
+ };
845
+ }, []);
846
+ ```
847
+
848
+ ---
849
+
850
+ ## 📚 延伸阅读
851
+
852
+ - **组件示例** → [../examples/component-basics.md](../examples/component-basics.md)
853
+ - **常用模式** → [../examples/common-patterns.md](../examples/common-patterns.md)
854
+ - **最佳实践** → [best-practices.md](./best-practices.md)
855
+ - **性能优化** → [performance-optimization.md](./performance-optimization.md)
856
+ - **故障排查** → [troubleshooting.md](./troubleshooting.md)
857
+ - **API 参考** → [../reference/framework-api-quick-ref.md](../reference/framework-api-quick-ref.md)