按钮位置调整+商标挂断
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m53s

This commit is contained in:
libingxiang 2026-07-06 15:15:43 +08:00
parent 4bf7369f92
commit 3b280eb579
2 changed files with 371 additions and 296 deletions

View File

@ -35,10 +35,10 @@
stroke="currentColor"
stroke-width="2"
>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" y1="19" x2="12" y2="23" />
<circle cx="18" cy="5" r="2" fill="currentColor" stroke="none" />
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<circle cx="18" cy="5" r="2" fill="currentColor" stroke="none"/>
</svg>
开始对话
</button>
@ -69,16 +69,14 @@
<!-- 字幕区域底部不遮挡数字人主体 -->
<div class="subtitle-overlay">
<div class="subtitle-container">
<SubtitlePanel :ai-subtitle="currentAISubtitle" />
<SubtitlePanel :ai-subtitle="currentAISubtitle"/>
</div>
</div>
<!-- 右侧垂直控制按钮组 -->
<div class="side-controls">
<!-- 拾音按钮 -->
<!-- 拾音按钮 - 左下角 -->
<button
v-if="audioMode === 'btn'"
class="mic-btn"
class="mic-btn mic-bottom-left"
:class="{
recording: pickPhase === 'recording',
'mic-processing': pickPhase === 'processing',
@ -110,10 +108,10 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" y1="19" x2="12" y2="23" />
<line x1="8" y1="23" x2="16" y2="23" />
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
<!-- 识别中文字气泡+三点跳动 -->
<svg
@ -212,8 +210,8 @@
fill="currentColor"
stroke="none"
/>
<path class="wave-1" d="M15.54 8.46a5 5 0 0 1 0 7.07" />
<path class="wave-2" d="M19.07 4.93a10 10 0 0 1 0 14.14" />
<path class="wave-1" d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path class="wave-2" d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
<!-- 空闲麦克风 -->
<svg
@ -227,10 +225,10 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" y1="19" x2="12" y2="23" />
<line x1="8" y1="23" x2="16" y2="23" />
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
<span class="mic-label">{{
pickPhase === "recording"
@ -245,10 +243,10 @@
}}</span>
</button>
<!-- RTC模式拾音按钮 -->
<!-- RTC模式拾音按钮 - 左下角 -->
<button
v-if="audioMode === 'rtc'"
class="mic-btn"
class="mic-btn mic-bottom-left"
:class="{
'mic-on': pickPhase === 'idle' && !isMuted,
'mic-off': pickPhase === 'idle' && isMuted,
@ -278,10 +276,10 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" y1="19" x2="12" y2="23" />
<line x1="8" y1="23" x2="16" y2="23" />
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
<!-- 识别中文字气泡+三点跳动 -->
<svg
@ -380,8 +378,8 @@
fill="currentColor"
stroke="none"
/>
<path class="wave-1" d="M15.54 8.46a5 5 0 0 1 0 7.07" />
<path class="wave-2" d="M19.07 4.93a10 10 0 0 1 0 14.14" />
<path class="wave-1" d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path class="wave-2" d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
<!-- 麦克风已关闭 -->
<svg
@ -395,9 +393,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="1" y1="1" x2="23" y2="23" />
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="1" y1="1" x2="23" y2="23"/>
</svg>
<span class="mic-label">{{
pickPhase === "processing"
@ -412,8 +410,8 @@
}}</span>
</button>
<!-- 打断按钮 -->
<button class="interrupt-btn" @click="dhInterrupt">
<!-- 打断按钮 - 右下角 -->
<button class="interrupt-btn interrupt-bottom-right" @click="dhInterrupt">
<svg
viewBox="0 0 24 24"
width="24"
@ -423,44 +421,28 @@
stroke-width="2.5"
stroke-linecap="round"
>
<path d="M18.36 6.64a9 9 0 1 1-12.73 0" />
<line x1="12" y1="2" x2="12" y2="12" />
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>
<line x1="12" y1="2" x2="12" y2="12"/>
</svg>
<span class="interrupt-label">打断</span>
</button>
<!-- 挂断按钮 -->
<button class="hangup-btn" @click="handleDisconnect" title="挂断">
<svg
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91"
/>
<line x1="1" y1="1" x2="23" y2="23" />
</svg>
<span class="hangup-label">挂断</span>
<!-- 挂断按钮 - 右上角 -->
<button class="hangup-btn hangup-top-left" @click="handleDisconnect" title="挂断">
<img src="./GP.webp" alt="挂断" class="hangup-img"/>
</button>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import {ref, watch} from "vue";
import {ElMessage} from "element-plus";
import SubtitlePanel from "@/components/SubtitlePanel.vue";
import { useDigitalHuman } from "@/hooks/useDigitalHuman";
import { useLLM } from "@/hooks/useLLM";
import {useDigitalHuman} from "@/hooks/useDigitalHuman";
import {useLLM} from "@/hooks/useLLM";
const { sendToChatBase } = useLLM();
const {sendToChatBase} = useLLM();
const {
isConnected,
@ -621,6 +603,7 @@ async function handleVoiceQueryComplete(text: string) {
background: linear-gradient(135deg, #4f6ef7, #6c8cff);
color: #fff;
box-shadow: 0 8px 32px rgba(79, 110, 247, 0.35);
&:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(79, 110, 247, 0.5);
@ -631,6 +614,7 @@ async function handleVoiceQueryComplete(text: string) {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
color: rgba(255, 255, 255, 0.8);
&:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
@ -670,6 +654,7 @@ async function handleVoiceQueryComplete(text: string) {
&::placeholder {
color: rgba(255, 255, 255, 0.25);
}
&:focus {
border-color: rgba(108, 140, 255, 0.5);
}
@ -720,17 +705,48 @@ async function handleVoiceQueryComplete(text: string) {
font-weight: 500;
}
/* ===== 右侧垂直控制按钮组 ===== */
.side-controls {
/* ===== 麦克风按钮 - 字幕下方左侧 ===== */
.mic-bottom-left {
position: absolute;
right: 24px;
top: 50%;
transform: translateY(-50%);
left: 50%;
transform: translateX(-120px);
bottom: 10%;
z-index: 25;
transition: transform 0.2s ease;
&:hover {
transform: translateX(-120px) scale(1.06);
}
&:active {
transform: translateX(-120px) scale(0.95);
}
}
/* ===== 打断按钮 - 字幕下方右侧 ===== */
.interrupt-bottom-right {
position: absolute;
right: 50%;
transform: translateX(120px);
bottom: 10%;
z-index: 25;
transition: transform 0.2s ease;
&:hover {
transform: translateX(120px) scale(1.06);
}
&:active {
transform: translateX(120px) scale(0.95);
}
}
/* ===== 挂断按钮 - 右上角 ===== */
.hangup-top-left {
position: absolute;
right: 32px;
top: 24px;
z-index: 25;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
/* ===== 字幕区域(屏幕中央居中) ===== */
@ -756,11 +772,10 @@ async function handleVoiceQueryComplete(text: string) {
/* ===== 拾音按钮 ===== */
.mic-btn {
position: relative;
flex-shrink: 0;
width: 72px;
height: 72px;
border-radius: 20px;
width: 110px;
height: 110px;
border-radius: 30px;
border: 2px solid rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(12px);
@ -785,11 +800,9 @@ async function handleVoiceQueryComplete(text: string) {
&:hover {
background: rgba(255, 255, 255, 0.14);
border-color: rgba(255, 255, 255, 0.3);
transform: scale(1.06);
}
&:active {
transform: scale(0.95);
}
&.recording {
@ -869,12 +882,15 @@ async function handleVoiceQueryComplete(text: string) {
.dot {
transform-origin: center;
}
.dot-1 {
animation: dotBounce 1.4s ease-in-out infinite;
}
.dot-2 {
animation: dotBounce 1.4s ease-in-out 0.2s infinite;
}
.dot-3 {
animation: dotBounce 1.4s ease-in-out 0.4s infinite;
}
@ -898,12 +914,15 @@ async function handleVoiceQueryComplete(text: string) {
.ellipsis-dot {
transform-origin: center;
}
.ed-1 {
animation: ellipsisPulse 1.8s ease-in-out infinite;
}
.ed-2 {
animation: ellipsisPulse 1.8s ease-in-out 0.3s infinite;
}
.ed-3 {
animation: ellipsisPulse 1.8s ease-in-out 0.6s infinite;
}
@ -928,6 +947,7 @@ async function handleVoiceQueryComplete(text: string) {
animation: waveExpand 2s ease-out infinite;
transform-origin: 12px 12px;
}
.wave-2 {
animation: waveExpand 2s ease-out 0.5s infinite;
transform-origin: 12px 12px;
@ -997,9 +1017,9 @@ async function handleVoiceQueryComplete(text: string) {
/* ===== 打断按钮 ===== */
.interrupt-btn {
flex-shrink: 0;
width: 72px;
height: 72px;
border-radius: 20px;
width: 110px;
height: 110px;
border-radius: 30px;
border: 2px solid rgba(251, 146, 60, 0.35);
background: rgba(251, 146, 60, 0.12);
backdrop-filter: blur(12px);
@ -1025,55 +1045,34 @@ async function handleVoiceQueryComplete(text: string) {
background: rgba(251, 146, 60, 0.22);
border-color: rgba(251, 146, 60, 0.6);
box-shadow: 0 0 32px rgba(251, 146, 60, 0.25);
transform: scale(1.06);
}
&:active {
background: rgba(251, 146, 60, 0.3);
border-color: rgba(251, 146, 60, 0.8);
box-shadow: 0 0 48px rgba(251, 146, 60, 0.4);
transform: scale(0.95);
}
}
/* ===== 挂断按钮 ===== */
.hangup-btn {
flex-shrink: 0;
width: 72px;
height: 72px;
border-radius: 20px;
border: 2px solid rgba(239, 68, 68, 0.35);
background: rgba(239, 68, 68, 0.12);
backdrop-filter: blur(12px);
color: #f87171;
padding: 0;
border: none;
background: none;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
transition: all 0.25s ease;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
transition: transform 0.2s ease;
.hangup-label {
font-size: 10px;
font-weight: 600;
opacity: 0.85;
.hangup-img {
width: 80px;
height: 80px;
object-fit: contain;
}
&:hover {
background: rgba(239, 68, 68, 0.22);
border-color: rgba(239, 68, 68, 0.6);
box-shadow: 0 0 32px rgba(239, 68, 68, 0.25);
transform: scale(1.06);
transform: scale(1.1);
}
&:active {
background: rgba(239, 68, 68, 0.3);
border-color: rgba(239, 68, 68, 0.8);
box-shadow: 0 0 48px rgba(239, 68, 68, 0.4);
transform: scale(0.95);
}
}
@ -1124,15 +1123,24 @@ async function handleVoiceQueryComplete(text: string) {
width: 65%;
}
.side-controls {
right: 12px;
gap: 8px;
.mic-bottom-left {
left: 50%;
transform: translateX(-100px);
bottom: 10%;
}
.mic-bottom-left:hover {
transform: translateX(-100px) scale(1.06);
}
.mic-bottom-left:active {
transform: translateX(-100px) scale(0.95);
}
.mic-btn {
width: 52px;
height: 52px;
border-radius: 16px;
width: 70px;
height: 70px;
border-radius: 20px;
.mic-label {
font-size: 8px;
@ -1143,24 +1151,39 @@ async function handleVoiceQueryComplete(text: string) {
border-radius: 20px;
}
.interrupt-bottom-right {
right: 50%;
transform: translateX(100px);
bottom: 10%;
}
.interrupt-bottom-right:hover {
transform: translateX(100px) scale(1.06);
}
.interrupt-bottom-right:active {
transform: translateX(100px) scale(0.95);
}
.interrupt-btn {
width: 52px;
height: 52px;
border-radius: 16px;
width: 70px;
height: 70px;
border-radius: 20px;
.interrupt-label {
font-size: 8px;
}
}
.hangup-btn {
width: 52px;
height: 52px;
border-radius: 16px;
.hangup-label {
font-size: 8px;
.hangup-top-left {
right: 16px;
top: 16px;
}
.hangup-img {
width: 56px;
height: 56px;
}
.config-row {
@ -1171,6 +1194,7 @@ async function handleVoiceQueryComplete(text: string) {
text-align: left;
min-width: auto;
}
input {
padding: 10px 14px;
font-size: 13px;
@ -1193,30 +1217,58 @@ async function handleVoiceQueryComplete(text: string) {
padding: 20px 28px;
}
.side-controls {
right: 16px;
.mic-bottom-left {
left: 50%;
transform: translateX(-110px);
bottom: 10%;
}
.mic-bottom-left:hover {
transform: translateX(-110px) scale(1.06);
}
.mic-bottom-left:active {
transform: translateX(-110px) scale(0.95);
}
.mic-btn {
width: 60px;
height: 60px;
border-radius: 18px;
width: 80px;
height: 80px;
border-radius: 22px;
}
.mic-btn-ring {
border-radius: 22px;
}
.interrupt-btn {
width: 60px;
height: 60px;
border-radius: 18px;
.interrupt-bottom-right {
right: 50%;
transform: translateX(110px);
bottom: 10%;
}
.hangup-btn {
width: 60px;
height: 60px;
border-radius: 18px;
.interrupt-bottom-right:hover {
transform: translateX(110px) scale(1.06);
}
.interrupt-bottom-right:active {
transform: translateX(110px) scale(0.95);
}
.interrupt-btn {
width: 80px;
height: 80px;
border-radius: 22px;
}
.hangup-top-left {
right: 20px;
top: 20px;
}
.hangup-img {
width: 64px;
height: 64px;
}
}
@ -1253,15 +1305,24 @@ async function handleVoiceQueryComplete(text: string) {
width: 70%;
}
.side-controls {
right: 10px;
gap: 6px;
.mic-bottom-left {
left: 50%;
transform: translateX(-90px);
bottom: 8%;
}
.mic-bottom-left:hover {
transform: translateX(-90px) scale(1.06);
}
.mic-bottom-left:active {
transform: translateX(-90px) scale(0.95);
}
.mic-btn {
width: 48px;
height: 48px;
border-radius: 14px;
width: 64px;
height: 64px;
border-radius: 18px;
.mic-label {
font-size: 8px;
@ -1272,24 +1333,38 @@ async function handleVoiceQueryComplete(text: string) {
border-radius: 18px;
}
.interrupt-bottom-right {
right: 50%;
transform: translateX(90px);
bottom: 8%;
}
.interrupt-bottom-right:hover {
transform: translateX(90px) scale(1.06);
}
.interrupt-bottom-right:active {
transform: translateX(90px) scale(0.95);
}
.interrupt-btn {
width: 48px;
height: 48px;
border-radius: 14px;
width: 64px;
height: 64px;
border-radius: 18px;
.interrupt-label {
font-size: 8px;
}
}
.hangup-btn {
width: 48px;
height: 48px;
border-radius: 14px;
.hangup-label {
font-size: 8px;
.hangup-top-left {
right: 14px;
top: 14px;
}
.hangup-img {
width: 52px;
height: 52px;
}
}
</style>

BIN
src/GP.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB