Files
hejiu/pages/circle-detail/circle-detail.vue
T
cg d335ec290a refactor(app): 重构应用主题和页面结构
- 移除动态主题切换功能,改为全局统一深色主题
- 实现自定义TabBar组件替换原生tabBar,解决iOS闪白问题
- 创建main容器页面统一管理三个tab页面的生命周期和状态
- 将所有页面的onShow/onHide等生命周期方法迁移到子组件内部
- 更新页面路径从index改为main,并调整路由跳转逻辑
- 移除theme-mixin和相关主题工具函数
- 统一页面背景色设置,优化用户体验一致性
2026-09-23 20:56:14 +08:00

354 lines
9.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-container detail-page">
<!-- 顶部导航 -->
<view class="detail-nav" :style="{ paddingTop: navPaddingTop }">
<view class="detail-nav-inner">
<view class="detail-back" @click="goBack">
<text class="detail-back-icon">‹</text>
</view>
<text class="detail-nav-title">动态详情</text>
<view class="detail-nav-ph"></view>
</view>
</view>
<!-- 动态内容 -->
<scroll-view class="detail-scroll" scroll-y>
<view class="detail-body">
<FeedCard
v-if="feed"
:feed="feed"
:can-delete="isMyFeed(feed)"
@like="handleLike"
@share="handleShare"
@delete="confirmDeleteFeed"
/>
<!-- 评论区 -->
<view class="comments-section">
<view class="comments-header">
<text class="comments-title">评论</text>
<text class="comments-count">{{ comments.length }}</text>
</view>
<CommentItem
v-for="c in comments"
:key="c.id"
:comment="c"
/>
<EmptyState
v-if="!comments.length"
icon="💬"
title="暂无评论"
desc="来说点什么吧"
/>
</view>
</view>
</scroll-view>
<!-- 底部评论输入 -->
<view class="comment-bar">
<input
class="comment-input"
v-model="commentText"
placeholder="写评论..."
placeholder-class="comment-placeholder"
confirm-type="send"
@confirm="submitComment"
/>
<view class="comment-send" :class="{ disabled: !commentText.trim() }" @click="submitComment">
<text class="comment-send-text">发送</text>
</view>
</view>
</view>
</template>
<script>
import FeedCard from '../../components/FeedCard.vue'
import CommentItem from '../../components/CommentItem.vue'
import EmptyState from '../../components/EmptyState.vue'
import { GetFeedComments, AddComment, LikeFeed, UnlikeFeed, DeleteFeed, requireLogin } from '../../common/api'
export default {
components: { FeedCard, CommentItem, EmptyState },
data() {
const menuBtn = uni.getMenuButtonBoundingClientRect()
return {
navPaddingTop: menuBtn.top + 'px',
feedId: '',
feed: null,
comments: [],
commentText: '',
// 当前分享的动态
shareFeed: null,
// 分享落地页携带的动态快照参数(好友打开分享链接时兼容展示)
shareOptions: {}
}
},
onLoad(options) {
this.feedId = options.id || ''
this.shareOptions = options || {}
this.loadFeed()
this.loadComments()
},
methods: {
goBack() {
// 分享落地场景:本页是页面栈中唯一页面(好友直接从分享卡片进入,未经过首页),
// navigateBack 无页面可退,此时直接回首页
if (getCurrentPages().length > 1) {
uni.navigateBack({
fail: () => {
uni.reLaunch({ url: '/pages/main/main?tab=0' })
}
})
} else {
uni.reLaunch({ url: '/pages/main/main?tab=0' })
}
},
async loadFeed() {
// 优先从 globalData 中查找对应动态
const app = getApp()
const feeds = (app.globalData && app.globalData.circleFeeds) || []
const found = feeds.find(f => String(f.id) === String(this.feedId)) || null
if (found) {
this.feed = found
return
}
// 分享落地页兼容:本地无缓存时用分享链接携带的快照参数展示
const o = this.shareOptions
if (o.nick || o.text) {
this.feed = {
id: this.feedId,
nickname: decodeURIComponent(o.nick || ''),
text: decodeURIComponent(o.text || ''),
time: decodeURIComponent(o.time || ''),
avatar: '',
likes: 0,
comments: 0
}
}
},
async loadComments() {
try {
const res = await GetFeedComments({ feedId: this.feedId })
this.comments = res.data.list
} catch (e) {
console.warn('加载评论失败', e)
}
},
async handleLike(feed) {
if (!requireLogin()) return
feed.liked = !feed.liked
feed.likes += feed.liked ? 1 : -1
try {
if (feed.liked) await LikeFeed({ feedId: feed.id })
else await UnlikeFeed({ feedId: feed.id })
} catch (e) { /* ignore */ }
},
/** 记录当前要分享的动态,供 onShareAppMessage 读取 */
handleShare(feed) {
this.shareFeed = feed
},
/** 判断是否为自己发布的动态(仅自己可删) */
isMyFeed(feed) {
let myId = ''
try {
const user = JSON.parse(uni.getStorageSync('user_info') || '{}')
myId = user.id !== undefined && user.id !== null ? String(user.id) : ''
} catch (e) { /* ignore */ }
if (!myId) return false
return feed && feed.userId !== undefined && feed.userId !== null && String(feed.userId) === myId
},
/** 删除自己的动态(二次确认),成功后返回列表 */
confirmDeleteFeed(feed) {
uni.showModal({
title: '删除动态',
content: '确定要删除这条动态吗?删除后不可恢复',
confirmText: '删除',
confirmColor: '#FF6B6B',
success: async (res) => {
if (!res.confirm) return
try {
await DeleteFeed({ feedId: feed.id })
uni.showToast({ title: '已删除', icon: 'none' })
setTimeout(() => this.goBack(), 600)
} catch (e) {
// httpRequest 已全局弹错误提示(如非本人动态后端会拒绝)
}
}
})
},
async submitComment() {
const text = this.commentText.trim()
if (!text) return
if (!requireLogin()) return
try {
await AddComment({ feedId: this.feedId, content: text })
this.comments.push({
id: 'c_' + Date.now(),
userId: 'me',
nickname: '我',
avatar: '',
content: text,
time: '刚刚'
})
this.commentText = ''
if (this.feed) this.feed.comments = (this.feed.comments || 0) + 1
} catch (e) {
uni.showToast({ title: '发送失败', icon: 'none' })
}
}
},
onShareAppMessage() {
const feed = this.shareFeed || this.feed
if (feed && feed.id) {
const text = (feed.text || '').replace(/\s+/g, ' ').trim()
const title = text
? `${feed.nickname || '酒友'}:${text.length > 24 ? text.slice(0, 24) + '…' : text}`
: `${feed.nickname || '酒友'}的动态 - 碰盏日记`
return {
title,
path: `/pages/circle-detail/circle-detail?id=${feed.id}&nick=${encodeURIComponent(feed.nickname || '')}&text=${encodeURIComponent(text.slice(0, 60))}&time=${encodeURIComponent(feed.time || '')}`
}
}
return {
title: '酒友圈 - 碰盏日记',
path: '/pages/main/main?tab=0'
}
}
}
</script>
<style lang="scss" scoped>
.detail-page {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.detail-nav {
background: $bg-base;
padding-left: $sp-lg;
padding-right: $sp-lg;
position: relative;
z-index: 10;
}
.detail-nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
}
.detail-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: $bg-card;
&:active {
background: $bg-card-alt;
}
}
.detail-back-icon {
font-size: $fs-xl;
color: $text-primary;
font-weight: $fw-bold;
}
.detail-nav-title {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.detail-nav-ph {
width: 64rpx;
}
.detail-scroll {
flex: 1;
height: 0;
}
.detail-body {
padding: $sp-md $sp-lg;
padding-bottom: $sp-xl;
}
.comments-section {
margin-top: $sp-lg;
background: $bg-card;
border-radius: $radius-xl;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
padding: $sp-xl;
}
.comments-header {
display: flex;
align-items: center;
gap: $sp-sm;
margin-bottom: $sp-md;
}
.comments-title {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.comments-count {
font-size: $fs-sm;
color: $text-tertiary;
}
/* 底部评论栏 */
.comment-bar {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-md $sp-lg;
padding-bottom: calc(env(safe-area-inset-bottom) + #{$sp-md});
background: $bg-card;
border-top: 1rpx solid var(--divider-color, rgba(255,255,255,0.08));
}
.comment-input {
flex: 1;
height: 72rpx;
padding: 0 $sp-lg;
background: $bg-elevated;
border-radius: $radius-full;
font-size: $fs-base;
color: $text-primary;
}
.comment-placeholder {
color: $text-tertiary;
}
.comment-send {
padding: $sp-sm $sp-lg;
background: linear-gradient(135deg, $amber, $amber-deep);
border-radius: $radius-full;
&.disabled {
opacity: 0.4;
}
&:active {
transform: scale(0.95);
}
}
.comment-send-text {
font-size: $fs-sm;
font-weight: $fw-bold;
color: $text-on-amber;
}
</style>