@zeewain/3d-avatar-sdk 2.1.2 → 2.1.4
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 +38 -4
- package/dist/examples/test-vue2/package.json +1 -1
- package/dist/examples/test-vue3/package.json +1 -1
- package/dist/examples/test-vue3/src/App.vue +7 -3
- package/dist/examples/test-vue3/src/components/BroadcastAPI.vue +270 -2
- package/dist/index.d.ts +409 -398
- package/dist/index.es5.js +97 -41
- package/dist/index.es5.umd.js +97 -41
- package/dist/index.esm.js +99 -45
- package/dist/index.umd.cjs +99 -45
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
> - 示例:SDK版本为 2.1.3,则 `assetsUrl` 应配置为:`https://cdn.zeewain3d.com/webgl/2.1`
|
|
49
49
|
>
|
|
50
50
|
> **3. 动作资源扩展(可选)**
|
|
51
|
-
> -
|
|
51
|
+
> - 动作资源可单独从开放平台下载(**版本号前两位也必须跟SDK版本号一致**),下载后解压到资源包所在目录即可(**覆盖合并**)。
|
|
52
52
|
> - 新增的动作将自动集成到现有资源体系中。
|
|
53
53
|
>
|
|
54
54
|
> **⚠️ 版本兼容性要求**
|
|
@@ -516,6 +516,7 @@ interface IAvatarSDKConfig {
|
|
|
516
516
|
env?: 'dev' | 'test' | 'prod' | 'custom'; // 运行环境,默认'prod'
|
|
517
517
|
apiUrl?: string; // 自定义API地址(env='custom'时需要)
|
|
518
518
|
idleMotionList?: string[]; // 待机动作编码列表,随机播放(排重方式),可选
|
|
519
|
+
operationTimeout?: number; // 操作超时时间(毫秒),默认30000ms,可选
|
|
519
520
|
|
|
520
521
|
// 回调函数
|
|
521
522
|
onProgress?: (progress: number) => void; // 加载进度回调,可选
|
|
@@ -690,9 +691,39 @@ interface IBroadcastParams {
|
|
|
690
691
|
**🚀 播报队列特性:**
|
|
691
692
|
- **智能排队**:多次追加播报自动按调用顺序排队
|
|
692
693
|
- **顺序保证**:即使网络响应乱序,也确保音频按序播报
|
|
693
|
-
-
|
|
694
|
+
- **串行请求**:队列中的任务按顺序依次请求,避免占用过多并发
|
|
694
695
|
- **自动清理**:已播报内容自动从队列中移除,节省内存
|
|
695
696
|
|
|
697
|
+
**💡 队列播报最佳实践(GPT流式场景):**
|
|
698
|
+
|
|
699
|
+
针对大模型流式响应的播报队列场景,推荐以下分段策略以优化首句响应速度和服务调用次数:
|
|
700
|
+
|
|
701
|
+
| 段落序号 | 建议字数 | 说明 |
|
|
702
|
+
|:--------:|:--------:|:-----|
|
|
703
|
+
| 第1段 | 20-30字 | 快速响应,减少用户等待时间 |
|
|
704
|
+
| 第2段 | 80字左右 | 适中长度,平衡响应速度 |
|
|
705
|
+
| 第3段及以后 | 200-250字 | 较长文本,节省服务调用次数 |
|
|
706
|
+
|
|
707
|
+
```javascript
|
|
708
|
+
// 示例:智能分段播报
|
|
709
|
+
async function smartBroadcast(textStream) {
|
|
710
|
+
const segments = splitTextSmartly(textStream); // 按上述策略分段
|
|
711
|
+
|
|
712
|
+
for (let i = 0; i < segments.length; i++) {
|
|
713
|
+
await sdk.startBroadcast({
|
|
714
|
+
type: BroadcastType.TEXT,
|
|
715
|
+
humanCode: 'avatar001',
|
|
716
|
+
text: segments[i],
|
|
717
|
+
voiceCode: 'VOICE001',
|
|
718
|
+
volume: 1.0,
|
|
719
|
+
isSubtitle: false
|
|
720
|
+
}, i > 0); // 第一段 isAppend=false,后续 isAppend=true
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
> **优势**:此策略能在不影响播放流畅度的情况下,有效减少API调用次数,降低服务成本。
|
|
726
|
+
|
|
696
727
|
**使用示例:**
|
|
697
728
|
```javascript
|
|
698
729
|
// 开始新的播报(清空队列)
|
|
@@ -806,9 +837,10 @@ getBroadcastStatus(): {
|
|
|
806
837
|
hasReceivedAudio: boolean;
|
|
807
838
|
queueInfo?: {
|
|
808
839
|
totalTasks: number; // 队列中总任务数
|
|
840
|
+
pendingTasks: number; // 等待请求的任务数
|
|
809
841
|
requestingTasks: number; // 正在请求中的任务数
|
|
810
842
|
completedTasks: number; // 已完成的任务数
|
|
811
|
-
failedTasks: number;
|
|
843
|
+
failedTasks: number; // 失败的任务数
|
|
812
844
|
totalPendingResponses: number; // 待发送的响应总数
|
|
813
845
|
currentSendingSequence: number; // 当前发送的序号
|
|
814
846
|
};
|
|
@@ -823,6 +855,7 @@ getBroadcastStatus(): {
|
|
|
823
855
|
- `hasReceivedAudio`: 是否已收到至少一条音频数据
|
|
824
856
|
- `queueInfo`: 队列详细信息
|
|
825
857
|
- `totalTasks`: 队列中的总任务数
|
|
858
|
+
- `pendingTasks`: 等待请求的任务数(尚未开始请求)
|
|
826
859
|
- `requestingTasks`: 正在发起SSE请求的任务数
|
|
827
860
|
- `completedTasks`: 已完成的任务数(即将被清理)
|
|
828
861
|
- `failedTasks`: 失败的任务数
|
|
@@ -838,6 +871,7 @@ console.log('播报服务状态:', {
|
|
|
838
871
|
活跃状态: status.isActive,
|
|
839
872
|
正在生成: status.isGeneratingAudio,
|
|
840
873
|
队列任务数: status.queueInfo?.totalTasks || 0,
|
|
874
|
+
等待请求: status.queueInfo?.pendingTasks || 0,
|
|
841
875
|
待发送响应: status.queueInfo?.totalPendingResponses || 0
|
|
842
876
|
});
|
|
843
877
|
|
|
@@ -845,7 +879,7 @@ console.log('播报服务状态:', {
|
|
|
845
879
|
setInterval(() => {
|
|
846
880
|
const status = sdk.getBroadcastStatus();
|
|
847
881
|
if (status.queueInfo && status.queueInfo.totalTasks > 0) {
|
|
848
|
-
console.log(`队列进度:
|
|
882
|
+
console.log(`队列进度: 等待${status.queueInfo.pendingTasks} / 请求中${status.queueInfo.requestingTasks} / 已完成${status.queueInfo.completedTasks}`);
|
|
849
883
|
}
|
|
850
884
|
}, 1000);
|
|
851
885
|
```
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@element-plus/icons-vue": "^2.3.1",
|
|
25
25
|
"@vueuse/core": "^13.5.0",
|
|
26
26
|
"@vueuse/integrations": "^13.5.0",
|
|
27
|
-
"@zeewain/3d-avatar-sdk": "^2.1.
|
|
27
|
+
"@zeewain/3d-avatar-sdk": "^2.1.4",
|
|
28
28
|
"dayjs": "^1.11.13",
|
|
29
29
|
"element-plus": "^2.10.4",
|
|
30
30
|
"vite-plugin-html": "^3.2.2",
|
|
@@ -463,6 +463,7 @@ async function handleTextBroadcast(params: {
|
|
|
463
463
|
voiceCode?: string;
|
|
464
464
|
speed?: number;
|
|
465
465
|
broadcastMotionString?: string;
|
|
466
|
+
isAppend?: boolean; // 队列播报模式下会传递此参数
|
|
466
467
|
}): Promise<void> {
|
|
467
468
|
if (!sdk.value) return;
|
|
468
469
|
|
|
@@ -481,9 +482,12 @@ async function handleTextBroadcast(params: {
|
|
|
481
482
|
motionPlayMode: 'random'
|
|
482
483
|
};
|
|
483
484
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
485
|
+
// 队列播报模式下使用传入的isAppend参数,单次播报模式下使用sdkStatus.isStartBroadcast
|
|
486
|
+
const isAppend = params.isAppend !== undefined ? params.isAppend : sdkStatus.isStartBroadcast;
|
|
487
|
+
|
|
488
|
+
console.warn('handleTextBroadcast: ', broadcastParams, isAppend);
|
|
489
|
+
addLog(`${isAppend ? '追加播报' : '开始播报'}:${JSON.stringify({'isAppend': isAppend})}`, 'success');
|
|
490
|
+
await sdk.value.startBroadcast(broadcastParams, isAppend);
|
|
487
491
|
// ElMessage.success('文本播报已开始');
|
|
488
492
|
} catch (error) {
|
|
489
493
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -54,7 +54,16 @@
|
|
|
54
54
|
|
|
55
55
|
<!-- 文本播报表单 -->
|
|
56
56
|
<div v-if="broadcastMode === 'text'" class="form-content">
|
|
57
|
-
|
|
57
|
+
<!-- 文本播报子模式选择 -->
|
|
58
|
+
<div class="text-mode-tabs">
|
|
59
|
+
<el-tabs v-model="textBroadcastMode" type="card">
|
|
60
|
+
<el-tab-pane label="单次播报" name="single" />
|
|
61
|
+
<el-tab-pane label="队列播报" name="queue" />
|
|
62
|
+
</el-tabs>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- 单次播报表单 -->
|
|
66
|
+
<el-form v-if="textBroadcastMode === 'single'" :model="textFormData" label-width="80px" size="default">
|
|
58
67
|
<el-form-item label="播报文本" required>
|
|
59
68
|
<el-input
|
|
60
69
|
v-model="textFormData.text"
|
|
@@ -129,6 +138,120 @@
|
|
|
129
138
|
</div>
|
|
130
139
|
</el-form-item>
|
|
131
140
|
</el-form>
|
|
141
|
+
|
|
142
|
+
<!-- 队列播报表单 -->
|
|
143
|
+
<el-form v-if="textBroadcastMode === 'queue'" :model="queueFormData" label-width="80px" size="default">
|
|
144
|
+
<el-form-item label="队列文本" required>
|
|
145
|
+
<el-input
|
|
146
|
+
v-model="queueFormData.queueTexts"
|
|
147
|
+
type="textarea"
|
|
148
|
+
:rows="8"
|
|
149
|
+
placeholder="请输入多段播报文本,每行一段(模拟GPT流式输出)..."
|
|
150
|
+
:disabled="!sdkStatus.canBroadcast || isQueueBroadcasting"
|
|
151
|
+
maxlength="2000"
|
|
152
|
+
show-word-limit
|
|
153
|
+
/>
|
|
154
|
+
<div class="input-help">
|
|
155
|
+
<span class="help-text">
|
|
156
|
+
每行一段文本,将按顺序依次播报,默认每间隔500ms调用一次SDK
|
|
157
|
+
</span>
|
|
158
|
+
</div>
|
|
159
|
+
</el-form-item>
|
|
160
|
+
|
|
161
|
+
<el-form-item label="音色编码" required>
|
|
162
|
+
<el-input
|
|
163
|
+
v-model="queueFormData.voiceCode"
|
|
164
|
+
placeholder="请输入音色编码"
|
|
165
|
+
prefix-icon="microphone"
|
|
166
|
+
:disabled="!sdkStatus.canBroadcast || isQueueBroadcasting"
|
|
167
|
+
/>
|
|
168
|
+
</el-form-item>
|
|
169
|
+
|
|
170
|
+
<el-form-item label="音量" required>
|
|
171
|
+
<el-slider
|
|
172
|
+
v-model="queueFormData.volume"
|
|
173
|
+
:min="0"
|
|
174
|
+
:max="1"
|
|
175
|
+
:step="0.1"
|
|
176
|
+
:disabled="!sdkStatus.canBroadcast || isQueueBroadcasting"
|
|
177
|
+
show-input
|
|
178
|
+
/>
|
|
179
|
+
</el-form-item>
|
|
180
|
+
|
|
181
|
+
<el-form-item label="语速">
|
|
182
|
+
<el-slider
|
|
183
|
+
v-model="queueFormData.speed"
|
|
184
|
+
:min="0.5"
|
|
185
|
+
:max="2.0"
|
|
186
|
+
:step="0.1"
|
|
187
|
+
:disabled="!sdkStatus.canBroadcast || isQueueBroadcasting"
|
|
188
|
+
show-input
|
|
189
|
+
/>
|
|
190
|
+
</el-form-item>
|
|
191
|
+
|
|
192
|
+
<el-form-item label="动作列表">
|
|
193
|
+
<el-input
|
|
194
|
+
v-model="queueFormData.broadcastMotionString"
|
|
195
|
+
placeholder="请输入动作列表"
|
|
196
|
+
:disabled="isQueueBroadcasting"
|
|
197
|
+
/>
|
|
198
|
+
<div class="input-help">
|
|
199
|
+
<span class="help-text">
|
|
200
|
+
动作编码列表,多个动作编码用逗号分隔
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
</el-form-item>
|
|
204
|
+
|
|
205
|
+
<el-form-item label="发送间隔">
|
|
206
|
+
<el-input-number
|
|
207
|
+
v-model="queueFormData.interval"
|
|
208
|
+
:min="100"
|
|
209
|
+
:max="5000"
|
|
210
|
+
:step="100"
|
|
211
|
+
:disabled="isQueueBroadcasting"
|
|
212
|
+
/>
|
|
213
|
+
<span class="interval-unit">毫秒</span>
|
|
214
|
+
<div class="input-help">
|
|
215
|
+
<span class="help-text">
|
|
216
|
+
每段文本之间的发送间隔(模拟GPT流式输出),建议100-1000ms
|
|
217
|
+
</span>
|
|
218
|
+
</div>
|
|
219
|
+
</el-form-item>
|
|
220
|
+
|
|
221
|
+
<!-- 队列播报进度 -->
|
|
222
|
+
<el-form-item v-if="isQueueBroadcasting || queueProgress.total > 0" label="队列进度">
|
|
223
|
+
<div class="queue-progress">
|
|
224
|
+
<el-progress
|
|
225
|
+
:percentage="queueProgressPercent"
|
|
226
|
+
:status="queueProgress.current === queueProgress.total ? 'success' : ''"
|
|
227
|
+
/>
|
|
228
|
+
<span class="progress-text">
|
|
229
|
+
已发送{{ queueProgress.current }} / 总数{{ queueProgress.total }}
|
|
230
|
+
</span>
|
|
231
|
+
</div>
|
|
232
|
+
</el-form-item>
|
|
233
|
+
|
|
234
|
+
<el-form-item class="action-buttons">
|
|
235
|
+
<div class="action-buttons-container">
|
|
236
|
+
<el-button
|
|
237
|
+
type="primary"
|
|
238
|
+
:loading="isQueueBroadcasting"
|
|
239
|
+
:disabled="!sdkStatus.canBroadcast || !queueFormData.queueTexts.trim() || !queueFormData.voiceCode || !globalConfig.avatarCode || isQueueBroadcasting"
|
|
240
|
+
@click="handleQueueBroadcast"
|
|
241
|
+
>
|
|
242
|
+
{{ isQueueBroadcasting ? `播报中 (${queueProgress.current}/${queueProgress.total})...` : '执行队列播报' }}
|
|
243
|
+
</el-button>
|
|
244
|
+
<el-button
|
|
245
|
+
type="info"
|
|
246
|
+
size="default"
|
|
247
|
+
style="width: 170px;"
|
|
248
|
+
:disabled="isQueueBroadcasting"
|
|
249
|
+
@click="handleGetBroadcastStatus">
|
|
250
|
+
获取播报状态
|
|
251
|
+
</el-button>
|
|
252
|
+
</div>
|
|
253
|
+
</el-form-item>
|
|
254
|
+
</el-form>
|
|
132
255
|
</div>
|
|
133
256
|
|
|
134
257
|
<!-- 音频播报表单 -->
|
|
@@ -295,7 +418,7 @@
|
|
|
295
418
|
</template>
|
|
296
419
|
|
|
297
420
|
<script setup lang="ts">
|
|
298
|
-
import { ref, reactive, watch, onMounted } from 'vue';
|
|
421
|
+
import { ref, reactive, watch, onMounted, computed } from 'vue';
|
|
299
422
|
import { ElMessage } from 'element-plus';
|
|
300
423
|
import { VideoPause, VideoPlay, Close, Refresh } from '@element-plus/icons-vue';
|
|
301
424
|
import { loadFromCache, saveToCache } from '@/utils';
|
|
@@ -323,12 +446,15 @@ const emit = defineEmits<{
|
|
|
323
446
|
|
|
324
447
|
// 本地缓存键名常量
|
|
325
448
|
const BROADCAST_MODE_CACHE_KEY = 'broadcast-mode';
|
|
449
|
+
const TEXT_BROADCAST_MODE_CACHE_KEY = 'text-broadcast-mode';
|
|
326
450
|
const TEXT_FORM_CACHE_KEY = 'broadcast-text-form-data';
|
|
451
|
+
const QUEUE_FORM_CACHE_KEY = 'broadcast-queue-form-data';
|
|
327
452
|
const AUDIO_FORM_CACHE_KEY = 'broadcast-audio-form-data';
|
|
328
453
|
|
|
329
454
|
|
|
330
455
|
// 初始化表单数据,优先从缓存加载
|
|
331
456
|
const broadcastMode = ref<'text' | 'audio'>(loadFromCache(BROADCAST_MODE_CACHE_KEY, 'text')!);
|
|
457
|
+
const textBroadcastMode = ref<'single' | 'queue'>(loadFromCache(TEXT_BROADCAST_MODE_CACHE_KEY, 'single')!);
|
|
332
458
|
|
|
333
459
|
const textFormData = reactive(loadFromCache(TEXT_FORM_CACHE_KEY, {
|
|
334
460
|
text: '欢迎使用ZEEAvatarSDK智能体播报功能。这是一个强大的数字人解决方案,支持文本和音频两种播报模式,让您的应用更具互动性!',
|
|
@@ -338,6 +464,16 @@ const textFormData = reactive(loadFromCache(TEXT_FORM_CACHE_KEY, {
|
|
|
338
464
|
broadcastMotionString: 'DH_ACTION_MODEL103_000003,DH_ACTION_MODEL103_000005',
|
|
339
465
|
})!);
|
|
340
466
|
|
|
467
|
+
// 队列播报表单数据
|
|
468
|
+
const queueFormData = reactive(loadFromCache(QUEUE_FORM_CACHE_KEY, {
|
|
469
|
+
queueTexts: '你好,欢迎使用队列播报功能。\n这是第二段文本,将在指定间隔后发送。\n第三段文本模拟GPT流式输出。\n最后一段文本,队列播报即将完成。',
|
|
470
|
+
volume: 1.0,
|
|
471
|
+
speed: 1.0,
|
|
472
|
+
voiceCode: 'VOICE_EXT_W_000011',
|
|
473
|
+
broadcastMotionString: 'DH_ACTION_MODEL103_000003,DH_ACTION_MODEL103_000005',
|
|
474
|
+
interval: 500, // 发送间隔,默认500ms
|
|
475
|
+
})!);
|
|
476
|
+
|
|
341
477
|
const audioFormData = reactive(loadFromCache(AUDIO_FORM_CACHE_KEY, {
|
|
342
478
|
audioText: '欢迎使用ZEEAvatarSDK智能体播报功能',
|
|
343
479
|
audioUrl: 'https://example.com/audio/sample.mp3',
|
|
@@ -354,6 +490,17 @@ const pauseLoading = ref(false);
|
|
|
354
490
|
const resumeLoading = ref(false);
|
|
355
491
|
const stopLoading = ref(false);
|
|
356
492
|
|
|
493
|
+
// 队列播报相关状态
|
|
494
|
+
const isQueueBroadcasting = ref(false);
|
|
495
|
+
const queueProgress = reactive({
|
|
496
|
+
current: 0,
|
|
497
|
+
total: 0
|
|
498
|
+
});
|
|
499
|
+
const queueProgressPercent = computed(() => {
|
|
500
|
+
if (queueProgress.total === 0) return 0;
|
|
501
|
+
return Math.round((queueProgress.current / queueProgress.total) * 100);
|
|
502
|
+
});
|
|
503
|
+
|
|
357
504
|
// 监听表单数据变化,自动保存到本地缓存
|
|
358
505
|
watch(
|
|
359
506
|
broadcastMode,
|
|
@@ -362,6 +509,13 @@ watch(
|
|
|
362
509
|
}
|
|
363
510
|
);
|
|
364
511
|
|
|
512
|
+
watch(
|
|
513
|
+
textBroadcastMode,
|
|
514
|
+
(newMode) => {
|
|
515
|
+
saveToCache(TEXT_BROADCAST_MODE_CACHE_KEY, newMode);
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
|
|
365
519
|
watch(
|
|
366
520
|
textFormData,
|
|
367
521
|
(newData) => {
|
|
@@ -370,6 +524,14 @@ watch(
|
|
|
370
524
|
{ deep: true }
|
|
371
525
|
);
|
|
372
526
|
|
|
527
|
+
watch(
|
|
528
|
+
queueFormData,
|
|
529
|
+
(newData) => {
|
|
530
|
+
saveToCache(QUEUE_FORM_CACHE_KEY, newData);
|
|
531
|
+
},
|
|
532
|
+
{ deep: true }
|
|
533
|
+
);
|
|
534
|
+
|
|
373
535
|
watch(
|
|
374
536
|
audioFormData,
|
|
375
537
|
(newData) => {
|
|
@@ -382,7 +544,9 @@ watch(
|
|
|
382
544
|
onMounted(() => {
|
|
383
545
|
console.log('BroadcastAPI: 已从本地缓存加载表单数据', {
|
|
384
546
|
broadcastMode: broadcastMode.value,
|
|
547
|
+
textBroadcastMode: textBroadcastMode.value,
|
|
385
548
|
textFormData: textFormData,
|
|
549
|
+
queueFormData: queueFormData,
|
|
386
550
|
audioFormData: audioFormData
|
|
387
551
|
});
|
|
388
552
|
});
|
|
@@ -417,6 +581,75 @@ async function handleTextBroadcast() {
|
|
|
417
581
|
}
|
|
418
582
|
}
|
|
419
583
|
|
|
584
|
+
async function handleQueueBroadcast() {
|
|
585
|
+
const { queueTexts, volume, speed, voiceCode, broadcastMotionString, interval } = queueFormData;
|
|
586
|
+
const { avatarCode } = props.globalConfig;
|
|
587
|
+
|
|
588
|
+
if (!avatarCode) {
|
|
589
|
+
result.value = '❌ 请先在全局配置中设置Avatar Code';
|
|
590
|
+
ElMessage.warning('请先设置Avatar Code');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (!queueTexts.trim() || !voiceCode) {
|
|
595
|
+
result.value = '❌ 队列文本和音色编码不能为空';
|
|
596
|
+
ElMessage.warning('请填写完整的播报信息');
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// 解析文本为数组,过滤空行
|
|
601
|
+
const textLines = queueTexts.split('\n').filter(line => line.trim());
|
|
602
|
+
if (textLines.length === 0) {
|
|
603
|
+
result.value = '❌ 请至少输入一段播报文本';
|
|
604
|
+
ElMessage.warning('请至少输入一段播报文本');
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
isQueueBroadcasting.value = true;
|
|
609
|
+
queueProgress.current = 0;
|
|
610
|
+
queueProgress.total = textLines.length;
|
|
611
|
+
|
|
612
|
+
result.value = `🔄 开始队列播报...\n📝 共 ${textLines.length} 段文本\n🎤 音色编码: ${voiceCode}\n🔊 音量: ${volume}\n⚡ 语速: ${speed}\n⏱️ 发送间隔: ${interval}ms\n\n📡 模拟GPT流式输出...`;
|
|
613
|
+
|
|
614
|
+
try {
|
|
615
|
+
for (let i = 0; i < textLines.length; i++) {
|
|
616
|
+
const text = textLines[i].trim();
|
|
617
|
+
const isAppend = i > 0; // 第一个不是追加,后续都是追加
|
|
618
|
+
|
|
619
|
+
queueProgress.current = i + 1;
|
|
620
|
+
result.value = `🔄 队列播报中...\n📝 正在发送第 ${i + 1}/${textLines.length} 段\n📄 文本: ${text.substring(0, 30)}${text.length > 30 ? '...' : ''}\n${isAppend ? '📎 追加模式' : '🆕 新播报'}\n\n⏳ 进度: ${queueProgress.current}/${queueProgress.total}`;
|
|
621
|
+
|
|
622
|
+
// 发送播报请求
|
|
623
|
+
emit('text-broadcast', {
|
|
624
|
+
avatarCode,
|
|
625
|
+
text,
|
|
626
|
+
volume,
|
|
627
|
+
speed,
|
|
628
|
+
voiceCode,
|
|
629
|
+
broadcastMotionString,
|
|
630
|
+
isAppend
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// 如果不是最后一个,等待配置的间隔时间再发送下一个
|
|
634
|
+
if (i < textLines.length - 1) {
|
|
635
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
result.value = `✅ 队列播报请求已全部发送完成\n📋 共发送 ${textLines.length} 段文本\n📡 SDK正在按顺序处理队列...\n\n📝 说明:\n- 第一段以新播报方式发送\n- 后续段落以追加模式发送\n- 每段间隔${interval}ms发送\n- 请查看右侧日志面板获取详细信息`;
|
|
640
|
+
|
|
641
|
+
// 延迟重置状态,让用户看到完成提示
|
|
642
|
+
setTimeout(() => {
|
|
643
|
+
isQueueBroadcasting.value = false;
|
|
644
|
+
}, 1000);
|
|
645
|
+
|
|
646
|
+
} catch (error: any) {
|
|
647
|
+
isQueueBroadcasting.value = false;
|
|
648
|
+
result.value = `❌ 队列播报失败: ${error.message}\n\n🔍 可能的原因:\n- 网络连接问题\n- 音色编码不存在\n- 服务器繁忙`;
|
|
649
|
+
ElMessage.error(`队列播报失败: ${error.message}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
420
653
|
async function handleAudioBroadcast() {
|
|
421
654
|
const { audioText, audioUrl, volume, broadcastMotionString } = audioFormData;
|
|
422
655
|
const { avatarCode } = props.globalConfig;
|
|
@@ -559,6 +792,18 @@ function clearResult() {
|
|
|
559
792
|
}
|
|
560
793
|
}
|
|
561
794
|
.form-content {
|
|
795
|
+
.text-mode-tabs {
|
|
796
|
+
margin-bottom: 15px;
|
|
797
|
+
:deep(.el-tabs__header) {
|
|
798
|
+
margin-bottom: 0;
|
|
799
|
+
}
|
|
800
|
+
:deep(.el-tabs__item) {
|
|
801
|
+
font-weight: 500;
|
|
802
|
+
&.is-active {
|
|
803
|
+
color: #409eff;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
562
807
|
.el-form-item {
|
|
563
808
|
margin-bottom: 18px;
|
|
564
809
|
:deep(.el-form-item__label) {
|
|
@@ -587,6 +832,29 @@ function clearResult() {
|
|
|
587
832
|
line-height: 1.4;
|
|
588
833
|
}
|
|
589
834
|
}
|
|
835
|
+
.interval-unit {
|
|
836
|
+
margin-left: 8px;
|
|
837
|
+
font-size: 13px;
|
|
838
|
+
color: #606266;
|
|
839
|
+
}
|
|
840
|
+
.queue-progress {
|
|
841
|
+
display: flex;
|
|
842
|
+
align-items: center;
|
|
843
|
+
gap: 15px;
|
|
844
|
+
width: 100%;
|
|
845
|
+
.el-progress {
|
|
846
|
+
flex: 1;
|
|
847
|
+
:deep(.el-progress__text) {
|
|
848
|
+
min-width: 20px;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
.progress-text {
|
|
852
|
+
font-size: 12px;
|
|
853
|
+
color: #606266;
|
|
854
|
+
font-weight: 500;
|
|
855
|
+
white-space: nowrap;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
590
858
|
}
|
|
591
859
|
}
|
|
592
860
|
|