// ============ 全局状态 ============
const state = {
currentPage: 'home',
currentSong: null,
playlist: [],
playlistIndex: -1,
isPlaying: false,
lyricsData: [],
currentLyricIndex: -1
};
// ============ 路由系统 ============
const router = {
go(path) {
history.pushState(null, '', path);
this.handleRoute();
},
handleRoute() {
const path = window.location.pathname;
// 更新导航激活状态
document.querySelectorAll('.header-nav a').forEach(a => {
a.classList.remove('active');
const page = a.dataset.page;
if (path === '/' && page === 'home') a.classList.add('active');
else if (path.startsWith('/' + page)) a.classList.add('active');
});
// 路由匹配
if (path === '/') {
this.renderHome();
} else if (path === '/discover') {
this.renderDiscover();
} else if (path === '/artists') {
this.renderArtists();
} else if (path === '/playlists') {
this.renderPlaylists();
} else if (path.startsWith('/song/')) {
const id = path.split('/')[2];
this.renderSongDetail(id);
} else if (path.startsWith('/artist/')) {
const id = path.split('/')[2];
this.renderArtistDetail(id);
} else if (path.startsWith('/album/')) {
const id = path.split('/')[2];
this.renderAlbumDetail(id);
} else if (path.startsWith('/playlist/')) {
const id = path.split('/')[2];
this.renderPlaylistDetail(id);
} else if (path.startsWith('/search')) {
const q = new URLSearchParams(window.location.search).get('q');
this.renderSearch(q);
} else {
this.renderHome();
}
},
// 首页
async renderHome() {
const app = document.getElementById('app');
app.innerHTML = '
加载中
';
try {
const data = await api.get('/api/home');
let html = '';
// 轮播图
if (data.banners.length > 0) {
html += '';
data.banners.forEach((banner, i) => {
html += `
${banner.title}
`;
});
html += '
';
data.banners.forEach((_, i) => {
html += ``;
});
html += '
';
}
// 推荐歌单
if (data.featuredPlaylists.length > 0) {
html += '推荐歌单
更多 ';
html += '';
data.featuredPlaylists.forEach(pl => {
html += `
`;
});
html += '
';
}
// 热门歌曲
if (data.hotSongs.length > 0) {
html += '热门推荐
';
html += '';
data.hotSongs.slice(0, 10).forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
}
// 最新专辑
if (data.albums.length > 0) {
html += '新碟上架
更多 ';
html += '';
data.albums.slice(0, 6).forEach(album => {
html += `
${album.title}
${album.artist_name || ''}
`;
});
html += '
';
}
// 热门歌手
if (data.artists.length > 0) {
html += '热门歌手
更多 ';
html += '';
data.artists.slice(0, 6).forEach(artist => {
html += `
`;
});
html += '
';
}
app.innerHTML = html;
// 初始化轮播
if (data.banners.length > 0) {
banner.init(data.banners.length);
}
} catch (err) {
app.innerHTML = '加载失败
';
console.error(err);
}
},
// 发现页
async renderDiscover() {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const [songsData, albumsData] = await Promise.all([
api.get('/api/songs?limit=50'),
api.get('/api/albums')
]);
let html = '全部歌曲
';
html += '';
songsData.songs.forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
html += '全部专辑
';
html += '';
albumsData.forEach(album => {
html += `
${album.title}
${album.artist_name || ''}
`;
});
html += '
';
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 歌手列表页
async renderArtists() {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const artists = await api.get('/api/artists');
let html = '全部歌手
';
html += '';
artists.forEach(artist => {
html += `
`;
});
html += '
';
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 歌单列表页
async renderPlaylists() {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const playlists = await api.get('/api/playlists');
let html = '全部歌单
';
html += '';
playlists.forEach(pl => {
html += `
`;
});
html += '
';
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 歌曲详情
async renderSongDetail(id) {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const song = await api.get(`/api/songs/${id}`);
let html = `
`;
app.innerHTML = html;
// 设置歌词
if (song.lyrics) {
lyrics.parse(song.lyrics);
}
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 歌手详情
async renderArtistDetail(id) {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const artist = await api.get(`/api/artists/${id}`);
let html = `
`;
if (artist.albums.length > 0) {
html += '专辑
';
html += '';
artist.albums.forEach(album => {
html += `
`;
});
html += '
';
}
if (artist.songs.length > 0) {
html += '热门歌曲
';
html += '';
artist.songs.forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
}
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 专辑详情
async renderAlbumDetail(id) {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const album = await api.get(`/api/albums/${id}`);
let html = `
`;
if (album.songs.length > 0) {
html += '歌曲列表
';
html += '';
album.songs.forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
}
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 歌单详情
async renderPlaylistDetail(id) {
const app = document.getElementById('app');
app.innerHTML = '加载中
';
try {
const playlist = await api.get(`/api/playlists/${id}`);
let html = `
`;
if (playlist.songs.length > 0) {
html += '歌曲列表
';
html += '';
playlist.songs.forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
}
app.innerHTML = html;
} catch (err) {
app.innerHTML = '加载失败
';
}
},
// 搜索结果
async renderSearch(q) {
const app = document.getElementById('app');
if (!q) {
app.innerHTML = '请输入搜索关键词
';
return;
}
app.innerHTML = '搜索中
';
try {
const data = await api.get(`/api/search?q=${encodeURIComponent(q)}`);
let html = ``;
html += `
搜索: "${q}"
`;
if (data.songs.length > 0) {
html += '
歌曲
';
html += '
';
data.songs.forEach((song, i) => {
html += renderSongItem(song, i + 1);
});
html += '
';
}
if (data.artists.length > 0) {
html += '
歌手
';
html += '
';
data.artists.forEach(artist => {
html += `
`;
});
html += '
';
}
if (data.albums.length > 0) {
html += '
专辑
';
html += '
';
data.albums.forEach(album => {
html += `
${album.title}
${album.artist_name || ''}
`;
});
html += '
';
}
if (data.songs.length === 0 && data.artists.length === 0 && data.albums.length === 0) {
html += '
未找到相关结果
';
}
html += '
';
app.innerHTML = html;
} catch (err) {
app.innerHTML = '搜索失败
';
}
}
};
// ============ API 工具 ============
const api = {
async get(url) {
const res = await fetch(url);
return res.json();
},
async post(url, data) {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return res.json();
}
};
// ============ 播放器 ============
const player = {
audio: null,
init() {
this.audio = document.getElementById('audio');
this.audio.addEventListener('timeupdate', () => this.onTimeUpdate());
this.audio.addEventListener('ended', () => this.next());
this.audio.addEventListener('loadedmetadata', () => {
document.getElementById('totalTime').textContent = formatTime(this.audio.duration);
});
},
playSong(song) {
state.currentSong = song;
// 更新UI
document.getElementById('playerTitle').textContent = song.title;
document.getElementById('playerArtist').textContent = song.artist_name || '未知歌手';
const coverEl = document.getElementById('playerCover');
if (song.cover) {
coverEl.innerHTML = `
`;
} else {
coverEl.innerHTML = '
';
}
// 设置音频源
this.audio.src = song.file_path;
this.audio.play();
state.isPlaying = true;
this.updatePlayBtn();
// 解析歌词
if (song.lyrics) {
lyrics.parse(song.lyrics);
} else {
lyrics.clear();
}
// 增加播放次数
api.post(`/api/songs/${song.id}/play`, {});
// 高亮当前播放
document.querySelectorAll('.song-item').forEach(el => el.classList.remove('playing'));
const currentEl = document.querySelector(`.song-item[data-id="${song.id}"]`);
if (currentEl) currentEl.classList.add('playing');
},
playAll(songs) {
if (songs.length === 0) return;
state.playlist = songs;
state.playlistIndex = 0;
this.playSong(songs[0]);
},
toggle() {
if (!state.currentSong) return;
if (state.isPlaying) {
this.audio.pause();
state.isPlaying = false;
} else {
this.audio.play();
state.isPlaying = true;
}
this.updatePlayBtn();
},
prev() {
if (state.playlist.length === 0) return;
state.playlistIndex = (state.playlistIndex - 1 + state.playlist.length) % state.playlist.length;
this.playSong(state.playlist[state.playlistIndex]);
},
next() {
if (state.playlist.length === 0) return;
state.playlistIndex = (state.playlistIndex + 1) % state.playlist.length;
this.playSong(state.playlist[state.playlistIndex]);
},
seek(e) {
if (!this.audio.duration) return;
const bar = e.currentTarget;
const rect = bar.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
this.audio.currentTime = percent * this.audio.duration;
},
setVolume(e) {
const bar = e.currentTarget;
const rect = bar.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
this.audio.volume = Math.max(0, Math.min(1, percent));
document.getElementById('volumeLevel').style.width = (percent * 100) + '%';
},
toggleMute() {
this.audio.muted = !this.audio.muted;
const btn = document.getElementById('volumeBtn');
btn.innerHTML = this.audio.muted ? '' : '';
},
updatePlayBtn() {
const btn = document.getElementById('playBtn');
btn.innerHTML = state.isPlaying ? '' : '';
},
onTimeUpdate() {
const current = this.audio.currentTime;
const duration = this.audio.duration || 0;
// 更新进度条
const percent = duration > 0 ? (current / duration) * 100 : 0;
document.getElementById('progress').style.width = percent + '%';
document.getElementById('currentTime').textContent = formatTime(current);
// 更新歌词高亮
lyrics.update(current);
}
};
// ============ 歌词模块 ============
const lyrics = {
toggle() {
const panel = document.getElementById('lyricsPanel');
panel.classList.toggle('open');
},
parse(lyricsText) {
const lines = lyricsText.split('\n');
state.lyricsData = [];
lines.forEach(line => {
const match = line.match(/\[(\d{2}):(\d{2})\.(\d{2,3})\](.*)/);
if (match) {
const min = parseInt(match[1]);
const sec = parseInt(match[2]);
const ms = parseInt(match[3].padEnd(3, '0'));
const time = min * 60 + sec + ms / 1000;
const text = match[4].trim();
if (text) {
state.lyricsData.push({ time, text });
}
}
});
this.render();
},
render() {
const content = document.getElementById('lyricsContent');
if (state.lyricsData.length === 0) {
content.innerHTML = '暂无歌词
';
return;
}
let html = '';
state.lyricsData.forEach((line, i) => {
html += `${line.text}
`;
});
html += '';
content.innerHTML = html;
state.currentLyricIndex = -1;
},
update(currentTime) {
if (state.lyricsData.length === 0) return;
let newIndex = -1;
for (let i = state.lyricsData.length - 1; i >= 0; i--) {
if (currentTime >= state.lyricsData[i].time) {
newIndex = i;
break;
}
}
if (newIndex !== state.currentLyricIndex) {
state.currentLyricIndex = newIndex;
// 更新高亮
document.querySelectorAll('.lyrics-line').forEach(el => el.classList.remove('active'));
if (newIndex >= 0) {
const activeLine = document.querySelector(`.lyrics-line[data-index="${newIndex}"]`);
if (activeLine) {
activeLine.classList.add('active');
// 滚动到可视区域
activeLine.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
},
clear() {
state.lyricsData = [];
state.currentLyricIndex = -1;
document.getElementById('lyricsContent').innerHTML = '暂无歌词
';
}
};
// ============ 轮播图 ============
const banner = {
timer: null,
currentIndex: 0,
total: 0,
init(total) {
this.total = total;
this.currentIndex = 0;
this.startAutoPlay();
},
goTo(index) {
this.currentIndex = index;
const slides = document.querySelectorAll('.banner-slide');
const dots = document.querySelectorAll('.banner-dots span');
slides.forEach((s, i) => s.classList.toggle('active', i === index));
dots.forEach((d, i) => d.classList.toggle('active', i === index));
},
next() {
this.goTo((this.currentIndex + 1) % this.total);
},
startAutoPlay() {
if (this.timer) clearInterval(this.timer);
this.timer = setInterval(() => this.next(), 5000);
}
};
// ============ 辅助函数 ============
function renderSongItem(song, index) {
return `
${index}
${song.title}
${song.artist_name || '未知歌手'}
${formatTime(song.duration)}
`;
}
function formatTime(seconds) {
if (!seconds || isNaN(seconds)) return '0:00';
const min = Math.floor(seconds / 60);
const sec = Math.floor(seconds % 60);
return `${min}:${sec.toString().padStart(2, '0')}`;
}
function formatNumber(num) {
if (!num) return '0';
if (num >= 10000) return (num / 10000).toFixed(1) + '万';
return num.toString();
}
function handleSearch(e) {
if (e.key === 'Enter') {
const q = e.target.value.trim();
if (q) {
router.go(`/search?q=${encodeURIComponent(q)}`);
}
}
}
// ============ 初始化 ============
document.addEventListener('DOMContentLoaded', () => {
player.init();
router.handleRoute();
// 处理浏览器后退
window.addEventListener('popstate', () => {
router.handleRoute();
});
});