@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.
- package/README.md +1 -32
- package/dist/733.js +7 -15
- package/dist/cli.js +1 -1
- package/dist/manifest.json +3 -11
- package/dist/skills/doubao-apps-dev/SKILL.md +103 -227
- package/dist/skills/doubao-apps-dev/references/examples/common-patterns.md +80 -66
- package/dist/skills/doubao-apps-dev/references/examples/component-basics.md +24 -3
- package/dist/skills/doubao-apps-dev/references/examples/component-recipes.md +71 -0
- package/dist/skills/doubao-apps-dev/references/examples/open-api-recipes.md +203 -0
- package/dist/skills/doubao-apps-dev/references/guides/component-development.md +66 -42
- package/dist/skills/doubao-apps-dev/references/guides/expired-widget.md +113 -0
- package/dist/skills/doubao-apps-dev/references/guides/mcp-ui.md +275 -0
- package/dist/skills/doubao-apps-dev/references/guides/performance-optimization.md +24 -7
- package/dist/skills/doubao-apps-dev/references/reference/components-quick-ref.md +18 -7
- package/dist/skills/doubao-apps-dev/references/reference/framework-api-quick-ref.md +125 -107
- package/dist/skills/doubao-apps-dev/references/reference/open-api/01-/345/237/272/347/241/200.md +136 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/02-/347/263/273/347/273/237.md +257 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/03-/345/256/232/344/275/215.md +326 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/04-/345/255/230/345/202/250.md +284 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/05-/350/267/257/347/224/261.md +162 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/06-/344/272/244/344/272/222.md +290 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/07-/350/276/223/345/205/245.md +63 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/08-/347/275/221/347/273/234.md +124 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/09-/345/252/222/344/275/223.md +192 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/11-/347/231/273/345/275/225.md +110 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/12-/346/224/257/344/273/230.md +224 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/14-/350/223/235/347/211/231.md +598 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/15-wi-fi.md +210 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/17-ibeacon.md +97 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/18-/347/275/227/347/233/230.md +78 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/20-/351/231/200/350/236/272/344/273/252.md +87 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/22-/347/237/255/344/277/241.md +40 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/23-/346/227/240/351/232/234/347/242/215.md +40 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/25-/347/224/265/346/261/240.md +63 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/26-/346/227/245/345/216/206.md +95 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/27-/345/211/252/350/264/264/346/235/277.md +64 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/28-/350/201/224/347/263/273/344/272/272.md +101 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/29-/345/212/240/345/257/206.md +43 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/30-/347/224/265/350/257/235.md +36 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/31-/346/211/253/347/240/201.md +58 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/32-/345/261/217/345/271/225.md +136 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api/33-/351/234/207/345/212/250.md +62 -0
- 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
- package/dist/skills/doubao-apps-dev/references/reference/open-api/README.md +34 -0
- package/dist/skills/doubao-apps-dev/references/reference/open-api.md +384 -2
- package/dist/skills/doubao-apps-dev/references/rules/dos-and-donts.md +44 -27
- package/dist/skills/h5-to-doubao/SKILL.md +8 -4
- package/dist/skills/weixin-to-doubao/SKILL.md +8 -4
- package/dist/skills/weixin-to-doubao/assets/migration-checklist-template.md +1 -1
- package/package.json +2 -3
- package/dist/contexts/doubao-apps-dev/AGENTS.md +0 -300
- package/dist/contexts/doubao-apps-dev/CLAUDE.md +0 -31
- package/dist/contexts/doubao-apps-dev/references/examples/common-patterns.md +0 -589
- package/dist/contexts/doubao-apps-dev/references/examples/component-basics.md +0 -526
- package/dist/contexts/doubao-apps-dev/references/guides/best-practices.md +0 -571
- package/dist/contexts/doubao-apps-dev/references/guides/component-development.md +0 -891
- package/dist/contexts/doubao-apps-dev/references/guides/performance-optimization.md +0 -402
- package/dist/contexts/doubao-apps-dev/references/guides/troubleshooting.md +0 -287
- package/dist/contexts/doubao-apps-dev/references/reference/components-quick-ref.md +0 -103
- package/dist/contexts/doubao-apps-dev/references/reference/framework-api-quick-ref.md +0 -550
- package/dist/contexts/doubao-apps-dev/references/reference/open-api/README.md +0 -8
- package/dist/contexts/doubao-apps-dev/references/reference/open-api.md +0 -11
- 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
|
-
###
|
|
17
|
+
### Open API / 端能力调用
|
|
18
18
|
|
|
19
|
-
使用
|
|
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 {
|
|
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(
|
|
35
|
+
const [error, setError] = useState('');
|
|
29
36
|
|
|
30
|
-
// 1. 网络请求
|
|
31
37
|
const fetchData = async () => {
|
|
32
38
|
try {
|
|
33
39
|
setLoading(true);
|
|
34
|
-
setError(
|
|
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
|
-
|
|
50
|
+
const responseData = response.data;
|
|
51
|
+
setData(responseData ?? null);
|
|
45
52
|
} catch (err) {
|
|
46
|
-
setError(err
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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>(
|
|
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: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
357
|
+
const [error, setError] = useState('');
|
|
346
358
|
|
|
347
359
|
useEffect(() => {
|
|
348
360
|
const fetchData = async () => {
|
|
349
361
|
try {
|
|
350
362
|
setLoading(true);
|
|
351
|
-
setError(
|
|
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>(
|
|
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`
|
|
416
|
-
|
|
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(
|
|
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 ? `${
|
|
440
|
+
return query ? `${pagePath}?${query}` : pagePath;
|
|
429
441
|
}
|
|
430
442
|
|
|
431
443
|
// 发送方
|
|
432
444
|
function navigateWithParams() {
|
|
433
445
|
navigateTo({
|
|
434
|
-
url: buildPageUrl('detail
|
|
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
|
|
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.
|
|
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: '
|
|
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,
|
|
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
|
+
```
|