Files
hejiu/pages/circle-detail/circle-detail.vue
T
cg 57eef74eb1 feat(sdk): 升级HaveADrink SDK并集成邀请系统
- 将HaveADrink依赖从1.0.8升级至1.0.12版本
- 集成邀请码功能,新增common/invite.js处理邀请链路
- 实现发送好友请求返回邀请码的新流程
- 添加删除动态功能,新增DeleteFeed接口
- 集成UniAppWebSocket适配器,重构WebSocket连接管理
- 优化好友请求支持关键词搜索功能
- 在FeedCard组件中添加删除按钮和分享按钮
- 更新SDK类型定义文件以匹配新接口规范
2026-08-04 23:32:45 +08:00

344 lines
8.7 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="themeClass" 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 } from '../../common/api'
import themeMixin from '../../common/theme-mixin'
export default {
mixins: [themeMixin],
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() {
uni.navigateBack()
},
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) {
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(() => uni.navigateBack(), 600)
} catch (e) {
// httpRequest 已全局弹错误提示(如非本人动态后端会拒绝)
}
}
})
},
async submitComment() {
const text = this.commentText.trim()
if (!text) 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/index/index'
}
}
}
</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>