🌈 style: 优化评论区效果

This commit is contained in:
底层用户
2025-10-28 14:51:58 +08:00
parent 9967bcb102
commit c4c6178b9e
8 changed files with 306 additions and 42 deletions

View File

@@ -0,0 +1,187 @@
import { FastifyInstance } from "fastify";
import mainWindow from "../../main/windows/main-window";
/**
* 播放控制接口
* @param fastify Fastify 实例
*/
export const initControlAPI = async (fastify: FastifyInstance) => {
// 播放控制路由前缀
await fastify.register(
async (fastify) => {
// 播放
fastify.get("/play", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
mainWin.webContents.send("play");
return reply.send({
code: 200,
message: "播放命令已发送",
data: null,
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "播放失败",
data: error,
});
}
});
// 暂停
fastify.get("/pause", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
mainWin.webContents.send("pause");
return reply.send({
code: 200,
message: "暂停命令已发送",
data: null,
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "暂停失败",
data: error,
});
}
});
// 播放/暂停切换
fastify.get("/toggle", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
// 这里可以根据当前播放状态来决定发送 play 还是 pause
// 暂时先发送 toggle 事件,如果渲染进程支持的话
mainWin.webContents.send("toggle");
return reply.send({
code: 200,
message: "播放/暂停切换命令已发送",
data: null,
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "播放/暂停切换失败",
data: error,
});
}
});
// 下一曲
fastify.get("/next", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
mainWin.webContents.send("playNext");
return reply.send({
code: 200,
message: "下一曲命令已发送",
data: null,
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "下一曲失败",
data: error,
});
}
});
// 上一曲
fastify.get("/prev", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
mainWin.webContents.send("playPrev");
return reply.send({
code: 200,
message: "上一曲命令已发送",
data: null,
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "上一曲失败",
data: error,
});
}
});
// 获取播放状态(可选功能)
fastify.get("/status", async (_request, reply) => {
try {
const mainWin = mainWindow.getWin();
if (!mainWin) {
return reply.code(500).send({
code: 500,
message: "主窗口未找到",
data: null,
});
}
// 这里可以通过 IPC 获取当前播放状态
// 暂时返回基本信息
return reply.send({
code: 200,
message: "获取状态成功",
data: {
connected: true,
window: "available",
},
});
} catch (error) {
return reply.code(500).send({
code: 500,
message: "获取状态失败",
data: error,
});
}
});
},
{ prefix: "/control" },
);
};

View File

