@zeewain/3d-avatar-sdk 1.2.0 → 1.2.2

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 (55) hide show
  1. package/README.md +6 -5
  2. package/dist/assets/Build/webgl.data.unityweb +0 -0
  3. package/dist/assets/Build/webgl.framework.js.unityweb +0 -0
  4. package/dist/assets/Build/webgl.wasm.unityweb +0 -0
  5. package/dist/examples/test-umd/index.html +762 -0
  6. package/dist/examples/test-vue2/.eslintignore +45 -0
  7. package/dist/examples/test-vue2/.eslintrc.js +174 -0
  8. package/dist/examples/test-vue2/.stylelintignore +50 -0
  9. package/dist/examples/test-vue2/.stylelintrc.js +79 -0
  10. package/dist/examples/test-vue2/README.md +139 -0
  11. package/dist/examples/test-vue2/babel.config.js +14 -0
  12. package/dist/examples/test-vue2/package.json +53 -0
  13. package/dist/examples/test-vue2/pnpm-lock.yaml +8776 -0
  14. package/dist/examples/test-vue2/public/index.html +19 -0
  15. package/dist/examples/test-vue2/setup.js +170 -0
  16. package/dist/examples/test-vue2/src/App.vue +943 -0
  17. package/dist/examples/test-vue2/src/components/BroadcastAPI.vue +666 -0
  18. package/dist/examples/test-vue2/src/components/CameraAPI.vue +414 -0
  19. package/dist/examples/test-vue2/src/components/GlobalConfig.vue +200 -0
  20. package/dist/examples/test-vue2/src/components/InfoCards.vue +294 -0
  21. package/dist/examples/test-vue2/src/components/InitAPI.vue +334 -0
  22. package/dist/examples/test-vue2/src/components/LogPanel.vue +249 -0
  23. package/dist/examples/test-vue2/src/components/MotionControlAPI.vue +400 -0
  24. package/dist/examples/test-vue2/src/components/UnityPreview.vue +201 -0
  25. package/dist/examples/test-vue2/src/main.js +16 -0
  26. package/dist/examples/test-vue2/vue.config.js +41 -0
  27. package/dist/examples/test-vue3/.eslintrc +3 -0
  28. package/dist/examples/test-vue3/.stylelintignore +3 -0
  29. package/dist/examples/test-vue3/.stylelintrc +48 -0
  30. package/dist/examples/test-vue3/README.md +236 -0
  31. package/dist/examples/test-vue3/env.d.ts +8 -0
  32. package/dist/examples/test-vue3/index.html +95 -0
  33. package/dist/examples/test-vue3/package.json +55 -0
  34. package/dist/examples/test-vue3/pnpm-lock.yaml +4636 -0
  35. package/dist/examples/test-vue3/setup.js +167 -0
  36. package/dist/examples/test-vue3/src/App.vue +962 -0
  37. package/dist/examples/test-vue3/src/components/BroadcastAPI.vue +636 -0
  38. package/dist/examples/test-vue3/src/components/CameraAPI.vue +376 -0
  39. package/dist/examples/test-vue3/src/components/GlobalConfig.vue +213 -0
  40. package/dist/examples/test-vue3/src/components/InfoCards.vue +288 -0
  41. package/dist/examples/test-vue3/src/components/InitAPI.vue +339 -0
  42. package/dist/examples/test-vue3/src/components/LogPanel.vue +236 -0
  43. package/dist/examples/test-vue3/src/components/MotionControlAPI.vue +373 -0
  44. package/dist/examples/test-vue3/src/components/UnityPreview.vue +189 -0
  45. package/dist/examples/test-vue3/src/main.ts +12 -0
  46. package/dist/examples/test-vue3/src/types.ts +9 -0
  47. package/dist/examples/test-vue3/tsconfig.json +44 -0
  48. package/dist/examples/test-vue3/tsconfig.node.json +14 -0
  49. package/dist/examples/test-vue3/vite.config.ts +75 -0
  50. package/dist/index.d.ts +142 -132
  51. package/dist/index.es5.js +93 -41
  52. package/dist/index.es5.umd.js +93 -41
  53. package/dist/index.esm.js +101 -42
  54. package/dist/index.umd.cjs +101 -42
  55. package/package.json +4 -3
