@doubao-apps/ai 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.
Files changed (69) hide show
  1. package/README.md +1 -32
  2. package/dist/733.js +7 -15
  3. package/dist/cli.js +1 -1
  4. package/dist/manifest.json +3 -11
  5. package/dist/skills/doubao-apps-dev/SKILL.md +103 -227
  6. package/dist/skills/doubao-apps-dev/references/examples/common-patterns.md +80 -66
  7. package/dist/skills/doubao-apps-dev/references/examples/component-basics.md +24 -3
  8. package/dist/skills/doubao-apps-dev/references/examples/component-recipes.md +71 -0
  9. package/dist/skills/doubao-apps-dev/references/examples/open-api-recipes.md +203 -0
  10. package/dist/skills/doubao-apps-dev/references/guides/component-development.md +66 -42
  11. package/dist/skills/doubao-apps-dev/references/guides/expired-widget.md +113 -0
  12. package/dist/skills/doubao-apps-dev/references/guides/mcp-ui.md +275 -0
  13. package/dist/skills/doubao-apps-dev/references/guides/performance-optimization.md +24 -7
  14. package/dist/skills/doubao-apps-dev/references/reference/components-quick-ref.md +18 -7
  15. package/dist/skills/doubao-apps-dev/references/reference/framework-api-quick-ref.md +125 -107
  16. package/dist/skills/doubao-apps-dev/references/reference/open-api/01-/345/237/272/347/241/200.md +136 -0
  17. package/dist/skills/doubao-apps-dev/references/reference/open-api/02-/347/263/273/347/273/237.md +257 -0
  18. package/dist/skills/doubao-apps-dev/references/reference/open-api/03-/345/256/232/344/275/215.md +326 -0
  19. package/dist/skills/doubao-apps-dev/references/reference/open-api/04-/345/255/230/345/202/250.md +284 -0
  20. package/dist/skills/doubao-apps-dev/references/reference/open-api/05-/350/267/257/347/224/261.md +162 -0
  21. package/dist/skills/doubao-apps-dev/references/reference/open-api/06-/344/272/244/344/272/222.md +290 -0
  22. package/dist/skills/doubao-apps-dev/references/reference/open-api/07-/350/276/223/345/205/245.md +63 -0
  23. package/dist/skills/doubao-apps-dev/references/reference/open-api/08-/347/275/221/347/273/234.md +124 -0
  24. package/dist/skills/doubao-apps-dev/references/reference/open-api/09-/345/252/222/344/275/223.md +192 -0
  25. package/dist/skills/doubao-apps-dev/references/reference/open-api/10-/344/270/232/345/212/241/350/203/275/345/212/233.md +431 -0
  26. package/dist/skills/doubao-apps-dev/references/reference/open-api/11-/347/231/273/345/275/225.md +110 -0
  27. package/dist/skills/doubao-apps-dev/references/reference/open-api/12-/346/224/257/344/273/230.md +224 -0
  28. package/dist/skills/doubao-apps-dev/references/reference/open-api/13-/346/225/260/346/215/256/345/210/206/346/236/220.md +43 -0
  29. package/dist/skills/doubao-apps-dev/references/reference/open-api/14-/350/223/235/347/211/231.md +598 -0
  30. package/dist/skills/doubao-apps-dev/references/reference/open-api/15-wi-fi.md +210 -0
  31. package/dist/skills/doubao-apps-dev/references/reference/open-api/16-/345/212/240/351/200/237/345/272/246/350/256/241.md +87 -0
  32. package/dist/skills/doubao-apps-dev/references/reference/open-api/17-ibeacon.md +97 -0
  33. package/dist/skills/doubao-apps-dev/references/reference/open-api/18-/347/275/227/347/233/230.md +78 -0
  34. package/dist/skills/doubao-apps-dev/references/reference/open-api/19-/350/256/276/345/244/207/346/226/271/345/220/221.md +86 -0
  35. package/dist/skills/doubao-apps-dev/references/reference/open-api/20-/351/231/200/350/236/272/344/273/252.md +87 -0
  36. package/dist/skills/doubao-apps-dev/references/reference/open-api/21-/350/256/276/345/244/207-/347/275/221/347/273/234.md +102 -0
  37. package/dist/skills/doubao-apps-dev/references/reference/open-api/22-/347/237/255/344/277/241.md +40 -0
  38. package/dist/skills/doubao-apps-dev/references/reference/open-api/23-/346/227/240/351/232/234/347/242/215.md +40 -0
  39. package/dist/skills/doubao-apps-dev/references/reference/open-api/24-/345/237/272/347/241/200/344/277/241/346/201/257.md +86 -0
  40. package/dist/skills/doubao-apps-dev/references/reference/open-api/25-/347/224/265/346/261/240.md +63 -0
  41. package/dist/skills/doubao-apps-dev/references/reference/open-api/26-/346/227/245/345/216/206.md +95 -0
  42. package/dist/skills/doubao-apps-dev/references/reference/open-api/27-/345/211/252/350/264/264/346/235/277.md +64 -0
  43. package/dist/skills/doubao-apps-dev/references/reference/open-api/28-/350/201/224/347/263/273/344/272/272.md +101 -0
  44. package/dist/skills/doubao-apps-dev/references/reference/open-api/29-/345/212/240/345/257/206.md +43 -0
  45. package/dist/skills/doubao-apps-dev/references/reference/open-api/30-/347/224/265/350/257/235.md +36 -0
  46. package/dist/skills/doubao-apps-dev/references/reference/open-api/31-/346/211/253/347/240/201.md +58 -0
  47. package/dist/skills/doubao-apps-dev/references/reference/open-api/32-/345/261/217/345/271/225.md +136 -0
  48. package/dist/skills/doubao-apps-dev/references/reference/open-api/33-/351/234/207/345/212/250.md +62 -0
  49. package/dist/skills/doubao-apps-dev/references/reference/open-api/34-/346/226/207/344/273/266/347/263/273/347/273/237.md +73 -0
  50. package/dist/skills/doubao-apps-dev/references/reference/open-api/README.md +34 -0
  51. package/dist/skills/doubao-apps-dev/references/reference/open-api.md +384 -2
  52. package/dist/skills/doubao-apps-dev/references/rules/dos-and-donts.md +44 -27
  53. package/dist/skills/h5-to-doubao/SKILL.md +8 -4
  54. package/dist/skills/weixin-to-doubao/SKILL.md +8 -4
  55. package/dist/skills/weixin-to-doubao/assets/migration-checklist-template.md +1 -1
  56. package/package.json +2 -3
  57. package/dist/contexts/doubao-apps-dev/AGENTS.md +0 -300
  58. package/dist/contexts/doubao-apps-dev/CLAUDE.md +0 -31
  59. package/dist/contexts/doubao-apps-dev/references/examples/common-patterns.md +0 -589
  60. package/dist/contexts/doubao-apps-dev/references/examples/component-basics.md +0 -526
  61. package/dist/contexts/doubao-apps-dev/references/guides/best-practices.md +0 -571
  62. package/dist/contexts/doubao-apps-dev/references/guides/component-development.md +0 -891
  63. package/dist/contexts/doubao-apps-dev/references/guides/performance-optimization.md +0 -402
  64. package/dist/contexts/doubao-apps-dev/references/guides/troubleshooting.md +0 -287
  65. package/dist/contexts/doubao-apps-dev/references/reference/components-quick-ref.md +0 -103
  66. package/dist/contexts/doubao-apps-dev/references/reference/framework-api-quick-ref.md +0 -550
  67. package/dist/contexts/doubao-apps-dev/references/reference/open-api/README.md +0 -8
  68. package/dist/contexts/doubao-apps-dev/references/reference/open-api.md +0 -11
  69. package/dist/contexts/doubao-apps-dev/references/rules/dos-and-donts.md +0 -467
