/** * 播放器详情页 * 大封面、进度条、倍速切换、播放控制 * 从 globalData.activeContent 获取当前节目 */ const app = getApp() const api = require('../../utils/api') const util = require('../../utils/util') Page({ data: { domain: {}, activeContent: null, isPlaying: false, isVip: false, isLiked: false, isFavorited: false, currentTime: 0, duration: 0, currentTimeText: '00:00', durationText: '00:00', displayDate: '', playbackRate: 1.0, statusBarHeight: 0, showTranscript: false, // 评论弹层 showComments: false, commentList: [], commentText: '', commentLoading: false, submitting: false, // 分享面板 // 分享面板 showSharePanel: false, // 倍速 ActionSheet showSpeedSheet: false, speedItems: [ { label: '0.75x' }, { label: '1.0x' }, { label: '1.25x' }, { label: '1.5x' }, { label: '2.0x' } ], // 频道节目列表(用于上/下期切换) _channelPrograms: [] }, _isSeeking: false, onLoad() { }, onShow() { this._syncState() // 监听播放状态 this._onPlayerChange = (state) => { if (this._isSeeking) return this.setData({ activeContent: state.activeContent, isPlaying: state.isPlaying, playbackRate: state.playbackRate }) this._updateDomain() } // 监听时间更新 this._onTimeUpdate = (data) => { if (this._isSeeking) return this.setData({ currentTime: data.currentTime, duration: data.duration || this.data.duration, currentTimeText: util.formatTime(data.currentTime), durationText: util.formatTime(data.duration || this.data.duration) }) } app.on('playerStateChange', this._onPlayerChange) app.on('timeUpdate', this._onTimeUpdate) // 查询当前节目点赞状态 this._loadLikeStatus() }, onHide() { if (this._onPlayerChange) app.off('playerStateChange', this._onPlayerChange) if (this._onTimeUpdate) app.off('timeUpdate', this._onTimeUpdate) }, /** * 同步当前状态 */ _syncState() { const gd = app.globalData const content = gd.activeContent if (!content) { wx.navigateBack() return } var dateStr = '' if (content.createdAt) { dateStr = content.createdAt.substring(0, 10).replace(/-/g, '.') } else if (content.date) { dateStr = util.dateToDot(content.date) } this.setData({ activeContent: content, isPlaying: gd.isPlaying, isVip: gd.isVip, currentTime: gd.currentTime, duration: gd.duration || content.duration, currentTimeText: util.formatTime(gd.currentTime), durationText: util.formatTime(gd.duration || content.duration), displayDate: dateStr, playbackRate: gd.playbackRate, statusBarHeight: gd.statusBarHeight || 0, isLiked: !!content.hasLiked, isFavorited: content.HasFavorite === 1 || content.hasFavorite === 1 }) this._updateDomain() this._loadChannelPrograms() }, /** * 获取频道信息 */ _updateDomain() { const content = this.data.activeContent if (!content) return var channelId = content.channelId || (content.channel && content.channel.id) if (!channelId) { if (content.channel) { this.setData({ domain: content.channel }) } return } if (this.data.domain && this.data.domain.id === channelId) return var self = this api.getChannelDetail(channelId).then(function (res) { if (res.code === 200 && res.data) { self.setData({ domain: res.data }) } }).catch(function (err) { console.error('[Player] 获取频道信息失败:', err) }) }, /** * 查询当前节目点赞和收藏状态(刷新最新值) */ _loadLikeStatus() { const content = this.data.activeContent if (!content) return var self = this api.getProgramDetail(content.id).then(function (res) { if (res.code === 200 && res.data) { self.setData({ isLiked: res.data.hasLiked === 1, isFavorited: res.data.HasFavorite === 1 || res.data.hasFavorite === 1 }) } }).catch(function () { }) }, /** * 加载当前频道的节目列表(用于上/下期切换) */ _loadChannelPrograms() { const content = this.data.activeContent if (!content) return var channelId = content.channelId || (content.channel && content.channel.id) if (!channelId) return var self = this api.getProgramList({ channelId: channelId, current: 1, pageSize: 100 }) .then(function (res) { if (res.code === 200 && res.data) { var list = res.data.list || res.data || [] self.setData({ _channelPrograms: list }) } }).catch(function () { }) }, /** * 上一期 */ onPrev() { var programs = this.data._channelPrograms var content = this.data.activeContent if (!content || programs.length === 0) { wx.showToast({ title: '没有更多了', icon: 'none' }) return } var idx = -1 for (var i = 0; i < programs.length; i++) { if (programs[i].id === content.id) { idx = i; break } } if (idx <= 0) { wx.showToast({ title: '已是第一期', icon: 'none' }) return } app.playContent(programs[idx - 1]) }, /** * 下一期 */ onNext() { var programs = this.data._channelPrograms var content = this.data.activeContent if (!content || programs.length === 0) { wx.showToast({ title: '没有更多了', icon: 'none' }) return } var idx = -1 for (var i = 0; i < programs.length; i++) { if (programs[i].id === content.id) { idx = i; break } } if (idx < 0 || idx >= programs.length - 1) { wx.showToast({ title: '已是最新一期', icon: 'none' }) return } app.playContent(programs[idx + 1]) }, /** * 播放/暂停 */ onTogglePlay() { app.togglePlay() }, /** * 进度条拖动中 */ onSliderChanging(e) { this._isSeeking = true this.setData({ currentTime: e.detail.value, currentTimeText: util.formatTime(e.detail.value) }) }, /** * 进度条拖动完成 → 跳转播放 */ onSliderChange(e) { this._isSeeking = false app.seekTo(e.detail.value) }, /** * 快退 15 秒 */ onBackward() { const newTime = Math.max(0, this.data.currentTime - 15) app.seekTo(newTime) }, /** * 快进 15 秒 */ onForward() { const newTime = Math.min(this.data.duration, this.data.currentTime + 15) app.seekTo(newTime) }, /** * 倍速设置(VIP 功能) */ onSpeed() { if (!this.data.isVip) { wx.showModal({ title: '会员提示', content: '倍速播放是会员专属功能,是否前往开通?', success: (res) => { if (res.confirm) { wx.navigateTo({ url: '/pages/vip/index' }) } } }) } else { this.setData({ showSpeedSheet: true }) } }, onSpeedSelect(e) { const label = this.data.speedItems[e.detail.index].label const rate = parseFloat(label) app.setPlaybackRate(rate) this.setData({ showSpeedSheet: false, playbackRate: rate }) }, onSpeedCancel() { this.setData({ showSpeedSheet: false }) }, /** * 下载(VIP 功能) */ onDownload() { if (!this.data.isVip) { wx.showModal({ title: '会员提示', content: '音频下载是会员专属功能,是否前往开通?', success: (res) => { if (res.confirm) { wx.navigateTo({ url: '/pages/vip/index' }) } } }) } else { wx.showToast({ title: '下载功能开发中', icon: 'none' }) } }, /** * 查看文案 */ onTranscript() { const content = this.data.activeContent if (content && content.content) { wx.showModal({ title: '完整文案', content: content.content, showCancel: false }) } }, /** * 点击中央 Banner:封面 ⇔ 文案切换 */ onBannerTap() { this.setData({ showTranscript: !this.data.showTranscript }) }, onLike() { const content = this.data.activeContent if (!content) return const self = this const wasLiked = this.data.isLiked // 乐观更新 this.setData({ isLiked: !wasLiked }) api.toggleLike(content.id).then(function (res) { if (res.code !== 200) { self.setData({ isLiked: wasLiked }) wx.showToast({ title: res.msg || '操作失败', icon: 'none' }) } else { wx.showToast({ title: wasLiked ? '已取消喜欢' : '已喜欢 ♥', icon: 'none' }) } }).catch(function () { self.setData({ isLiked: wasLiked }) wx.showToast({ title: '网络异常', icon: 'none' }) }) }, onFavorite() { const content = this.data.activeContent if (!content) return const self = this const wasFavorited = this.data.isFavorited // 乐观更新 this.setData({ isFavorited: !wasFavorited }) const fn = wasFavorited ? api.removeFavorite(content.id) : api.addFavorite(content.id) fn.then(function (res) { if (res.code !== 200) { self.setData({ isFavorited: wasFavorited }) wx.showToast({ title: res.msg || '操作失败', icon: 'none' }) } else { wx.showToast({ title: wasFavorited ? '已取消收藏' : '已收藏 🔖', icon: 'none' }) } }).catch(function () { self.setData({ isFavorited: wasFavorited }) wx.showToast({ title: '网络异常', icon: 'none' }) }) }, // ===== 评论弹层 ===== onOpenComments() { this.setData({ showComments: true }) this._loadComments() }, onCloseComments() { this.setData({ showComments: false, commentText: '' }) }, _loadComments() { const content = this.data.activeContent if (!content) return const self = this this.setData({ commentLoading: true }) api.getCommentList(content.id, { current: 1, pageSize: 30 }).then(function (res) { if (res.code === 200 && res.data) { var list = (res.data.list || res.data || []).map(function (c) { return Object.assign({}, c, { _isOwn: c.userId === (getApp().globalData.userInfo && getApp().globalData.userInfo.id) }) }) self.setData({ commentList: list, commentLoading: false }) } else { self.setData({ commentLoading: false }) } }).catch(function () { self.setData({ commentLoading: false }) }) }, onCommentInput(e) { this.setData({ commentText: e.detail.value }) }, onSubmitComment() { const text = (this.data.commentText || '').trim() if (!text) return const content = this.data.activeContent if (!content) return const self = this this.setData({ submitting: true }) api.addComment(content.id, text).then(function (res) { self.setData({ submitting: false, commentText: '' }) if (res.code === 200) { wx.showToast({ title: '发布成功', icon: 'none' }) self._loadComments() } else { wx.showToast({ title: res.msg || '发布失败', icon: 'none' }) } }).catch(function () { self.setData({ submitting: false }) wx.showToast({ title: '网络异常', icon: 'none' }) }) }, onDeleteComment(e) { const id = e.currentTarget.dataset.id const self = this wx.showModal({ title: '提示', content: '确认删除该评论吗?', success(res) { if (!res.confirm) return api.deleteComment(id).then(function (r) { if (r.code === 200) { self._loadComments() } else { wx.showToast({ title: '删除失败', icon: 'none' }) } }) } }) }, onShareTap() { this.setData({ showSharePanel: true }) }, onCloseShare() { this.setData({ showSharePanel: false }) }, /** * 朋友圈分享按钮点击 → 引导用户使用右上角胶囊菜单 * 微信小程序限制:无法编程式触发朋友圈分享,只能从胶囊菜单触发 */ onShareMomentTip() { this.setData({ showSharePanel: false }) wx.showModal({ title: '分享到朋友圈', content: '请点击右上角「···」菜单,选择「分享」即可发布到朋友圈', showCancel: false, confirmText: '我知道了' }) }, onShare() { this.onShareTap() }, goBack() { wx.navigateBack() }, // ===== 小程序分享生命周期钩子 ===== /** * 转发给朋友(胶囊菜单内「转发」,或 button open-type=share 触发) */ onShareAppMessage() { const content = this.data.activeContent || {} const domain = this.data.domain || {} this.setData({ showSharePanel: false }) return { title: (domain.name ? '【' + domain.name + '】' : '') + (content.title || '全声汇'), path: '/pages/index/index', imageUrl: '' } }, /** * 分享到朋友圈(定义此函数后,胶囊菜单中「分享」自动出现) */ onShareTimeline() { const content = this.data.activeContent || {} const domain = this.data.domain || {} return { title: (domain.name ? '【' + domain.name + '】' : '') + (content.title || '全声汇'), query: '', imageUrl: '' } } })