first commit
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 首页 — 订阅频道 + 免费频道
|
||||
*
|
||||
* 数据来源:
|
||||
* - 订阅列表: 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)]
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user