@@ -14,24 +14,30 @@ API 集成、状态管理、页面导航的常用开发模式和最佳实践。
14
14
 
15
15
  ## 🌐 API 集成模式
16
16
 
17
- ### Bridge API 调用
17
+ ### Open API / 端能力调用
18
18
 
19
- 使用 Bridge API 访问系统能力(网络请求、存储、设备功能等)。
19
+ 使用 Open API 访问端能力时,先确定调用位置和状态反馈。具体 API 的参数和返回类型以
20
+ [Open API 目录](../reference/open-api/README.md) 为准。
21
+ `request` 本身不是泛型函数,不要写 `request<T>()`;如果需要业务返回类型,先接收 `response`,再对
22
+ `response.data` 做类型收窄或断言。
20
23
 
21
24
  ```tsx
22
25
  import { useState } from '@doubao-apps/framework';
23
- import { getLocation, getPhoneNumber, request } from '@doubao-apps/framework/api';
26
+ import { request } from '@doubao-apps/framework/api';
27
+
28
+ function getErrorMessage(error: unknown, fallback: string) {
29
+ return error instanceof Error ? error.message : fallback;
30
+ }
24
31
 
25
32
  function DataFetchExample() {
26
- const [data, setData] = useState(null);
33
+ const [data, setData] = useState<Record<string, unknown> | string | ArrayBuffer | null>(null);
27
34
  const [loading, setLoading] = useState(false);
28
- const [error, setError] = useState(null);
35
+ const [error, setError] = useState('');
29
36
 
30
- // 1. 网络请求
31
37
  const fetchData = async () => {
32
38
  try {
33
39
  setLoading(true);
34
- setError(null);
40
+ setError('');
35
41
 
36
42
  const response = await request({
37
43
  url: 'https://api.example.com/data',
@@ -41,39 +47,18 @@ function DataFetchExample() {
41
47
  }
42
48
  });
43
49
 
44
- setData(response.data);
50
+ const responseData = response.data;
51
+ setData(responseData ?? null);
45
52
  } catch (err) {
46
- setError(err.message);
53
+ setError(getErrorMessage(err, '获取数据失败'));
47
54
  } finally {
48
55
  setLoading(false);
49
56
  }
50
57
  };
51
58
 
52
- // 2. 获取手机号(需要用户授权)
53
- const fetchPhoneNumber = async () => {
54
- try {
55
- const result = await getPhoneNumber();
56
- console.log('手机号信息:', result);
57
- } catch (err) {
58
- console.error('获取手机号失败:', err);
59
- }
60
- };
61
-
62
- // 3. 获取地理位置
63
- const fetchLocation = async () => {
64
- try {
65
- const location = await getLocation();
66
- console.log('位置:', location);
67
- } catch (err) {
68
- console.error('获取位置失败:', err);
69
- }
70
- };
71
-
72
59
  return (
73
60
  <view>
74
61
  <button onClick={fetchData}>获取数据</button>
75
- <button onClick={fetchPhoneNumber}>获取手机号</button>
76
- <button onClick={fetchLocation}>获取位置</button>
77
62
  {loading && <text>加载中...</text>}
78
63
  {error && <text>错误: {error}</text>}
79
64
  {data && <text>数据: {JSON.stringify(data)}</text>}
@@ -85,7 +70,7 @@ function DataFetchExample() {
85
70
  ### POST 请求示例
86
71
 
87
72
  ```tsx