@@ -1,8 +1,9 @@
import { join } from "path";
import { isDev } from "../main/utils/config";
import { serverLog } from "../main/logger";
import initNcmAPI from "./netease";
import initUnblockAPI from "./unblock";
import { initNcmAPI } from "./netease";
import { initUnblockAPI } from "./unblock";
import { initControlAPI } from "./control";
import fastifyCookie from "@fastify/cookie";
import fastifyMultipart from "@fastify/multipart";
import fastifyStatic from "@fastify/static";
@@ -47,6 +48,7 @@ const initAppServer = async () => {
// 注册接口
server.register(initNcmAPI, { prefix: "/api" });
server.register(initUnblockAPI, { prefix: "/api" });
server.register(initControlAPI, { prefix: "/api" });
// 启动端口
const port = Number(process.env["VITE_SERVER_PORT"] || 25884);
await server.listen({ port });

View File

@@ -29,7 +29,7 @@ const getHandler = (name: string, neteaseApi: (params: any) => any) => {
};
// 初始化 NcmAPI
const initNcmAPI = async (fastify: FastifyInstance) => {
export const initNcmAPI = async (fastify: FastifyInstance) => {
// 主信息
fastify.get("/netease", (_, reply) => {
reply.send({
@@ -62,5 +62,3 @@ const initNcmAPI = async (fastify: FastifyInstance) => {
serverLog.info("🌐 Register NcmAPI successfully");
};
export default initNcmAPI;

View File

@@ -26,7 +26,7 @@ const getNeteaseSongUrl = async (id: number | string): Promise<SongUrlResult> =>
};
// 初始化 UnblockAPI
const UnblockAPI = async (fastify: FastifyInstance) => {
export const initUnblockAPI = async (fastify: FastifyInstance) => {
// 主信息
fastify.get("/unblock", (_, reply) => {
reply.send({
@@ -64,5 +64,3 @@ const UnblockAPI = async (fastify: FastifyInstance) => {
serverLog.info("🌐 Register UnblockAPI successfully");
};
export default UnblockAPI;

View File

@@ -246,9 +246,8 @@ onBeforeUnmount(() => {
}
&.show-comment {
.content-left {
position: static;
min-width: 40vw;
max-width: 50vh;
min-width: 40%;
width: 40%;
padding: 0 60px;
.player-cover,
.player-data {

View File

@@ -118,9 +118,11 @@ onMounted(() => {
<style lang="scss" scoped>
.player-comment {
position: absolute;
right: 0;
width: 60%;
flex: 1;
height: 100%;
width: 100%;
overflow: hidden;
filter: drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.2));
mask: linear-gradient(

View File

@@ -221,7 +221,7 @@ const jumpPage = debounce(
}
}
.player-tip {
width: 240px;
max-width: 240px;
padding: 12px 20px;
border-radius: 12px;
color: rgb(var(--theme));

View File

@@ -59,14 +59,20 @@ class Player {
/**
* 处理播放状态
*/
private handlePlayStatus() {
private handlePlayStatus(sessionId?: number) {
// const musicStore = useMusicStore();
const statusStore = useStatusStore();
const settingStore = useSettingStore();
const currentSessionId = sessionId ?? this.playSessionId;
// 清理定时器
clearInterval(this.playerInterval);
// 更新播放状态
this.playerInterval = setInterval(() => {
// 检查会话是否过期
if (currentSessionId !== this.playSessionId) {
clearInterval(this.playerInterval);
return;
}
if (!this.player.playing()) return;
const currentTime = this.getSeek();
const duration = this.player.duration();
@@ -178,6 +184,12 @@ class Player {
const settingStore = useSettingStore();
// 播放信息
const { id, path, type } = musicStore.playSong;
const currentSessionId = sessionId ?? this.playSessionId;
// 检查会话是否过期
if (currentSessionId !== this.playSessionId) {
console.log("🚫 Session expired, skipping player creation");
return;
}
// 清理播放器(移除事件,停止并卸载)
try {
this.player.off();
@@ -186,6 +198,13 @@ class Player {
}
Howler.stop();
Howler.unload();
// 清理所有定时器
this.cleanupAllTimers();
// 再次检查会话是否过期(异步操作后)
if (currentSessionId !== this.playSessionId) {
console.log("🚫 Session expired after cleanup, aborting");
return;
}
// 创建播放器(禁用内置 autoplay统一走手动 play
this.player = new Howl({
src,
@@ -198,7 +217,7 @@ class Player {
rate: statusStore.playRate,
});
// 播放器事件(绑定当前会话)
this.playerEvent({ seek, sessionId });
this.playerEvent({ seek, sessionId: currentSessionId });
// 播放设备
if (!settingStore.showSpectrums) this.toggleOutputDevice();
// 自动播放(仅一次性触发)
@@ -208,7 +227,7 @@ class Player {
getLyricData(id);
} else resetSongLyric();
// 定时获取状态
if (!this.playerInterval) this.handlePlayStatus();
if (!this.playerInterval) this.handlePlayStatus(currentSessionId);
// 新增播放历史
if (type !== "radio") dataStore.setHistory(musicStore.playSong);
// 获取歌曲封面主色
@@ -616,6 +635,11 @@ class Player {
async play() {
const statusStore = useStatusStore();
const settingStore = useSettingStore();
// 检查播放器状态
if (!this.player || this.player.state() === "unloaded") {
console.warn("⚠️ Player not ready for play");
return;
}
// 已在播放
if (this.player.playing()) {
statusStore.playStatus = true;
@@ -640,14 +664,13 @@ class Player {
const statusStore = useStatusStore();
const settingStore = useSettingStore();
// 播放器未加载完成
if (this.player.state() !== "loaded") {
// 播放器未加载完成或不存在
if (!this.player || this.player.state() !== "loaded") {
if (changeStatus) statusStore.playStatus = false;
return;
}
// 立即设置播放状态
if (changeStatus) statusStore.playStatus = false;
// 淡出
await new Promise<void>((resolve) => {
this.player.fade(statusStore.playVolume, 0, settingStore.getFadeTime);
@@ -672,12 +695,20 @@ class Player {
* @param autoEnd 是否为歌曲自动播放结束
*/
async nextOrPrev(type: "next" | "prev" = "next", play: boolean = true, autoEnd: boolean = false) {
const statusStore = useStatusStore();
const dataStore = useDataStore();
const musicStore = useMusicStore();
try {
if (this.switching) return;
if (this.switching) {
console.log("🔄 Already switching, ignoring request");
return;
}
this.switching = true;
const statusStore = useStatusStore();
const dataStore = useDataStore();
const musicStore = useMusicStore();
// 立即更新UI状态防止用户重复点击
statusStore.playLoading = true;
statusStore.playStatus = false;
// 获取数据
const { playList } = dataStore;
const { playSong } = musicStore;
@@ -724,12 +755,15 @@ class Player {
// 重置播放进度(切换歌曲时必须重置)
statusStore.currentTime = 0;
statusStore.progress = 0;
// 暂停
// 暂停当前播放
await this.pause(false);
// 清理定时器,防止旧定时器继续运行
this.cleanupAllTimers();
// 初始化播放器不传入seek参数确保从头开始播放
await this.initPlayer(play, 0);
} catch (error) {
console.error("Error in nextOrPrev:", error);
statusStore.playLoading = false;
throw error;
} finally {
this.switching = false;
@@ -815,6 +849,11 @@ class Player {
*/
setSeek(time: number) {
const statusStore = useStatusStore();
// 检查播放器状态
if (!this.player || this.player.state() !== "loaded") {
console.warn("⚠️ Player not ready for seek");
return;
}
this.player.seek(time);
statusStore.currentTime = time;
}
@@ -823,6 +862,8 @@ class Player {
* @returns 播放进度
*/
getSeek(): number {
// 检查播放器状态
if (!this.player || this.player.state() !== "loaded") return 0;
return this.player.seek();
}
/**
@@ -981,23 +1022,43 @@ class Player {
async togglePlayIndex(index: number, play: boolean = false) {
const dataStore = useDataStore();
const statusStore = useStatusStore();
// 获取数据
const { playList } = dataStore;
// 若超出播放列表
if (index >= playList.length) return;
// 相同
if (!play && statusStore.playIndex === index) {
this.play();
return;
try {
if (this.switching) {
console.log("🔄 Already switching, ignoring request");
return;
}
this.switching = true;
// 立即更新UI状态防止用户重复点击
statusStore.playLoading = true;
statusStore.playStatus = false;
// 获取数据
const { playList } = dataStore;
// 若超出播放列表
if (index >= playList.length) return;
// 相同
if (!play && statusStore.playIndex === index) {
this.play();
return;
}
// 更改状态
statusStore.playIndex = index;
// 重置播放进度(切换歌曲时必须重置)
statusStore.currentTime = 0;
statusStore.progress = 0;
statusStore.lyricIndex = -1;
// 暂停当前播放
await this.pause(false);
// 清理定时器,防止旧定时器继续运行
this.cleanupAllTimers();
// 清理并播放不传入seek参数确保从头开始播放
await this.initPlayer(true, 0);
} catch (error) {
console.error("Error in togglePlayIndex:", error);
statusStore.playLoading = false;
throw error;
} finally {
this.switching = false;
}
// 更改状态
statusStore.playIndex = index;
// 重置播放进度(切换歌曲时必须重置)
statusStore.currentTime = 0;
statusStore.progress = 0;
// 清理并播放不传入seek参数确保从头开始播放
await this.initPlayer(true, 0);
}
/**
* 移除指定歌曲
@@ -1322,7 +1383,24 @@ class Player {
}, 1000);
}
/**
* 执行自动关闭操作
* 清理所有定时器和资源
*/
private cleanupAllTimers() {
// 清理播放状态定时器
if (this.playerInterval) {
clearInterval(this.playerInterval);
this.playerInterval = undefined;
}
// 清理自动关闭定时器
if (this.autoCloseInterval) {
clearInterval(this.autoCloseInterval);
this.autoCloseInterval = undefined;
}
console.log("🧹 All timers cleaned up");
}
/**
* 执行自动关闭
*/
private executeAutoClose() {
console.log("🔄 执行自动关闭");