feat: 优化UI

This commit is contained in:
Blizzard
2026-03-05 17:04:40 +08:00
parent 0a61c4ddec
commit 7f51b2a0a8
28 changed files with 1773 additions and 964 deletions
+115 -15
View File
@@ -13,6 +13,7 @@ Page({
activeContent: null,
isPlaying: false,
isVip: false,
isLiked: false,
currentTime: 0,
duration: 0,
currentTimeText: '00:00',
@@ -20,16 +21,13 @@ Page({
displayDate: '',
playbackRate: 1.0,
statusBarHeight: 0,
showTranscript: false, // 封面 ⇔ 文案切换
showSpeedSheet: false,
speedItems: [
{ label: '0.5x' },
{ label: '0.75x' },
{ label: '1.0x' },
{ label: '1.25x' },
{ label: '1.5x' },
{ label: '2.0x' }
]
showTranscript: false,
// 评论弹层
showComments: false,
commentList: [],
commentText: '',
commentLoading: false,
submitting: false
},
_isSeeking: false,
@@ -65,6 +63,9 @@ Page({
app.on('playerStateChange', this._onPlayerChange)
app.on('timeUpdate', this._onTimeUpdate)
// 查询当前节目点赞状态
this._loadLikeStatus()
},
onHide() {
@@ -108,7 +109,7 @@ Page({
},
/**
* 获取频道信息 — 从后端 API 获取
* 获取频道信息
*/
_updateDomain() {
const content = this.data.activeContent
@@ -116,14 +117,12 @@ Page({
var channelId = content.channelId || (content.channel && content.channel.id)
if (!channelId) {
// 如果节目数据中直接包含 channel 信息
if (content.channel) {
this.setData({ domain: content.channel })
}
return
}
// 如果已经加载过且 channelId 没变,跳过
if (this.data.domain && this.data.domain.id === channelId) return
var self = this
@@ -136,6 +135,20 @@ Page({
})
},
/**
* 查询当前节目点赞状态
*/
_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.isLiked })
}
}).catch(function () { })
},
/**
* 播放/暂停
*/
@@ -251,13 +264,100 @@ Page({
onLike() {
const content = this.data.activeContent
if (!content) return
api.toggleLike({ contentId: content.id }).then(function (res) {
wx.showToast({ title: res.code === 200 ? '已收藏 ♥' : '操作失败', icon: 'none' })
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' })
})
},
// ===== 评论弹层 =====
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' })
}
})
}
})
},
onShare() {
wx.showToast({ title: '分享功能开发中', icon: 'none' })
},
+65 -14
View File
@@ -2,7 +2,7 @@
<page-meta page-style="overflow:hidden; background:#1A1208;" />
<view class="player-page">
<!-- 状态栏占位(custom 导航模式必须手动留出状态栏高度) -->
<!-- 状态栏占位 -->
<view style="height: {{statusBarHeight}}px; flex-shrink: 0;"></view>
<!-- 环境光背景 -->
@@ -10,7 +10,7 @@
style="background: radial-gradient(ellipse at 50% -10%, {{domain.bgColor || '#FF9D42'}}44 0%, transparent 65%);">
</view>
<!-- 顶部:返回 + 标题 + 分享 -->
<!-- 顶部:返回 + 标题 + 评论 -->
<view class="top-bar">
<view class="top-btn tap-active" bindtap="goBack">
<text class="top-back"></text>
@@ -19,8 +19,8 @@
<text class="top-label">正在播放</text>
<text class="top-title">{{activeContent.title || '加载中...'}}</text>
</view>
<view class="top-btn tap-active" bindtap="onShare">
<text class="top-share"></text>
<view class="top-btn tap-active" bindtap="onOpenComments">
<text class="top-comment-icon">💬</text>
</view>
</view>
@@ -36,7 +36,7 @@
<view class="ripple-ring" style="animation-delay: 1.4s;"></view>
</view>
<!-- 大 emoji,无卡片背景 -->
<!-- 大 emoji -->
<text class="cover-emoji {{isPlaying ? 'emoji-active' : ''}}">📻</text>
<!-- 频道名 -->
@@ -58,11 +58,11 @@
</view>
</view>
<!-- 日期 + like 同行,进度条上方 -->
<!-- 日期 + like 同行 -->
<view class="date-like-row">
<text class="date-text">{{displayDate}}</text>
<view class="like-btn-inline tap-active" bindtap="onLike">
<text class="like-icon">♡</text>
<text class="like-icon {{isLiked ? 'liked' : ''}}">{{isLiked ? '♥' : '♡'}}</text>
</view>
</view>
@@ -107,11 +107,62 @@
</view>
</view>
<!-- 倍速选择面板 -->
<t-action-sheet
visible="{{showSpeedSheet}}"
items="{{speedItems}}"
bind:selected="onSpeedSelect"
bind:cancel="onSpeedCancel"
/>
<!-- 评论弹层 -->
<view class="comment-mask" wx:if="{{showComments}}" bindtap="onCloseComments"></view>
<view class="comment-sheet {{showComments ? 'sheet-up' : ''}}">
<view class="sheet-handle"></view>
<view class="sheet-header">
<text class="sheet-title">评论 ({{commentList.length}})</text>
<view class="sheet-close tap-active" bindtap="onCloseComments">
<text class="sheet-close-icon">✕</text>
</view>
</view>
<!-- 输入区 -->
<view class="comment-input-row">
<input
class="comment-input"
placeholder="说点什么..."
placeholder-style="color:rgba(255,200,120,0.3);"
value="{{commentText}}"
bindinput="onCommentInput"
confirm-type="send"
bindconfirm="onSubmitComment"
maxlength="200"
/>
<view class="send-btn tap-active {{submitting ? 'sending' : ''}}" bindtap="onSubmitComment">
<text class="send-text">发送</text>
</view>
</view>
<!-- 评论列表 -->
<scroll-view scroll-y class="comment-list-scroll" enhanced show-scrollbar="{{false}}">
<view wx:if="{{commentLoading}}" class="comment-loading">
<text class="comment-loading-text">加载中...</text>
</view>
<view wx:elif="{{commentList.length === 0}}" class="comment-empty">
<text class="comment-empty-icon">💬</text>
<text class="comment-empty-text">还没有评论,来说第一句话</text>
</view>
<view
wx:for="{{commentList}}"
wx:key="id"
class="comment-item"
>
<view class="comment-avatar">
<text class="comment-avatar-text">{{item.userName ? item.userName[0] : '?'}}</text>
</view>
<view class="comment-body">
<text class="comment-author">{{item.userName || '匿名'}}</text>
<text class="comment-text">{{item.content}}</text>
<text class="comment-time">{{item.createdAtStr || item.createdAt}}</text>
</view>
<view wx:if="{{item._isOwn}}" class="comment-del tap-active" bindtap="onDeleteComment" data-id="{{item.id}}">
<text class="comment-del-icon">🗑</text>
</view>
</view>
<view style="height: 40rpx;"></view>
</scroll-view>
</view>
</view>
+147
View File
@@ -320,3 +320,150 @@
background: #FFF8EE;
border-radius: 5rpx;
}
/* ── like 状态 ── */
.like-icon.liked {
color: #FF4D6D;
}
.top-comment-icon {
font-size: 38rpx;
line-height: 1;
}
/* ── 评论弹层 ── */
.comment-mask {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 200;
}
.comment-sheet {
position: fixed;
bottom: -100%;
left: 0;
right: 0;
height: 70vh;
background: #231808;
border-radius: 40rpx 40rpx 0 0;
z-index: 201;
display: flex;
flex-direction: column;
padding: 0 0 env(safe-area-inset-bottom);
transition: bottom 0.3s cubic-bezier(0.32,0.72,0,1);
}
.comment-sheet.sheet-up {
bottom: 0;
}
.sheet-handle {
width: 72rpx;
height: 6rpx;
background: rgba(255,200,120,0.2);
border-radius: 3rpx;
margin: 20rpx auto 0;
}
.sheet-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 40rpx;
border-bottom: 1rpx solid rgba(255,200,120,0.08);
}
.sheet-title {
font-size: 30rpx;
font-weight: 700;
color: rgba(255,240,210,0.9);
}
.sheet-close-icon {
font-size: 32rpx;
color: rgba(255,200,120,0.4);
}
.comment-input-row {
display: flex;
align-items: center;
gap: 16rpx;
padding: 20rpx 32rpx;
border-bottom: 1rpx solid rgba(255,200,120,0.08);
}
.comment-input {
flex: 1;
background: rgba(255,200,120,0.07);
border-radius: 40rpx;
padding: 16rpx 28rpx;
font-size: 28rpx;
color: rgba(255,240,210,0.9);
min-height: 0;
}
.send-btn {
background: #FF9D42;
border-radius: 32rpx;
padding: 14rpx 28rpx;
}
.send-btn.sending { opacity: 0.5; }
.send-text {
font-size: 26rpx;
font-weight: 700;
color: #FFF;
}
.comment-list-scroll {
flex: 1;
padding: 8rpx 0;
}
.comment-loading, .comment-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 0;
gap: 16rpx;
}
.comment-loading-text, .comment-empty-text {
font-size: 26rpx;
color: rgba(255,200,120,0.3);
}
.comment-empty-icon { font-size: 60rpx; }
.comment-item {
display: flex;
align-items: flex-start;
gap: 20rpx;
padding: 24rpx 32rpx;
border-bottom: 1rpx solid rgba(255,200,120,0.05);
}
.comment-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: rgba(255,157,66,0.25);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.comment-avatar-text {
font-size: 28rpx;
color: #FF9D42;
font-weight: 700;
}
.comment-body {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.comment-author {
font-size: 24rpx;
font-weight: 600;
color: rgba(255,200,120,0.6);
}
.comment-text {
font-size: 28rpx;
color: rgba(255,240,210,0.85);
line-height: 1.6;
}
.comment-time {
font-size: 22rpx;
color: rgba(255,200,120,0.3);
}
.comment-del {
padding: 8rpx;
}
.comment-del-icon { font-size: 32rpx; }