2025-06-06 15:17:30 +08:00
|
|
|
const utils = require("../../../utils/utils");
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
data: {
|
2026-08-04 09:59:35 +08:00
|
|
|
title: "音乐",
|
|
|
|
|
data: {},
|
|
|
|
|
playlist: [],
|
|
|
|
|
currentIndex: 0,
|
|
|
|
|
value: "00:00",
|
|
|
|
|
duration: "00:00",
|
|
|
|
|
percent: 0,
|
|
|
|
|
isPlay: false,
|
|
|
|
|
isSingleLoop: false,
|
|
|
|
|
isShow: false,
|
|
|
|
|
isClosing: false
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onLoad(options) {
|
2026-08-04 09:59:35 +08:00
|
|
|
let current = {};
|
|
|
|
|
try {
|
|
|
|
|
current = JSON.parse(decodeURIComponent(options.data || "{}"));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("资源参数解析失败", error);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 15:17:30 +08:00
|
|
|
this.setData({
|
2026-08-04 09:59:35 +08:00
|
|
|
data: current,
|
|
|
|
|
playlist: current.resourceUrl ? [current] : [],
|
|
|
|
|
title: current.id == 1 ? "口令" : "音乐"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.createAudioContext();
|
|
|
|
|
if (current.resourceUrl) {
|
|
|
|
|
this.loadTrack(current, false);
|
|
|
|
|
}
|
2025-06-06 15:17:30 +08:00
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
const eventChannel = this.getOpenerEventChannel && this.getOpenerEventChannel();
|
|
|
|
|
if (eventChannel && eventChannel.on) {
|
|
|
|
|
eventChannel.on("setPlaylist", ({ playlist = [], currentIndex = 0 }) => {
|
|
|
|
|
if (!playlist.length) return;
|
|
|
|
|
const safeIndex = Math.min(Math.max(Number(currentIndex) || 0, 0), playlist.length - 1);
|
|
|
|
|
this.setData({ playlist, currentIndex: safeIndex, data: playlist[safeIndex] });
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
createAudioContext() {
|
|
|
|
|
this.audioCtx = wx.createInnerAudioContext();
|
|
|
|
|
this.audioCtx.obeyMuteSwitch = false;
|
|
|
|
|
|
|
|
|
|
this.audioCtx.onCanplay(() => this.updateProgress());
|
|
|
|
|
this.audioCtx.onTimeUpdate(() => this.updateProgress());
|
|
|
|
|
this.audioCtx.onPlay(() => this.setData({ isPlay: true }));
|
|
|
|
|
this.audioCtx.onPause(() => this.setData({ isPlay: false }));
|
|
|
|
|
this.audioCtx.onStop(() => this.setData({ isPlay: false }));
|
|
|
|
|
this.audioCtx.onEnded(() => {
|
|
|
|
|
if (!this.data.isSingleLoop) this.changeTrack(1, true);
|
|
|
|
|
});
|
|
|
|
|
this.audioCtx.onError((error) => {
|
|
|
|
|
console.error("音频播放失败", error);
|
|
|
|
|
this.setData({ isPlay: false });
|
|
|
|
|
wx.showToast({ title: "音频加载失败,请稍后重试", icon: "none" });
|
|
|
|
|
});
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
loadTrack(track, autoplay = true) {
|
|
|
|
|
if (!track || !track.resourceUrl || !this.audioCtx) return;
|
|
|
|
|
this.audioCtx.stop();
|
|
|
|
|
this.audioCtx.loop = this.data.isSingleLoop;
|
|
|
|
|
this.audioCtx.src = track.resourceUrl;
|
2025-06-06 15:17:30 +08:00
|
|
|
this.setData({
|
2026-08-04 09:59:35 +08:00
|
|
|
data: track,
|
|
|
|
|
value: "00:00",
|
|
|
|
|
duration: "00:00",
|
|
|
|
|
percent: 0,
|
|
|
|
|
isPlay: autoplay
|
|
|
|
|
});
|
|
|
|
|
if (autoplay) this.audioCtx.play();
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
updateProgress() {
|
|
|
|
|
if (!this.audioCtx) return;
|
|
|
|
|
const currentTime = Number(this.audioCtx.currentTime) || 0;
|
|
|
|
|
const duration = Number(this.audioCtx.duration) || 0;
|
2025-06-06 15:17:30 +08:00
|
|
|
this.setData({
|
2026-08-04 09:59:35 +08:00
|
|
|
value: utils.formatTimeStr(currentTime),
|
|
|
|
|
duration: utils.formatTimeStr(duration),
|
|
|
|
|
percent: duration ? Math.min((currentTime / duration) * 100, 100) : 0
|
|
|
|
|
});
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
btnCilck() {
|
|
|
|
|
if (!this.audioCtx || !this.data.data.resourceUrl) return;
|
|
|
|
|
this.data.isPlay ? this.audioCtx.pause() : this.audioCtx.play();
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
playPrevious() {
|
|
|
|
|
this.changeTrack(-1, true);
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
playNext() {
|
|
|
|
|
this.changeTrack(1, true);
|
|
|
|
|
},
|
2025-06-06 15:17:30 +08:00
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
changeTrack(step, autoplay) {
|
|
|
|
|
const { playlist, currentIndex } = this.data;
|
|
|
|
|
if (!playlist.length) return;
|
|
|
|
|
const nextIndex = (currentIndex + step + playlist.length) % playlist.length;
|
|
|
|
|
this.setData({ currentIndex: nextIndex }, () => this.loadTrack(playlist[nextIndex], autoplay));
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
selectTrack(e) {
|
|
|
|
|
const index = Number(e.currentTarget.dataset.index);
|
|
|
|
|
if (!Number.isInteger(index) || !this.data.playlist[index]) return;
|
|
|
|
|
this.setData({ currentIndex: index }, () => {
|
|
|
|
|
this.loadTrack(this.data.playlist[index], true);
|
|
|
|
|
this.hideList();
|
|
|
|
|
});
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
toggleLoop() {
|
|
|
|
|
const isSingleLoop = !this.data.isSingleLoop;
|
|
|
|
|
if (this.audioCtx) this.audioCtx.loop = isSingleLoop;
|
|
|
|
|
this.setData({ isSingleLoop });
|
|
|
|
|
wx.showToast({ title: isSingleLoop ? "单曲循环" : "列表循环", icon: "none" });
|
|
|
|
|
},
|
2025-06-06 15:17:30 +08:00
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
seekAudio(e) {
|
|
|
|
|
if (!this.audioCtx || !this.audioCtx.duration) return;
|
|
|
|
|
wx.createSelectorQuery().in(this).select("#audio-progress").boundingClientRect((rect) => {
|
|
|
|
|
if (!rect || !rect.width) return;
|
|
|
|
|
const ratio = Math.min(Math.max((e.detail.x - rect.left) / rect.width, 0), 1);
|
|
|
|
|
this.audioCtx.seek(this.audioCtx.duration * ratio);
|
|
|
|
|
}).exec();
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
showList() {
|
|
|
|
|
if (this.closeTimer) {
|
|
|
|
|
clearTimeout(this.closeTimer);
|
|
|
|
|
this.closeTimer = null;
|
|
|
|
|
}
|
|
|
|
|
this.setData({ isShow: true, isClosing: false });
|
|
|
|
|
},
|
2025-06-06 15:17:30 +08:00
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
hideList() {
|
|
|
|
|
if (!this.data.isShow || this.data.isClosing) return;
|
|
|
|
|
this.setData({ isClosing: true });
|
|
|
|
|
this.closeTimer = setTimeout(() => {
|
|
|
|
|
this.setData({ isShow: false, isClosing: false });
|
|
|
|
|
this.closeTimer = null;
|
|
|
|
|
}, 240);
|
2025-06-06 15:17:30 +08:00
|
|
|
},
|
|
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
preventClose() {},
|
2025-06-06 15:17:30 +08:00
|
|
|
|
2026-08-04 09:59:35 +08:00
|
|
|
onUnload() {
|
|
|
|
|
if (this.closeTimer) clearTimeout(this.closeTimer);
|
|
|
|
|
if (this.audioCtx) {
|
|
|
|
|
this.audioCtx.stop();
|
|
|
|
|
if (this.audioCtx.destroy) this.audioCtx.destroy();
|
|
|
|
|
this.audioCtx = null;
|
|
|
|
|
}
|
2025-06-06 15:17:30 +08:00
|
|
|
}
|
2026-08-04 09:59:35 +08:00
|
|
|
});
|