roamfuding-xcx/page_fenbao/guangchang/dongtaixq.vue

1264 lines
41 KiB
Vue
Raw Normal View History

2025-11-08 11:21:57 +08:00
<template>
<view class="page">
<u-navbar title="动态详情" :border-bottom="false" :background="bgc" back-icon-color="#262B37" title-color='#262B37'
title-size='36' height='36' id="navbar" :custom-back="btnfh">
</u-navbar>
2025-12-20 14:32:28 +08:00
<scroll-view v-if="dongtaiflag == false" class="list" @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black" :style="listHeightPx ? ('height:' + listHeightPx + 'px') : ''">
2025-11-08 11:21:57 +08:00
<view class="top">
<view class="info">
2025-12-20 14:32:28 +08:00
<image :src="dtobj.userAvatar" mode="aspectFill"></image>
2025-11-08 11:21:57 +08:00
<view class="xx">
<view class="name">
{{dtobj.nickName == null ? '--' : dtobj.nickName}}
</view>
<view class="riqi">
{{dtobj.createTime == null ? '--' : dtobj.createTime}}
</view>
</view>
</view>
<view class="guanzhu" v-if="userId != dtobj.userId">
<view @click="btngzdel" class="yiguanzhu" v-if="dtobj.isFollowed" >
已关注
</view>
<view @click="btngzadd" class="weiguanzhu" v-else>
+ 关注
</view>
</view>
<view class="guanzhu" @click="btndel" v-else>
<image src="https://api.ccttiot.com/smartmeter/img/static/upijMXHu57BobzwGJMso" style="width: 60rpx;height: 60rpx;" mode=""></image>
</view>
</view>
2025-12-20 14:32:28 +08:00
<view class="card-content" v-if="dtobj.picture && dtobj.picture.length > 0">
<view class="thumb-single" v-for="(item, idx) in dtobj.picture.split(',')" :key="idx" @click="handleImageClick(idx)">
<image v-if="!isVideoItem(item)" :src="item" mode="aspectFill" />
<video v-else :src="item" :controls="true"></video>
</view>
2025-11-08 11:21:57 +08:00
</view>
2025-12-20 14:32:28 +08:00
<text style="display: block;" class="contwz">
2025-11-08 11:21:57 +08:00
{{dtobj.content == null ? '...' : dtobj.content}}
2025-12-20 14:32:28 +08:00
</text>
2025-11-08 11:21:57 +08:00
<view class="dizhi" @click="btndh">
<image class="qian" src="https://api.ccttiot.com/smartmeter/img/static/ugQMH5UxepQ6r2VfKtlP" mode=""></image> {{ dtobj.location || '暂无位置' }} <image class="hou" src="https://api.ccttiot.com/smartmeter/img/static/uOtVmaBGci0kew9b0EpI" mode=""></image>
</view>
<!-- 评论 -->
<view class="comment-section">
<view class="c-title">评论</view>
<view class="c-item" v-for="(c, ci) in comments" :key="c.id">
<view class="c-hd">
<image class="avatar" :src="c.avatar" mode="aspectFill"></image>
<view class="user">
<view class="name-row">
<text class="name">{{ c.name }}</text>
<text v-if="c.isAuthor" class="tag">作者</text>
</view>
<view class="content">{{ c.content }}</view>
<view class="meta">
<text class="time">{{ c.time }}</text>
<text class="reply" @click="onReply(c)">回复</text>
</view>
</view>
<view class="like">
<image style="width: 30rpx;height: 30rpx;" @click="pldianzan(c,ci)" :src="c.liked ? 'https://api.ccttiot.com/smartmeter/img/static/uCxUJSEQTStvqaf93xox' : 'https://api.ccttiot.com/smartmeter/img/static/ubfJKqgy9ckXL1oxmWHq'" mode=""></image>
<text class="num">{{ c.likes }}</text>
</view>
</view>
<!-- 子回复 -->
<view class="replies" v-if="c.replies && c.replies.length">
<block v-if="!c.expand">
<text class="expand" @click="toggleExpand(ci)">展开{{ c.replies.length }}条回复</text>
</block>
<block v-else>
<view class="r-item" v-for="(r, ri) in c.replies" :key="r.id">
<image class="avatar small" :src="r.avatar" mode="aspectFill"></image>
<view class="r-body">
<view class="name-row">
<text class="name">{{ r.name }}</text>
<text v-if="r.isAuthor" class="tag">作者</text>
</view>
<view class="content"><text style="font-size: 24rpx;color: #4292c1;margin-right: 10rpx;">@{{r.parentNickName}}</text> {{ r.content }}</view>
<view class="meta">
<text class="time">{{ r.time }}</text>
<text class="reply" @click="onReply(r, c)">回复</text>
</view>
</view>
<view class="like">
<image style="width: 30rpx;height: 30rpx;" @click="pldianzantwo(r,ci, ri)" :src="r.liked ? 'https://api.ccttiot.com/smartmeter/img/static/uCxUJSEQTStvqaf93xox' : 'https://api.ccttiot.com/smartmeter/img/static/ubfJKqgy9ckXL1oxmWHq'" mode=""></image>
<text class="num">{{ r.likes }}</text>
</view>
</view>
<text class="collapse" @click="toggleExpand(ci)">收起回复</text>
</block>
</view>
</view>
</view>
<view class="" style="width: 100%;margin-top: 30rpx;text-align: center;color: #ccc;">
暂时没有更多评论咯...
</view>
</scroll-view>
<!-- 回复输入栏常驻 -->
2025-12-20 14:32:28 +08:00
<view v-if="dongtaiflag == false" class="reply-bar" id="replyBar" :style="keyboardHeight ? ('transform: translateY(' + (-keyboardHeight) + 'px)') : ''">
2025-11-08 11:21:57 +08:00
<view class="reply-inner">
<view class="reply-box">
<text class="icon-pencil"></text>
<input
class="reply-input"
v-model="replyText"
:placeholder="replyPlaceholder"
maxlength="200"
confirm-type="send"
@confirm="sendReply"
:focus="replyFocus"
@blur="onInputBlur"
:adjust-position="false"
cursor-spacing="10"
/>
</view>
<view class="reply-actions">
<view class="action">
<image class="dz" src="https://api.ccttiot.com/smartmeter/img/static/ubfJKqgy9ckXL1oxmWHq" @click="toggleStar" mode="" v-if="!dtobj.isLiked"></image>
<image class="dz" src="https://api.ccttiot.com/smartmeter/img/static/uCxUJSEQTStvqaf93xox" @click="toggleStardel" mode="" v-else></image>
<text class="num">{{ dtobj.likes }}</text>
</view>
<view class="action">
<image class="dz" src="https://api.ccttiot.com/smartmeter/img/static/ux8FkyzvEgehlxvwN49V" @click="btndianzan" mode="" v-if="!dtobj.isCollected"></image>
<image class="dz" src="https://api.ccttiot.com/smartmeter/img/static/uVV3pzN4IPMAleZ2yRVw" @click="btndianzandel" mode="" v-else></image>
<text class="num">{{ dtobj.collections }}</text>
</view>
<view class="action" @click="doShare">
<image class="fx" src="https://api.ccttiot.com/smartmeter/img/static/uoHMIOnyYlNJCJcz9eLF" mode=""></image>
<text class="num">{{ dtobj.forwards }}</text>
</view>
</view>
</view>
</view>
2025-12-20 14:32:28 +08:00
<image v-if="dongtaiflag == true" style="width: 100%;height: 100vh;position: fixed;top: 160rpx;left: 0;z-index: 999;" src="https://api.ccttiot.com/mgV9maxWGSQb6aa145cf863980513eecdf75080f75a0.png" mode=""></image>
2025-11-08 11:21:57 +08:00
<!-- 分享弹窗 -->
<view v-if="showSharePopup" class="share-mask" @click="closeShare"></view>
<view v-if="showSharePopup" class="share-popup">
<view class="share-title">分享至</view>
<!-- 微信小程序用原生 share 按钮唤起分享面板已开启朋友圈 -->
<!-- #ifdef MP-WEIXIN -->
<button class="share-btn" open-type="share" @click.stop="btnfx">微信好友</button>
<!-- #endif -->
<!-- APP/H5使用 uni.share 指定场景 -->
<!-- #ifdef APP-PLUS -->
<view class="share-btn" @click.stop="shareApp('session')">微信好友</view>
<view class="share-btn" @click.stop="shareApp('timeline')">朋友圈</view>
<!-- #endif -->
<view class="share-cancel" @click="closeShare">取消</view>
</view>
2025-12-20 14:32:28 +08:00
<view class="denglu" v-if="dengliflag">
<view class="imgbox">
<image src="https://api.ccttiot.com/smartmeter/img/static/uMGDxwcAsNPLHOaZ2eG9" mode=""></image>
</view>
<button class="button" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
授权登录
</button>
</view>
2025-11-08 11:21:57 +08:00
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#fff",
},
flag:false,
currentIndex: 0,
list: [],
comments: [],
showReplyBar: true,
replyText: '',
replyFocus: false,
replyTarget: null,
replyParent: null,
replyPlaceholder: '说点什么...',
starActive: false,
starCount: 20,
shareCount: 20,
dtid:'',
dtobj:{},
pageNum:1,
parentId:'',
rootId:'',
isRefreshing:false,
total:0,
pageSize:20,
hasMore:true,
showSharePopup:false,
share:'',
userId:'',
listHeightPx: 0,
2025-12-20 14:32:28 +08:00
keyboardHeight: 0,
dengliflag:false,
dongtaiflag:false
2025-11-08 11:21:57 +08:00
}
},
onLoad(option) {
this.dtid = option.id
this.getxq()
if(option.from){
this.share = option.from
}
console.log(option);
// 启用微信分享菜单,展示好友和朋友圈
// #ifdef MP-WEIXIN
wx.showShareMenu({withShareTicket:true, menus:['shareAppMessage','shareTimeline']})
// #endif
},
onShow() {
this.userId = uni.getStorageSync('userId')
// 进入页面或从后台回到前台时,重新计算一次高度
this.$nextTick(()=>{ this.calcListHeight() })
},
onReady() {
this.$nextTick(() => {
this.syncVideoPlayState()
this.calcListHeight()
})
},
onUnload(){
// #ifdef MP-WEIXIN
if(this.__kbListener){ this.__kbListener = null }
// #endif
},
methods: {
2025-12-20 14:32:28 +08:00
// 点击输入框进行是否登录请求
btnshenhe(){
this.$u.get("getInfo").then((res) => {
if(res.code == 200){
this.dengliflag = false
}else if(res.code == 401){
this.dengliflag = true
}
})
},
// 微信自带点击获取微信手机号登录
getPhoneNumber(e) {
let that = this;
console.log("eeeeeeee", e)
const wxLoginAsync = () => {
return new Promise((resolve, reject) => {
wx.login({
success(res) {
if (res.code) {
console.log('登录!', res)
let data = {
loginCode: res.code,
mobileCode: e.detail.code,
appId:that.$store.state.appid
};
resolve(data)
} else {
reject(res.errMsg)
}
},
fail(err) {
reject(err)
}
})
})
}
wxLoginAsync().then(async (data) => {
this.$u.post("/wxLogin",data).then((res) => {
if (res.code == 200) {
console.log(res,'resres')
wx.setStorageSync('token', res.token)
that.ceshi()
}
})
})
},
// 判断是否登陆成功进行跳转到首页
async ceshi() {
this.$u.get("getInfo").then((res) => {
console.log('进入跳转')
if(res.code == 200){
if(res.user.infoUpdated == true){
this.dengliflag = false
}else{
uni.navigateTo({
url:'/pages/login/logouser?type=1'
})
setTimeout(()=>{
this.dengliflag = false
},1500)
}
}else{
uni.showToast({ title: '清除失败', icon: 'none',duration:3000 })
}
})
},
2025-11-08 11:21:57 +08:00
// 点击删除
btndel(){
let that = this
uni.showModal({
title: '提示',
content: '您确定要删除当前动态吗?',
success: function(res) {
if (res.confirm) {
that.$u.delete(`/app/feed/delete/${that.dtid}`).then(res => {
if (res.code == 200) {
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
// uni.reLaunch({
// url:'/pages/login/login'
// })
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
} else if (res.cancel) {}
}
})
},
calcListHeight(){
// 获取窗口高度并减去顶部导航和底部回复栏的高度,锁定列表高度,防止键盘弹出时页面位移
// #ifdef MP-WEIXIN
const sys = uni.getSystemInfoSync()
const windowH = sys.windowHeight || 0
// 测量 navbar 与 replyBar
this.$nextTick(()=>{
const q = uni.createSelectorQuery().in(this)
q.select('#navbar').boundingClientRect()
q.select('#replyBar').boundingClientRect()
q.exec(res=>{
const navH = (res && res[0] && res[0].height) ? res[0].height : 0
const replyH = (res && res[1] && res[1].height) ? res[1].height : 0
const h = Math.max(0, Math.floor(windowH - navH - replyH))
this.listHeightPx = h
})
})
// 监听键盘高度变更,抬升底部栏而不改变页面布局
if (!this.__kbListener && uni.onKeyboardHeightChange) {
this.__kbListener = uni.onKeyboardHeightChange((res)=>{
this.keyboardHeight = res.height || 0
})
}
// #endif
},
// 点击跳转导航目的地
btndh(){
// 检查坐标数据是否存在
if (!this.dtobj.latitude || !this.dtobj.location) {
uni.showToast({
title: '当前暂无位置',
icon: 'none',
duration:3000
})
return
}
// 先申请位置权限
uni.getSetting({
success: (res) => {
if (res.authSetting['scope.userLocation'] === false) {
// 用户拒绝了位置权限,引导用户开启
uni.showModal({
title: '位置权限',
content: '需要获取您的位置信息才能进行导航,请在设置中开启位置权限',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting()
}
}
})
return
}
// 权限正常,打开地图
this.openMap()
}
})
},
// 打开地图
openMap() {
uni.openLocation({
latitude: parseFloat(this.dtobj.latitude), // 确保是数字类型
longitude: parseFloat(this.dtobj.longitude), // 确保是数字类型
name: this.dtobj.location || '目的地', // 地点名称
success: function(res) {
console.log('打开地图成功', res)
},
fail: function(err) {
console.error('打开地图失败', err)
uni.showToast({
title: '打开地图失败: ' + (err.errMsg || '未知错误'),
icon: 'none',
duration: 3000
})
}
})
},
// 判断返回条件 ,返回上一级不同的页面
btnfh(){
if(this.share == 'share'){
uni.reLaunch({
url:'/pages/myorder/index'
})
}else{
uni.navigateBack()
}
},
// 点击分享好友
btnfx(){
this.$u.put(`/app/feed/add/forwards/${this.dtid}`).then(res => {
if(res.code == 200){
this.showSharePopup = false
uni.showToast({ title: '分享成功', icon: 'success',duration:3000 })
}else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 上拉加载更多数据
handqixing() {
console.log(this.comments.length,this.total)
if(this.comments.length < this.total){
this.pageNum ++
this.getpl()
}
console.log('加载更多')
},
// 下拉刷新
onRefresh() {
this.isRefreshing = true
setTimeout(() => {
this.isRefreshing = false
this.pageNum = 1
this.getxq()
}, 1000)
},
// 点击添加关注
btngzadd(){
this.$u.post(`/app/follow/add?followedId=${this.dtobj.userId}`).then(res => {
if(res.code == 200){
this.dtobj.isFollowed = !this.dtobj.isFollowed
uni.showToast({ title: '关注成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
}else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 点击取消关注
btngzdel(){
this.$u.delete(`/app/follow/cancel?followedId=${this.dtobj.userId}`).then(res => {
if(res.code == 200){
this.dtobj.isFollowed = !this.dtobj.isFollowed
uni.showToast({ title: '取关成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 请求动态详情
getxq(){
this.$u.get(`/app/feed/detail/${this.dtid}`).then(res => {
if(res.code == 200){
this.dtobj = res.data
// 动态详情加载完成后再获取评论列表
this.getpl()
2025-12-20 14:32:28 +08:00
}else if(res.msg == '动态不存在'){
this.dongtaiflag = true
2025-11-08 11:21:57 +08:00
}
})
},
// 查询动态所有评论
getpl(){
// 仅分页父评,子评由后端 children 一并返回
2025-12-20 14:32:28 +08:00
this.$u.get(`/app/comment/list?pageNum=${this.pageNum}&pageSize=${this.pageSize}&bstTypes=1&bstId=${this.dtid}&isParent=1&sortBy=time_desc`).then(res => {
2025-11-08 11:21:57 +08:00
if(res.code == 200){
// 不再强依赖后端 total使用 pageSize 判断是否还有更多
const rows = Array.isArray(res.rows) ? res.rows : []
// 映射为组件所需结构rows 已为父评)
const newComments = rows.map(r => {
const root = this.mapServerComment(r, true)
const children = Array.isArray(r.children) ? r.children : []
root.replies = children.map(c => this.mapServerComment(c, false))
root.expand = false
return root
})
// 分页:第一页覆盖,之后追加并去重
if (this.pageNum == 1) {
this.comments = newComments
} else {
// 去重:只添加不存在的评论
const existingIds = this.comments.map(c => c.id)
const uniqueNewComments = newComments.filter(c => !existingIds.includes(c.id))
this.comments = this.comments.concat(uniqueNewComments)
}
// 页码推进
// this.pageNum ++
this.hasMore = newComments.length === this.pageSize
// 同步一个近似 total便于日志观察
this.total = this.comments.length + (this.hasMore ? 1 : 0)
console.log('newComments:', newComments.length, 'hasMore:', this.hasMore, 'pageNum:', this.pageNum)
}
})
},
// 重新获取评论列表并展开相关评论
getplWithExpand(){
// 保存当前回复的目标ID用于后续展开
const targetId = this.replyTarget ? this.replyTarget.id : null
const parentId = this.replyParent ? this.replyParent.id : null
this.$u.get(`/app/comment/list?pageNum=${this.pageNum}&pageSize=${this.pageSize}&bstTypes=1&bstId=${this.dtid}&isParent=1`).then(res => {
if(res.code == 200){
const rows = Array.isArray(res.rows) ? res.rows : []
// 映射为组件所需结构rows 已为父评)
const newComments = rows.map(r => {
const root = this.mapServerComment(r, true)
const children = Array.isArray(r.children) ? r.children : []
root.replies = children.map(c => this.mapServerComment(c, false))
// 如果是回复的目标评论,或者包含回复的父评论,则展开
if (targetId && (r.id === targetId || (r.replies && r.replies.some(reply => reply.id === targetId)))) {
root.expand = true
} else if (parentId && r.id === parentId) {
root.expand = true
} else {
root.expand = false
}
return root
})
// 分页:第一页覆盖,之后追加并去重
if (this.pageNum == 1) {
this.comments = newComments
} else {
// 去重:只添加不存在的评论
const existingIds = this.comments.map(c => c.id)
const uniqueNewComments = newComments.filter(c => !existingIds.includes(c.id))
this.comments = this.comments.concat(uniqueNewComments)
}
// 页码推进
// this.pageNum ++
// 还有更多:返回条数等于 pageSize
this.hasMore = newComments.length === this.pageSize
// 同步一个近似 total便于日志观察
this.total = this.comments.length + (this.hasMore ? 1 : 0)
}
})
},
// 重新获取评论列表并保持当前展开状态
getplWithCurrentExpand(){
// 保存当前展开状态的评论ID
const expandedIds = this.comments.filter(c => c.expand).map(c => c.id)
this.$u.get(`/app/comment/list?pageNum=${this.pageNum}&pageSize=${this.pageSize}&bstTypes=1&bstId=${this.dtid}&isParent=1`).then(res => {
if(res.code == 200){
const rows = Array.isArray(res.rows) ? res.rows : []
// 映射为组件所需结构rows 已为父评)
const newComments = rows.map(r => {
const root = this.mapServerComment(r, true)
const children = Array.isArray(r.children) ? r.children : []
root.replies = children.map(c => this.mapServerComment(c, false))
// 保持之前的展开状态
root.expand = expandedIds.includes(r.id)
return root
})
// 分页:第一页覆盖,之后追加并去重
if (this.pageNum == 1) {
this.comments = newComments
} else {
// 去重:只添加不存在的评论
const existingIds = this.comments.map(c => c.id)
const uniqueNewComments = newComments.filter(c => !existingIds.includes(c.id))
this.comments = this.comments.concat(uniqueNewComments)
}
// 页码推进
// this.pageNum ++
// 还有更多:返回条数等于 pageSize
this.hasMore = newComments.length === this.pageSize
// 同步一个近似 total便于日志观察
this.total = this.comments.length + (this.hasMore ? 1 : 0)
}
})
},
// 按需加载直到包含目标根评论,并展开它
async loadUntilRootAndExpand(targetRootId){
try{
if(!targetRootId){
// 无目标时退化为第一页刷新
this.pageNum = 1
await this.getpl()
return
}
let tempPage = 1
let accumulated = []
let found = false
while(true){
const res = await this.$u.get(`/app/comment/list?pageNum=${tempPage}&pageSize=${this.pageSize}&bstTypes=1&bstId=${this.dtid}&isParent=1`)
if(res.code != 200){ break }
const rows = Array.isArray(res.rows) ? res.rows : []
const pageComments = rows.map(r => {
const root = this.mapServerComment(r, true)
const children = Array.isArray(r.children) ? r.children : []
root.replies = children.map(c => this.mapServerComment(c, false))
root.expand = r.id === targetRootId // 默认命中页时展开
return root
})
// 去重合并
const accIds = accumulated.map(c => c.id)
const uniquePage = pageComments.filter(c => !accIds.includes(c.id))
accumulated = accumulated.concat(uniquePage)
// 是否已包含目标根评论
found = accumulated.some(c => c.id === targetRootId)
// 终止条件:找到目标,或本页不足 pageSize
if(found || rows.length < this.pageSize){
break
}
tempPage += 1
}
// 应用到界面
this.comments = accumulated
this.pageNum = tempPage + 1
this.hasMore = !found && (this.comments.length % this.pageSize === 0)
// 保证目标根评论展开
const idx = this.comments.findIndex(c => c.id === targetRootId)
if(idx !== -1){ this.comments[idx].expand = true }
}catch(e){
console.error('loadUntilRootAndExpand error:', e)
}
},
// 将服务端评论结构映射为界面所需结构
mapServerComment(row, isRoot){
return {
id: row.id,
avatar: row.userAvatar || '',
name: row.nickName || '匿名',
isAuthor: this.dtobj && row.userId === this.dtobj.userId,
content: row.content || '',
time: row.createTime || '',
likes: Number(row.likes || 0),
liked: !!row.isLiked,
parentId:row.parentId || '',
rootId:row.rootId || '',
parentNickName:row.parentNickName,
// 根评论:后续会补充 replies/expand子回复不需要
...(isRoot ? { replies: [], expand: false } : {})
}
},
isVideoItem(item) {
if (!item) return false
if (item.video) return true
const url = item || ''
return /\.(mp4|m4v|mov|avi|wmv|flv|m3u8)(\?|#|$)/i.test(url)
},
getImageUrl(item) {
if (!item) return ''
if (item && !this.isVideoItem(item)) return item
return item.poster || ''
},
getVideoUrl(item) {
if (!item) return ''
return item || item || ''
},
// 切换轮播图
onSwiperChange(e) {
this.currentIndex = e.detail.current || 0
this.$nextTick(() => {
this.syncVideoPlayState()
})
},
2025-12-20 14:32:28 +08:00
// 点击图片放大预览
handleImageClick(index) {
if (!this.dtobj.picture) return
const pictureList = this.dtobj.picture.split(',').filter(item => item && item.trim())
const clickedItem = pictureList[index]
2025-11-08 11:21:57 +08:00
if (!clickedItem) return
// 统一在微信小程序端使用 previewMedia 实现图文混合预览
// #ifdef MP-WEIXIN
2025-12-20 14:32:28 +08:00
const sources = pictureList.map((it) => {
2025-11-08 11:21:57 +08:00
if (this.isVideoItem(it)) {
2025-12-20 14:32:28 +08:00
return { url: it, type: 'video', poster: '' }
2025-11-08 11:21:57 +08:00
}
2025-12-20 14:32:28 +08:00
return { url: it, type: 'image' }
2025-11-08 11:21:57 +08:00
})
uni.previewMedia({ current: index, sources })
return
// #endif
// 其他平台回退为仅图片可预览
if (this.isVideoItem(clickedItem)) {
2025-12-20 14:32:28 +08:00
// 视频不能预览,直接返回
2025-11-08 11:21:57 +08:00
return
}
2025-12-20 14:32:28 +08:00
// 过滤出所有图片URL排除视频
const imageUrls = pictureList.filter(it => !this.isVideoItem(it) && it.trim())
// 找到当前点击的图片在所有图片中的索引
let currentImageIndex = 0
for (let i = 0; i < index; i++) {
if (!this.isVideoItem(pictureList[i]) && pictureList[i].trim()) {
currentImageIndex++
}
}
if (imageUrls.length > 0) {
uni.previewImage({
current: imageUrls[currentImageIndex] || imageUrls[0],
urls: imageUrls
})
}
},
// 保留原有的 handleSwiperClick 以防其他地方使用
handleSwiperClick() {
this.handleImageClick(this.currentIndex)
2025-11-08 11:21:57 +08:00
},
// 点击展开/关闭 评论回复
toggleExpand(ci){
const c = this.comments[ci]
if(!c) return
c.expand = !c.expand
},
// 点击回复别的评论
onReply(target, parent){
this.replyTarget = target || null
this.replyParent = parent || null
this.replyText = ''
this.showReplyBar = true
this.replyFocus = true
this.replyPlaceholder = target && target.name ? ('回复 ' + target.name + ' …') : '说点什么...'
console.log(target,parent);
if(parent){ //判断是否是回复父级下面的评论 有就拿子级的parentId和rootId 没有就拿父级的id
this.rootId = target.rootId
this.parentId = target.parentId
}else{
this.rootId = target.id
this.parentId = target.id
}
},
// 点击键盘回车键 进行请求操作
sendReply(){
const text = (this.replyText || '').trim()
if(!text){
return uni.showToast({ title: '请输入回复内容', icon: 'none' })
}
// 显示加载状态
uni.showLoading({ title: '发送中...' })
let bstType = ''
// if(this.parentId == '' && this.rootId == ''){
// bstType = 1
// }else{
// bstType = 2
// }
let data = {
bstType: 1,
bstId: this.dtid,
content: this.replyText,
parentId: this.parentId,
2025-12-20 14:32:28 +08:00
rootId: this.rootId,
2025-11-08 11:21:57 +08:00
}
// 在清空状态前记录目标根评论ID
const targetRootId = this.rootId || (this.replyParent ? this.replyParent.id : (this.replyTarget ? this.replyTarget.id : ''))
this.$u.post(`/app/comment/add`, data).then(res => {
uni.hideLoading()
if(res.code == 200){
// 回复成功后按需加载直到包含目标根评论,并展开
this.loadUntilRootAndExpand(targetRootId)
// 清空输入框和重置状态
this.replyText = ''
this.rootId = ''
this.parentId = ''
this.replyFocus = false
this.showReplyBar = false
this.replyTarget = null
this.replyParent = null
this.replyPlaceholder = '说点什么...'
uni.showToast({ title: '回复成功', icon: 'success' })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else {
uni.showToast({ title: res.message || '回复失败', icon: 'none' })
}
}).catch(err => {
uni.hideLoading()
uni.showToast({ title: '网络错误,请重试', icon: 'none' })
console.error('回复失败:', err)
})
},
cancelReply(){
this.replyFocus = false
this.replyText = ''
this.replyPlaceholder = '说点什么...'
},
// inout失焦后执行事件
onInputBlur(){
// 失焦仅重置占位,不隐藏输入栏
this.cancelReply()
},
// 父评论点赞和取消点赞
pldianzan(item, ci){
console.log(item);
if(!item.liked){
let data = {
bstType:3,
2025-12-20 14:32:28 +08:00
bstId:item.id,
2025-11-08 11:21:57 +08:00
}
this.$u.post(`/app/like/add`,data).then(res => {
if(res.code == 200){
// 本地立即高亮并加一
const c = this.comments[ci]
if(c){
c.liked = true
c.likes = Number(c.likes || 0) + 1
}
// 点赞成功后重新获取评论数据,保持展开状态
this.getplWithCurrentExpand()
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
}else{
this.$u.delete(`/app/like/cancel/${item.id}?bstType=3`).then(res => {
if(res.code == 200){
// 本地立即取消高亮并减一
const c = this.comments[ci]
if(c){
c.liked = false
c.likes = Math.max(0, Number(c.likes || 0) - 1)
}
// 取消点赞成功后重新获取评论数据,保持展开状态
this.getplWithCurrentExpand()
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
}
},
// 子评论点赞和取消点赞
pldianzantwo(item,ci,ri){
console.log(item);
if(!item.liked){
let data = {
bstType:3,
2025-12-20 14:32:28 +08:00
bstId:item.id,
2025-11-08 11:21:57 +08:00
}
this.$u.post(`/app/like/add`,data).then(res => {
if(res.code == 200){
// 本地立即高亮并加一
const r = this.comments[ci]?.replies?.[ri]
if(r){
r.liked = true
r.likes = Number(r.likes || 0) + 1
}
// 点赞成功后重新获取评论数据,保持展开状态
this.getplWithCurrentExpand()
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
}else{
this.$u.delete(`/app/like/cancel/${item.id}?bstType=3`).then(res => {
if(res.code == 200){
// 本地立即取消高亮并减一
const r = this.comments[ci]?.replies?.[ri]
if(r){
r.liked = false
r.likes = Math.max(0, Number(r.likes || 0) - 1)
}
// 取消点赞成功后重新获取评论数据,保持展开状态
this.getplWithCurrentExpand()
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
}
},
// 动态底部点击进行收藏
btndianzan(){
let data = {
bstType:2,
bstId:this.dtid
}
this.$u.post(`/app/favorite/add`,data).then(res => {
if(res.code == 200){
this.dtobj.isCollected = !this.dtobj.isCollected
this.dtobj.collections = Number(this.dtobj.collections) + 1
uni.showToast({ title: '收藏成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 动态底部点击取消收藏
btndianzandel(){
this.$u.delete(`/app/favorite/cancel/${this.dtid}?bstType=2`).then(res => {
if(res.code == 200){
this.dtobj.isCollected = !this.dtobj.isCollected
this.dtobj.collections = Number(this.dtobj.collections) - 1
uni.showToast({ title: '取消收藏成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 动态底部进行点赞 动态底部进行点赞 动态底部进行点赞
toggleStar(){
let data = {
bstType:2,
bstId:this.dtid
}
this.$u.post(`/app/like/add`,data).then(res => {
if(res.code == 200){
this.dtobj.isLiked = !this.dtobj.isLiked
this.dtobj.likes = Number(this.dtobj.likes) + 1
uni.showToast({ title: '点赞成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
}else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 动态底部取消点赞 动态底部取消点赞 动态底部取消点赞
toggleStardel(){
this.$u.delete(`/app/like/cancel/${this.dtid}?bstType=2`).then(res => {
if(res.code == 200){
this.dtobj.isLiked = !this.dtobj.isLiked
this.dtobj.likes = Number(this.dtobj.likes) - 1
uni.showToast({ title: '取消点赞成功', icon: 'success',duration:3000 })
}else if(res.code == 401){
2025-12-20 14:32:28 +08:00
this.dengliflag = true
2025-11-08 11:21:57 +08:00
} else{
uni.showToast({ title: res.msg, icon: 'none',duration:3000 })
}
})
},
// 点击转发/分享
doShare(){
this.showSharePopup = true
},
// 点击转发微信好友等...
closeShare(){
this.showSharePopup = false
},
// APP 端分享
// #ifdef APP-PLUS
shareApp(scene){
const summary = this.dtobj.content || '精彩内容分享'
const href = `/page_fenbao/guangchang/dongtaixq?from=share&id=${this.dtid}`
const imageUrl = (Array.isArray(this.dtobj.picture) && this.dtobj.picture.find(p=>!/\.(mp4|m4v|mov|avi|wmv|flv|m3u8)(\?|#|$)/i.test(p))) || ''
uni.share({
provider:'weixin',
scene: scene === 'timeline' ? 'WXSenceTimeline' : 'WXSceneSession',
type:0,
href: href,
title: this.dtobj.nickName || '分享',
summary: summary,
imageUrl: imageUrl,
success: ()=>{ this.showSharePopup = false; uni.showToast({ title:'已分享', icon:'none' }) },
fail: (e)=>{ this.showSharePopup = false; uni.showToast({ title:'分享失败', icon:'none' }) }
})
},
// #endif
syncVideoPlayState() {
// 仅在微信小程序端使用 VideoContext 精细控制
// #ifdef MP-WEIXIN
this.dtobj.picture.forEach((item, idx) => {
if (!this.isVideoItem(item)) return
const ctx = uni.createVideoContext('swiper-video-' + idx, this)
if (idx === this.currentIndex) {
ctx.play()
} else {
ctx.pause()
}
})
// #endif
},
isCurrentVideo(idx) {
return this.currentIndex === idx && this.isVideoItem(this.dtobj.picture[idx])
}
},
// 微信小程序分享配置
// #ifdef MP-WEIXIN
onShareAppMessage(){
const path = `/page_fenbao/guangchang/dongtaixq?from=share&id=${this.dtid}`
const imageUrl = (Array.isArray(this.dtobj.picture) && this.dtobj.picture.find(p=>!/\.(mp4|m4v|mov|avi|wmv|flv|m3u8)(\?|#|$)/i.test(p))) || ''
return {
title: this.dtobj.content || '分享一个有趣的内容',
path,
imageUrl
}
},
onShareTimeline(){
const query = `id=${this.dtid}`
const imageUrl = (Array.isArray(this.dtobj.picture) && this.dtobj.picture.find(p=>!/\.(mp4|m4v|mov|avi|wmv|flv|m3u8)(\?|#|$)/i.test(p))) || ''
return {
title: this.dtobj.content || '分享一个有趣的内容',
query,
imageUrl
}
}
// #endif
}
</script>
<style lang="scss">
page {
background: #fff;
}
.list {
padding-bottom: 20rpx;
box-sizing: border-box;
height: 84vh;
overflow: scroll;
}
2025-12-20 14:32:28 +08:00
.card-content {
margin-top: 20rpx;
padding-left: 28rpx;
display: flex;
flex-wrap: wrap;
gap:10rpx;
.text { color: #333; }
.thumb { width: 98px; height: 98px; border-radius: 12rpx; margin-top: 12rpx; }
// 单图等比缩放最大宽度100%
.thumb-single {
image { width: 230rpx; height: 230rpx; border-radius: 12rpx; display: block; }
video { width: 230rpx; height: 230rpx; border-radius: 12rpx; object-fit: cover; display: block; }
}
// 多图:正方形九宫格,自适应裁剪
.thumb-grid {
margin-top: 12rpx;
display: grid;
grid-template-columns: repeat(3, 98px);
gap: 10rpx;
.grid-item {
position: relative;
width: 98px; height: 98px;
image, video {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
width: 98px; height: 98px;
border-radius: 8rpx;
}
}
}
}
.denglu{
.imgbox {
width: 750rpx;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 998;
image {
width: 750rpx;
height: 100vh;
}
}
.button{
width: 586rpx;
height: 90rpx;
line-height: 90rpx;
background: #c11b1d;
border-radius: 54rpx 54rpx 54rpx 54rpx;
font-weight: 500;
font-size: 40rpx;
color: #FFFFFF;
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 106rpx;
z-index: 999;
}
}
2025-11-08 11:21:57 +08:00
.dizhi{
font-weight: 600;
font-size: 26rpx;
color: #1EC28B;
display: flex;
align-items: center;
margin-top: 10rpx;
padding-left: 30rpx;
.qian{
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
}
.hou{
width: 32rpx;
height: 32rpx;
margin-left: 4rpx;
}
}
.contwz{
margin: auto;
margin-top: 28rpx;
font-size: 28rpx;
color: #3D3D3D;
2025-12-20 14:32:28 +08:00
width: 700rpx;
2025-11-08 11:21:57 +08:00
}
.wrap{
2025-12-20 14:32:28 +08:00
width: 700rpx !important;
2025-11-08 11:21:57 +08:00
height: 392rpx !important;
margin: auto;
border-radius: 10rpx;
overflow: hidden;
margin-top: 30rpx;
}
.swiper{
2025-12-20 14:32:28 +08:00
width: 700rpx !important;
2025-11-08 11:21:57 +08:00
height: 392rpx !important;
}
.media-wrap{
2025-12-20 14:32:28 +08:00
width: 700rpx !important;
2025-11-08 11:21:57 +08:00
height: 392rpx !important;
}
.media-wrap image,
.media-wrap video{
width: 100%;
height: 100%;
display: block;
}
.comment-section{
2025-12-20 14:32:28 +08:00
width: 700rpx;
2025-11-08 11:21:57 +08:00
margin: 32rpx auto 20rpx; // 预留底部输入栏高度
.c-title{
font-size: 34rpx;
font-weight: 600;
color: #3D3D3D;
margin-bottom: 24rpx;
}
.c-item{
padding: 20rpx 0;
.c-hd{ display: flex; }
.avatar{ width: 64rpx; height: 64rpx; border-radius: 50%; margin-right: 16rpx; }
.user{ flex: 1; }
.name-row{ display: flex; align-items: center; }
.name{ font-size: 28rpx; color: #3D3D3D; }
.tag{ margin-left: 12rpx; font-size: 22rpx; color: #fff; background: #1EC28B; padding: 2rpx 10rpx; border-radius: 20rpx; }
.content{ font-size: 28rpx; color: #3D3D3D; margin: 6rpx 0 8rpx; }
.meta{ font-size: 24rpx; color: #909090; }
.meta .reply{ margin-left: 20rpx; color: #2b85e4; }
.like{ width: 80rpx; text-align: center;
image{
width: 29rpx;
height: 25rpx;
}}
.heart{ font-size: 34rpx; color: #D8D8D8; }
.heart.active{ color: #ff4d4f; }
.num{ display: block; font-size: 24rpx; color: #909090; margin-top: 4rpx; }
}
.replies{ margin-left: 80rpx; }
.expand, .collapse{ color: #2b85e4; font-size: 26rpx; }
.r-item{ display: flex; align-items: flex-start; justify-content: space-between; padding: 16rpx 0; }
.avatar.small{ width: 48rpx; height: 48rpx; }
.r-body{ flex: 1; margin-left: 16rpx; }
}
// 底部输入栏
.reply-mask{
position: fixed; left: 0; right: 0; top: 0; bottom: 0;
background: rgba(0,0,0,0.35);
z-index: 9;
}
.reply-bar{
position: fixed;
2025-12-20 14:32:28 +08:00
left: 0; right: 0; bottom: -4rpx;
transition: transform 0.3s ease;
2025-11-08 11:21:57 +08:00
background: #fff;
padding: 40rpx 24rpx;
box-shadow: 0 -6rpx 20rpx rgba(0,0,0,0.06);
z-index: 10;
.reply-inner{ display: flex; align-items: center; }
.reply-box{ flex: 1; height: 72rpx; background: #e9f0fa; border-radius: 36rpx; display: flex; align-items: center; padding: 0 20rpx; }
.icon-pencil{ color: #7a9bc2; font-size: 28rpx; margin-right: 12rpx; }
.reply-input{ flex: 1; height: 72rpx; font-size: 28rpx; color: #333; }
.reply-actions{ display: flex; align-items: center; margin-left: 20rpx; }
.action{ display: flex; align-items: center; margin-left: 20rpx;
.dz,
.fx{
width: 32rpx;
height: 32rpx;
} }
.star{ font-size: 36rpx; color: #c8a15d; }
.star.active{ color: #d9a441; }
.share{ font-size: 32rpx; color: #7a9bc2; transform: rotate(90deg); }
.num{ font-size: 24rpx; color: #606060; margin-left: 8rpx; }
}
.top{
2025-12-20 14:32:28 +08:00
width: 700rpx;
2025-11-08 11:21:57 +08:00
margin: auto;
margin-top: 36rpx;
display: flex;
align-items: center;
justify-content: space-between;
.guanzhu{
.yiguanzhu{
width: 118rpx;
height: 46rpx;
border-radius: 26rpx 26rpx 26rpx 26rpx;
border: 2rpx solid #606060;
font-size: 24rpx;
color: #606060;
text-align: center;
line-height: 46rpx;
}
.weiguanzhu{
width: 118rpx;
height: 46rpx;
border-radius: 26rpx 26rpx 26rpx 26rpx;
border: 2rpx solid #606060;
font-size: 24rpx;
color: #606060;
text-align: center;
line-height: 46rpx;
}
}
.info{
display: flex;
align-items: center;
image{
width: 84rpx;
height: 84rpx;
margin-right: 14rpx;
border-radius: 50%;
}
.xx{
.name{
font-weight: 600;
font-size: 34rpx;
color: #3D3D3D;
}
.riqi{
font-size: 24rpx;
color: #606060;
margin-top: 12rpx;
}
}
}
}
/* 分享弹窗样式 */
.share-mask{ position: fixed; left:0; right:0; top:0; bottom:0; background: rgba(0,0,0,0.45); z-index: 999; }
.share-popup{ position: fixed; left:0; right:0; bottom:0; background:#fff; border-radius: 16rpx 16rpx 0 0; padding: 24rpx; z-index: 1000; }
.share-title{ text-align:center; font-size: 30rpx; color:#333; margin-bottom: 16rpx; }
.share-btn{ background:#f5f5f5; margin: 12rpx 0; padding: 20rpx; border-radius: 12rpx; text-align:center; }
.share-cancel{ text-align:center; color:#666; padding: 22rpx 0; }
</style>