返回 AiToEarn
README.md
根目录 / project / aitoearn-web / src / store / plugin / docs / README.md
1 # 浏览器插件模块
2
3 ## 📁 文件结构
4
5 ```
6 src/store/plugin/
7 ├── index.ts # 统一导出
8 ├── request.ts # 插件代理请求封装
9 ├── store.ts # Zustand Store 状态管理
10 ├── hooks.ts # 自定义 Hooks
11 ├── baseTypes.ts # TypeScript 类型定义
12 ├── constants.ts # 常量定义
13 ├── utils.ts # 工具函数
14 ├── README.md # 本文档
15 └── examples/ # 使用示例
16 ├── basic.example.tsx
17 └── advanced.example.tsx
18 ```
19
20 ## 🚀 快速开始
21
22 ### 1. 基础使用
23
24 ```typescript
25 import { usePlugin } from '@/store/plugin';
26
27 export function MyComponent() {
28 // 自动轮询插件状态,每2秒检测一次
29 const { isConnected, status } = usePlugin(true, 2000);
30
31 return (
32 <div>
33 状态: {isConnected ? '已连接' : '未连接'}
34 </div>
35 );
36 }
37 ```
38
39 ### 2. 登录功能
40
41 ```typescript
42 import { usePluginLogin } from '@/store/plugin';
43
44 export function LoginButton() {
45 const { login } = usePluginLogin();
46
47 const handleLogin = async () => {
48 const result = await login('douyin');
49 if (result.success) {
50 console.log('登录成功:', result.data?.nickname);
51 }
52 };
53
54 return <button onClick={handleLogin}>登录抖音</button>;
55 }
56 ```
57
58 ### 3. 发布视频
59
60 ```typescript
61 import { usePluginPublish } from '@/store/plugin';
62
63 export function PublishVideo() {
64 const { publishVideo, isPublishing, publishProgress } = usePluginPublish();
65
66 const handlePublish = async (videoFile: File, coverFile: File) => {
67 const result = await publishVideo(
68 'douyin',
69 videoFile,
70 coverFile,
71 { title: '我的视频', desc: '描述' },
72 (progress) => console.log(`进度: ${progress.progress}%`)
73 );
74
75 if (result.success) {
76 alert('发布成功!');
77 }
78 };
79
80 return (
81 <div>
82 <button onClick={() => handlePublish(...)} disabled={isPublishing}>
83 {isPublishing ? '发布中...' : '发布视频'}
84 </button>
85 {publishProgress && <div>进度: {publishProgress.progress}%</div>}
86 </div>
87 );
88 }
89 ```
90
91 ### 4. 完整工作流
92
93 ```typescript
94 import { usePluginWorkflow } from '@/store/plugin';
95
96 export function OneClickPublish() {
97 const { loginAndPublishVideo } = usePluginWorkflow();
98
99 const handleOneClick = async (videoFile: File, coverFile: File) => {
100 // 自动登录 + 发布
101 const result = await loginAndPublishVideo(
102 'douyin',
103 videoFile,
104 coverFile,
105 { title: '标题' }
106 );
107
108 if (result.success) {
109 alert('发布成功!');
110 }
111 };
112
113 return <button onClick={() => handleOneClick(...)}>一键发布</button>;
114 }
115 ```
116
117 ### 5. 代理请求
118
119 ```typescript
120 import { proxyRequest } from '@/store/plugin'
121
122 const response = await proxyRequest({
123 url: 'https://httpbin.org/anything',
124 headers: {
125 accept: 'application/json',
126 },
127 body: {
128 source: 'aitoearn-website',
129 },
130 })
131
132 console.log(response.status, response.body)
133 ```
134
135 ## 📚 API 文档
136
137 ### Hooks
138
139 #### `usePlugin(autoPolling?, pollingInterval?)`
140
141 基础插件状态和方法
142
143 **参数:**
144
145 - `autoPolling` - 是否自动轮询,默认 `true`
146 - `pollingInterval` - 轮询间隔(毫秒),默认 `2000`
147
148 **返回:**
149
150 ```typescript
151 {
152 status: PluginStatus;
153 isConnected: boolean;
154 isNotInstalled: boolean;
155 isChecking: boolean;
156 isPublishing: boolean;
157 publishProgress: ProgressEvent | null;
158 checkPlugin: () => boolean;
159 startPolling: (interval?: number) => void;
160 stopPolling: () => void;
161 login: (platform: PlatformType) => Promise<PlatAccountInfo>;
162 publish: (params: PublishParams, onProgress?) => Promise<PublishResult>;
163 resetPublishState: () => void;
164 }
165 ```
166
167 #### `usePluginLogin()`
168
169 登录功能
170
171 **返回:**
172
173 ```typescript
174 {
175 login: (platform: PlatformType) => Promise<OperationResult<PlatAccountInfo>>
176 }
177 ```
178
179 #### `usePluginPublish()`
180
181 发布功能
182
183 **返回:**
184
185 ```typescript
186 {
187 publish: (params: PublishParams, onProgress?) => Promise<OperationResult>;
188 publishVideo: (platform, video, cover, options?, onProgress?) => Promise<OperationResult>;
189 publishImages: (platform, images, options?, onProgress?) => Promise<OperationResult>;
190 isPublishing: boolean;
191 publishProgress: ProgressEvent | null;
192 resetPublishState: () => void;
193 }
194 ```
195
196 #### `usePluginWorkflow()`
197
198 完整工作流(登录+发布)
199
200 **返回:**
201
202 ```typescript
203 {
204 isConnected: boolean;
205 loginAndPublishVideo: (...) => Promise<OperationResult>;
206 loginAndPublishImages: (...) => Promise<OperationResult>;
207 }
208 ```
209
210 ### 工具函数
211
212 ```typescript
213 // 状态文本
214 getPluginStatusText(status: PluginStatus): string;
215 getPublishStageText(stage: ProgressEvent['stage']): string;
216
217 // 状态判断
218 isPluginConnected(status: PluginStatus): boolean;
219 isPluginNotInstalled(status: PluginStatus): boolean;
220
221 // 格式化
222 formatProgress(progress: number): string;
223 formatFileSize(bytes: number): string;
224
225 // 文件验证
226 validateFileType(file: File, acceptTypes: string[]): boolean;
227 isValidVideoFile(file: File): boolean;
228 isValidImageFile(file: File): boolean;
229 validateFileSize(file: File, maxSize: number): boolean;
230
231 // 重试机制
232 withRetry<T>(fn: () => Promise<T>, maxRetries?, delay?): () => Promise<T>;
233 ```
234
235 ### 类型定义
236
237 ```typescript
238 // 插件状态
239 enum PluginStatus {
240 UNKNOWN = 'UNKNOWN',
241 CHECKING = 'CHECKING',
242 CONNECTED = 'CONNECTED',
243 NOT_INSTALLED = 'NOT_INSTALLED',
244 }
245
246 // 平台类型
247 type PlatformType = 'douyin' | 'xhs' | 'kwai' | 'bilibili'
248
249 // 发布参数
250 interface PublishParams {
251 platform: PlatformType
252 type: 'video' | 'image'
253 title?: string
254 desc?: string
255 video?: File | string
256 cover?: File | string
257 images?: (File | string)[]
258 topics?: string[]
259 visibility?: 'public' | 'private' | 'friends'
260 // ...更多字段
261 }
262
263 // 操作结果
264 interface OperationResult<T = any> {
265 success: boolean
266 data?: T
267 error?: string
268 }
269 ```
270
271 ### 常量
272
273 ```typescript
274 // 默认轮询间隔(2秒)
275 DEFAULT_POLLING_INTERVAL = 2000
276
277 // 插件状态文本
278 PLUGIN_STATUS_TEXT = {
279 UNKNOWN: '未检测',
280 CHECKING: '检测中...',
281 CONNECTED: '已连接',
282 NOT_INSTALLED: '未安装',
283 }
284
285 // 发布阶段文本
286 PUBLISH_STAGE_TEXT = {
287 download: '下载资源',
288 upload: '上传文件',
289 publish: '发布中',
290 complete: '完成',
291 error: '错误',
292 }
293
294 // 错误消息
295 ERROR_MESSAGES = {
296 PLUGIN_NOT_INSTALLED: '请先安装 Aitoearn 浏览器插件',
297 PUBLISHING_IN_PROGRESS: '当前正在发布中,请稍后再试',
298 LOGIN_FAILED: '登录失败',
299 PUBLISH_FAILED: '发布失败',
300 }
301 ```
302
303 ## 💡 使用示例
304
305 ### 示例 1: 插件状态显示
306
307 ```typescript
308 import { usePlugin, getPluginStatusText } from '@/store/plugin';
309
310 export function PluginStatus() {
311 const { status } = usePlugin();
312
313 return (
314 <div>
315 插件状态: {getPluginStatusText(status)}
316 </div>
317 );
318 }
319 ```
320
321 ### 示例 2: 文件验证
322
323 ```typescript
324 import { isValidVideoFile, validateFileSize } from '@/store/plugin'
325
326 function handleFileSelect(file: File) {
327 if (!isValidVideoFile(file)) {
328 alert('请选择有效的视频文件')
329 return
330 }
331
332 if (!validateFileSize(file, 500 * 1024 * 1024)) {
333 alert('视频文件不能超过 500MB')
334 return
335 }
336
337 // 处理文件...
338 }
339 ```
340
341 ### 示例 3: 带重试的发布
342
343 ```typescript
344 import { usePluginPublish, withRetry } from '@/store/plugin';
345
346 export function PublishWithRetry() {
347 const { publish } = usePluginPublish();
348
349 const handlePublish = async (params: PublishParams) => {
350 // 最多重试 3 次,每次延迟 2 秒
351 const publishWithRetry = withRetry(
352 () => publish(params),
353 3,
354 2000
355 );
356
357 try {
358 const result = await publishWithRetry();
359 console.log('发布成功:', result);
360 } catch (error) {
361 console.error('重试后仍然失败:', error);
362 }
363 };
364
365 return <button onClick={() => handlePublish(...)}>发布</button>;
366 }
367 ```
368
369 ### 示例 4: 进度展示
370
371 ```typescript
372 import { usePluginPublish, formatProgress, getPublishStageText } from '@/store/plugin';
373 import { Progress } from 'antd';
374
375 export function PublishWithProgress() {
376 const { publishVideo, publishProgress } = usePluginPublish();
377
378 return (
379 <div>
380 {publishProgress && (
381 <>
382 <Progress percent={publishProgress.progress} />
383 <p>
384 {getPublishStageText(publishProgress.stage)}: {formatProgress(publishProgress.progress)}
385 </p>
386 </>
387 )}
388 </div>
389 );
390 }
391 ```
392
393 ## 📝 注意事项
394
395 1. **轮询管理**
396 - 使用 `usePlugin` 时会自动管理轮询
397 - 组件卸载时会自动清理定时器
398 - 无需手动调用 `stopPolling()`
399
400 2. **错误处理**
401 - Hooks 返回的方法都使用 `OperationResult` 格式
402 - 包含 `success`、`data` 和 `error` 字段
403 - 建议使用这种方式而不是 try-catch
404
405 3. **文件大小限制**
406 - 视频: 建议 ≤ 500MB
407 - 图片: 建议每张 ≤ 10MB
408 - 图文: 最多 9 张
409
410 4. **并发限制**
411 - 同一时间只能有一个发布任务
412 - `isPublishing` 为 `true` 时无法开始新发布
413
414 5. **平台差异**
415 - 不同平台支持的功能可能不同
416 - 参考各平台的具体文档
417
418 ## 🔗 相关链接
419
420 - [Web API 文档](../../../demo/docs/WEB_API.md)
421 - [类型定义](../../../demo/PublishType/)
422 - [示例代码](./examples/)
423
424 ## 📞 技术支持
425
426 如有问题或建议,请联系技术支持团队。
427
428 ---
429
430 **版本**: 1.0.0
431 **最后更新**: 2025-12-01
432
432 lines MARKDOWN