浏览代码

Revert "feat: 新增背景音乐播放功能"

This reverts commit 512f959455f370fa1af848857355b1abbb50ec4e.
urban 2 周之前
父节点
当前提交
71bc08ff24

+ 0 - 3
src/App.vue

@@ -3,8 +3,6 @@
   <loading-view />
   <Toaster position="top-center" richColors />
   <bind-parent-popup ref="bindParentPopupRef" />
-  <!-- 音乐播放器 -->
-  <music-player />
   <!-- 语言选择栏弹出容器 -->
   <div id="language-popover-teleport"></div>
 </template>
@@ -15,7 +13,6 @@ import "vue-sonner/style.css";
 import { Toaster } from "vue-sonner";
 import { onMounted, ref, computed, watch } from "vue";
 import BindParentPopup from "@/components/BindParentDialog/index.vue";
-import MusicPlayer from "@/components/MusicPlayer/index.vue";
 import { useGlobalStore } from "./store/modules/globalStore";
 import { useThemeStore } from "./store/modules/themeStore";
 const bindParentPopupRef = ref(null);

+ 0 - 1
src/api/index.ts

@@ -5,4 +5,3 @@ export * from './task';
 export * from './finance';
 export * from './team';
 export * from './home';
-export * from './music';

+ 0 - 34
src/api/music.ts

@@ -1,34 +0,0 @@
-import { http } from "@/utils/http";
-
-// 歌曲
-interface MusicItem {
-  id: number;
-  groupId: number;
-  title: string;
-  artist: string;
-  url: string;
-  coverUrl: string;
-  duration: number;
-  sort: number;
-  status: number;
-}
-
-// 歌单分组(含歌曲列表)
-interface MusicGroup {
-  id: number;
-  name: string;
-  cover: string;
-  sort: number;
-  status: number;
-  songs: MusicItem[];
-}
-
-// 获取歌单列表(含歌曲)
-export function requestGetMusicGroups(): Promise<CommonResponse<MusicGroup[]>> {
-  return http.request({
-    url: "/api/v1/dt/music/groups",
-    method: "get"
-  });
-}
-
-export type { MusicItem, MusicGroup };

+ 0 - 410
src/components/MusicPlayer/index.vue

