Files
sundynix-radio-mp/pages/index/index.js
T
2026-03-05 09:08:21 +08:00

322 lines
10 KiB
JavaScript

/**
* 首页 — 订阅频道 + 免费频道
*
* 数据来源:
* - 订阅列表: POST /radio/subscription/list
* - 免费频道: POST /radio/channel/freeList
*
* 订阅返回结构: { data: { list: [{ id, channel: { id, name, cover, Programs[] } }] } }
* 免费频道结构: { data: { list: [{ id, name, cover, Programs[] }] } } (待确认类似)
*/
const app = getApp()
const api = require('../../utils/api')
const util = require('../../utils/util')
Page({
data: {
greetingSub: '',
locationName: '',
weather: null,
dateDisplay: '',
weekDay: '',
subscribedData: [],
freeChannels: [],
isPlaying: false,
isVip: false,
statusBarHeight: 0,
loadingSub: true,
loadingFree: true
},
onShow() {
const gd = app.globalData
this.setData({
greetingSub: this._getGreeting(),
dateDisplay: util.getDateDisplay(),
weekDay: util.getWeekDay(),
locationName: gd.locationName || '',
weather: gd.weather || null,
isVip: gd.isVip || false,
statusBarHeight: gd.statusBarHeight || 0
})
this._loadAll()
this._bindEvents()
},
onHide() {
this._unbindEvents()
},
// ===================== 数据加载 =====================
/** 并行加载订阅列表 + 免费频道 */
_loadAll() {
this._refreshSubscriptions()
this._loadFreeChannels()
},
/**
* 拉取已订阅频道列表
* 首次无数据时显示骨架屏,后续刷新静默替换(无闪烁)
*/
_refreshSubscriptions() {
const self = this
const gd = app.globalData
const isFirstLoad = self.data.subscribedData.length === 0
// 只有首次加载才显示骨架屏
if (isFirstLoad) {
self.setData({ loadingSub: true })
}
api.getSubscriptionList({ current: 1, pageSize: 50 })
.then(function (res) {
if (res.code !== 200 || !res.data) {
self.setData({ subscribedData: [], loadingSub: false })
return
}
const subList = res.data.list || []
const subscribedData = subList.map(function (subItem) {
return self._mapChannel(subItem.channel || {}, gd)
})
self.setData({
subscribedData: subscribedData,
isPlaying: gd.isPlaying || false,
loadingSub: false
})
})
.catch(function (err) {
console.error('[首页] 订阅列表失败', err)
self.setData({ loadingSub: false })
})
},
/**
* 拉取免费频道列表
* 首次无数据时显示骨架屏,后续静默刷新
*/
_loadFreeChannels() {
const self = this
const isFirstLoad = self.data.freeChannels.length === 0
if (isFirstLoad) {
self.setData({ loadingFree: true })
}
api.getFreeChannelList({ current: 1, pageSize: 20 })
.then(function (res) {
if (res.code !== 200 || !res.data) {
self.setData({ freeChannels: [], loadingFree: false })
return
}
const list = res.data.list || []
const freeChannels = list.map(function (ch) {
return {
id: ch.id,
name: ch.name || '未命名',
// cover 直接是 emoji 字符串
cover: ch.cover || '📻',
bgColor: self._genColor(ch.id),
programCount: (ch.Programs || ch.programs || []).length,
isFree: ch.isFree
}
})
self.setData({ freeChannels: freeChannels, loadingFree: false })
})
.catch(function (err) {
console.error('[首页] 免费频道失败', err)
self.setData({ loadingFree: false })
})
},
/**
* 将频道对象映射为 UI 数据结构(订阅区通用)
*/
_mapChannel(channel, gd) {
const programs = channel.programs || []
const latest = programs.length > 0 ? programs[0] : null
// cover 直接是 emoji 字符串
const cover = channel.cover || '📻'
const todayContent = latest ? Object.assign({}, latest, {
durationText: util.formatTime(latest.duration || 0)
}) : null
const isThisPlaying = !!(todayContent &&
gd.activeContent &&
gd.activeContent.id === todayContent.id)
return {
id: channel.id,
name: channel.name || '未命名频道',
description: channel.description || '',
isFree: channel.isFree,
isVipOnly: channel.isVipOnly,
cover: cover,
bgColor: this._genColor(channel.id),
_todayContent: todayContent,
_isThisPlaying: isThisPlaying
}
},
/**
* 根据 id 生成稳定的暖色系颜色
*/
_genColor(id) {
const palette = [
'#FF9D42', '#FFB366', '#FF8C69', '#FFA07A',
'#E8956D', '#D4845A', '#F4A460', '#CD853F'
]
if (!id) return palette[0]
var hash = 0
for (var i = 0; i < id.length; i++) {
hash = (hash + id.charCodeAt(i)) % palette.length
}
return palette[hash]
},
// ===================== 事件监听 =====================
_bindEvents() {
const self = this
// 播放状态变化
this._onPlayerChange = function () {
const gd = app.globalData
const data = self.data.subscribedData.map(function (channel) {
const latest = channel._todayContent
return Object.assign({}, channel, {
_isThisPlaying: !!(latest &&
gd.activeContent &&
gd.activeContent.id === latest.id)
})
})
self.setData({ subscribedData: data, isPlaying: gd.isPlaying || false })
}
// 订阅变化
this._onSubChange = function () {
self._loadAll()
}
// 冷启动位置/天气数据就绪(异步,可能比页面加载晚)
this._onLocationWeather = function (data) {
self.setData({
locationName: data.locationName || '',
weather: data.weather || null
})
}
app.on('playerStateChange', this._onPlayerChange)
app.on('subscriptionChange', this._onSubChange)
app.on('locationWeatherReady', this._onLocationWeather)
},
_unbindEvents() {
if (this._onPlayerChange) app.off('playerStateChange', this._onPlayerChange)
if (this._onSubChange) app.off('subscriptionChange', this._onSubChange)
if (this._onLocationWeather) app.off('locationWeatherReady', this._onLocationWeather)
},
// ===================== 用户操作 =====================
/** 点击播放/暂停 */
onPlayContent(e) {
const contentId = e.currentTarget.dataset.contentId
const gd = app.globalData
var content = null
for (var i = 0; i < this.data.subscribedData.length; i++) {
var c = this.data.subscribedData[i]._todayContent
if (c && c.id === contentId) { content = c; break }
}
if (!content) return
if (gd.activeContent && gd.activeContent.id === contentId) {
app.togglePlay()
} else {
app.playContent(content)
}
},
/** 跳转发现广场 */
goDiscover() {
wx.switchTab({ url: '/pages/discover/index' })
},
/** 跳转 VIP 页 */
goVip() {
wx.navigateTo({ url: '/pages/vip/index' })
},
/** 跳转频道详情(订阅或免费) */
goChannel(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({ url: '/pages/channel-detail/index?id=' + id })
},
// ===================== 工具方法 =====================
/** 根据时间段返回问候语 */
_getGreeting() {
const hour = new Date().getHours()
var pool
if (hour >= 5 && hour < 9) {
pool = [
'新的一天,从声音开始',
'早起的人,先听一段',
'清晨的第一居声,属于你',
'晚起不如早起,早起不如听起'
]
} else if (hour >= 9 && hour < 12) {
pool = [
'上午充能量,声音加持',
'专注工作,也别忘了呼吸',
'高效早上,入耳知识',
'好状态,从一段内容开始'
]
} else if (hour >= 12 && hour < 14) {
pool = [
'午后小憩,听点轻松的',
'借耳机隔绝喧嚣,中午也有自己的时间',
'饭后十分钟,充电一下',
'慢下来,下午还长'
]
} else if (hour >= 14 && hour < 17) {
pool = [
'下午了,来点声音撤个困',
'下午三点,刚好开始听一段',
'下午的阳光和一段好内容',
'止止广告,喝口内容'
]
} else if (hour >= 17 && hour < 19) {
pool = [
'日落时分,放慢脚步',
'下班了,耳机里换一个频道',
'偶尔不刷短视频,试试听点实的',
'归途中最适合听一段'
]
} else if (hour >= 19 && hour < 22) {
pool = [
'夜晚温柔,适合倾听',
'夜晚的声音,不用赶时间',
'放下手机,喂口内容',
'夹着夜色,听一段值得的'
]
} else {
pool = [
'夜深了,听点帮助入眠的',
'出发前最后一段,晚安',
'夜奥的小时光,留给自己',
'好梦将至,晚安'
]
}
return pool[Math.floor(Math.random() * pool.length)]
}
})