@@ -0,0 +1,666 @@
1
+ <template>
2
+ <div class="broadcast-api">
3
+ <div class="api-description">
4
+ <el-alert
5
+ title="API说明"
6
+ type="info"
7
+ :closable="false"
8
+ show-icon
9
+ >
10
+ <template slot="default">
11
+ <div class="api-signature">
12
+ <code>startBroadcast(params: IBroadcastParams): Promise&lt;void&gt;</code>
13
+ <br />
14
+ <code>pauseBroadcast(): Promise&lt;void&gt;</code>
15
+ <br />
16
+ <code>resumeBroadcast(): Promise&lt;void&gt;</code>
17
+ <br />
18
+ <code>stopBroadcast(): Promise&lt;void&gt;</code>
19
+ </div>
20
+ <p>支持文本和音频两种播报模式,以及播报过程中的实时控制功能。token在SDK初始化时配置,支持流式播报。</p>
21
+ <p class="api-features">
22
+ ✅ 流式播报支持 &nbsp;&nbsp;
23
+ ✅ 实时控制功能 &nbsp;&nbsp;
24
+ ✅ 多种音频格式
25
+ </p>
26
+ </template>
27
+ </el-alert>
28
+ </div>
29
+
30
+ <div class="api-controls">
31
+ <el-card shadow="hover" class="control-card">
32
+ <div slot="header" class="card-header">
33
+ <span>智能播报控制</span>
34
+ <el-tag v-if="sdkStatus.canBroadcast" type="success" size="mini">可用</el-tag>
35
+ <el-tag v-else type="info" size="mini">不可用</el-tag>
36
+ </div>
37
+
38
+ <div class="control-content">
39
+ <!-- 播报模式选择 -->
40
+ <div class="mode-selector">
41
+ <el-radio-group v-model="broadcastMode" size="small">
42
+ <el-radio-button label="text">
43
+ <i class="el-icon-chat-dot-round"></i>
44
+ 文本播报
45
+ </el-radio-button>
46
+ <el-radio-button label="audio">
47
+ <i class="el-icon-headset"></i>
48
+ 音频播报
49
+ </el-radio-button>
50
+ </el-radio-group>
51
+ </div>
52
+
53
+ <!-- 文本播报表单 -->
54
+ <div v-if="broadcastMode === 'text'" class="form-content">
55
+ <el-form :model="textFormData" label-width="80px" size="small">
56
+ <el-form-item label="播报文本" required>
57
+ <el-input
58
+ v-model="textFormData.text"
59
+ type="textarea"
60
+ :rows="3"
61
+ placeholder="请输入播报文本..."
62
+ :disabled="!sdkStatus.canBroadcast"
63
+ maxlength="500"
64
+ show-word-limit
65
+ />
66
+ </el-form-item>
67
+
68
+ <el-form-item label="音色编码" required>
69
+ <el-input
70
+ v-model="textFormData.voiceCode"
71
+ placeholder="请输入音色编码"
72
+ prefix-icon="el-icon-microphone"
73
+ :disabled="!sdkStatus.canBroadcast"
74
+ />
75
+ <div class="input-help">
76
+ <span class="help-text">
77
+ 常用音色:woman008、man001、child001等
78
+ </span>
79
+ </div>
80
+ </el-form-item>
81
+
82
+ <el-form-item label="音量" required>
83
+ <el-slider
84
+ v-model="textFormData.volume"
85
+ :min="0"
86
+ :max="1"
87
+ :step="0.1"
88
+ :disabled="!sdkStatus.canBroadcast"
89
+ show-input
90
+ />
91
+ </el-form-item>
92
+
93
+ <el-form-item label="语速">
94
+ <el-slider
95
+ v-model="textFormData.speed"
96
+ :min="0.5"
97
+ :max="2.0"
98
+ :step="0.1"
99
+ :disabled="!sdkStatus.canBroadcast"
100
+ show-input
101
+ />
102
+ </el-form-item>
103
+
104
+ <el-form-item class="action-buttons">
105
+ <el-button
106
+ type="primary"
107
+ size="medium"
108
+ icon="el-icon-chat-dot-round"
109
+ :loading="isTextLoading"
110
+ :disabled="!sdkStatus.canBroadcast || !textFormData.text || !textFormData.voiceCode || !globalConfig.avatarCode"
111
+ @click="handleTextBroadcast"
112
+ >
113
+ {{ isTextLoading ? '播报中...' : '执行文本播报' }}
114
+ </el-button>
115
+ </el-form-item>
116
+ </el-form>
117
+ </div>
118
+
119
+ <!-- 音频播报表单 -->
120
+ <div v-if="broadcastMode === 'audio'" class="form-content">
121
+ <el-form :model="audioFormData" label-width="80px" size="small">
122
+ <el-form-item label="播报文本">
123
+ <el-input
124
+ v-model="audioFormData.audioText"
125
+ type="textarea"
126
+ :rows="3"
127
+ placeholder="播报文本(可选,用于字幕显示)"
128
+ :disabled="!sdkStatus.canBroadcast"
129
+ maxlength="500"
130
+ show-word-limit
131
+ />
132
+ </el-form-item>
133
+
134
+ <el-form-item label="音频地址" required>
135
+ <el-input
136
+ v-model="audioFormData.audioUrl"
137
+ placeholder="请输入音频URL地址"
138
+ prefix-icon="el-icon-link"
139
+ :disabled="!sdkStatus.canBroadcast"
140
+ />
141
+ <div class="input-help">
142
+ <span class="help-text">
143
+ 支持MP3、WAV格式,建议使用HTTPS地址,文件大小建议小于5MB
144
+ </span>
145
+ </div>
146
+ </el-form-item>
147
+
148
+ <el-form-item label="音量">
149
+ <el-slider
150
+ v-model="audioFormData.volume"
151
+ :min="0"
152
+ :max="1"
153
+ :step="0.1"
154
+ :disabled="!sdkStatus.canBroadcast"
155
+ show-input
156
+ />
157
+ </el-form-item>
158
+
159
+ <el-form-item class="action-buttons">
160
+ <el-button
161
+ type="primary"
162
+ size="medium"
163
+ icon="el-icon-headset"
164
+ :loading="isAudioLoading"
165
+ :disabled="!sdkStatus.canBroadcast || !audioFormData.audioUrl || !globalConfig.avatarCode"
166
+ @click="handleAudioBroadcast"
167
+ >
168
+ {{ isAudioLoading ? '播报中...' : '执行音频播报' }}
169
+ </el-button>
170
+ </el-form-item>
171
+ </el-form>
172
+ </div>
173
+
174
+ <!-- 播报控制按钮 -->
175
+ <div class="broadcast-controls">
176
+ <div class="controls-header">
177
+ <span class="controls-title">播报控制</span>
178
+ <el-tag v-if="sdkStatus.canControlBroadcast" type="success" size="mini">可控制</el-tag>
179
+ <el-tag v-else type="info" size="mini">不可控制</el-tag>
180
+ </div>
181
+
182
+ <div class="control-buttons">
183
+ <el-button
184
+ type="warning"
185
+ size="small"
186
+ icon="el-icon-video-pause"
187
+ :loading="pauseLoading"
188
+ :disabled="!sdkStatus.canControlBroadcast"
189
+ @click="handlePauseBroadcast"
190
+ >
191
+ {{ pauseLoading ? '暂停中...' : '暂停' }}
192
+ </el-button>
193
+
194
+ <el-button
195
+ type="success"
196
+ size="small"
197
+ icon="el-icon-video-play"
198
+ :loading="resumeLoading"
199
+ :disabled="!sdkStatus.canControlBroadcast"
200
+ @click="handleResumeBroadcast"
201
+ >
202
+ {{ resumeLoading ? '恢复中...' : '恢复' }}
203
+ </el-button>
204
+
205
+ <el-button
206
+ type="danger"
207
+ size="small"
208
+ icon="el-icon-close"
209
+ :loading="stopLoading"
210
+ :disabled="!sdkStatus.canControlBroadcast"
211
+ @click="handleStopBroadcast"
212
+ >
213
+ {{ stopLoading ? '停止中...' : '停止' }}
214
+ </el-button>
215
+ </div>
216
+ </div>
217
+
218
+ <div class="status-tips">
219
+ <el-alert
220
+ v-if="!sdkStatus.canBroadcast"
221
+ title="请先初始化Avatar后再进行播报"
222
+ type="warning"
223
+ :closable="false"
224
+ show-icon
225
+ />
226
+ <el-alert
227
+ v-else-if="!globalConfig.avatarCode"
228
+ title="请先在全局配置中设置Avatar Code"
229
+ type="warning"
230
+ :closable="false"
231
+ show-icon
232
+ />
233
+ </div>
234
+ </div>
235
+ </el-card>
236
+ </div>
237
+
238
+ <div class="api-result">
239
+ <el-card shadow="hover" class="result-card">
240
+ <div slot="header" class="card-header">
241
+ <span>执行结果</span>
242
+ <el-button
243
+ type="text"
244
+ size="mini"
245
+ icon="el-icon-refresh"
246
+ @click="clearResult"
247
+ >
248
+ 清空
249
+ </el-button>
250
+ </div>
251
+
252
+ <div class="result-content">
253
+ <pre class="result-text">{{ result }}</pre>
254
+ </div>
255
+ </el-card>
256
+ </div>
257
+ </div>
258
+ </template>
259
+
260
+ <script>
261
+ export default {
262
+ name: 'BroadcastAPI',
263
+ props: {
264
+ sdkStatus: {
265
+ type: Object,
266
+ required: true
267
+ },
268
+ globalConfig: {
269
+ type: Object,
270
+ required: true
271
+ }
272
+ },
273
+ data () {
274
+ return {
275
+ broadcastMode: 'text',
276
+ textFormData: {
277
+ text: '欢迎使用ZEEAvatarSDK智能体播报功能。这是一个强大的数字人解决方案,支持文本和音频两种播报模式,让您的应用更具互动性!',
278
+ volume: 1.0,
279
+ speed: 1.0,
280
+ voiceCode: 'woman008'
281
+ },
282
+ audioFormData: {
283
+ audioText: '欢迎使用ZEEAvatarSDK智能体播报功能',
284
+ audioUrl: 'https://example.com/audio/sample.mp3',
285
+ volume: 1.0
286
+ },
287
+ result: '等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n- 实时播报控制(暂停/恢复/停止)',
288
+ isTextLoading: false,
289
+ isAudioLoading: false,
290
+ // 播报控制状态
291
+ pauseLoading: false,
292
+ resumeLoading: false,
293
+ stopLoading: false
294
+ }
295
+ },
296
+ methods: {
297
+ async handleTextBroadcast () {
298
+ const { text, volume, speed, voiceCode } = this.textFormData
299
+ const { avatarCode } = this.globalConfig
300
+
301
+ if (!avatarCode) {
302
+ this.result = '❌ 请先在全局配置中设置Avatar Code'
303
+ this.$message.warning('请先设置Avatar Code')
304
+ return
305
+ }
306
+
307
+ if (!text || !voiceCode) {
308
+ this.result = '❌ 播报文本和音色编码不能为空'
309
+ this.$message.warning('请填写完整的播报信息')
310
+ return
311
+ }
312
+
313
+ this.isTextLoading = true
314
+ this.result = `🔄 正在执行文本播报...\n📝 播报文本: ${text.substring(0, 30)}${text.length > 30 ? '...' : ''}\n🎤 音色编码: ${voiceCode}\n🔊 音量: ${volume}\n⚡ 语速: ${speed}\n\n📡 流式播报中,请等待服务器响应...`
315
+
316
+ try {
317
+ this.$emit('text-broadcast', {
318
+ avatarCode,
319
+ text,
320
+ volume,
321
+ speed,
322
+ voiceCode
323
+ })
324
+
325
+ // 模拟加载时间
326
+ setTimeout(() => {
327
+ this.isTextLoading = false
328
+ this.result = '✅ 文本播报请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 播报将以流式方式进行\n- 可使用下方控制按钮暂停/继续/停止\n- 播报过程中会触发相应的事件回调'
329
+ }, 1000)
330
+ } catch (error) {
331
+ this.isTextLoading = false
332
+ this.result = `❌ 播报失败: ${error.message}\n\n🔍 可能的原因:\n- 网络连接问题\n- 音色编码不存在\n- 服务器繁忙\n- 文本内容不符合要求`
333
+ this.$message.error(`播报失败: ${error.message}`)
334
+ }
335
+ },
336
+
337
+ async handleAudioBroadcast () {
338
+ const { audioText, audioUrl, volume } = this.audioFormData
339
+ const { avatarCode } = this.globalConfig
340
+
341
+ if (!avatarCode) {
342
+ this.result = '❌ 请先在全局配置中设置Avatar Code'
343
+ this.$message.warning('请先设置Avatar Code')
344
+ return
345
+ }
346
+
347
+ if (!audioUrl) {
348
+ this.result = '❌ 音频URL不能为空'
349
+ this.$message.warning('请填写音频URL')
350
+ return
351
+ }
352
+
353
+ this.isAudioLoading = true
354
+ this.result = `🔄 正在执行音频播报...\n🎵 音频地址: ${audioUrl}\n📝 字幕文本: ${audioText || '无'}\n🔊 音量: ${volume}\n\n📡 正在验证音频文件并加载...`
355
+
356
+ try {
357
+ this.$emit('audio-broadcast', {
358
+ avatarCode,
359
+ audioText,
360
+ audioUrl,
361
+ volume
362
+ })
363
+
364
+ // 模拟加载时间
365
+ setTimeout(() => {
366
+ this.isAudioLoading = false
367
+ this.result = '✅ 音频播报请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 音频将直接驱动数字人口型\n- 支持MP3、WAV等格式\n- 可使用下方控制按钮进行控制'
368
+ }, 1000)
369
+ } catch (error) {
370
+ this.isAudioLoading = false
371
+ this.result = `❌ 播报失败: ${error.message}\n\n🔍 可能的原因:\n- 音频文件无法访问\n- 音频格式不支持\n- 文件过大(建议<5MB)\n- 网络连接问题`
372
+ this.$message.error(`播报失败: ${error.message}`)
373
+ }
374
+ },
375
+
376
+ // 播报控制方法
377
+ handlePauseBroadcast () {
378
+ this.pauseLoading = true
379
+ this.result = '🔄 正在暂停播报...\n📡 发送暂停指令到Unity引擎...'
380
+
381
+ try {
382
+ this.$emit('pause-broadcast')
383
+
384
+ setTimeout(() => {
385
+ this.pauseLoading = false
386
+ this.result = '⏸️ 播报暂停请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 播报已暂停,可以随时恢复\n- 暂停状态下可以开始新的播报\n- 使用恢复按钮继续当前播报'
387
+ }, 800)
388
+ } catch (error) {
389
+ this.pauseLoading = false
390
+ this.result = `❌ 暂停播报失败: ${error.message}\n\n🔍 可能的原因:\n- 当前没有正在播报的内容\n- Unity通信异常\n- 播报服务未初始化`
391
+ }
392
+ },
393
+
394
+ handleResumeBroadcast () {
395
+ this.resumeLoading = true
396
+ this.result = '🔄 正在恢复播报...\n📡 发送恢复指令到Unity引擎...'
397
+
398
+ try {
399
+ this.$emit('resume-broadcast')
400
+
401
+ setTimeout(() => {
402
+ this.resumeLoading = false
403
+ this.result = '▶️ 播报恢复请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 播报已恢复,继续之前的内容\n- 可以随时再次暂停或停止\n- 播报完成后会自动结束'
404
+ }, 800)
405
+ } catch (error) {
406
+ this.resumeLoading = false
407
+ this.result = `❌ 恢复播报失败: ${error.message}\n\n🔍 可能的原因:\n- 当前没有暂停的播报内容\n- Unity通信异常\n- 播报服务未初始化`
408
+ }
409
+ },
410
+
411
+ handleStopBroadcast () {
412
+ this.stopLoading = true
413
+ this.result = '🔄 正在停止播报...\n📡 发送停止指令到Unity引擎...\n🧹 清理播报资源和队列...'
414
+
415
+ try {
416
+ this.$emit('stop-broadcast')
417
+
418
+ setTimeout(() => {
419
+ this.stopLoading = false
420
+ this.result = '⏹️ 播报停止请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 播报已完全停止\n- 所有播报队列已清空\n- 可以开始新的播报任务'
421
+ }, 800)
422
+ } catch (error) {
423
+ this.stopLoading = false
424
+ this.result = `❌ 停止播报失败: ${error.message}\n\n🔍 可能的原因:\n- Unity通信异常\n- 播报服务未初始化\n- 系统资源异常`
425
+ }
426
+ },
427
+
428
+ clearResult () {
429
+ this.result = '等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n- 实时播报控制(暂停/恢复/停止)'
430
+ }
431
+ }
432
+ }
433
+ </script>
434
+
435
+ <style lang="scss" scoped>
436
+ .broadcast-api {
437
+ .api-description {
438
+ margin-bottom: 20px;
439
+
440
+ .api-signature {
441
+ background-color: #f5f5f5;
442
+ padding: 8px 12px;
443
+ border-radius: 4px;
444
+ margin-bottom: 10px;
445
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
446
+ font-size: 12px;
447
+ border-left: 4px solid #67c23a;
448
+
449
+ code {
450
+ color: #2c3e50;
451
+ font-weight: 600;
452
+ display: block;
453
+ margin: 2px 0;
454
+ }
455
+ }
456
+
457
+ p {
458
+ margin: 5px 0;
459
+ color: #606266;
460
+ font-size: 14px;
461
+
462
+ &.api-features {
463
+ color: #67c23a;
464
+ font-weight: 500;
465
+ margin-top: 8px;
466
+ }
467
+ }
468
+ }
469
+
470
+ .api-controls {
471
+ margin-bottom: 20px;
472
+
473
+ .control-card {
474
+ .card-header {
475
+ display: flex;
476
+ justify-content: space-between;
477
+ align-items: center;
478
+ font-weight: 600;
479
+ }
480
+
481
+ .control-content {
482
+ .mode-selector {
483
+ margin-bottom: 20px;
484
+ text-align: center;
485
+
486
+ .el-radio-group {
487
+ width: 100%;
488
+
489
+ .el-radio-button {
490
+ flex: 1;
491
+
492
+ :deep(.el-radio-button__inner) {
493
+ width: 100%;
494
+ padding: 8px 15px;
495
+
496
+ i {
497
+ margin-right: 5px;
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
503
+
504
+ .form-content {
505
+ .el-form-item {
506
+ margin-bottom: 18px;
507
+
508
+ :deep(.el-form-item__label) {
509
+ font-weight: 500;
510
+ color: #303133;
511
+ }
512
+
513
+ &.action-buttons {
514
+ margin-bottom: 0;
515
+ margin-top: 25px;
516
+
517
+ .el-button {
518
+ width: 100%;
519
+ padding: 10px 0;
520
+ font-weight: 500;
521
+ }
522
+ }
523
+
524
+ .input-help {
525
+ margin-top: 5px;
526
+
527
+ .help-text {
528
+ font-size: 12px;
529
+ color: #909399;
530
+ line-height: 1.4;
531
+ }
532
+ }
533
+ }
534
+ }
535
+
536
+ // 播报控制样式
537
+ .broadcast-controls {
538
+ margin-top: 25px;
539
+ padding: 15px;
540
+ border: 1px solid #e4e7ed;
541
+ border-radius: 6px;
542
+ background-color: #fafbfc;
543
+
544
+ .controls-header {
545
+ display: flex;
546
+ justify-content: space-between;
547
+ align-items: center;
548
+ margin-bottom: 12px;
549
+
550
+ .controls-title {
551
+ font-weight: 600;
552
+ color: #303133;
553
+ font-size: 14px;
554
+ }
555
+ }
556
+
557
+ .control-buttons {
558
+ display: flex;
559
+ gap: 8px;
560
+
561
+ .el-button {
562
+ flex: 1;
563
+ font-size: 12px;
564
+ padding: 8px 12px;
565
+ }
566
+ }
567
+ }
568
+
569
+ .status-tips {
570
+ margin-top: 15px;
571
+
572
+ .el-alert {
573
+ margin-bottom: 10px;
574
+
575
+ &:last-child {
576
+ margin-bottom: 0;
577
+ }
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+
584
+ .api-result {
585
+ .result-card {
586
+ .card-header {
587
+ display: flex;
588
+ justify-content: space-between;
589
+ align-items: center;
590
+ font-weight: 600;
591
+ }
592
+
593
+ .result-content {
594
+ max-height: 300px;
595
+ overflow-y: auto;
596
+
597
+ .result-text {
598
+ margin: 0;
599
+ padding: 15px;
600
+ background-color: #f8f9fa;
601
+ border-radius: 4px;
602
+ border: 1px solid #e9ecef;
603
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
604
+ font-size: 13px;
605
+ line-height: 1.6;
606
+ color: #2c3e50;
607
+ white-space: pre-wrap;
608
+ word-wrap: break-word;
609
+ }
610
+ }
611
+ }
612
+ }
613
+ }
614
+
615
+ // 移动端优化
616
+ @media screen and (width <= 768px) {
617
+ .broadcast-api {
618
+ .mode-selector {
619
+ .el-radio-group {
620
+ .el-radio-button {
621
+ :deep(.el-radio-button__inner) {
622
+ padding: 6px 10px;
623
+ font-size: 12px;
624
+
625
+ i {
626
+ margin-right: 3px;
627
+ }
628
+ }
629
+ }
630
+ }
631
+ }
632
+
633
+ .form-content {
634
+ .el-form {
635
+ :deep(.el-form-item__label) {
636
+ font-size: 13px;
637
+ }
638
+
639
+ .el-textarea {
640
+ :deep(.el-textarea__inner) {
641
+ font-size: 13px;
642
+ }
643
+ }
644
+
645
+ .el-input {
646
+ :deep(.el-input__inner) {
647
+ font-size: 13px;
648
+ }
649
+ }
650
+ }
651
+ }
652
+
653
+ .broadcast-controls {
654
+ .control-buttons {
655
+ flex-direction: column;
656
+ gap: 8px;
657
+
658
+ .el-button {
659
+ width: 100%;
660
+ margin: 0;
661
+ }
662
+ }
663
+ }
664
+ }
665
+ }
666
+ </style>