@zeewain/3d-avatar-sdk 1.2.4 → 1.2.5
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 +4 -8
- package/dist/examples/test-vue2/package.json +1 -1
- package/dist/examples/test-vue3/package.json +1 -1
- package/dist/examples/test-vue3/pnpm-lock.yaml +7 -7
- package/dist/examples/test-vue3/src/App.vue +34 -12
- package/dist/examples/test-vue3/src/components/BroadcastAPI.vue +78 -26
- package/dist/examples/test-vue3/src/components/GlobalConfig.vue +1 -0
- package/dist/examples/test-vue3/src/components/LogPanel.vue +5 -5
- package/dist/examples/test-vue3/src/components/MotionControlAPI.vue +44 -7
- package/dist/examples/test-vue3/src/utils/index.ts +35 -0
- package/dist/index.d.ts +26 -10
- package/dist/index.es5.js +65 -43
- package/dist/index.es5.umd.js +65 -43
- package/dist/index.esm.js +56 -35
- package/dist/index.umd.cjs +56 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -750,7 +750,7 @@ updateBroadcastCallbacks(callbacks: IBroadcastCallbacks): void
|
|
|
750
750
|
```typescript
|
|
751
751
|
interface IBroadcastCallbacks {
|
|
752
752
|
onStart?: () => void; // 播报开始
|
|
753
|
-
onFinish?: () => void; //
|
|
753
|
+
onFinish?: () => void; // 播报完成(每调用一次`startBroadcast`,在播报结束后都会触发一次回调)
|
|
754
754
|
onError?: (error: Error) => void; // 播报错误
|
|
755
755
|
onPause?: () => void; // 播报暂停
|
|
756
756
|
onResume?: () => void; // 播报恢复
|
|
@@ -775,8 +775,6 @@ getBroadcastStatus(): {
|
|
|
775
775
|
isActive: boolean;
|
|
776
776
|
isGeneratingAudio: boolean;
|
|
777
777
|
hasReceivedAudio: boolean;
|
|
778
|
-
pendingCallbacks: number;
|
|
779
|
-
hasController: boolean;
|
|
780
778
|
queueInfo?: {
|
|
781
779
|
totalTasks: number; // 队列中总任务数
|
|
782
780
|
requestingTasks: number; // 正在请求中的任务数
|
|
@@ -788,14 +786,12 @@ getBroadcastStatus(): {
|
|
|
788
786
|
}
|
|
789
787
|
```
|
|
790
788
|
|
|
791
|
-
|
|
789
|
+
获取播报状态信息,包括队列状态监控。`当播报队列中最后一条音频播放完毕后,队列状态信息将被重置。`
|
|
792
790
|
|
|
793
791
|
**返回值说明:**
|
|
794
|
-
- `isActive`:
|
|
792
|
+
- `isActive`: 播报服务是否活跃(是否正在播报音频或正在生成音频)
|
|
795
793
|
- `isGeneratingAudio`: 是否正在生成音频
|
|
796
|
-
- `hasReceivedAudio`:
|
|
797
|
-
- `pendingCallbacks`: 待处理的回调数量
|
|
798
|
-
- `hasController`: 是否有活跃的请求控制器
|
|
794
|
+
- `hasReceivedAudio`: 是否已收到至少一条音频数据
|
|
799
795
|
- `queueInfo`: 队列详细信息
|
|
800
796
|
- `totalTasks`: 队列中的总任务数
|
|
801
797
|
- `requestingTasks`: 正在发起SSE请求的任务数
|
|
@@ -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": "^1.2.
|
|
27
|
+
"@zeewain/3d-avatar-sdk": "^1.2.5",
|
|
28
28
|
"dayjs": "^1.11.13",
|
|
29
29
|
"element-plus": "^2.10.4",
|
|
30
30
|
"vite-plugin-html": "^3.2.2",
|
|
@@ -15,8 +15,8 @@ dependencies:
|
|
|
15
15
|
specifier: ^13.5.0
|
|
16
16
|
version: 13.5.0(vue@3.5.17)
|
|
17
17
|
'@zeewain/3d-avatar-sdk':
|
|
18
|
-
specifier: ^1.2.
|
|
19
|
-
version: 1.2.
|
|
18
|
+
specifier: ^1.2.4
|
|
19
|
+
version: 1.2.4
|
|
20
20
|
dayjs:
|
|
21
21
|
specifier: ^1.11.13
|
|
22
22
|
version: 1.11.13
|
|
@@ -1124,11 +1124,11 @@ packages:
|
|
|
1124
1124
|
- vue
|
|
1125
1125
|
dev: false
|
|
1126
1126
|
|
|
1127
|
-
/@zeewain/3d-avatar-sdk@1.2.
|
|
1128
|
-
resolution: {integrity: sha512-
|
|
1127
|
+
/@zeewain/3d-avatar-sdk@1.2.4:
|
|
1128
|
+
resolution: {integrity: sha512-a+y9HpUjbZgObzR+RfnfjxamdAa6Nk9CWkdHMejPIsYfSogCM6UtN/Jbh1A08RzIH9xrJnsUpTm1nXvvIsT6Vw==}
|
|
1129
1129
|
dependencies:
|
|
1130
1130
|
'@microsoft/fetch-event-source': 2.0.1
|
|
1131
|
-
core-js: 3.
|
|
1131
|
+
core-js: 3.45.1
|
|
1132
1132
|
dev: false
|
|
1133
1133
|
|
|
1134
1134
|
/acorn-jsx@5.3.2(acorn@8.15.0):
|
|
@@ -1493,8 +1493,8 @@ packages:
|
|
|
1493
1493
|
resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
|
|
1494
1494
|
dev: false
|
|
1495
1495
|
|
|
1496
|
-
/core-js@3.
|
|
1497
|
-
resolution: {integrity: sha512-
|
|
1496
|
+
/core-js@3.45.1:
|
|
1497
|
+
resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
|
|
1498
1498
|
requiresBuild: true
|
|
1499
1499
|
dev: false
|
|
1500
1500
|
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
<el-tab-pane label="智能播报" name="text-broadcast">
|
|
49
49
|
<BroadcastAPI
|
|
50
|
+
ref="broadcastAPI"
|
|
50
51
|
:sdk-status="sdkStatus"
|
|
51
52
|
:global-config="globalConfig"
|
|
52
53
|
@text-broadcast="handleTextBroadcast"
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
@pause-broadcast="handlePauseBroadcast"
|
|
55
56
|
@resume-broadcast="handleResumeBroadcast"
|
|
56
57
|
@stop-broadcast="handleStopBroadcast"
|
|
58
|
+
@get-broadcast-status="handleGetBroadcastStatus"
|
|
57
59
|
/>
|
|
58
60
|
</el-tab-pane>
|
|
59
61
|
|
|
@@ -135,6 +137,7 @@
|
|
|
135
137
|
|
|
136
138
|
<el-tab-pane label="智能播报" name="text-broadcast">
|
|
137
139
|
<BroadcastAPI
|
|
140
|
+
ref="broadcastAPI"
|
|
138
141
|
:sdk-status="sdkStatus"
|
|
139
142
|
:global-config="globalConfig"
|
|
140
143
|
@text-broadcast="handleTextBroadcast"
|
|
@@ -142,6 +145,7 @@
|
|
|
142
145
|
@pause-broadcast="handlePauseBroadcast"
|
|
143
146
|
@resume-broadcast="handleResumeBroadcast"
|
|
144
147
|
@stop-broadcast="handleStopBroadcast"
|
|
148
|
+
@get-broadcast-status="handleGetBroadcastStatus"
|
|
145
149
|
/>
|
|
146
150
|
</el-tab-pane>
|
|
147
151
|
|
|
@@ -229,6 +233,7 @@ import InfoCards from './components/InfoCards.vue';
|
|
|
229
233
|
import UnityPreview from './components/UnityPreview.vue';
|
|
230
234
|
|
|
231
235
|
import type { IGlobalConfig } from './types';
|
|
236
|
+
import { loadFromCache, saveToCache } from './utils';
|
|
232
237
|
|
|
233
238
|
interface ISDKStatus {
|
|
234
239
|
unityLoaded: boolean;
|
|
@@ -238,6 +243,7 @@ interface ISDKStatus {
|
|
|
238
243
|
canGetMotion: boolean;
|
|
239
244
|
canControlBroadcast: boolean;
|
|
240
245
|
canControlCamera: boolean;
|
|
246
|
+
isStartBroadcast: boolean;
|
|
241
247
|
}
|
|
242
248
|
|
|
243
249
|
interface ILogEntry {
|
|
@@ -267,7 +273,8 @@ const sdkStatus = reactive<ISDKStatus>({
|
|
|
267
273
|
canBroadcast: false,
|
|
268
274
|
canGetMotion: false,
|
|
269
275
|
canControlBroadcast: false,
|
|
270
|
-
canControlCamera: false
|
|
276
|
+
canControlCamera: false,
|
|
277
|
+
isStartBroadcast: false,
|
|
271
278
|
});
|
|
272
279
|
|
|
273
280
|
// 操作日志
|
|
@@ -278,6 +285,7 @@ const logs = reactive<ILogEntry[]>([
|
|
|
278
285
|
|
|
279
286
|
// SDK实例
|
|
280
287
|
const sdk = ref<ZEEAvatarSDK | null>(null);
|
|
288
|
+
const broadcastAPI = ref<InstanceType<typeof BroadcastAPI> | null>(null);
|
|
281
289
|
|
|
282
290
|
// 初始化SDK配置
|
|
283
291
|
function initializeSDKConfig(): ZEEAvatarSDK | null {
|
|
@@ -316,12 +324,15 @@ function initializeSDKConfig(): ZEEAvatarSDK | null {
|
|
|
316
324
|
// 播报回调
|
|
317
325
|
broadcastCallbacks: {
|
|
318
326
|
onStart: () => {
|
|
327
|
+
sdkStatus.isStartBroadcast = true;
|
|
319
328
|
addLog('播报开始', 'info');
|
|
320
329
|
},
|
|
321
330
|
onFinish: () => {
|
|
331
|
+
sdkStatus.isStartBroadcast = false;
|
|
322
332
|
addLog('播报完成', 'success');
|
|
323
333
|
},
|
|
324
334
|
onError: (error:any) => {
|
|
335
|
+
sdkStatus.isStartBroadcast = false;
|
|
325
336
|
addLog(`播报失败: ${error.message}`, 'error');
|
|
326
337
|
},
|
|
327
338
|
onPause: () => {
|
|
@@ -331,7 +342,8 @@ function initializeSDKConfig(): ZEEAvatarSDK | null {
|
|
|
331
342
|
addLog('播报恢复', 'success');
|
|
332
343
|
},
|
|
333
344
|
onStop: () => {
|
|
334
|
-
|
|
345
|
+
sdkStatus.isStartBroadcast = false;
|
|
346
|
+
addLog('播报停止', 'warning');
|
|
335
347
|
},
|
|
336
348
|
}
|
|
337
349
|
};
|
|
@@ -342,9 +354,8 @@ function initializeSDKConfig(): ZEEAvatarSDK | null {
|
|
|
342
354
|
|
|
343
355
|
// 从本地存储加载配置
|
|
344
356
|
function loadStoredConfig(): void {
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
347
|
-
const configObj = JSON.parse(config);
|
|
357
|
+
const configObj = loadFromCache('global-config');
|
|
358
|
+
if (configObj) {
|
|
348
359
|
Object.assign(globalConfig, configObj);
|
|
349
360
|
}
|
|
350
361
|
}
|
|
@@ -352,7 +363,7 @@ function loadStoredConfig(): void {
|
|
|
352
363
|
// 保存全局配置
|
|
353
364
|
function handleSaveConfig(): void {
|
|
354
365
|
|
|
355
|
-
|
|
366
|
+
saveToCache('global-config', globalConfig);
|
|
356
367
|
|
|
357
368
|
addLog('全局配置已保存', 'success');
|
|
358
369
|
ElMessage.success('配置保存成功');
|
|
@@ -452,7 +463,6 @@ async function handleTextBroadcast(params: {
|
|
|
452
463
|
voiceCode?: string;
|
|
453
464
|
speed?: number;
|
|
454
465
|
broadcastMotionString?: string;
|
|
455
|
-
isAppend?: boolean;
|
|
456
466
|
}): Promise<void> {
|
|
457
467
|
if (!sdk.value) return;
|
|
458
468
|
|
|
@@ -471,7 +481,8 @@ async function handleTextBroadcast(params: {
|
|
|
471
481
|
motionPlayMode: 'random'
|
|
472
482
|
};
|
|
473
483
|
|
|
474
|
-
|
|
484
|
+
console.warn('handleTextBroadcast: ', broadcastParams, sdkStatus.isStartBroadcast);
|
|
485
|
+
await sdk.value.startBroadcast(broadcastParams, sdkStatus.isStartBroadcast);
|
|
475
486
|
addLog('文本播报已开始', 'success');
|
|
476
487
|
// ElMessage.success('文本播报已开始');
|
|
477
488
|
} catch (error) {
|
|
@@ -508,7 +519,7 @@ async function handleAudioBroadcast(params: {
|
|
|
508
519
|
motionPlayMode: 'random'
|
|
509
520
|
};
|
|
510
521
|
|
|
511
|
-
await sdk.value.startBroadcast(broadcastParams,
|
|
522
|
+
await sdk.value.startBroadcast(broadcastParams, sdkStatus.isStartBroadcast);
|
|
512
523
|
addLog('音频播报已开始', 'success');
|
|
513
524
|
ElMessage.success('音频播报已开始');
|
|
514
525
|
} catch (error) {
|
|
@@ -585,6 +596,14 @@ async function handleStopBroadcast(): Promise<void> {
|
|
|
585
596
|
}
|
|
586
597
|
}
|
|
587
598
|
|
|
599
|
+
function handleGetBroadcastStatus() {
|
|
600
|
+
if (!sdk.value) return;
|
|
601
|
+
const status = sdk.value.getBroadcastStatus();
|
|
602
|
+
addLog(`播报状态: ${JSON.stringify(status)}`, 'success');
|
|
603
|
+
console.warn('AAAAAAAAA播报状态: ', status);
|
|
604
|
+
ElMessage.success(`播报状态: ${JSON.stringify(status)}`);
|
|
605
|
+
}
|
|
606
|
+
|
|
588
607
|
// 设置预设镜头
|
|
589
608
|
async function handleSetCameraPreset(params: { preset: string }): Promise<void> {
|
|
590
609
|
if (!sdk.value) return;
|
|
@@ -645,6 +664,7 @@ async function handleUnloadAvatar(): Promise<void> {
|
|
|
645
664
|
sdkStatus.canGetMotion = false;
|
|
646
665
|
sdkStatus.canControlBroadcast = false;
|
|
647
666
|
sdkStatus.canControlCamera = false;
|
|
667
|
+
sdkStatus.isStartBroadcast = false; // 重置播报状态
|
|
648
668
|
loadingProgress.value = 0;
|
|
649
669
|
|
|
650
670
|
ElMessage.success('Avatar已卸载');
|
|
@@ -668,9 +688,6 @@ function handleDestroySDK(){
|
|
|
668
688
|
sdk.value = null;
|
|
669
689
|
}
|
|
670
690
|
|
|
671
|
-
addLog('SDK 实例已销毁', 'warning');
|
|
672
|
-
ElMessage.warning('SDK 实例已销毁');
|
|
673
|
-
|
|
674
691
|
// 重置状态
|
|
675
692
|
sdkStatus.unityLoaded = false;
|
|
676
693
|
sdkStatus.avatarLoaded = false;
|
|
@@ -679,8 +696,13 @@ function handleDestroySDK(){
|
|
|
679
696
|
sdkStatus.canGetMotion = false;
|
|
680
697
|
sdkStatus.canControlBroadcast = false;
|
|
681
698
|
sdkStatus.canControlCamera = false;
|
|
699
|
+
sdkStatus.isStartBroadcast = false; // 重置播报状态
|
|
682
700
|
loadingProgress.value = 0;
|
|
683
701
|
|
|
702
|
+
|
|
703
|
+
addLog('SDK 实例已销毁', 'warning');
|
|
704
|
+
ElMessage.warning('SDK 实例已销毁');
|
|
705
|
+
|
|
684
706
|
} catch (error) {
|
|
685
707
|
const message = error instanceof Error ? error.message : String(error);
|
|
686
708
|
addLog(`销毁失败: ${message}`, 'error');
|
|
@@ -74,11 +74,6 @@
|
|
|
74
74
|
prefix-icon="microphone"
|
|
75
75
|
:disabled="!sdkStatus.canBroadcast"
|
|
76
76
|
/>
|
|
77
|
-
<div class="input-help">
|
|
78
|
-
<span class="help-text">
|
|
79
|
-
常用音色:woman008、man001、child001等
|
|
80
|
-
</span>
|
|
81
|
-
</div>
|
|
82
77
|
</el-form-item>
|
|
83
78
|
|
|
84
79
|
<el-form-item label="音量" required>
|
|
@@ -118,14 +113,19 @@
|
|
|
118
113
|
<div class="action-buttons-container">
|
|
119
114
|
<el-button
|
|
120
115
|
type="primary"
|
|
121
|
-
icon="chat-dot-round"
|
|
122
116
|
:loading="isTextLoading"
|
|
123
117
|
:disabled="!sdkStatus.canBroadcast || !textFormData.text || !textFormData.voiceCode || !globalConfig.avatarCode"
|
|
124
118
|
@click="handleTextBroadcast"
|
|
125
119
|
>
|
|
126
120
|
{{ isTextLoading ? '播报中...' : '执行文本播报' }}
|
|
127
121
|
</el-button>
|
|
128
|
-
<el-button
|
|
122
|
+
<el-button
|
|
123
|
+
type="info"
|
|
124
|
+
size="default"
|
|
125
|
+
style="width: 170px;"
|
|
126
|
+
@click="handleGetBroadcastStatus">
|
|
127
|
+
获取播报状态
|
|
128
|
+
</el-button>
|
|
129
129
|
</div>
|
|
130
130
|
</el-form-item>
|
|
131
131
|
</el-form>
|
|
@@ -194,7 +194,13 @@
|
|
|
194
194
|
>
|
|
195
195
|
{{ isAudioLoading ? '播报中...' : '执行音频播报' }}
|
|
196
196
|
</el-button>
|
|
197
|
-
<el-button
|
|
197
|
+
<el-button
|
|
198
|
+
type="info"
|
|
199
|
+
size="default"
|
|
200
|
+
style="width: 170px;"
|
|
201
|
+
@click="handleGetBroadcastStatus">
|
|
202
|
+
获取播报状态
|
|
203
|
+
</el-button>
|
|
198
204
|
</div>
|
|
199
205
|
</el-form-item>
|
|
200
206
|
</el-form>
|
|
@@ -241,6 +247,7 @@
|
|
|
241
247
|
>
|
|
242
248
|
{{ stopLoading ? '停止中...' : '停止' }}
|
|
243
249
|
</el-button>
|
|
250
|
+
|
|
244
251
|
</div>
|
|
245
252
|
</div>
|
|
246
253
|
|
|
@@ -288,9 +295,10 @@
|
|
|
288
295
|
</template>
|
|
289
296
|
|
|
290
297
|
<script setup lang="ts">
|
|
291
|
-
import { ref, reactive } from 'vue';
|
|
298
|
+
import { ref, reactive, watch, onMounted } from 'vue';
|
|
292
299
|
import { ElMessage } from 'element-plus';
|
|
293
|
-
import { VideoPause, VideoPlay, Close } from '@element-plus/icons-vue';
|
|
300
|
+
import { VideoPause, VideoPlay, Close, Refresh } from '@element-plus/icons-vue';
|
|
301
|
+
import { loadFromCache, saveToCache } from '@/utils';
|
|
294
302
|
|
|
295
303
|
interface ISDKStatus {
|
|
296
304
|
canBroadcast: boolean;
|
|
@@ -310,31 +318,76 @@ const emit = defineEmits<{
|
|
|
310
318
|
(e: 'pause-broadcast'): void;
|
|
311
319
|
(e: 'resume-broadcast'): void;
|
|
312
320
|
(e: 'stop-broadcast'): void;
|
|
321
|
+
(e: 'get-broadcast-status'): void;
|
|
313
322
|
}>();
|
|
314
323
|
|
|
315
|
-
|
|
316
|
-
const
|
|
324
|
+
// 本地缓存键名常量
|
|
325
|
+
const BROADCAST_MODE_CACHE_KEY = 'broadcast-mode';
|
|
326
|
+
const TEXT_FORM_CACHE_KEY = 'broadcast-text-form-data';
|
|
327
|
+
const AUDIO_FORM_CACHE_KEY = 'broadcast-audio-form-data';
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
// 初始化表单数据,优先从缓存加载
|
|
331
|
+
const broadcastMode = ref<'text' | 'audio'>(loadFromCache(BROADCAST_MODE_CACHE_KEY, 'text')!);
|
|
332
|
+
|
|
333
|
+
const textFormData = reactive(loadFromCache(TEXT_FORM_CACHE_KEY, {
|
|
317
334
|
text: '欢迎使用ZEEAvatarSDK智能体播报功能。这是一个强大的数字人解决方案,支持文本和音频两种播报模式,让您的应用更具互动性!',
|
|
318
335
|
volume: 1.0,
|
|
319
336
|
speed: 1.0,
|
|
320
|
-
voiceCode: '
|
|
337
|
+
voiceCode: 'VOICE_EXT_W_000011',
|
|
321
338
|
broadcastMotionString: 'DH_ACTION_MODEL103_000003,DH_ACTION_MODEL103_000005',
|
|
322
|
-
});
|
|
323
|
-
|
|
339
|
+
})!);
|
|
340
|
+
|
|
341
|
+
const audioFormData = reactive(loadFromCache(AUDIO_FORM_CACHE_KEY, {
|
|
324
342
|
audioText: '欢迎使用ZEEAvatarSDK智能体播报功能',
|
|
325
343
|
audioUrl: 'https://example.com/audio/sample.mp3',
|
|
326
344
|
volume: 1.0,
|
|
327
345
|
broadcastMotionString: 'DH_ACTION_MODEL103_000023,DH_ACTION_MODEL103_000010',
|
|
328
|
-
});
|
|
346
|
+
})!);
|
|
347
|
+
|
|
329
348
|
const result = ref(
|
|
330
|
-
'等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n-
|
|
349
|
+
'等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n- 实时播报控制(暂停/恢复/停止)\n\n💾 表单数据已启用自动缓存功能'
|
|
331
350
|
);
|
|
332
351
|
const isTextLoading = ref(false);
|
|
333
352
|
const isAudioLoading = ref(false);
|
|
334
353
|
const pauseLoading = ref(false);
|
|
335
354
|
const resumeLoading = ref(false);
|
|
336
355
|
const stopLoading = ref(false);
|
|
337
|
-
|
|
356
|
+
|
|
357
|
+
// 监听表单数据变化,自动保存到本地缓存
|
|
358
|
+
watch(
|
|
359
|
+
broadcastMode,
|
|
360
|
+
(newMode) => {
|
|
361
|
+
saveToCache(BROADCAST_MODE_CACHE_KEY, newMode);
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
watch(
|
|
366
|
+
textFormData,
|
|
367
|
+
(newData) => {
|
|
368
|
+
saveToCache(TEXT_FORM_CACHE_KEY, newData);
|
|
369
|
+
},
|
|
370
|
+
{ deep: true }
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
watch(
|
|
374
|
+
audioFormData,
|
|
375
|
+
(newData) => {
|
|
376
|
+
saveToCache(AUDIO_FORM_CACHE_KEY, newData);
|
|
377
|
+
},
|
|
378
|
+
{ deep: true }
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
// 组件挂载时的初始化操作
|
|
382
|
+
onMounted(() => {
|
|
383
|
+
console.log('BroadcastAPI: 已从本地缓存加载表单数据', {
|
|
384
|
+
broadcastMode: broadcastMode.value,
|
|
385
|
+
textFormData: textFormData,
|
|
386
|
+
audioFormData: audioFormData
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
|
|
338
391
|
|
|
339
392
|
async function handleTextBroadcast() {
|
|
340
393
|
const { text, volume, speed, voiceCode, broadcastMotionString } = textFormData;
|
|
@@ -352,8 +405,7 @@ async function handleTextBroadcast() {
|
|
|
352
405
|
isTextLoading.value = true;
|
|
353
406
|
result.value = `🔄 正在执行文本播报...\n📝 播报文本: ${text.substring(0, 30)}${text.length > 30 ? '...' : ''}\n🎤 音色编码: ${voiceCode}\n🔊 音量: ${volume}\n⚡ 语速: ${speed}\n\n📡 流式播报中,请等待服务器响应...`;
|
|
354
407
|
try {
|
|
355
|
-
emit('text-broadcast', { avatarCode, text, volume, speed, voiceCode, broadcastMotionString
|
|
356
|
-
isStartBroadcast.value = true;
|
|
408
|
+
emit('text-broadcast', { avatarCode, text, volume, speed, voiceCode, broadcastMotionString });
|
|
357
409
|
setTimeout(() => {
|
|
358
410
|
isTextLoading.value = false;
|
|
359
411
|
result.value = '✅ 文本播报请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 播报将以流式方式进行\n- 可使用下方控制按钮暂停/继续/停止\n- 播报过程中会触发相应的事件回调';
|
|
@@ -381,8 +433,7 @@ async function handleAudioBroadcast() {
|
|
|
381
433
|
isAudioLoading.value = true;
|
|
382
434
|
result.value = `🔄 正在执行音频播报...\n🎵 音频地址: ${audioUrl}\n📝 字幕文本: ${audioText || '无'}\n🔊 音量: ${volume}\n\n📡 正在验证音频文件并加载...`;
|
|
383
435
|
try {
|
|
384
|
-
emit('audio-broadcast', { avatarCode, audioText, audioUrl, volume, broadcastMotionString
|
|
385
|
-
isStartBroadcast.value = true;
|
|
436
|
+
emit('audio-broadcast', { avatarCode, audioText, audioUrl, volume, broadcastMotionString });
|
|
386
437
|
setTimeout(() => {
|
|
387
438
|
isAudioLoading.value = false;
|
|
388
439
|
result.value = '✅ 音频播报请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 音频将直接驱动数字人口型\n- 支持MP3、WAV等格式\n- 可使用下方控制按钮进行控制';
|
|
@@ -425,7 +476,6 @@ function handleResumeBroadcast() {
|
|
|
425
476
|
}
|
|
426
477
|
|
|
427
478
|
function handleStopBroadcast() {
|
|
428
|
-
handleResetBroadcast();
|
|
429
479
|
stopLoading.value = true;
|
|
430
480
|
result.value = '🔄 正在停止播报...\n📡 发送停止指令到Unity引擎...\n🧹 清理播报资源和队列...';
|
|
431
481
|
try {
|
|
@@ -440,13 +490,15 @@ function handleStopBroadcast() {
|
|
|
440
490
|
}
|
|
441
491
|
}
|
|
442
492
|
|
|
443
|
-
function
|
|
444
|
-
|
|
493
|
+
function handleGetBroadcastStatus() {
|
|
494
|
+
emit('get-broadcast-status');
|
|
445
495
|
}
|
|
446
496
|
|
|
497
|
+
|
|
447
498
|
function clearResult() {
|
|
448
|
-
result.value = '等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n-
|
|
499
|
+
result.value = '等待播报操作...\n\n新版SDK特性:\n- 支持流式播报,实时响应\n- 内置事件回调机制\n- token统一管理,无需重复传递\n- 支持音量和语速控制\n- 实时播报控制(暂停/恢复/停止)\n\n💾 表单数据已启用自动缓存功能';
|
|
449
500
|
}
|
|
501
|
+
|
|
450
502
|
</script>
|
|
451
503
|
|
|
452
504
|
<style lang="scss" scoped>
|
|
@@ -73,9 +73,9 @@ watch(
|
|
|
73
73
|
() => props.logs,
|
|
74
74
|
async () => {
|
|
75
75
|
await nextTick();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
scrollToBottom();
|
|
77
|
+
},{
|
|
78
|
+
deep: true,
|
|
79
79
|
}
|
|
80
80
|
);
|
|
81
81
|
|
|
@@ -133,14 +133,14 @@ function getTypeLabel(type: ILogEntry['type']) {
|
|
|
133
133
|
<style lang="scss" scoped>
|
|
134
134
|
.log-panel {
|
|
135
135
|
height: 100%;
|
|
136
|
-
max-height:
|
|
136
|
+
max-height: calc(100vh - 80px);
|
|
137
137
|
display: flex;
|
|
138
138
|
flex-direction: column;
|
|
139
139
|
.log-container {
|
|
140
140
|
height: 100%;
|
|
141
141
|
display: flex;
|
|
142
142
|
flex-direction: column;
|
|
143
|
-
overflow:
|
|
143
|
+
overflow: hidden;
|
|
144
144
|
.log-list {
|
|
145
145
|
flex: 1;
|
|
146
146
|
overflow-y: auto;
|
|
@@ -160,8 +160,9 @@
|
|
|
160
160
|
</template>
|
|
161
161
|
|
|
162
162
|
<script setup lang="ts">
|
|
163
|
-
import { ref, reactive } from 'vue';
|
|
163
|
+
import { ref, reactive, watch, onMounted } from 'vue';
|
|
164
164
|
import { ElMessage } from 'element-plus';
|
|
165
|
+
import { loadFromCache, saveToCache } from '@/utils';
|
|
165
166
|
|
|
166
167
|
interface ISDKStatus {
|
|
167
168
|
canPlayMotion: boolean;
|
|
@@ -180,18 +181,53 @@ const emit = defineEmits<{
|
|
|
180
181
|
(e: 'get-current-motion', params: { getRemainingTime: boolean }): void;
|
|
181
182
|
}>();
|
|
182
183
|
|
|
183
|
-
|
|
184
|
+
// 本地缓存键名常量
|
|
185
|
+
const MOTION_CACHE_KEY = 'motion-data';
|
|
186
|
+
const QUERY_CACHE_KEY = 'query-data';
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
// 初始化表单数据,优先从缓存加载
|
|
190
|
+
const motionData = reactive(loadFromCache(MOTION_CACHE_KEY, {
|
|
184
191
|
clipCode: 'DH_ACTION_MODEL103_000023',
|
|
185
|
-
});
|
|
186
|
-
|
|
192
|
+
})!);
|
|
193
|
+
|
|
194
|
+
const queryData = reactive(loadFromCache(QUERY_CACHE_KEY, {
|
|
187
195
|
getRemainingTime: true,
|
|
188
|
-
});
|
|
196
|
+
})!);
|
|
197
|
+
|
|
189
198
|
const result = ref(
|
|
190
|
-
'等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n'
|
|
199
|
+
'等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n💾 表单数据已启用自动缓存功能'
|
|
191
200
|
);
|
|
192
201
|
const playLoading = ref(false);
|
|
193
202
|
const queryLoading = ref(false);
|
|
194
203
|
|
|
204
|
+
|
|
205
|
+
// 监听表单数据变化,自动保存到本地缓存
|
|
206
|
+
watch(
|
|
207
|
+
motionData,
|
|
208
|
+
(newData) => {
|
|
209
|
+
saveToCache(MOTION_CACHE_KEY, newData);
|
|
210
|
+
},
|
|
211
|
+
{ deep: true }
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
watch(
|
|
215
|
+
queryData,
|
|
216
|
+
(newData) => {
|
|
217
|
+
saveToCache(QUERY_CACHE_KEY, newData);
|
|
218
|
+
},
|
|
219
|
+
{ deep: true }
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// 组件挂载时的初始化操作
|
|
223
|
+
onMounted(() => {
|
|
224
|
+
console.log('MotionControlAPI: 已从本地缓存加载表单数据', {
|
|
225
|
+
motionData: motionData,
|
|
226
|
+
queryData: queryData
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
|
|
195
231
|
async function handlePlayMotion() {
|
|
196
232
|
const { avatarCode } = props.globalConfig;
|
|
197
233
|
const { clipCode } = motionData;
|
|
@@ -241,8 +277,9 @@ async function handleGetCurrentMotion() {
|
|
|
241
277
|
}
|
|
242
278
|
|
|
243
279
|
function clearResult() {
|
|
244
|
-
result.value = '等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n
|
|
280
|
+
result.value = '等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n💾 表单数据已启用自动缓存功能';
|
|
245
281
|
}
|
|
282
|
+
|
|
246
283
|
</script>
|
|
247
284
|
|
|
248
285
|
<style lang="scss" scoped>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓存键名前缀
|
|
3
|
+
*/
|
|
4
|
+
export const CACHE_KEY_PREFIX = '3d-avatar-sdk__';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 从本地缓存加载数据
|
|
8
|
+
* @param key 缓存键名
|
|
9
|
+
* @param defaultValue 默认值
|
|
10
|
+
* @returns 缓存的数据或默认值
|
|
11
|
+
*/
|
|
12
|
+
export function loadFromCache<T>(key: string, defaultValue?: T): T | undefined {
|
|
13
|
+
try {
|
|
14
|
+
const cached = localStorage.getItem(CACHE_KEY_PREFIX + key);
|
|
15
|
+
if (cached) {
|
|
16
|
+
return JSON.parse(cached);
|
|
17
|
+
}
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.warn(`加载缓存失败 [${key}]:`, error);
|
|
20
|
+
}
|
|
21
|
+
return defaultValue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 保存数据到本地缓存
|
|
26
|
+
* @param key 缓存键名
|
|
27
|
+
* @param data 要保存的数据
|
|
28
|
+
*/
|
|
29
|
+
export function saveToCache<T>(key: string, data: T): void {
|
|
30
|
+
try {
|
|
31
|
+
localStorage.setItem(CACHE_KEY_PREFIX + key, JSON.stringify(data));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.warn(`保存缓存失败 [${key}]:`, error);
|
|
34
|
+
}
|
|
35
|
+
}
|