88
- const submitForm = async (formData: any) => {
73
+ const submitForm = async (formData: Record<string, unknown>) => {
89
74
  try {
90
75
  const response = await request({
91
76
  url: 'https://api.example.com/submit',
@@ -113,7 +98,25 @@ interface ApiError {
113
98
  message: string;
114
99
  }
115
100
 
116
- async function apiRequest<T>(url: string, options: any): Promise<T> {
101
+ interface ApiRequestOptions {
102
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
103
+ header?: Record<string, string>;
104
+ data?: Record<string, unknown> | string | ArrayBuffer;
105
+ dataType?: 'json' | 'string';
106
+ }
107
+
108
+ function getResponseMessage(data: unknown) {
109
+ if (typeof data === 'object' && data !== null && 'message' in data) {
110
+ return String(data.message);
111
+ }
112
+ return '请求失败';
113
+ }
114
+
115
+ function isApiError(error: unknown): error is ApiError {
116
+ return typeof error === 'object' && error !== null && 'code' in error && 'message' in error;
117
+ }
118
+
119
+ async function apiRequest<T>(url: string, options: ApiRequestOptions): Promise<T> {
117
120
  try {
118
121
  const response = await request({
119
122
  url,
@@ -124,19 +127,19 @@ async function apiRequest<T>(url: string, options: any): Promise<T> {
124
127
  if (response.statusCode >= 400) {
125
128
  throw {
126
129
  code: response.statusCode,
127
- message: response.data?.message || '请求失败'
130
+ message: getResponseMessage(response.data)
128
131
  } as ApiError;
129
132
  }
130
133
 
131
134
  return response.data as T;
132
135
  } catch (err) {
133
136
  // 统一错误处理
134
- if (err.code === 401) {
137
+ if (isApiError(err) && err.code === 401) {
135
138
  console.error('未授权,请登录');
136
- } else if (err.code === 404) {
139
+ } else if (isApiError(err) && err.code === 404) {
137
140
  console.error('资源不存在');
138
141
  } else {
139
- console.error('请求错误:', err.message);
142
+ console.error('请求错误:', getErrorMessage(err, '请求失败'));
140
143
  }
141
144
  throw err;
142
145
  }
@@ -145,7 +148,7 @@ async function apiRequest<T>(url: string, options: any): Promise<T> {
145
148
  // 使用示例
146
149
  async function fetchUserData(userId: string) {
147
150
  try {
148
- const data = await apiRequest<User>(`/api/users/${userId}`, {
151
+ const data = await apiRequest<User>(`https://api.example.com/users/${userId}`, {
149
152
  method: 'GET'
150
153
  });
151
154
  return data;
@@ -156,7 +159,10 @@ async function fetchUserData(userId: string) {
156
159
  }
157
160
  ```
158
161
 
159
- ---
162
+ ### Open API 使用配方
163
+
164
+ `common-patterns.md` 只保留通用调用结构。`getLocation`、Storage 等具体端能力的组合示例见
165
+ [open-api-recipes.md](./open-api-recipes.md);完整参数和返回类型见 [Open API 目录](../reference/open-api/README.md)。
160
166
 
161
167
  ## 📊 状态管理模式
162
168
 
@@ -190,23 +196,25 @@ import { useState, useEffect } from '@doubao-apps/framework';
190
196
  import { request } from '@doubao-apps/framework/api';
191
197
 
192
198
  function DataLoader() {
193
- const [data, setData] = useState(null);
199
+ const [data, setData] = useState<Record<string, unknown> | string | ArrayBuffer | null>(null);
200
+ const [error, setError] = useState('');
194
201
 
195
202
  useEffect(() => {
196
203
  // 数据获取
197
204
  const fetchData = async () => {
198
205
  try {
206
+ setError('');
199
207
  const response = await request({
200
- url: '/api/data',
208
+ url: 'https://api.example.com/data',
201
209
  method: 'GET'
202
210
  });
203
- setData(response.data);
211
+ setData(response.data ?? null);
204
212
  } catch (error) {
205
- console.error('获取数据失败:', error);
213
+ setError(error instanceof Error ? error.message : '获取数据失败');
206
214
  }
207
215
  };
208
216
 
209
- fetchData();
217
+ void fetchData();
210
218
 
211
219
  // 清理函数
212
220
  return () => {
@@ -214,7 +222,11 @@ function DataLoader() {
214
222
  };
215
223
  }, []); // 空依赖数组表示只在挂载时执行一次
216
224
 
217
- return <view>{data ? <text>{data}</text> : <text>加载中...</text>}</view>;
225
+ if (error) {
226
+ return <view>{error}</view>;
227
+ }
228
+
229
+ return <view>{data ? <text>{JSON.stringify(data)}</text> : <text>加载中...</text>}</view>;
218
230
  }
219
231
  ```
220
232
 
@@ -342,31 +354,31 @@ import { request } from '@doubao-apps/framework/api';
342
354
  function useFetch<T>(url: string) {
343
355
  const [data, setData] = useState<T | null>(null);
344
356
  const [loading, setLoading] = useState(true);
345
- const [error, setError] = useState<string | null>(null);
357
+ const [error, setError] = useState('');
346
358
 
347
359
  useEffect(() => {
348
360
  const fetchData = async () => {
349
361
  try {
350
362
  setLoading(true);
351
- setError(null);
363
+ setError('');
352
364
  const response = await request({ url, method: 'GET' });
353
365
  setData(response.data as T);
354
366
  } catch (err) {
355
- setError(err.message);
367
+ setError(err instanceof Error ? err.message : '请求失败');
356
368
  } finally {
357
369
  setLoading(false);
358
370
  }
359
371
  };
360
372
 
361
- fetchData();
373
+ void fetchData();
362
374
  }, [url]);
363
375
 
364
376
  return { data, loading, error };
365
377
  }
366
378
 
367
379
  // 使用自定义 Hook
368
- function UserProfile({ userId }) {
369
- const { data: user, loading, error } = useFetch<User>(`/api/users/${userId}`);
380
+ function UserProfile({ userId }: { userId: string }) {
381
+ const { data: user, loading, error } = useFetch<User>(`https://api.example.com/users/${userId}`);
370
382
 
371
383
  if (loading) return <text>加载中...</text>;
372
384
  if (error) return <text>错误: {error}</text>;
@@ -393,45 +405,45 @@ import { navigateTo, redirectTo, reLaunch } from '@doubao-apps/framework/api';
393
405
  // 保留当前页面,打开详情页
394
406
  function openFullPage() {
395
407
  navigateTo({
396
- url: 'user-profile?userId=12345'
408
+ url: '/pages/user-profile/index?userId=12345'
397
409
  });
398
410
  }
399
411
 
400
412
  // 替换当前页面
401
413
  function openSettingsPage() {
402
414
  redirectTo({
403
- url: 'settings'
415
+ url: '/pages/settings/index'
404
416
  });
405
417
  }
406
418
 
407
419
  // 清空页面栈并回到首页
408
420
  function relaunchHome() {
409
421
  reLaunch({
410
- url: 'home'
422
+ url: '/pages/home/index'
411
423
  });
412
424
  }
413
425
  ```
414
426
 
415
- 这里的 `url` 直接使用目标页面的 `pageId`。显式配置时取 `src/app.config.ts` 中对应 page 的 `id`;不配置时取入口目录名,例如
416
- `src/pages/detail/index.tsx` 默认为 `detail`。如需传参,可继续拼接 query string。
427
+ 这里的 `url` 使用目标 Page 路径,通常和 `src/pages/<page-name>/index.tsx` 对应,例如
428
+ `/pages/detail/index`。如需传参,可继续拼接 query string。
417
429
 
418
430
  ### 2. 页面间传参
419
431
 
420
432
  ```tsx
421
433
  import { navigateTo } from '@doubao-apps/framework/api';
422
434
 
423
- function buildPageUrl(pageId: string, params: Record<string, string | number | boolean>) {
435
+ function buildPageUrl(pagePath: string, params: Record<string, string | number | boolean>) {
424
436
  const query = Object.entries(params)
425
437
  .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
426
438
  .join('&');
427
439
 
428
- return query ? `${pageId}?${query}` : pageId;
440
+ return query ? `${pagePath}?${query}` : pagePath;
429
441
  }
430
442
 
431
443
  // 发送方
432
444
  function navigateWithParams() {
433
445
  navigateTo({
434
- url: buildPageUrl('detail-page', {
446
+ url: buildPageUrl('/pages/detail/index', {
435
447
  id: '123',
436
448
  name: '商品名称',
437
449
  price: 99.99
@@ -439,7 +451,7 @@ function navigateWithParams() {
439
451
  });
440
452
  }
441
453
 
442
- // 接收方 (detail-page)
454
+ // 接收方 (src/pages/detail/index.tsx)
443
455
  import { getViewData } from '@doubao-apps/framework';
444
456
 
445
457
  interface DetailPageData {
@@ -450,7 +462,7 @@ interface DetailPageData {
450
462
 
451
463
  export default definePage({
452
464
  render() {
453
- // getViewData() 直接返回传入页面的最终对象
465
+ // getViewData<T>() 直接返回传入页面的最终对象
454
466
  const { id, name, price } = getViewData<DetailPageData>();
455
467
 
456
468
  return (
@@ -523,7 +535,7 @@ import { navigateTo } from '@doubao-apps/framework/api';
523
535
  // 打开九宫格页面
524
536
  function openGridPage() {
525
537
  navigateTo({
526
- url: 'app-grid'
538
+ url: '/pages/app-grid/index'
527
539
  });
528
540
  }
529
541
 
@@ -531,9 +543,9 @@ function openGridPage() {
531
543
  export default definePage({
532
544
  render() {
533
545
  const apps = [
534
- { id: 'app1', name: '应用1', icon: '🎯' },
535
- { id: 'app2', name: '应用2', icon: '📱' },
536
- { id: 'app3', name: '应用3', icon: '⚡' }
546
+ { id: 'app1', name: '应用1', icon: '🎯', path: '/pages/app1/index' },
547
+ { id: 'app2', name: '应用2', icon: '📱', path: '/pages/app2/index' },
548
+ { id: 'app3', name: '应用3', icon: '⚡', path: '/pages/app3/index' }
537
549
  ];
538
550
 
539
551
  return (
@@ -542,7 +554,7 @@ export default definePage({
542
554
  <view
543
555
  key={app.id}
544
556
  className="grid-item"
545
- onClick={() => navigateTo({ url: app.id })}
557
+ onClick={() => navigateTo({ url: app.path })}
546
558
  >
547
559
  <text className="icon">{app.icon}</text>
548
560
  <text className="name">{app.name}</text>
@@ -585,5 +597,7 @@ export default definePage({
585
597
 
586
598
  - **组件基础** → [./component-basics.md](./component-basics.md)
587
599
  - **组件开发完整指南** → [../guides/component-development.md](../guides/component-development.md)
600
+ - **Open API 使用配方** → [./open-api-recipes.md](./open-api-recipes.md)
601
+ - **内置组件使用配方** → [./component-recipes.md](./component-recipes.md)
588
602
  - **API 参考** → [../reference/framework-api-quick-ref.md](../reference/framework-api-quick-ref.md)
589
603
  - **最佳实践** → [../guides/best-practices.md](../guides/best-practices.md)
@@ -37,7 +37,7 @@ src/pages/user-profile/
37
37
  import { defineAppConfig } from '@doubao-apps/kit';
38
38
 
39
39
  export default defineAppConfig({
40
- appId: 'com.example.my-doubao-app',
40
+ appId: 'db_xxxxxx',
41
41
  name: '我的豆包应用',
42
42
  pages: [
43
43
  {
@@ -53,9 +53,14 @@ export default defineAppConfig({
53
53
  ### index.tsx
54
54
 
55
55
  ```tsx
56
- import { definePage, useState, useEffect } from '@doubao-apps/framework';
56
+ import { definePage, getViewData, useEffect, useState } from '@doubao-apps/framework';
57
57
  import './index.scss';
58
58
 
59
+ interface UserProfilePageData {
60
+ userId?: string;
61
+ title?: string;
62
+ }
63
+
59
64
  interface UserProfile {
60
65
  id: string;
61
66
  name: string;
@@ -69,6 +74,7 @@ interface UserProfile {
69
74
  }
70
75
 
71
76
  function UserProfilePage() {
77
+ const viewData = getViewData<UserProfilePageData>();
72
78
  const [user, setUser] = useState<UserProfile | null>(null);
73
79
  const [loading, setLoading] = useState(true);
74
80
  const [error, setError] = useState<string | null>(null);
@@ -83,7 +89,7 @@ function UserProfilePage() {
83
89
  await new Promise(resolve => setTimeout(resolve, 1000));
84
90
 
85
91
  const mockData: UserProfile = {
86
- id: '12345',
92
+ id: viewData.userId || '12345',
87
93
  name: '张三',
88
94
  avatar: 'https://via.placeholder.com/150',
89
95
  email: 'zhangsan@example.com',
@@ -107,6 +113,10 @@ function UserProfilePage() {
107
113
  console.log('编辑用户资料');
108
114
  };
109
115
 
116
+ useEffect(() => {
117
+ void fetchUserProfile();
118
+ }, [viewData.userId]);
119
+
110
120
  // 渲染
111
121
  if (loading) {
112
122
  return (
@@ -133,6 +143,7 @@ function UserProfilePage() {
133
143
  <view className="profile-container">
134
144
  {/* 头部 */}
135
145
  <view className="profile-header">
146
+ <text className="page-title">{viewData.title || '用户资料'}</text>
136
147
  <image className="avatar" src={user.avatar} />
137
148
  <text className="name">{user.name}</text>
138
149
  <text className="email">{user.email}</text>
@@ -195,6 +206,14 @@ export default definePage({
195
206
  text-align: center;
196
207
  margin-bottom: 32px;
197
208
 
209
+ .page-title {
210
+ display: block;
211
+ font-size: 32px;
212
+ font-weight: 600;
213
+ color: #222;
214
+ margin-bottom: 24px;
215
+ }
216
+
198
217
  .avatar {
199
218
  width: 160px;
200
219
  height: 160px;
@@ -523,4 +542,6 @@ export default defineWidget({
523
542
 
524
543
  - **组件开发完整指南** → [../guides/component-development.md](../guides/component-development.md)
525
544
  - **常用开发模式** → [./common-patterns.md](./common-patterns.md)
545
+ - **Open API 使用配方** → [./open-api-recipes.md](./open-api-recipes.md)
546
+ - **内置组件使用配方** → [./component-recipes.md](./component-recipes.md)
526
547
  - **API 参考** → [../reference/framework-api-quick-ref.md](../reference/framework-api-quick-ref.md)
@@ -0,0 +1,71 @@
1
+ # 内置组件使用配方
2
+
3
+ 本文件放内置组件和 Page / Widget 状态组合的示例。组件的完整 props 和事件签名以
4
+ [组件库快速参考](../reference/components-quick-ref.md) 为准;这里重点展示受控状态、事件处理和保存态如何组合。
5
+
6
+ ---
7
+
8
+ ## Settings Page
9
+
10
+ 内置组件用小写标签直接写 JSX,不要 import 组件。需要按钮、开关和滑块时,三个标签都要直接出现在 TSX 中:
11
+ `button` 用 `onClick`;`switch` / `slider` 的 `onChange` 直接接收值,不是事件对象。
12
+
13
+ ```tsx
14
+ import { definePage, getViewData, useState } from '@doubao-apps/framework';
15
+ import './index.scss';
16
+
17
+ interface SettingsPanelData {
18
+ title: string;
19
+ enabled: boolean;
20
+ volume: number;
21
+ }
22
+
23
+ function SettingsPanel() {
24
+ const viewData = getViewData<SettingsPanelData>();
25
+ const [enabled, setEnabled] = useState(viewData.enabled);
26
+ const [volume, setVolume] = useState(viewData.volume);
27
+ const [saving, setSaving] = useState(false);
28
+ const [message, setMessage] = useState('');
29
+
30
+ const handleSave = async () => {
31
+ setSaving(true);
32
+ setMessage('');
33
+ try {
34
+ await Promise.resolve();
35
+ setMessage(`已保存: ${enabled ? '开启' : '关闭'} / ${volume}`);
36
+ } catch {
37
+ setMessage('保存失败,请重试');
38
+ } finally {
39
+ setSaving(false);
40
+ }
41
+ };
42
+
43
+ return (
44
+ <view className="settings-panel">
45
+ <text className="settings-title">{viewData.title}</text>
46
+ <button
47
+ text={saving ? '保存中' : '保存'}
48
+ type="primary"
49
+ loading={saving}
50
+ disabled={saving}
51
+ onClick={handleSave}
52
+ />
53
+ <switch checked={enabled} onChange={(checked: boolean) => setEnabled(checked)} />
54
+ <slider
55
+ value={volume}
56
+ min={0}
57
+ max={100}
58
+ showValue={true}
59
+ onChange={(nextVolume: number) => setVolume(nextVolume)}
60
+ />
61
+ {message && <text className="settings-message">{message}</text>}
62
+ </view>
63
+ );
64
+ }
65
+
66
+ export default definePage({
67
+ render() {
68
+ return <SettingsPanel />;
69
+ }
70
+ });
71
+ ```