refactor(floating-list): 重构浮动列表界面并优化交互体验

- 重构拖拽手柄样式,使用新的渐变条形设计替代原有灰色线条
- 添加图标包装器容器,统一管理菜单项图标的显示和交互
- 集成动态图标切换功能,根据静音状态显示不同图标(音量高/静音)
- 更新麦克风图标为更直观的麦克风/禁用麦克风图标
- 替换控制台图标为显示器图标,提高功能识别度
- 实现静音状态响应式更新,通过 isMuted 和 isTeacherMuted 状态追踪
- 调整菜单项点击逻辑,支持静音/取消静音双向切换
- 优化菜单项样式,添加悬停缩放效果和背景过渡动画
- 改进图标容器样式,增加圆角背景和阴影效果
- 调整文字颜色和阴影,提升在半透明背景下的可读性
- 重新组织CSS类结构,提高代码可维护性和复用性
This commit is contained in:
2026-03-15 16:33:07 +08:00
parent b262cfac14
commit 95eccdb4c3

View File

@ -1,35 +1,42 @@
<template>
<div class="menu-container">
<div
style="box-sizing: border-box; width: 100%; padding: 10px; cursor: pointer"
@click="toggleFold()"
@mousedown="handleMouseDown"
@touchstart="handleTouchStart"
>
<div style="width: 100%; height: 6px; background: rgba(255, 255, 255, 0.6); border-radius: 4px"></div>
<div class="drag-handle" @click="toggleFold()" @mousedown="handleMouseDown" @touchstart="handleTouchStart">
<div class="drag-bar"></div>
</div>
<div style="width: calc(100% - 12px); height: 1px; margin: 9px 10px; background: rgba(180, 180, 180, 0.6)"></div>
<!-- 会议控制按钮 -->
<div class="menu-item" @click="muteAllStudents()">
<icon-mdi-volume-high class="menu-icon"></icon-mdi-volume-high>
<span class="menu-text">全员静音</span>
</div>
<div class="menu-item" @click="muteTeacher()">
<icon-mdi-microphone-off class="menu-icon"></icon-mdi-microphone-off>
<span class="menu-text">老师静音</span>
</div>
<div class="menu-item" @click="endClass()">
<icon-mdi-school class="menu-icon"></icon-mdi-school>
<span class="menu-text">下课</span>
</div>
<div class="menu-item" @click="openConsole()">
<icon-mdi-console class="menu-icon"></icon-mdi-console>
<span class="menu-text">控制台</span>
<div class="menu-list">
<!-- 会议控制按钮 -->
<div class="menu-item" @click="muteAllStudents()">
<div class="icon-wrapper">
<icon-mdi-volume-high v-if="!isMuted" class="menu-icon"></icon-mdi-volume-high>
<icon-mdi-volume-off v-else class="menu-icon muted"></icon-mdi-volume-off>
</div>
<span class="menu-text">全员静音</span>
</div>
<div class="menu-item" @click="muteTeacher()">
<div class="icon-wrapper">
<icon-mdi-microphone v-if="!isTeacherMuted" class="menu-icon"></icon-mdi-microphone>
<icon-mdi-microphone-off v-else class="menu-icon muted"></icon-mdi-microphone-off>
</div>
<span class="menu-text">老师静音</span>
</div>
<div class="menu-item" @click="endClass()">
<div class="icon-wrapper">
<icon-mdi-school class="menu-icon"></icon-mdi-school>
</div>
<span class="menu-text">下课</span>
</div>
<div class="menu-item" @click="openConsole()">
<div class="icon-wrapper">
<icon-mdi-monitor class="menu-icon"></icon-mdi-monitor>
</div>
<span class="menu-text">控制台</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { emitTo } from '@tauri-apps/api/event';
import { windowLabel } from '@/utils/tauri/windows-util';
import { handleMouseDown, handleTouchStart } from '../move-window';
@ -39,11 +46,19 @@
// 导入图标组件
import IconMdiVolumeHigh from '~icons/mdi/volume-high';
import IconMdiVolumeOff from '~icons/mdi/volume-off';
import IconMdiMicrophone from '~icons/mdi/microphone';
import IconMdiMicrophoneOff from '~icons/mdi/microphone-off';
import IconMdiSchool from '~icons/mdi/school';
import IconMdiConsole from '~icons/mdi/console';
import IconMdiMonitor from '~icons/mdi/monitor';
const isFold = defineModel<boolean>();
/** 全员静音状态 */
const isMuted = ref(false);
/** 老师静音状态 */
const isTeacherMuted = ref(false);
/**
* 切换折叠状态
*/
@ -55,9 +70,10 @@
*/
function muteAllStudents() {
playPromptTone();
isMuted.value = !isMuted.value;
// 发送事件给主窗口,让主窗口的 socket 处理
emitTo(windowLabel.main, 'meeting-control', {
action: 'mute_all_students',
action: isMuted.value ? 'mute_all_students' : 'unmute_all_students',
});
}
@ -66,9 +82,10 @@
*/
function muteTeacher() {
playPromptTone();
isTeacherMuted.value = !isTeacherMuted.value;
// 发送事件给主窗口,让主窗口的 socket 处理
emitTo(windowLabel.main, 'meeting-control', {
action: 'mute_teacher',
action: isTeacherMuted.value ? 'mute_teacher' : 'unmute_teacher',
});
}
@ -107,73 +124,122 @@
-webkit-user-drag: none;
}
.time-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 10px;
.time-box {
display: flex;
align-items: flex-end;
justify-content: center;
width: 100%;
margin-bottom: 2px;
line-height: 1;
.time-text {
font-size: 18px;
font-weight: bold;
color: #ffffff;
text-shadow:
1px 1px 2px #000000,
0 0 4px #000000;
}
.time-unit {
margin-left: 2px;
font-size: 12px;
color: #ffffff;
text-shadow:
1px 1px 2px #000000,
0 0 4px #000000;
}
}
}
.menu-container {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
padding: 10px 0;
padding: 4px 0;
overflow: hidden;
background: rgba(0, 0, 0, 0.2);
border-radius: 6px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.5);
}
.menu-container .menu-item {
/** 拖动区域 */
.drag-handle {
width: 100%;
padding: 8px 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s;
.drag-bar {
width: 32px;
height: 4px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.3));
border-radius: 2px;
transition: all 0.3s;
}
&:hover .drag-bar {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.5));
}
}
.menu-list {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
/** 菜单项 */
.menu-item {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
&:hover {
transform: scale(1.05);
}
&:active {
transform: scale(0.98);
}
.icon-wrapper {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 4px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
transition: all 0.3s;
.menu-item:hover & {
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.menu-icon {
width: 24px;
height: 24px;
color: #e0e0e0;
transition: all 0.3s;
&.muted {
color: #ff6b6b;
}
}
}
.menu-text {
font-size: 11px;
color: rgba(255, 255, 255, 0.85);
font-weight: 500;
letter-spacing: 0.5px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.status-indicator {
font-size: 9px;
color: #ff6b6b;
margin-top: 2px;
font-weight: 600;
animation: fadeIn 0.3s ease;
}
}
.menu-container .menu-item .menu-icon {
width: 36px;
height: 36px;
color: #ffffff; // 白色图标,与深色背景形成对比
}
.menu-container .menu-item .menu-text {
font-size: 12px;
color: #ffffff; // 白色文字
text-shadow: 2px 2px 4px #000000;
/** 淡入动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>