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