@@ -1,410 +0,0 @@
-<template>
-  <!-- 音乐控制面板 -->
-  <van-popup
-    v-model:show="panelVisible"
-    position="bottom"
-    round
-    :style="{ background: 'var(--bg-secondary, #1a1a2e)' }"
-  >
-    <div class="music-panel">
-      <!-- 金色渐变标题栏 -->
-      <div class="panel-header">
-        <div class="header-left">
-          <svg class="header-icon" viewBox="0 0 24 24" fill="none">
-            <path d="M9 18V5l12-2v13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-            <circle cx="6" cy="18" r="3" stroke="currentColor" stroke-width="2"/>
-            <circle cx="18" cy="16" r="3" stroke="currentColor" stroke-width="2"/>
-          </svg>
-          <span class="header-title">{{ $t('music.control') }}</span>
-        </div>
-        <div class="header-close" @click="panelVisible = false">
-          <van-icon name="cross" />
-        </div>
-      </div>
-
-      <div class="panel-body">
-        <!-- 背景音乐开关 — 立即生效 -->
-        <div class="control-item">
-          <span class="control-label">{{ $t('music.bgMusic') }}</span>
-          <div class="control-right">
-            <span class="switch-text">{{ musicStore.isEnabled ? $t('music.on') : $t('music.off') }}</span>
-            <van-switch
-              :model-value="musicStore.isEnabled"
-              @update:model-value="musicStore.setEnabled"
-              size="22px"
-              active-color="#e8a820"
-            />
-          </div>
-        </div>
-
-        <!-- 播放控制栏:上一首 / 播放暂停 / 下一首 / 播放模式 -->
-        <div class="control-item playback-controls">
-          <span class="control-label">{{ $t('music.playControl') }}</span>
-          <div class="playback-btns">
-            <!-- 上一首 -->
-            <div class="ctrl-btn" @click="musicStore.prev()">
-              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
-            </div>
-            <!-- 播放/暂停 -->
-            <div class="ctrl-btn ctrl-btn-main" @click="onTogglePlay">
-              <svg v-if="musicStore.isPlaying" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
-              <svg v-else viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
-            </div>
-            <!-- 下一首 -->
-            <div class="ctrl-btn" @click="musicStore.next()">
-              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
-            </div>
-            <!-- 播放模式 — 点击循环切换 -->
-            <div class="ctrl-btn" @click="musicStore.cyclePlayMode()">
-              <!-- 顺序播放 -->
-              <svg v-if="musicStore.playMode === 'sequential'" viewBox="0 0 24 24" fill="currentColor">
-                <path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/>
-              </svg>
-              <!-- 单曲循环 -->
-              <svg v-else-if="musicStore.playMode === 'loop'" viewBox="0 0 24 24" fill="currentColor">
-                <path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"/>
-              </svg>
-              <!-- 随机播放 -->
-              <svg v-else viewBox="0 0 24 24" fill="currentColor">
-                <path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/>
-              </svg>
-            </div>
-          </div>
-        </div>
-
-        <!-- 当前播放信息 -->
-        <div v-if="musicStore.currentSongTitle" class="control-item now-playing">
-          <span class="control-label">{{ $t('music.nowPlaying') }}</span>
-          <span class="playing-title">{{ musicStore.currentSongTitle }}</span>
-        </div>
-
-        <!-- 播放模式提示 -->
-        <div class="mode-hint">
-          {{ playModeLabel }}
-        </div>
-
-        <!-- 音乐列表 -->
-        <div class="song-list-title">{{ $t('music.songList') }}</div>
-        <div class="song-list">
-          <div
-            v-for="song in musicStore.allSongs"
-            :key="song.id"
-            class="song-item"
-            :class="{ active: musicStore.currentSong?.id === song.id && musicStore.isPlaying }"
-            @click="onSelectSong(song)"
-          >
-            <div class="song-info">
-              <div class="song-title">{{ song.title }}</div>
-              <div v-if="song.artist" class="song-artist">{{ song.artist }}</div>
-            </div>
-            <div class="song-right">
-              <span v-if="song.duration" class="song-duration">{{ formatDuration(song.duration) }}</span>
-              <div v-if="musicStore.currentSong?.id === song.id && musicStore.isPlaying" class="playing-indicator">
-                <span></span><span></span><span></span>
-              </div>
-            </div>
-          </div>
-          <div v-if="!musicStore.allSongs.length" class="song-empty">{{ $t('music.noSongs') }}</div>
-        </div>
-      </div>
-    </div>
-  </van-popup>
-</template>
-
-<script setup lang="ts">
-import { computed, onMounted } from "vue";
-import { useMusicStore } from "@/store/modules/musicStore";
-import { useI18n } from "vue-i18n";
-import type { MusicItem } from "@/api/music";
-
-const { t } = useI18n();
-const musicStore = useMusicStore();
-
-const panelVisible = computed({
-  get: () => musicStore.isPanelOpen,
-  set: (val: boolean) => { musicStore.isPanelOpen = val; }
-});
-
-const playModeLabel = computed(() => {
-  const map: Record<string, string> = {
-    sequential: t("music.sequential"),
-    shuffle: t("music.shuffle"),
-    loop: t("music.loop"),
-  };
-  return map[musicStore.playMode] || t("music.sequential");
-});
-
-function onTogglePlay() {
-  if (!musicStore.isEnabled) {
-    musicStore.setEnabled(true);
-  } else if (musicStore.isPlaying) {
-    musicStore.pause();
-  } else {
-    musicStore.play();
-  }
-}
-
-function onSelectSong(song: MusicItem) {
-  musicStore.selectSong(song.id);
-  if (!musicStore.isEnabled) {
-    musicStore.setEnabled(true);
-  }
-}
-
-function formatDuration(seconds: number): string {
-  const m = Math.floor(seconds / 60);
-  const s = seconds % 60;
-  return `${m}:${s.toString().padStart(2, "0")}`;
-}
-
-onMounted(async () => {
-  await musicStore.loadSongs();
-  musicStore.restoreSettings();
-});
-</script>
-
-<style lang="scss" scoped>
-.music-panel {
-  color: var(--text-primary, #fff);
-  max-height: 80vh;
-  display: flex;
-  flex-direction: column;
-
-  // ========== 金色渐变标题栏 ==========
-  .panel-header {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 16px 20px;
-    background: linear-gradient(135deg, #b8860b 0%, #daa520 50%, #f0c040 100%);
-    border-radius: 16px 16px 0 0;
-    flex-shrink: 0;
-
-    .header-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-
-      .header-icon {
-        width: 22px;
-        height: 22px;
-        color: #fff;
-      }
-
-      .header-title {
-        font-size: 16px;
-        font-weight: 700;
-        color: #fff;
-        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-      }
-    }
-
-    .header-close {
-      width: 28px;
-      height: 28px;
-      border-radius: 50%;
-      background: rgba(0, 0, 0, 0.2);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      color: #fff;
-      font-size: 14px;
-    }
-  }
-
-  // ========== 控制区域 ==========
-  .panel-body {
-    padding: 8px 20px 24px;
-    overflow-y: auto;
-    flex: 1;
-
-    .control-item {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      padding: 14px 0;
-      border-bottom: 1px solid var(--border-color, rgba(255, 255, 255, 0.06));
-
-      .control-label {
-        font-size: 14px;
-        font-weight: 500;
-        color: var(--text-primary, #fff);
-        flex-shrink: 0;
-      }
-
-      .control-right {
-        display: flex;
-        align-items: center;
-        gap: 10px;
-
-        .switch-text {
-          font-size: 13px;
-          color: var(--text-secondary, rgba(255, 255, 255, 0.5));
-        }
-      }
-
-      // 当前播放
-      &.now-playing {
-        .playing-title {
-          font-size: 13px;
-          color: var(--color-accent, #e8a820);
-          max-width: 200px;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-        }
-      }
-    }
-
-    // ========== 播放控制栏 ==========
-    .playback-controls {
-      .playback-btns {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-
-        .ctrl-btn {
-          width: 36px;
-          height: 36px;
-          border-radius: 50%;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          background: var(--bg-tertiary, rgba(255, 255, 255, 0.06));
-          cursor: pointer;
-          transition: background 0.2s, transform 0.15s;
-          color: var(--text-primary, #fff);
-
-          &:active {
-            transform: scale(0.9);
-          }
-
-          svg {
-            width: 18px;
-            height: 18px;
-          }
-
-          &.ctrl-btn-main {
-            width: 42px;
-            height: 42px;
-            background: linear-gradient(135deg, #b8860b 0%, #daa520 50%, #f0c040 100%);
-            color: #fff;
-
-            svg {
-              width: 22px;
-              height: 22px;
-            }
-          }
-        }
-      }
-    }
-
-    // ========== 模式提示 ==========
-    .mode-hint {
-      text-align: center;
-      font-size: 12px;
-      color: var(--text-secondary, rgba(255, 255, 255, 0.4));
-      padding: 6px 0 2px;
-    }
-
-    // ========== 歌曲列表 ==========
-    .song-list-title {
-      font-size: 14px;
-      font-weight: 600;
-      color: var(--text-primary, #fff);
-      padding: 14px 0 8px;
-    }
-
-    .song-list {
-      max-height: 280px;
-      overflow-y: auto;
-
-      .song-item {
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        padding: 12px;
-        border-radius: 10px;
-        cursor: pointer;
-        transition: background 0.2s;
-
-        &:active {
-          background: var(--bg-hover, rgba(255, 255, 255, 0.08));
-        }
-
-        &.active {
-          background: var(--bg-hover, rgba(255, 255, 255, 0.06));
-
-          .song-title {
-            color: var(--color-accent, #e8a820);
-          }
-        }
-
-        .song-info {
-          flex: 1;
-          min-width: 0;
-
-          .song-title {
-            font-size: 14px;
-            color: var(--text-primary, #fff);
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-          }
-
-          .song-artist {
-            font-size: 12px;
-            color: var(--text-secondary, rgba(255, 255, 255, 0.45));
-            margin-top: 2px;
-          }
-        }
-
-        .song-right {
-          display: flex;
-          align-items: center;
-          gap: 8px;
-          flex-shrink: 0;
-
-          .song-duration {
-            font-size: 12px;
-            color: var(--text-secondary, rgba(255, 255, 255, 0.4));
-          }
-
-          // 播放指示器动画
-          .playing-indicator {
-            display: flex;
-            align-items: flex-end;
-            gap: 2px;
-            height: 14px;
-
-            span {
-              display: block;
-              width: 3px;
-              background: var(--color-accent, #e8a820);
-              border-radius: 1px;
-              animation: musicBar 0.8s ease-in-out infinite;
-
-              &:nth-child(1) { height: 6px; animation-delay: 0s; }
-              &:nth-child(2) { height: 10px; animation-delay: 0.2s; }
-              &:nth-child(3) { height: 4px; animation-delay: 0.4s; }
-            }
-          }
-        }
-      }
-
-      .song-empty {
-        text-align: center;
-        padding: 30px 0;
-        font-size: 13px;
-        color: var(--text-secondary, rgba(255, 255, 255, 0.35));
-      }
-    }
-  }
-}
-
-@keyframes musicBar {
-  0%, 100% { transform: scaleY(1); }
-  50% { transform: scaleY(2); }
-}
-</style>

+ 0 - 78
src/components/headerView/index.vue

@@ -11,18 +11,6 @@
         <div class="header-right">
           <lang-popover ref="langPopoverRef" />
 
-          <!-- 音乐控制按钮 -->
-          <div class="music-toggle-container" @click.stop="onToggleMusic">
-            <div class="music-toggle-btn" :class="{ active: musicStore.isEnabled }">
-              <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="music-icon-svg">
-                <path d="M9 18V5l12-2v13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-                <circle cx="6" cy="18" r="3" stroke="currentColor" stroke-width="2"/>
-                <circle cx="18" cy="16" r="3" stroke="currentColor" stroke-width="2"/>
-              </svg>
-              <div v-if="!musicStore.isEnabled" class="music-off-slash"></div>
-            </div>
-          </div>
-
           <!-- 主题切换按钮 -->
           <div class="theme-toggle-container" @click.stop="onToggleThemePopover">
             <div class="theme-toggle-btn">
@@ -119,7 +107,6 @@ import { useI18n } from "vue-i18n";
 import { copyText } from "@/utils/utils";
 import { useGlobalStore } from "@/store/modules/globalStore";
 import { useThemeStore } from "@/store/modules/themeStore";
-import { useMusicStore } from "@/store/modules/musicStore";
 import type { ThemeType } from "@/store/modules/themeStore";
 import { requestGetUserInfo } from "@/api";
 import defaultAvatar from "@/assets/images/common/logo.svg";
@@ -129,17 +116,6 @@ const router = useRouter();
 const userInfo = ref<UserInfo>();
 const avatarSrc = computed(() => userInfo.value?.avatar || defaultAvatar);
 
-// 音乐相关
-const musicStore = useMusicStore();
-const onToggleMusic = () => {
-  // 关闭其他弹出
-  isThemePopoverOpen.value = false;
-  isAvatarDropdownOpen.value = false;
-  langPopoverRef.value?.close();
-  // 打开音乐控制面板(MusicPlayer组件的面板)
-  musicStore.togglePanel();
-};
-
 // 主题相关
 const themeStore = useThemeStore();
 const currentTheme = computed(() => themeStore.currentTheme);
@@ -295,60 +271,6 @@ onUnmounted(() => {
     }
   }
 
-  // ======== 音乐控制按钮 ========
-  .music-toggle-container {
-    position: relative;
-    display: flex;
-    align-items: center;
-
-    .music-toggle-btn {
-      width: min(8vw, 34px);
-      height: min(8vw, 34px);
-      border-radius: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      background: var(--bg-theme-toggle, #2a2a2c);
-      transition: transform 0.2s ease, background 0.3s ease;
-      position: relative;
-
-      &:hover {
-        transform: scale(1.1);
-      }
-      &:active {
-        transform: scale(0.95);
-      }
-
-      &.active {
-        .music-icon-svg {
-          animation: musicSpin 3s linear infinite;
-        }
-        background: var(--bg-music-active, rgba(255, 195, 0, 0.15));
-      }
-
-      .music-icon-svg {
-        width: min(4.5vw, 18px);
-        height: min(4.5vw, 18px);
-        color: var(--color-theme-toggle-icon, #ffc300);
-      }
-
-      .music-off-slash {
-        position: absolute;
-        width: 22px;
-        height: 2px;
-        background: #ff4444;
-        transform: rotate(-45deg);
-        border-radius: 1px;
-      }
-    }
-  }
-
-  @keyframes musicSpin {
-    from { transform: rotate(0deg); }
-    to { transform: rotate(360deg); }
-  }
-
   // ======== 主题切换按钮 ========
   .theme-toggle-container {
     position: relative;

+ 0 - 13
src/locales/en.json

@@ -347,18 +347,5 @@
     "dark": "Dark Mode",
     "blue": "Classic Blue",
     "light": "Light Mode"
-  },
-  "music": {
-    "control": "Music Control",
-    "bgMusic": "Background Music",
-    "playControl": "Play Control",
-    "nowPlaying": "Now Playing",
-    "songList": "Song List",
-    "noSongs": "No songs available",
-    "on": "Music On",
-    "off": "Music Off",
-    "sequential": "Sequential",
-    "shuffle": "Shuffle",
-    "loop": "Loop"
   }
 }

+ 0 - 13
src/locales/id.json

@@ -321,18 +321,5 @@
     "dark": "Mode Gelap",
     "blue": "Biru Klasik",
     "light": "Mode Terang"
-  },
-  "music": {
-    "control": "Kontrol Musik",
-    "bgMusic": "Musik Latar",
-    "playControl": "Kontrol Putar",
-    "nowPlaying": "Sedang Diputar",
-    "songList": "Daftar Lagu",
-    "noSongs": "Tidak ada lagu",
-    "on": "Musik Aktif",
-    "off": "Musik Mati",
-    "sequential": "Urutan",
-    "shuffle": "Acak",
-    "loop": "Ulangi"
   }
 }

+ 0 - 13
src/locales/vi.json

@@ -324,18 +324,5 @@
     "dark": "Chế độ tối",
     "blue": "Xanh cổ điển",
     "light": "Chế độ sáng"
-  },
-  "music": {
-    "control": "Điều khiển âm nhạc",
-    "bgMusic": "Nhạc nền",
-    "playControl": "Điều khiển phát",
-    "nowPlaying": "Đang phát",
-    "songList": "Danh sách nhạc",
-    "noSongs": "Không có bài hát",
-    "on": "Bật nhạc",
-    "off": "Tắt nhạc",
-    "sequential": "Phát theo thứ tự",
-    "shuffle": "Phát ngẫu nhiên",
-    "loop": "Phát lặp lại"
   }
 }

+ 0 - 13
src/locales/zh.json

@@ -347,18 +347,5 @@
     "dark": "暗黑模式",
     "blue": "蓝白经典",
     "light": "浅色护眼"
-  },
-  "music": {
-    "control": "音乐控制",
-    "bgMusic": "背景音乐",
-    "playControl": "播放控制",
-    "nowPlaying": "正在播放",
-    "songList": "音乐列表",
-    "noSongs": "暂无歌曲",
-    "on": "开启音乐",
-    "off": "关闭音乐",
-    "sequential": "顺序播放",
-    "shuffle": "随机播放",
-    "loop": "单曲循环"
   }
 }

+ 0 - 233
src/store/modules/musicStore.ts

@@ -1,233 +0,0 @@
-import { ref, computed } from "vue";
-import { defineStore } from "pinia";
-import { requestGetMusicGroups } from "@/api/music";
-import type { MusicItem } from "@/api/music";
-
-type PlayMode = "sequential" | "shuffle" | "loop";
-
-export const useMusicStore = defineStore("music", () => {
-  // 状态
-  const isEnabled = ref(false);
-  const isPlaying = ref(false);
-  const isPanelOpen = ref(false);
-  const playMode = ref<PlayMode>("sequential");
-  const currentSongIndex = ref(0);
-  const allSongs = ref<MusicItem[]>([]);
-  const audio = ref<HTMLAudioElement | null>(null);
-  const loaded = ref(false);
-
-  // 当前选中歌曲 id(用户在列表中选择)
-  const selectedSongId = ref<number | null>(null);
-
-  // 计算属性
-  const currentSong = computed(() => {
-    if (!allSongs.value.length) return null;
-    // 如果有选中歌曲,优先使用
-    if (selectedSongId.value) {
-      const found = allSongs.value.find(s => s.id === selectedSongId.value);
-      if (found) return found;
-    }
-    const idx = currentSongIndex.value % allSongs.value.length;
-    return allSongs.value[idx] || null;
-  });
-
-  const currentSongTitle = computed(() => {
-    const song = currentSong.value;
-    if (!song) return "";
-    return song.artist ? `${song.title} - ${song.artist}` : song.title;
-  });
-
-  // 初始化 Audio
-  function initAudio() {
-    if (audio.value) return;
-    audio.value = new Audio();
-    audio.value.volume = 0.5;
-    audio.value.addEventListener("ended", onSongEnded);
-    audio.value.addEventListener("error", onSongError);
-  }
-
-  // 加载所有歌曲(从歌单接口拍平)
-  async function loadSongs() {
-    if (loaded.value) return;
-    try {
-      const res = await requestGetMusicGroups();
-      if (res.data) {
-        // 拍平所有歌单的歌曲到一个列表
-        const songs: MusicItem[] = [];
-        for (const group of res.data) {
-          if (group.songs) {
-            songs.push(...group.songs);
-          }
-        }
-        allSongs.value = songs;
-        loaded.value = true;
-      }
-    } catch (e) {
-      console.error("Failed to load music", e);
-    }
-  }
-
-  // 播放
-  function play() {
-    initAudio();
-    const song = currentSong.value;
-    if (!song || !audio.value) return;
-
-    if (audio.value.src !== song.url) {
-      audio.value.src = song.url;
-    }
-    audio.value.play().then(() => {
-      isPlaying.value = true;
-    }).catch(e => {
-      console.error("Play failed", e);
-      isPlaying.value = false;
-    });
-  }
-
-  // 暂停
-  function pause() {
-    if (audio.value) {
-      audio.value.pause();
-    }
-    isPlaying.value = false;
-  }
-
-  // 开启/关闭背景音乐(立即生效)
-  function setEnabled(val: boolean) {
-    isEnabled.value = val;
-    localStorage.setItem("music_enabled", val ? "1" : "0");
-    if (val) {
-      play();
-    } else {
-      pause();
-    }
-  }
-
-  // 设置播放模式(立即生效)
-  function setPlayMode(mode: PlayMode) {
-    playMode.value = mode;
-    localStorage.setItem("music_play_mode", mode);
-  }
-
-  // 循环切换播放模式: sequential -> loop -> shuffle -> sequential
-  function cyclePlayMode() {
-    const modes: PlayMode[] = ["sequential", "loop", "shuffle"];
-    const idx = modes.indexOf(playMode.value);
-    const nextMode = modes[(idx + 1) % modes.length];
-    setPlayMode(nextMode);
-  }
-
-  // 选择歌曲播放
-  function selectSong(songId: number) {
-    const idx = allSongs.value.findIndex(s => s.id === songId);
-    if (idx >= 0) {
-      selectedSongId.value = songId;
-      currentSongIndex.value = idx;
-      localStorage.setItem("music_song_id", String(songId));
-      if (isEnabled.value) {
-        // 强制重新加载
-        if (audio.value) {
-          audio.value.src = "";
-        }
-        play();
-      }
-    }
-  }
-
-  // 下一首
-  function next() {
-    if (!allSongs.value.length) return;
-    selectedSongId.value = null;
-    if (playMode.value === "shuffle") {
-      currentSongIndex.value = Math.floor(Math.random() * allSongs.value.length);
-    } else {
-      currentSongIndex.value = (currentSongIndex.value + 1) % allSongs.value.length;
-    }
-    if (isEnabled.value) {
-      if (audio.value) audio.value.src = "";
-      play();
-    }
-  }
-
-  // 上一首
-  function prev() {
-    if (!allSongs.value.length) return;
-    selectedSongId.value = null;
-    if (playMode.value === "shuffle") {
-      currentSongIndex.value = Math.floor(Math.random() * allSongs.value.length);
-    } else {
-      currentSongIndex.value = (currentSongIndex.value - 1 + allSongs.value.length) % allSongs.value.length;
-    }
-    if (isEnabled.value) {
-      if (audio.value) audio.value.src = "";
-      play();
-    }
-  }
-
-  // 歌曲结束回调
-  function onSongEnded() {
-    if (playMode.value === "loop") {
-      // 单曲循环,重新播放
-      if (audio.value) {
-        audio.value.currentTime = 0;
-        audio.value.play().catch(() => {});
-      }
-    } else {
-      next();
-    }
-  }
-
-  // 歌曲错误回调
-  function onSongError() {
-    console.error("Song play error, skipping to next");
-    next();
-  }
-
-  // 切换面板显示
-  function togglePanel() {
-    isPanelOpen.value = !isPanelOpen.value;
-  }
-
-  // 恢复设置
-  function restoreSettings() {
-    const savedMode = localStorage.getItem("music_play_mode");
-    const savedSongId = localStorage.getItem("music_song_id");
-
-    if (savedMode) {
-      playMode.value = savedMode as PlayMode;
-    }
-    if (savedSongId) {
-      selectedSongId.value = Number(savedSongId);
-      const idx = allSongs.value.findIndex(s => s.id === Number(savedSongId));
-      if (idx >= 0) currentSongIndex.value = idx;
-    }
-    // 不自动恢复播放状态,用户需要手动开启
-  }
-
-  return {
-    // State
-    isEnabled,
-    isPlaying,
-    isPanelOpen,
-    playMode,
-    currentSongIndex,
-    allSongs,
-    selectedSongId,
-    loaded,
-    // Computed
-    currentSong,
-    currentSongTitle,
-    // Actions
-    loadSongs,
-    play,
-    pause,
-    setEnabled,
-    setPlayMode,
-    cyclePlayMode,
-    selectSong,
-    next,
-    prev,
-    togglePanel,
-    restoreSettings,
-  };
-});