@zeewain/3d-avatar-sdk 1.2.1 → 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.
- 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 +64 -17
- package/dist/index.es5.umd.js +64 -17
- package/dist/index.esm.js +71 -16
- package/dist/index.umd.cjs +71 -16
- package/package.json +4 -3
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div id="app">
|
|
3
|
+
<div class="app-container">
|
|
4
|
+
<!-- 主要内容区域 -->
|
|
5
|
+
<div class="app-main">
|
|
6
|
+
<!-- 全局配置 -->
|
|
7
|
+
<el-card class="global-config-card" shadow="never">
|
|
8
|
+
<GlobalConfig
|
|
9
|
+
v-model="globalConfig"
|
|
10
|
+
@save-config="handleSaveConfig"
|
|
11
|
+
@apply-all="handleApplyAll"
|
|
12
|
+
/>
|
|
13
|
+
</el-card>
|
|
14
|
+
|
|
15
|
+
<!-- 移动端和PC端响应式布局 -->
|
|
16
|
+
<div class="main-content" :class="{ 'mobile-layout': isMobile }">
|
|
17
|
+
<!-- PC端布局 -->
|
|
18
|
+
<el-row v-if="!isMobile" :gutter="20">
|
|
19
|
+
<!-- 左侧控制面板 -->
|
|
20
|
+
<el-col :md="8" :lg="8" :xl="8">
|
|
21
|
+
<el-card class="control-panel" shadow="hover">
|
|
22
|
+
<template #header>
|
|
23
|
+
<div class="card-header">
|
|
24
|
+
<span>API 测试面板</span>
|
|
25
|
+
<el-tag type="info" size="small">SDK演示</el-tag>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<!-- API选项卡 -->
|
|
30
|
+
<el-tabs v-model="activeTab" type="card" class="api-tabs">
|
|
31
|
+
<el-tab-pane label="初始化" name="init">
|
|
32
|
+
<InitAPI
|
|
33
|
+
:sdk-status="sdkStatus"
|
|
34
|
+
:global-config="globalConfig"
|
|
35
|
+
@init-avatar-with-unity="handleInitAvatarWithUnity"
|
|
36
|
+
/>
|
|
37
|
+
</el-tab-pane>
|
|
38
|
+
|
|
39
|
+
<el-tab-pane label="动作管理" name="motion">
|
|
40
|
+
<MotionControlAPI
|
|
41
|
+
:sdk-status="sdkStatus"
|
|
42
|
+
:global-config="globalConfig"
|
|
43
|
+
@play-motion="handlePlayMotion"
|
|
44
|
+
@get-current-motion="handleGetCurrentMotion"
|
|
45
|
+
/>
|
|
46
|
+
</el-tab-pane>
|
|
47
|
+
|
|
48
|
+
<el-tab-pane label="智能播报" name="text-broadcast">
|
|
49
|
+
<BroadcastAPI
|
|
50
|
+
:sdk-status="sdkStatus"
|
|
51
|
+
:global-config="globalConfig"
|
|
52
|
+
@text-broadcast="handleTextBroadcast"
|
|
53
|
+
@audio-broadcast="handleAudioBroadcast"
|
|
54
|
+
@pause-broadcast="handlePauseBroadcast"
|
|
55
|
+
@resume-broadcast="handleResumeBroadcast"
|
|
56
|
+
@stop-broadcast="handleStopBroadcast"
|
|
57
|
+
/>
|
|
58
|
+
</el-tab-pane>
|
|
59
|
+
|
|
60
|
+
<el-tab-pane label="镜头控制" name="camera">
|
|
61
|
+
<CameraAPI
|
|
62
|
+
:sdk-status="sdkStatus"
|
|
63
|
+
:global-config="globalConfig"
|
|
64
|
+
@set-camera-preset="handleSetCameraPreset"
|
|
65
|
+
/>
|
|
66
|
+
</el-tab-pane>
|
|
67
|
+
</el-tabs>
|
|
68
|
+
</el-card>
|
|
69
|
+
</el-col>
|
|
70
|
+
|
|
71
|
+
<!-- 中间Unity预览 -->
|
|
72
|
+
<el-col :md="10" :lg="10" :xl="10">
|
|
73
|
+
<UnityPreview
|
|
74
|
+
:sdk-status="sdkStatus"
|
|
75
|
+
:loading-progress="loadingProgress"
|
|
76
|
+
@unload-avatar="handleUnloadAvatar"
|
|
77
|
+
@destroy-sdk="handleDestroySDK"
|
|
78
|
+
/>
|
|
79
|
+
</el-col>
|
|
80
|
+
|
|
81
|
+
<!-- 右侧操作日志 -->
|
|
82
|
+
<el-col :md="6" :lg="6" :xl="6">
|
|
83
|
+
<el-card class="log-panel" shadow="hover">
|
|
84
|
+
<template #header>
|
|
85
|
+
<div class="card-header">
|
|
86
|
+
<span>操作日志</span>
|
|
87
|
+
<el-link
|
|
88
|
+
size="small"
|
|
89
|
+
@click="clearLogs"
|
|
90
|
+
:icon="Delete"
|
|
91
|
+
>
|
|
92
|
+
清空
|
|
93
|
+
</el-link>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<LogPanel
|
|
98
|
+
:logs="logs"
|
|
99
|
+
@clear-logs="clearLogs"
|
|
100
|
+
/>
|
|
101
|
+
</el-card>
|
|
102
|
+
</el-col>
|
|
103
|
+
</el-row>
|
|
104
|
+
|
|
105
|
+
<!-- 移动端布局 -->
|
|
106
|
+
<div v-else class="mobile-panels">
|
|
107
|
+
<!-- API 测试面板 -->
|
|
108
|
+
<div class="mobile-panel-item">
|
|
109
|
+
<el-card class="control-panel" shadow="hover">
|
|
110
|
+
<template #header>
|
|
111
|
+
<div class="card-header">
|
|
112
|
+
<span>API 测试面板</span>
|
|
113
|
+
<el-tag type="info" size="small">SDK演示</el-tag>
|
|
114
|
+
</div>
|
|
115
|
+
</template>
|
|
116
|
+
|
|
117
|
+
<!-- API选项卡 -->
|
|
118
|
+
<el-tabs v-model="activeTab" type="card" class="api-tabs">
|
|
119
|
+
<el-tab-pane label="初始化" name="init">
|
|
120
|
+
<InitAPI
|
|
121
|
+
:sdk-status="sdkStatus"
|
|
122
|
+
:global-config="globalConfig"
|
|
123
|
+
@init-avatar-with-unity="handleInitAvatarWithUnity"
|
|
124
|
+
/>
|
|
125
|
+
</el-tab-pane>
|
|
126
|
+
|
|
127
|
+
<el-tab-pane label="动作管理" name="motion">
|
|
128
|
+
<MotionControlAPI
|
|
129
|
+
:sdk-status="sdkStatus"
|
|
130
|
+
:global-config="globalConfig"
|
|
131
|
+
@play-motion="handlePlayMotion"
|
|
132
|
+
@get-current-motion="handleGetCurrentMotion"
|
|
133
|
+
/>
|
|
134
|
+
</el-tab-pane>
|
|
135
|
+
|
|
136
|
+
<el-tab-pane label="智能播报" name="text-broadcast">
|
|
137
|
+
<BroadcastAPI
|
|
138
|
+
:sdk-status="sdkStatus"
|
|
139
|
+
:global-config="globalConfig"
|
|
140
|
+
@text-broadcast="handleTextBroadcast"
|
|
141
|
+
@audio-broadcast="handleAudioBroadcast"
|
|
142
|
+
@pause-broadcast="handlePauseBroadcast"
|
|
143
|
+
@resume-broadcast="handleResumeBroadcast"
|
|
144
|
+
@stop-broadcast="handleStopBroadcast"
|
|
145
|
+
/>
|
|
146
|
+
</el-tab-pane>
|
|
147
|
+
|
|
148
|
+
<el-tab-pane label="镜头控制" name="camera">
|
|
149
|
+
<CameraAPI
|
|
150
|
+
:sdk-status="sdkStatus"
|
|
151
|
+
:global-config="globalConfig"
|
|
152
|
+
@set-camera-preset="handleSetCameraPreset"
|
|
153
|
+
/>
|
|
154
|
+
</el-tab-pane>
|
|
155
|
+
</el-tabs>
|
|
156
|
+
</el-card>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Unity 预览 -->
|
|
160
|
+
<div class="mobile-panel-item">
|
|
161
|
+
<UnityPreview
|
|
162
|
+
:sdk-status="sdkStatus"
|
|
163
|
+
:loading-progress="loadingProgress"
|
|
164
|
+
@unload-avatar="handleUnloadAvatar"
|
|
165
|
+
@destroy-sdk="handleDestroySDK"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<!-- 操作日志 -->
|
|
170
|
+
<div class="mobile-panel-item">
|
|
171
|
+
<el-card class="log-panel" shadow="hover">
|
|
172
|
+
<template #header>
|
|
173
|
+
<div class="card-header">
|
|
174
|
+
<span>操作日志</span>
|
|
175
|
+
<el-link
|
|
176
|
+
size="small"
|
|
177
|
+
@click="clearLogs"
|
|
178
|
+
:icon="Delete"
|
|
179
|
+
>
|
|
180
|
+
清空
|
|
181
|
+
</el-link>
|
|
182
|
+
</div>
|
|
183
|
+
</template>
|
|
184
|
+
|
|
185
|
+
<LogPanel
|
|
186
|
+
:logs="logs"
|
|
187
|
+
@clear-logs="clearLogs"
|
|
188
|
+
/>
|
|
189
|
+
</el-card>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<!-- 信息卡片 -->
|
|
195
|
+
<el-card class="info-cards" shadow="never">
|
|
196
|
+
<info-cards />
|
|
197
|
+
</el-card>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<!-- 页脚 -->
|
|
202
|
+
<el-footer class="app-footer">
|
|
203
|
+
<div class="footer-content">
|
|
204
|
+
<span>ZEEAvatarSDK © 2025 | 测试控制台 v2.0.0</span>
|
|
205
|
+
<el-divider direction="vertical" />
|
|
206
|
+
<span>SDK重构版演示Demo</span>
|
|
207
|
+
</div>
|
|
208
|
+
</el-footer>
|
|
209
|
+
</div>
|
|
210
|
+
</template>
|
|
211
|
+
|
|
212
|
+
<script setup lang="ts">
|
|
213
|
+
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
|
214
|
+
import { Delete } from '@element-plus/icons-vue';
|
|
215
|
+
import { ElMessage } from 'element-plus';
|
|
216
|
+
|
|
217
|
+
// 引入最新版SDK(单一入口类方式)
|
|
218
|
+
import { ZEEAvatarSDK, AvatarCameraType, BroadcastType } from '@zeewain/3d-avatar-sdk';
|
|
219
|
+
import type { IAvatarSDKConfig, IBroadcastParams } from '@zeewain/3d-avatar-sdk';
|
|
220
|
+
|
|
221
|
+
// 引入组件
|
|
222
|
+
import GlobalConfig from './components/GlobalConfig.vue';
|
|
223
|
+
import InitAPI from './components/InitAPI.vue';
|
|
224
|
+
import MotionControlAPI from './components/MotionControlAPI.vue';
|
|
225
|
+
import BroadcastAPI from './components/BroadcastAPI.vue';
|
|
226
|
+
import CameraAPI from './components/CameraAPI.vue';
|
|
227
|
+
import LogPanel from './components/LogPanel.vue';
|
|
228
|
+
import InfoCards from './components/InfoCards.vue';
|
|
229
|
+
import UnityPreview from './components/UnityPreview.vue';
|
|
230
|
+
|
|
231
|
+
import type { IGlobalConfig } from './types';
|
|
232
|
+
|
|
233
|
+
interface ISDKStatus {
|
|
234
|
+
unityLoaded: boolean;
|
|
235
|
+
avatarLoaded: boolean;
|
|
236
|
+
canPlayMotion: boolean;
|
|
237
|
+
canBroadcast: boolean;
|
|
238
|
+
canGetMotion: boolean;
|
|
239
|
+
canControlBroadcast: boolean;
|
|
240
|
+
canControlCamera: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
interface ILogEntry {
|
|
244
|
+
time: string;
|
|
245
|
+
message: string;
|
|
246
|
+
type: 'info' | 'success' | 'warning' | 'error';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 响应式数据
|
|
250
|
+
const activeTab = ref('init');
|
|
251
|
+
const isMobile = ref(false);
|
|
252
|
+
const loadingProgress = ref(0);
|
|
253
|
+
|
|
254
|
+
// 全局配置
|
|
255
|
+
const globalConfig = reactive<IGlobalConfig>({
|
|
256
|
+
token: '',
|
|
257
|
+
avatarCode: '',
|
|
258
|
+
env: 'dev',
|
|
259
|
+
webglPath: './assets/Build'
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// SDK状态
|
|
263
|
+
const sdkStatus = reactive<ISDKStatus>({
|
|
264
|
+
unityLoaded: false,
|
|
265
|
+
avatarLoaded: false,
|
|
266
|
+
canPlayMotion: false,
|
|
267
|
+
canBroadcast: false,
|
|
268
|
+
canGetMotion: false,
|
|
269
|
+
canControlBroadcast: false,
|
|
270
|
+
canControlCamera: false
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// 操作日志
|
|
274
|
+
const logs = reactive<ILogEntry[]>([
|
|
275
|
+
{ time: new Date().toLocaleTimeString(), message: '系统准备就绪,请选择API进行测试', type: 'info' },
|
|
276
|
+
{ time: new Date().toLocaleTimeString(), message: '支持移动端响应式布局', type: 'info' }
|
|
277
|
+
]);
|
|
278
|
+
|
|
279
|
+
// SDK实例
|
|
280
|
+
const sdk = ref<ZEEAvatarSDK | null>(null);
|
|
281
|
+
|
|
282
|
+
// 初始化SDK配置
|
|
283
|
+
function initializeSDKConfig(): ZEEAvatarSDK | null {
|
|
284
|
+
if (!globalConfig.token) {
|
|
285
|
+
addLog('请先配置Token', 'warning');
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const config: IAvatarSDKConfig = {
|
|
290
|
+
// Unity构建文件配置
|
|
291
|
+
loaderUrl: `${globalConfig.webglPath}/webgl.loader.js`,
|
|
292
|
+
dataUrl: `${globalConfig.webglPath}/webgl.data.unityweb`,
|
|
293
|
+
frameworkUrl: `${globalConfig.webglPath}/webgl.framework.js.unityweb`,
|
|
294
|
+
codeUrl: `${globalConfig.webglPath}/webgl.wasm.unityweb`,
|
|
295
|
+
containerId: 'unity-container',
|
|
296
|
+
|
|
297
|
+
// 认证配置
|
|
298
|
+
token: globalConfig.token,
|
|
299
|
+
env: globalConfig.env,
|
|
300
|
+
apiUrl: globalConfig.apiBaseUrl,
|
|
301
|
+
resourcesUrl: globalConfig.resourcesUrl || undefined,
|
|
302
|
+
|
|
303
|
+
// 待机动作列表:比心,左手指示(低)
|
|
304
|
+
idleMotionList: globalConfig.idleMotionListString?.split(',').filter(item => item.trim()) || [],
|
|
305
|
+
|
|
306
|
+
// 进度回调
|
|
307
|
+
onProgress: (progress: number) => {
|
|
308
|
+
loadingProgress.value = progress;
|
|
309
|
+
if(progress === 1 )
|
|
310
|
+
{
|
|
311
|
+
sdkStatus.unityLoaded = true;
|
|
312
|
+
}
|
|
313
|
+
addLog(`加载进度: ${Math.round(progress * 100)}%`, 'info');
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// 播报回调
|
|
317
|
+
broadcastCallbacks: {
|
|
318
|
+
onStart: () => {
|
|
319
|
+
addLog('播报开始', 'info');
|
|
320
|
+
},
|
|
321
|
+
onFinish: () => {
|
|
322
|
+
addLog('播报完成', 'success');
|
|
323
|
+
},
|
|
324
|
+
onError: (error:any) => {
|
|
325
|
+
addLog(`播报失败: ${error.message}`, 'error');
|
|
326
|
+
},
|
|
327
|
+
onPause: () => {
|
|
328
|
+
addLog('播报暂停', 'warning');
|
|
329
|
+
},
|
|
330
|
+
onResume: () => {
|
|
331
|
+
addLog('播报恢复', 'success');
|
|
332
|
+
},
|
|
333
|
+
onStop: () => {
|
|
334
|
+
addLog('播报停止', 'error');
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
sdk.value = new ZEEAvatarSDK(config);
|
|
340
|
+
return sdk.value;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 从本地存储加载配置
|
|
344
|
+
function loadStoredConfig(): void {
|
|
345
|
+
const config = localStorage.getItem('zee_config');
|
|
346
|
+
if (config) {
|
|
347
|
+
const configObj = JSON.parse(config);
|
|
348
|
+
Object.assign(globalConfig, configObj);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 保存全局配置
|
|
353
|
+
function handleSaveConfig(): void {
|
|
354
|
+
|
|
355
|
+
localStorage.setItem('zee_config', JSON.stringify(globalConfig));
|
|
356
|
+
|
|
357
|
+
addLog('全局配置已保存', 'success');
|
|
358
|
+
ElMessage.success('配置保存成功');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 应用配置到所有接口
|
|
362
|
+
function handleApplyAll(): void {
|
|
363
|
+
addLog('配置已应用到所有接口', 'info');
|
|
364
|
+
ElMessage.info('配置已应用到所有接口');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 初始化Avatar(包含Unity实例化)
|
|
368
|
+
async function handleInitAvatarWithUnity(params: { avatarCode?: string }): Promise<void> {
|
|
369
|
+
try {
|
|
370
|
+
ElMessage.info('开始初始化Unity和Avatar...');
|
|
371
|
+
|
|
372
|
+
// 重新初始化SDK配置(包含最新token)
|
|
373
|
+
if (!initializeSDKConfig()) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 初始化Avatar
|
|
378
|
+
await handleInitAvatar(params);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
381
|
+
addLog(`初始化失败: ${message}`, 'error');
|
|
382
|
+
ElMessage.error(`初始化失败: ${message}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 初始化Avatar
|
|
387
|
+
async function handleInitAvatar(params: { avatarCode?: string }): Promise<void> {
|
|
388
|
+
if (!sdk.value) return;
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
addLog('开始初始化 Avatar...', 'info');
|
|
392
|
+
|
|
393
|
+
// 使用新的SDK单一入口类方式初始化Avatar
|
|
394
|
+
const response = await sdk.value.initializeAvatar(
|
|
395
|
+
params.avatarCode || globalConfig.avatarCode,
|
|
396
|
+
AvatarCameraType.WHOLE
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
if (response.success) {
|
|
400
|
+
addLog('Avatar初始化成功', 'success');
|
|
401
|
+
|
|
402
|
+
// 更新所有状态
|
|
403
|
+
sdkStatus.avatarLoaded = true;
|
|
404
|
+
sdkStatus.canPlayMotion = true;
|
|
405
|
+
sdkStatus.canBroadcast = true;
|
|
406
|
+
sdkStatus.canGetMotion = true;
|
|
407
|
+
sdkStatus.canControlBroadcast = true;
|
|
408
|
+
sdkStatus.canControlCamera = true;
|
|
409
|
+
|
|
410
|
+
loadingProgress.value = 1;
|
|
411
|
+
ElMessage.success('Avatar初始化成功');
|
|
412
|
+
} else {
|
|
413
|
+
addLog(`Avatar初始化失败: ${response.message}`, 'error');
|
|
414
|
+
ElMessage.error(`Avatar初始化失败: ${response.message}`);
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
418
|
+
addLog(`Avatar初始化失败: ${message}`, 'error');
|
|
419
|
+
ElMessage.error(`Avatar初始化失败: ${message}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 播放动作
|
|
424
|
+
async function handlePlayMotion(params: { clipCode: string }): Promise<void> {
|
|
425
|
+
if (!sdk.value) return;
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const response = await sdk.value.playMotion(params.clipCode);
|
|
429
|
+
|
|
430
|
+
if (response.success) {
|
|
431
|
+
addLog('动作播放成功', 'success');
|
|
432
|
+
if (response.data?.motionId) {
|
|
433
|
+
addLog(`动作ID: ${response.data.motionId}`, 'info');
|
|
434
|
+
}
|
|
435
|
+
ElMessage.success('动作播放成功');
|
|
436
|
+
} else {
|
|
437
|
+
addLog(`动作播放失败: ${response.message}`, 'error');
|
|
438
|
+
ElMessage.error(`动作播放失败: ${response.message}`);
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
441
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
442
|
+
addLog(`动作播放失败: ${message}`, 'error');
|
|
443
|
+
ElMessage.error(`动作播放失败: ${message}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 文本播报
|
|
448
|
+
async function handleTextBroadcast(params: {
|
|
449
|
+
text: string;
|
|
450
|
+
avatarCode?: string;
|
|
451
|
+
volume?: number;
|
|
452
|
+
voiceCode?: string;
|
|
453
|
+
speed?: number;
|
|
454
|
+
broadcastMotionString?: string;
|
|
455
|
+
}): Promise<void> {
|
|
456
|
+
if (!sdk.value) return;
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
addLog('开始文本播报...', 'info');
|
|
460
|
+
|
|
461
|
+
const broadcastParams: IBroadcastParams = {
|
|
462
|
+
type: BroadcastType.TEXT,
|
|
463
|
+
humanCode: params.avatarCode || globalConfig.avatarCode,
|
|
464
|
+
text: params.text,
|
|
465
|
+
volume: params.volume ?? 100,
|
|
466
|
+
voiceCode: params.voiceCode || '',
|
|
467
|
+
speed: params.speed ?? 1,
|
|
468
|
+
isSubtitle: false,
|
|
469
|
+
motionList: params.broadcastMotionString?.split(',').filter(item => item.trim()) || [],
|
|
470
|
+
motionPlayMode: 'random'
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
await sdk.value.startBroadcast(broadcastParams);
|
|
474
|
+
addLog('文本播报已开始', 'success');
|
|
475
|
+
// ElMessage.success('文本播报已开始');
|
|
476
|
+
} catch (error) {
|
|
477
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
478
|
+
addLog(`文本播报失败: ${message}`, 'error');
|
|
479
|
+
ElMessage.error(`文本播报失败: ${message}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// 音频播报
|
|
484
|
+
async function handleAudioBroadcast(params: {
|
|
485
|
+
audioUrl: string;
|
|
486
|
+
avatarCode?: string;
|
|
487
|
+
volume?: number;
|
|
488
|
+
speed?: number;
|
|
489
|
+
audioText?: string;
|
|
490
|
+
broadcastMotionString?: string;
|
|
491
|
+
}): Promise<void> {
|
|
492
|
+
if (!sdk.value) return;
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
addLog('开始音频播报...', 'info');
|
|
496
|
+
|
|
497
|
+
const broadcastParams: IBroadcastParams = {
|
|
498
|
+
type: BroadcastType.AUDIO,
|
|
499
|
+
humanCode: params.avatarCode || globalConfig.avatarCode,
|
|
500
|
+
audioUrl: params.audioUrl,
|
|
501
|
+
// text: params.audioText || '',
|
|
502
|
+
volume: params.volume || 1.0,
|
|
503
|
+
speed: params.speed || 1.0,
|
|
504
|
+
isSubtitle: false,
|
|
505
|
+
motionList: params.broadcastMotionString?.split(',').filter(item => item.trim()) || [],
|
|
506
|
+
motionPlayMode: 'random'
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
await sdk.value.startBroadcast(broadcastParams);
|
|
510
|
+
addLog('音频播报已开始', 'success');
|
|
511
|
+
ElMessage.success('音频播报已开始');
|
|
512
|
+
} catch (error) {
|
|
513
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
514
|
+
addLog(`音频播报失败: ${message}`, 'error');
|
|
515
|
+
ElMessage.error(`音频播报失败: ${message}`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// 获取当前动作
|
|
520
|
+
async function handleGetCurrentMotion(params: { getRemainingTime?: boolean }): Promise<void> {
|
|
521
|
+
if (!sdk.value) return;
|
|
522
|
+
|
|
523
|
+
try {
|
|
524
|
+
const response = await sdk.value.getCurrentMotion(params.getRemainingTime);
|
|
525
|
+
|
|
526
|
+
if (response.success) {
|
|
527
|
+
if (params.getRemainingTime) {
|
|
528
|
+
addLog(`当前动作编码: ${response.data?.motionId || '无'}, 剩余时间: ${response.data?.motionRemainingTime || 0}ms`, 'success');
|
|
529
|
+
} else {
|
|
530
|
+
addLog(`当前动作编码: ${response.data?.motionId || '无'}`, 'success');
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
addLog(`获取动作失败: ${response.message}`, 'error');
|
|
534
|
+
}
|
|
535
|
+
} catch (error) {
|
|
536
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
537
|
+
addLog(`获取动作失败: ${message}`, 'error');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// 暂停播报
|
|
542
|
+
async function handlePauseBroadcast(): Promise<void> {
|
|
543
|
+
if (!sdk.value) return;
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
await sdk.value.pauseBroadcast(true);
|
|
547
|
+
addLog('播报已暂停', 'success');
|
|
548
|
+
ElMessage.success('播报已暂停');
|
|
549
|
+
} catch (error) {
|
|
550
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
551
|
+
addLog(`暂停播报失败: ${message}`, 'error');
|
|
552
|
+
ElMessage.error(`暂停播报失败: ${message}`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// 恢复播报
|
|
557
|
+
async function handleResumeBroadcast(): Promise<void> {
|
|
558
|
+
if (!sdk.value) return;
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
await sdk.value.resumeBroadcast();
|
|
562
|
+
addLog('播报已恢复', 'success');
|
|
563
|
+
ElMessage.success('播报已恢复');
|
|
564
|
+
} catch (error) {
|
|
565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
566
|
+
addLog(`恢复播报失败: ${message}`, 'error');
|
|
567
|
+
ElMessage.error(`恢复播报失败: ${message}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// 停止播报
|
|
572
|
+
async function handleStopBroadcast(): Promise<void> {
|
|
573
|
+
if (!sdk.value) return;
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
await sdk.value.stopBroadcast();
|
|
577
|
+
addLog('播报已停止', 'success');
|
|
578
|
+
ElMessage.success('播报已停止');
|
|
579
|
+
} catch (error) {
|
|
580
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
581
|
+
addLog(`停止播报失败: ${message}`, 'error');
|
|
582
|
+
ElMessage.error(`停止播报失败: ${message}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// 设置预设镜头
|
|
587
|
+
async function handleSetCameraPreset(params: { preset: string }): Promise<void> {
|
|
588
|
+
if (!sdk.value) return;
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
const cameraType = convertCameraPreset(params.preset);
|
|
592
|
+
const response = await sdk.value.setCamera(cameraType);
|
|
593
|
+
|
|
594
|
+
if (response && response.success) {
|
|
595
|
+
addLog(`镜头已设置为${getCameraPresetName(params.preset)}`, 'success');
|
|
596
|
+
ElMessage.success('镜头设置成功');
|
|
597
|
+
} else {
|
|
598
|
+
addLog(`镜头设置失败: ${response?.message || '未知错误'}`, 'error');
|
|
599
|
+
ElMessage.error('镜头设置失败');
|
|
600
|
+
}
|
|
601
|
+
} catch (error) {
|
|
602
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
603
|
+
addLog(`镜头设置失败: ${message}`, 'error');
|
|
604
|
+
ElMessage.error(`镜头设置失败: ${message}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// 转换镜头预设
|
|
609
|
+
function convertCameraPreset(preset: string): AvatarCameraType {
|
|
610
|
+
const mapping = {
|
|
611
|
+
whole: AvatarCameraType.WHOLE,
|
|
612
|
+
half: AvatarCameraType.HALF,
|
|
613
|
+
face: AvatarCameraType.FACE
|
|
614
|
+
};
|
|
615
|
+
return mapping[preset as keyof typeof mapping] || AvatarCameraType.WHOLE;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 获取预设镜头名称
|
|
619
|
+
function getCameraPresetName(preset: string): string {
|
|
620
|
+
const names = {
|
|
621
|
+
whole: '全身',
|
|
622
|
+
half: '半身',
|
|
623
|
+
face: '近景'
|
|
624
|
+
};
|
|
625
|
+
return names[preset as keyof typeof names] || preset;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// 卸载Avatar
|
|
629
|
+
async function handleUnloadAvatar(): Promise<void> {
|
|
630
|
+
if (!sdk.value) return;
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
const response = await sdk.value.unloadAvatar();
|
|
634
|
+
|
|
635
|
+
if (response.success) {
|
|
636
|
+
addLog('Avatar已卸载', 'success');
|
|
637
|
+
|
|
638
|
+
// 重置状态
|
|
639
|
+
sdkStatus.avatarLoaded = false;
|
|
640
|
+
sdkStatus.unityLoaded = false;
|
|
641
|
+
sdkStatus.canPlayMotion = false;
|
|
642
|
+
sdkStatus.canBroadcast = false;
|
|
643
|
+
sdkStatus.canGetMotion = false;
|
|
644
|
+
sdkStatus.canControlBroadcast = false;
|
|
645
|
+
sdkStatus.canControlCamera = false;
|
|
646
|
+
loadingProgress.value = 0;
|
|
647
|
+
|
|
648
|
+
ElMessage.success('Avatar已卸载');
|
|
649
|
+
} else {
|
|
650
|
+
addLog(`卸载失败: ${response.message}`, 'error');
|
|
651
|
+
ElMessage.error(`卸载失败: ${response.message}`);
|
|
652
|
+
}
|
|
653
|
+
} catch (error) {
|
|
654
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
655
|
+
addLog(`卸载失败: ${message}`, 'error');
|
|
656
|
+
ElMessage.error(`卸载失败: ${message}`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// 销毁SDK实例
|
|
661
|
+
function handleDestroySDK(){
|
|
662
|
+
try {
|
|
663
|
+
// 销毁SDK
|
|
664
|
+
if (sdk.value) {
|
|
665
|
+
sdk.value.destroy();
|
|
666
|
+
sdk.value = null;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
addLog('SDK 实例已销毁', 'warning');
|
|
670
|
+
ElMessage.warning('SDK 实例已销毁');
|
|
671
|
+
|
|
672
|
+
// 重置状态
|
|
673
|
+
sdkStatus.unityLoaded = false;
|
|
674
|
+
sdkStatus.avatarLoaded = false;
|
|
675
|
+
sdkStatus.canPlayMotion = false;
|
|
676
|
+
sdkStatus.canBroadcast = false;
|
|
677
|
+
sdkStatus.canGetMotion = false;
|
|
678
|
+
sdkStatus.canControlBroadcast = false;
|
|
679
|
+
sdkStatus.canControlCamera = false;
|
|
680
|
+
loadingProgress.value = 0;
|
|
681
|
+
|
|
682
|
+
} catch (error) {
|
|
683
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
684
|
+
addLog(`销毁失败: ${message}`, 'error');
|
|
685
|
+
ElMessage.error(`销毁失败: ${message}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// 添加日志
|
|
690
|
+
function addLog(message: string, type: ILogEntry['type'] = 'info'): void {
|
|
691
|
+
const log: ILogEntry = {
|
|
692
|
+
time: new Date().toLocaleTimeString(),
|
|
693
|
+
message,
|
|
694
|
+
type
|
|
695
|
+
};
|
|
696
|
+
logs.push(log);
|
|
697
|
+
|
|
698
|
+
// 限制日志数量
|
|
699
|
+
if (logs.length > 100) {
|
|
700
|
+
logs.shift();
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// 清除日志
|
|
705
|
+
function clearLogs(): void {
|
|
706
|
+
logs.splice(0, logs.length);
|
|
707
|
+
addLog('日志已清除', 'info');
|
|
708
|
+
ElMessage.info('日志已清除');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// 检测移动端
|
|
712
|
+
function checkMobile(): void {
|
|
713
|
+
isMobile.value = window.innerWidth <= 768;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// 处理窗口大小变化
|
|
717
|
+
function handleResize(): void {
|
|
718
|
+
checkMobile();
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// 生命周期钩子
|
|
722
|
+
onMounted(() => {
|
|
723
|
+
loadStoredConfig();
|
|
724
|
+
checkMobile();
|
|
725
|
+
addLog('测试页面已加载(SDK重构版)', 'info');
|
|
726
|
+
addLog(`浏览器: ${navigator.userAgent.split(' ')[0]}`, 'info');
|
|
727
|
+
|
|
728
|
+
// 监听窗口大小变化
|
|
729
|
+
window.addEventListener('resize', handleResize);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
onUnmounted(() => {
|
|
733
|
+
// 清理监听器
|
|
734
|
+
window.removeEventListener('resize', handleResize);
|
|
735
|
+
|
|
736
|
+
// 清理SDK资源
|
|
737
|
+
if (sdk.value) {
|
|
738
|
+
sdk.value.destroy();
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
</script>
|
|
742
|
+
|
|
743
|
+
<style lang="scss">
|
|
744
|
+
// 使用Element Plus主题变量
|
|
745
|
+
@import "element-plus/theme-chalk/index.css";
|
|
746
|
+
|
|
747
|
+
// 全局样式
|
|
748
|
+
html,
|
|
749
|
+
body {
|
|
750
|
+
width: 100vw;
|
|
751
|
+
height: 100vh;
|
|
752
|
+
padding: 0;
|
|
753
|
+
margin: 0;
|
|
754
|
+
box-sizing: border-box;
|
|
755
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
|
756
|
+
}
|
|
757
|
+
ul,
|
|
758
|
+
li {
|
|
759
|
+
padding: 0;
|
|
760
|
+
margin: 0;
|
|
761
|
+
list-style: none;
|
|
762
|
+
}
|
|
763
|
+
#app {
|
|
764
|
+
width: 100vw;
|
|
765
|
+
height: 100vh;
|
|
766
|
+
display: flex;
|
|
767
|
+
flex-direction: column;
|
|
768
|
+
}
|
|
769
|
+
.app-container {
|
|
770
|
+
flex: 1;
|
|
771
|
+
display: flex;
|
|
772
|
+
flex-direction: column;
|
|
773
|
+
.app-main {
|
|
774
|
+
display: flex;
|
|
775
|
+
padding: 20px;
|
|
776
|
+
background-color: #f5f7fa;
|
|
777
|
+
flex: 1;
|
|
778
|
+
flex-direction: column;
|
|
779
|
+
|
|
780
|
+
// 移动端优化
|
|
781
|
+
@media screen and (width <= 768px) {
|
|
782
|
+
min-height: calc(100vh - 120px); // 减去头部和底部的大致高度
|
|
783
|
+
padding: 10px;
|
|
784
|
+
flex: none; // 取消flex撑满,允许内容滚动
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// 全局配置卡片
|
|
790
|
+
.global-config-card {
|
|
791
|
+
margin-bottom: 20px;
|
|
792
|
+
.el-card__body {
|
|
793
|
+
padding: 15px;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// 主要内容区域
|
|
798
|
+
.main-content {
|
|
799
|
+
margin-bottom: 20px;
|
|
800
|
+
|
|
801
|
+
// PC端布局
|
|
802
|
+
&:not(.mobile-layout) {
|
|
803
|
+
// 让主要内容区域撑满剩余空间
|
|
804
|
+
flex: 1;
|
|
805
|
+
display: flex;
|
|
806
|
+
|
|
807
|
+
// 确保el-row也能撑满高度
|
|
808
|
+
.el-row {
|
|
809
|
+
flex: 1;
|
|
810
|
+
display: flex;
|
|
811
|
+
align-items: stretch; // 确保所有列高度相同
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// 让所有列都有相同的高度
|
|
815
|
+
.el-col {
|
|
816
|
+
display: flex;
|
|
817
|
+
flex-direction: column;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// 移动端布局
|
|
822
|
+
&.mobile-layout {
|
|
823
|
+
flex: none;
|
|
824
|
+
.mobile-panels {
|
|
825
|
+
display: flex;
|
|
826
|
+
flex-direction: column;
|
|
827
|
+
gap: 20px;
|
|
828
|
+
.mobile-panel-item {
|
|
829
|
+
width: 100%;
|
|
830
|
+
|
|
831
|
+
// 移动端面板高度优化
|
|
832
|
+
.control-panel,
|
|
833
|
+
.log-panel {
|
|
834
|
+
height: auto;
|
|
835
|
+
.el-card__body {
|
|
836
|
+
flex: none;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
.api-tabs {
|
|
840
|
+
height: auto;
|
|
841
|
+
.el-tabs__content {
|
|
842
|
+
flex: none;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// 控制面板
|
|
851
|
+
.control-panel {
|
|
852
|
+
display: flex;
|
|
853
|
+
height: 100%;
|
|
854
|
+
flex-direction: column;
|
|
855
|
+
.card-header {
|
|
856
|
+
display: flex;
|
|
857
|
+
justify-content: space-between;
|
|
858
|
+
align-items: center;
|
|
859
|
+
}
|
|
860
|
+
.el-card__body {
|
|
861
|
+
flex: 1;
|
|
862
|
+
display: flex;
|
|
863
|
+
flex-direction: column;
|
|
864
|
+
}
|
|
865
|
+
.api-tabs {
|
|
866
|
+
flex: 1;
|
|
867
|
+
display: flex;
|
|
868
|
+
flex-direction: column;
|
|
869
|
+
.el-tabs__content {
|
|
870
|
+
padding: 15px 0;
|
|
871
|
+
flex: 1;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// 日志面板
|
|
877
|
+
.log-panel {
|
|
878
|
+
display: flex;
|
|
879
|
+
height: 100%;
|
|
880
|
+
flex-direction: column;
|
|
881
|
+
.card-header {
|
|
882
|
+
display: flex;
|
|
883
|
+
justify-content: space-between;
|
|
884
|
+
align-items: center;
|
|
885
|
+
}
|
|
886
|
+
.el-card__body {
|
|
887
|
+
flex: 1;
|
|
888
|
+
display: flex;
|
|
889
|
+
flex-direction: column;
|
|
890
|
+
overflow: hidden;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// 信息卡片
|
|
895
|
+
.info-cards {
|
|
896
|
+
.el-card__body {
|
|
897
|
+
padding: 15px;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// 页脚
|
|
902
|
+
.app-footer {
|
|
903
|
+
padding: 15px 0;
|
|
904
|
+
background-color: #fff;
|
|
905
|
+
border-top: 1px solid #e4e7ed;
|
|
906
|
+
display: flex;
|
|
907
|
+
flex-direction: column;
|
|
908
|
+
align-items: center;
|
|
909
|
+
justify-content: center;
|
|
910
|
+
.footer-content {
|
|
911
|
+
display: flex;
|
|
912
|
+
justify-content: center;
|
|
913
|
+
align-items: center;
|
|
914
|
+
font-size: 12px;
|
|
915
|
+
color: #606266;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// 移动端优化
|
|
920
|
+
@media screen and (width <= 768px) {
|
|
921
|
+
.mobile-panels {
|
|
922
|
+
.mobile-panel-item {
|
|
923
|
+
.api-tabs {
|
|
924
|
+
.el-tabs__header {
|
|
925
|
+
.el-tabs__nav-wrap {
|
|
926
|
+
.el-tabs__nav-scroll {
|
|
927
|
+
.el-tabs__nav {
|
|
928
|
+
.el-tabs__item {
|
|
929
|
+
padding: 0 10px;
|
|
930
|
+
font-size: 12px;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// 响应式断点优化
|
|
942
|
+
@media screen and (width <= 992px) {
|
|
943
|
+
.main-content:not(.mobile-layout) {
|
|
944
|
+
.el-row {
|
|
945
|
+
flex-direction: column; // 平板端也使用垂直布局
|
|
946
|
+
}
|
|
947
|
+
.el-col {
|
|
948
|
+
margin-bottom: 20px;
|
|
949
|
+
&:last-child {
|
|
950
|
+
margin-bottom: 0;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// 平板端面板高度优化
|
|
955
|
+
.control-panel,
|
|
956
|
+
.log-panel {
|
|
957
|
+
height: auto;
|
|
958
|
+
min-height: 400px; // 设置最小高度
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
</style>
|