1809 lines
59 KiB
JavaScript
1809 lines
59 KiB
JavaScript
|
|
const config = require('../../config/config.js');
|
|||
|
|
const apiClient = require('../../utils/api-client.js');
|
|||
|
|
const { getListImageUrl, getPreviewImageUrl, getOriginalImageUrl } = require('../../utils/image-url-optimizer.js');
|
|||
|
|
|
|||
|
|
Page({
|
|||
|
|
data: {
|
|||
|
|
feedList: [], // 动态列表数据
|
|||
|
|
userList: [], // 用户列表(从头像模块显示)
|
|||
|
|
page: 1, // 当前页码
|
|||
|
|
pageSize: 5, // 每页加载数量
|
|||
|
|
loading: false, // 是否正在加载
|
|||
|
|
noMore: false, // 是否没有更多数据
|
|||
|
|
currentYear: new Date().getFullYear(), // 当前年份
|
|||
|
|
// 定位相关数据
|
|||
|
|
latitude: null,
|
|||
|
|
longitude: null,
|
|||
|
|
radius: 30,
|
|||
|
|
modeFrom: '',
|
|||
|
|
feedUuid: '',
|
|||
|
|
pendingFeedUuid: '',
|
|||
|
|
scrollIntoFeedId: '',
|
|||
|
|
highlightFeedUuid: '',
|
|||
|
|
lastRedirectToken: '',
|
|||
|
|
// 评论弹窗相关
|
|||
|
|
showCommentModal: false, // 是否显示评论弹窗
|
|||
|
|
currentFeedUuid: '', // 当前评论的动态UUID
|
|||
|
|
currentFeedIndex: -1, // 当前评论的动态索引
|
|||
|
|
currentComments: [], // 当前显示的评论列表
|
|||
|
|
commentInputValue: '', // 评论输入内容
|
|||
|
|
// 回复相关
|
|||
|
|
replyingCommentId: null, // 当前正在回复的评论ID
|
|||
|
|
replyingCommentIndex: null, // 当前正在回复的评论索引(一级评论)
|
|||
|
|
replyingToCommentId: null, // 回复的目标评论ID(二级评论的父评论)
|
|||
|
|
replyInputValue: '', // 回复输入内容
|
|||
|
|
showReplyInput: {}, // 控制每个评论的回复输入框显示状态 {commentId: true/false}
|
|||
|
|
submittingReply: false // 是否正在提交回复,防止重复点击
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onLoad(options = {}) {
|
|||
|
|
this._skipNextOnShowReload = true;
|
|||
|
|
this.highlightTimer = null;
|
|||
|
|
|
|||
|
|
const mapLatitude = parseFloat(options.latitude);
|
|||
|
|
const mapLongitude = parseFloat(options.longitude);
|
|||
|
|
const hasMapParams = !Number.isNaN(mapLatitude) && !Number.isNaN(mapLongitude);
|
|||
|
|
|
|||
|
|
if (hasMapParams) {
|
|||
|
|
const parsedRadius = parseInt(options.radius, 10);
|
|||
|
|
const safeRadius = Number.isNaN(parsedRadius) ? this.data.radius : Math.max(10, parsedRadius);
|
|||
|
|
|
|||
|
|
this._skipNextOnShowReload = true;
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
latitude: mapLatitude,
|
|||
|
|
longitude: mapLongitude,
|
|||
|
|
radius: safeRadius,
|
|||
|
|
modeFrom: options.mode || 'map',
|
|||
|
|
feedUuid: options.feedUuid || '',
|
|||
|
|
pendingFeedUuid: options.feedUuid || ''
|
|||
|
|
}, () => {
|
|||
|
|
this.resetAndLoadFeedList();
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 先获取定位,再加载数据
|
|||
|
|
this.getLocation().then(() => {
|
|||
|
|
this.loadFeedList();
|
|||
|
|
}).catch(() => {});
|
|||
|
|
|
|||
|
|
this.apiClient = apiClient;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 获取用户地理位置
|
|||
|
|
getLocation() {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.getLocation({
|
|||
|
|
type: 'gcj02',
|
|||
|
|
success: (res) => {
|
|||
|
|
this.setData({
|
|||
|
|
latitude: res.latitude,
|
|||
|
|
longitude: res.longitude
|
|||
|
|
});
|
|||
|
|
resolve(); // 定位成功,允许加载数据
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '获取位置失败,无法加载附近动态',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
reject(err);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
resetAndLoadFeedList() {
|
|||
|
|
if (!this.data.latitude || !this.data.longitude) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.setData({ page: 1, feedList: [], noMore: false, scrollIntoFeedId: '' }, () => {
|
|||
|
|
this.loadFeedList();
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onShow() {
|
|||
|
|
// 🔥 设置tabBar选中状态为"圈子"(索引1)
|
|||
|
|
try {
|
|||
|
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|||
|
|
this.getTabBar().setData({ selected: 1 });
|
|||
|
|
}
|
|||
|
|
} catch (_) {}
|
|||
|
|
|
|||
|
|
const appInstance = getApp();
|
|||
|
|
const redirectPayload = appInstance?.globalData?.mapFeedRedirect;
|
|||
|
|
if (redirectPayload && redirectPayload.token && redirectPayload.token !== this.data.lastRedirectToken) {
|
|||
|
|
appInstance.globalData.mapFeedRedirect = null;
|
|||
|
|
this.applyRedirectPayload(redirectPayload);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this._skipNextOnShowReload) {
|
|||
|
|
// 延迟清除标志,确保能捕获到预览关闭事件
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this._skipNextOnShowReload = false;
|
|||
|
|
}, 500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 页面显示时刷新数据
|
|||
|
|
if (this.data.latitude && this.data.longitude) {
|
|||
|
|
this.resetAndLoadFeedList();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载动态列表
|
|||
|
|
loadFeedList() {
|
|||
|
|
// 检查是否有定位信息
|
|||
|
|
if (!this.data.latitude || !this.data.longitude) return;
|
|||
|
|
|
|||
|
|
if (this.data.loading || this.data.noMore) return;
|
|||
|
|
this.setData({ loading: true });
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 通过字符串模板拼接带参数的URL
|
|||
|
|
const { page, pageSize, latitude, longitude } = this.data;
|
|||
|
|
// 查看所有动态
|
|||
|
|
const fullRequestUrl = `${config.api.baseUrl}/api/v1/feeds?` +
|
|||
|
|
`type=timeline&` +
|
|||
|
|
`page=${page}&` +
|
|||
|
|
`pageSize=${pageSize}&` +
|
|||
|
|
`latitude=${latitude}&` +
|
|||
|
|
`longitude=${longitude}&` +
|
|||
|
|
`radius=30`;
|
|||
|
|
|
|||
|
|
// 先检查URL是否有效
|
|||
|
|
if (!fullRequestUrl || !fullRequestUrl.startsWith('http')) {
|
|||
|
|
console.error('无效的请求URL:', fullRequestUrl);
|
|||
|
|
this.setData({ loading: false });
|
|||
|
|
wx.showToast({ title: '加载失败,请重试', icon: 'none' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 调用接口
|
|||
|
|
wx.request({
|
|||
|
|
url: fullRequestUrl,
|
|||
|
|
method: 'GET',
|
|||
|
|
header: {
|
|||
|
|
'Authorization': `Bearer ${apiClient.getToken() || ''}`
|
|||
|
|
},
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (res.data.data?.feeds && res.data.data.feeds.length > 0) {
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (res.data.data.feeds[0].media && res.data.data.feeds[0].media.length > 0) {
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.setData({ loading: false });
|
|||
|
|
|
|||
|
|
// 🔥 统一处理401 - 使用apiClient的统一处理
|
|||
|
|
if (apiClient.is401Error(res)) {
|
|||
|
|
const app = getApp();
|
|||
|
|
const isLoggedIn = app?.globalData?.isLoggedIn || false;
|
|||
|
|
apiClient.handle401Error(isLoggedIn);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (res.data.code !== 200) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: res.data.message || '加载失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const newFeeds = res.data.data.feeds || [];
|
|||
|
|
const processedFeeds = newFeeds.map((feed, feedIdx) => {
|
|||
|
|
// 时间格式化
|
|||
|
|
let formattedTime = '未知时间';
|
|||
|
|
try {
|
|||
|
|
formattedTime = this.formatTime(feed.createdAt || '');
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('时间格式化失败:', error);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 用户信息
|
|||
|
|
const feedUser = feed.user || {};
|
|||
|
|
console.log("当前动态的用户信息", feedUser)
|
|||
|
|
|
|||
|
|
// 处理用户头像URL
|
|||
|
|
let validAvatar = feedUser.avatar || '';
|
|||
|
|
|
|||
|
|
if (validAvatar && !validAvatar.startsWith('http') && validAvatar.startsWith('/')) {
|
|||
|
|
try {
|
|||
|
|
const baseDomain = config.api.baseUrl.replace(/\/api\/v1$/, '');
|
|||
|
|
validAvatar = `${baseDomain}${validAvatar}`;
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error(`头像路径转换失败:`, e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (validAvatar && validAvatar.startsWith('http://')) {
|
|||
|
|
validAvatar = validAvatar.replace('http://', 'https://');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!validAvatar || (!validAvatar.startsWith('http://') && !validAvatar.startsWith('https://'))) {
|
|||
|
|
console.warn(`无效的头像URL,使用占位图:`, validAvatar);
|
|||
|
|
validAvatar = '/images/findme-logo.png';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理动态图片
|
|||
|
|
let processedMedia = [];
|
|||
|
|
if (feed.media && Array.isArray(feed.media)) {
|
|||
|
|
processedMedia = feed.media.map((mediaItem, index) => {
|
|||
|
|
if (!mediaItem || typeof mediaItem !== 'object') {
|
|||
|
|
console.warn(`动态${feed.id || index}的图片无效:`, mediaItem);
|
|||
|
|
return {
|
|||
|
|
url: '/images/placeholder.svg',
|
|||
|
|
thumbnailUrl: '/images/placeholder.svg',
|
|||
|
|
type: 'image'
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保图片URL有效
|
|||
|
|
let validUrl = mediaItem.url || mediaItem.src || '';
|
|||
|
|
let validThumbnailUrl = mediaItem.thumbnailUrl || mediaItem.thumbnail || validUrl;
|
|||
|
|
|
|||
|
|
if (validUrl && !validUrl.startsWith('http') && validUrl.startsWith('/')) {
|
|||
|
|
try {
|
|||
|
|
const baseDomain = config.api.baseUrl.replace(/\/api\/v1$/, '');
|
|||
|
|
validUrl = `${baseDomain}${validUrl}`;
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error(`图片路径转换失败:`, e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (validUrl && validUrl.startsWith('http://')) {
|
|||
|
|
validUrl = validUrl.replace('http://', 'https://');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!validUrl || (!validUrl.startsWith('http://') && !validUrl.startsWith('https://'))) {
|
|||
|
|
console.warn(`无效的图片URL,使用占位图:`, validUrl);
|
|||
|
|
validUrl = '/images/placeholder.svg';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (validThumbnailUrl && !validThumbnailUrl.startsWith('http') && validThumbnailUrl.startsWith('/')) {
|
|||
|
|
try {
|
|||
|
|
const baseDomain = config.api.baseUrl.replace(/\/api\/v1$/, '');
|
|||
|
|
validThumbnailUrl = `${baseDomain}${validThumbnailUrl}`;
|
|||
|
|
} catch (e) {}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (validThumbnailUrl && validThumbnailUrl.startsWith('http://')) {
|
|||
|
|
validThumbnailUrl = validThumbnailUrl.replace('http://', 'https://');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!validThumbnailUrl || (!validThumbnailUrl.startsWith('http://') && !validThumbnailUrl.startsWith('https://'))) {
|
|||
|
|
validThumbnailUrl = validUrl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优化图片URL,提高清晰度
|
|||
|
|
// 列表显示使用中等质量,预览使用高质量
|
|||
|
|
const optimizedUrl = getListImageUrl(validUrl, 750);
|
|||
|
|
const optimizedThumbnailUrl = getListImageUrl(validThumbnailUrl, 400);
|
|||
|
|
const originalUrl = getOriginalImageUrl(validUrl); // 保存原图URL用于预览
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
...mediaItem,
|
|||
|
|
url: optimizedUrl, // 列表显示用优化后的URL
|
|||
|
|
thumbnailUrl: optimizedThumbnailUrl,
|
|||
|
|
originalUrl: originalUrl, // 保存原图URL
|
|||
|
|
type: mediaItem.type || 'image',
|
|||
|
|
loading: true // 初始状态为加载中
|
|||
|
|
};
|
|||
|
|
}).filter(Boolean);
|
|||
|
|
} else if (feed.images && Array.isArray(feed.images)) {
|
|||
|
|
processedMedia = feed.images.map(imageUrl => {
|
|||
|
|
// 优化图片URL,提高清晰度
|
|||
|
|
const optimizedUrl = getListImageUrl(imageUrl, 750);
|
|||
|
|
const originalUrl = getOriginalImageUrl(imageUrl);
|
|||
|
|
return {
|
|||
|
|
url: optimizedUrl,
|
|||
|
|
thumbnailUrl: optimizedUrl,
|
|||
|
|
originalUrl: originalUrl,
|
|||
|
|
type: 'image',
|
|||
|
|
loading: true // 初始状态为加载中
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理评论数据,格式化评论时间,组织嵌套结构
|
|||
|
|
let processedComments = [];
|
|||
|
|
if (feed.comments && Array.isArray(feed.comments) && feed.comments.length > 0) {
|
|||
|
|
// 获取当前用户ID,用于判断是否是自己的评论
|
|||
|
|
const app = getApp();
|
|||
|
|
const currentUser = app.globalData.userInfo?.user || {};
|
|||
|
|
const currentUserId = currentUser.id || currentUser.userId || currentUser.customId || '';
|
|||
|
|
|
|||
|
|
// 分离一级评论和回复
|
|||
|
|
const topLevelComments = []; // 一级评论(没有replyToId的)
|
|||
|
|
const repliesList = []; // 所有回复(有replyToId的)
|
|||
|
|
|
|||
|
|
feed.comments.forEach((item, idx) => {
|
|||
|
|
// 判断是否是当前用户的评论
|
|||
|
|
const itemUserId = item.userId || item.user?.id || item.user?.userId || '';
|
|||
|
|
const isOwn = currentUserId && itemUserId && currentUserId.toString() === itemUserId.toString();
|
|||
|
|
|
|||
|
|
// 确保user对象存在,如果不存在则创建默认值
|
|||
|
|
const user = item.user || {};
|
|||
|
|
if (!user.nickname && item.userName) {
|
|||
|
|
user.nickname = item.userName;
|
|||
|
|
}
|
|||
|
|
if (!user.nickname) {
|
|||
|
|
user.nickname = '未知用户';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!item.replyToId) {
|
|||
|
|
// 一级评论
|
|||
|
|
topLevelComments.push({
|
|||
|
|
...item,
|
|||
|
|
id: item.id || item.uuid || `comment_${feedIdx}_${idx}`,
|
|||
|
|
formattedTime: item.formattedTime || this.formatCommentTime(item.createdAt || ''),
|
|||
|
|
replies: [],
|
|||
|
|
visibleReplyCount: 5, // 默认显示5条回复
|
|||
|
|
isOwn: isOwn,
|
|||
|
|
user: user // 确保user对象被绑定
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 回复
|
|||
|
|
repliesList.push({
|
|||
|
|
...item,
|
|||
|
|
id: item.id || item.uuid || `reply_${feedIdx}_${idx}`,
|
|||
|
|
formattedTime: item.formattedTime || this.formatCommentTime(item.createdAt || ''),
|
|||
|
|
isOwn: isOwn,
|
|||
|
|
user: user // 确保user对象被绑定
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 将回复组织到对应的一级评论下(支持多级嵌套)
|
|||
|
|
// 使用多次遍历的方式,确保所有回复都能正确找到父节点
|
|||
|
|
let remainingReplies = [...repliesList];
|
|||
|
|
let maxIterations = 10; // 防止无限循环
|
|||
|
|
let iteration = 0;
|
|||
|
|
|
|||
|
|
while (remainingReplies.length > 0 && iteration < maxIterations) {
|
|||
|
|
iteration++;
|
|||
|
|
const newRemainingReplies = [];
|
|||
|
|
|
|||
|
|
remainingReplies.forEach(reply => {
|
|||
|
|
// 先尝试在一级评论中查找
|
|||
|
|
const parentComment = topLevelComments.find(c => c.id === reply.replyToId);
|
|||
|
|
if (parentComment) {
|
|||
|
|
// 回复一级评论
|
|||
|
|
if (!parentComment.replies) {
|
|||
|
|
parentComment.replies = [];
|
|||
|
|
}
|
|||
|
|
// 设置 replyToUser(如果父评论有 user 对象)
|
|||
|
|
if (parentComment.user) {
|
|||
|
|
reply.replyToUser = parentComment.user;
|
|||
|
|
}
|
|||
|
|
parentComment.replies.push(reply);
|
|||
|
|
} else {
|
|||
|
|
// 尝试在所有已处理的回复中查找父回复
|
|||
|
|
let found = false;
|
|||
|
|
for (let comment of topLevelComments) {
|
|||
|
|
if (comment.replies && comment.replies.length > 0) {
|
|||
|
|
const targetReply = comment.replies.find(r => r.id === reply.replyToId);
|
|||
|
|
if (targetReply) {
|
|||
|
|
// 找到父回复,设置 replyToUser 并添加到同一评论的 replies 中
|
|||
|
|
if (targetReply.user) {
|
|||
|
|
reply.replyToUser = targetReply.user;
|
|||
|
|
}
|
|||
|
|
// 在父回复后插入
|
|||
|
|
const targetIndex = comment.replies.findIndex(r => r.id === reply.replyToId);
|
|||
|
|
if (targetIndex >= 0) {
|
|||
|
|
comment.replies.splice(targetIndex + 1, 0, reply);
|
|||
|
|
} else {
|
|||
|
|
comment.replies.push(reply);
|
|||
|
|
}
|
|||
|
|
found = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!found) {
|
|||
|
|
// 如果找不到父节点,可能是父节点还未处理,留到下一轮
|
|||
|
|
newRemainingReplies.push(reply);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
remainingReplies = newRemainingReplies;
|
|||
|
|
|
|||
|
|
// 如果这一轮没有处理任何回复,说明有循环依赖或找不到父节点,跳出
|
|||
|
|
if (remainingReplies.length === repliesList.length && iteration > 1) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果还有剩余回复(找不到父节点),作为第一个评论的回复(兜底处理)
|
|||
|
|
if (remainingReplies.length > 0 && topLevelComments.length > 0) {
|
|||
|
|
if (!topLevelComments[0].replies) {
|
|||
|
|
topLevelComments[0].replies = [];
|
|||
|
|
}
|
|||
|
|
topLevelComments[0].replies.push(...remainingReplies);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 对一级评论按时间倒序排序(最新的在前),确保与本地添加评论的顺序一致
|
|||
|
|
topLevelComments.sort((a, b) => {
|
|||
|
|
const timeA = new Date(a.createdAt || 0).getTime();
|
|||
|
|
const timeB = new Date(b.createdAt || 0).getTime();
|
|||
|
|
return timeB - timeA; // 倒序:最新在前
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 对每个评论的回复也按时间倒序排序
|
|||
|
|
topLevelComments.forEach(comment => {
|
|||
|
|
if (comment.replies && comment.replies.length > 0) {
|
|||
|
|
comment.replies.sort((a, b) => {
|
|||
|
|
const timeA = new Date(a.createdAt || 0).getTime();
|
|||
|
|
const timeB = new Date(b.createdAt || 0).getTime();
|
|||
|
|
return timeB - timeA; // 倒序:最新在前
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
processedComments = topLevelComments;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
...feed,
|
|||
|
|
user: {
|
|||
|
|
...feedUser,
|
|||
|
|
avatar: validAvatar,
|
|||
|
|
nickname: feedUser.nickname || feedUser.customId || feedUser.phone || '未知用户',
|
|||
|
|
},
|
|||
|
|
formattedTime: formattedTime,
|
|||
|
|
media: processedMedia,
|
|||
|
|
comments: processedComments || [], // 确保是数组
|
|||
|
|
visibleCommentCount: feed.visibleCommentCount || 20 // 默认显示20条评论,如果已存在则保留
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 排序动态
|
|||
|
|
const sortedFeeds = this.sortFeeds(processedFeeds);
|
|||
|
|
|
|||
|
|
// 去重处理
|
|||
|
|
let finalFeeds = sortedFeeds;
|
|||
|
|
if (this.data.page !== 1 && this.data.feedList && this.data.feedList.length > 0) {
|
|||
|
|
// 获取现有动态ID的集合
|
|||
|
|
const existingIds = new Set(this.data.feedList.map(item => item.id || item.uuid || item.dynamicId));
|
|||
|
|
// 过滤掉已经存在的动态
|
|||
|
|
finalFeeds = sortedFeeds.filter(feed => {
|
|||
|
|
const feedId = feed.id || feed.uuid || feed.dynamicId;
|
|||
|
|
// 如果没有ID或ID不存在于现有集合中保留动态
|
|||
|
|
return !feedId || !existingIds.has(feedId);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const feedsToSet = finalFeeds.filter(feed => feed && typeof feed === 'object');
|
|||
|
|
|
|||
|
|
const updatedFeedList = this.data.page === 1 ? feedsToSet : [...this.data.feedList, ...feedsToSet];
|
|||
|
|
const pendingFeedUuid = this.data.pendingFeedUuid;
|
|||
|
|
const foundTarget = pendingFeedUuid
|
|||
|
|
? updatedFeedList.some(feed => {
|
|||
|
|
const feedId = feed.uuid || feed.id || feed.dynamicId;
|
|||
|
|
return feedId && feedId === pendingFeedUuid;
|
|||
|
|
})
|
|||
|
|
: false;
|
|||
|
|
|
|||
|
|
// 🔥 提取唯一用户列表
|
|||
|
|
const uniqueUsers = this.extractUniqueUsers(updatedFeedList);
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
feedList: updatedFeedList,
|
|||
|
|
userList: uniqueUsers,
|
|||
|
|
page: this.data.page + 1,
|
|||
|
|
noMore: !res.data.data.hasMore,
|
|||
|
|
loading: false
|
|||
|
|
}, () => {
|
|||
|
|
this.afterFeedListUpdate(foundTarget, !res.data.data.hasMore);
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error(`=== 接口请求失败(${fullRequestUrl}) ===`, err);
|
|||
|
|
this.setData({ loading: false });
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '加载失败,请检查网络',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 🔥 从动态列表中提取唯一用户
|
|||
|
|
extractUniqueUsers(feedList) {
|
|||
|
|
const userMap = new Map();
|
|||
|
|
|
|||
|
|
if (!feedList || !Array.isArray(feedList)) {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
feedList.forEach(feed => {
|
|||
|
|
if (feed.user && feed.user.customId) {
|
|||
|
|
const customId = feed.user.customId;
|
|||
|
|
// 如果用户不存在或者当前动态更新的用户信息更完整,则更新
|
|||
|
|
if (!userMap.has(customId) ||
|
|||
|
|
(!userMap.get(customId).avatar && feed.user.avatar) ||
|
|||
|
|
(!userMap.get(customId).nickname && feed.user.nickname)) {
|
|||
|
|
userMap.set(customId, {
|
|||
|
|
customId: customId,
|
|||
|
|
avatar: feed.user.avatar || '/images/default-avatar.png',
|
|||
|
|
nickname: feed.user.nickname || feed.user.customId || '用户'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return Array.from(userMap.values());
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
afterFeedListUpdate(foundTarget, noMore) {
|
|||
|
|
const pendingUuid = this.data.pendingFeedUuid;
|
|||
|
|
if (!pendingUuid) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (foundTarget) {
|
|||
|
|
const targetFeed = this.data.feedList.find(feed => {
|
|||
|
|
const feedId = feed.uuid || feed.id || feed.dynamicId;
|
|||
|
|
return feedId && feedId === pendingUuid;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (targetFeed) {
|
|||
|
|
const targetId = targetFeed.uuid || targetFeed.id || targetFeed.dynamicId;
|
|||
|
|
const anchorId = `feed-item-${targetId}`;
|
|||
|
|
this.setData({
|
|||
|
|
scrollIntoFeedId: anchorId,
|
|||
|
|
highlightFeedUuid: targetId,
|
|||
|
|
pendingFeedUuid: ''
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (this.highlightTimer) {
|
|||
|
|
clearTimeout(this.highlightTimer);
|
|||
|
|
}
|
|||
|
|
this.highlightTimer = setTimeout(() => {
|
|||
|
|
this.setData({
|
|||
|
|
highlightFeedUuid: '',
|
|||
|
|
scrollIntoFeedId: ''
|
|||
|
|
});
|
|||
|
|
}, 4000);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!noMore) {
|
|||
|
|
this.loadFeedList();
|
|||
|
|
} else {
|
|||
|
|
this.setData({ pendingFeedUuid: '' });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
applyRedirectPayload(payload) {
|
|||
|
|
const safeLatitude = parseFloat(payload.latitude);
|
|||
|
|
const safeLongitude = parseFloat(payload.longitude);
|
|||
|
|
if (Number.isNaN(safeLatitude) || Number.isNaN(safeLongitude)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const safeRadius = Math.max(10, parseInt(payload.radius, 10) || this.data.radius);
|
|||
|
|
const feedUuid = payload.feedUuid || '';
|
|||
|
|
const redirectToken = payload.token || `${Date.now()}`;
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
latitude: safeLatitude,
|
|||
|
|
longitude: safeLongitude,
|
|||
|
|
radius: safeRadius,
|
|||
|
|
modeFrom: payload.mode || 'map',
|
|||
|
|
feedUuid,
|
|||
|
|
pendingFeedUuid: feedUuid,
|
|||
|
|
lastRedirectToken: redirectToken
|
|||
|
|
}, () => {
|
|||
|
|
this.resetAndLoadFeedList();
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 排序动态
|
|||
|
|
sortFeeds(feeds) {
|
|||
|
|
const currentUser = getApp().globalData.userInfo || {};
|
|||
|
|
|
|||
|
|
const currentGender = currentUser.gender !== undefined ? currentUser.gender : 0;
|
|||
|
|
|
|||
|
|
return [...feeds].sort((a, b) => {
|
|||
|
|
// 确保用户信息存在
|
|||
|
|
const aUser = a.user || {};
|
|||
|
|
const bUser = b.user || {};
|
|||
|
|
const isAFriend = aUser.isFriend || false;
|
|||
|
|
const isBFriend = bUser.isFriend || false;
|
|||
|
|
|
|||
|
|
// 好友优先级高于非好友
|
|||
|
|
if (isAFriend && !isBFriend) return -1;
|
|||
|
|
if (!isAFriend && isBFriend) return 1;
|
|||
|
|
|
|||
|
|
// 好友/非好友分组内排序
|
|||
|
|
const aCreateTime = new Date(a.createdAt || 0).getTime();
|
|||
|
|
const bCreateTime = new Date(b.createdAt || 0).getTime();
|
|||
|
|
|
|||
|
|
if (isAFriend && isBFriend) {
|
|||
|
|
// 好友:按发布时间倒序
|
|||
|
|
return bCreateTime - aCreateTime;
|
|||
|
|
} else {
|
|||
|
|
// 非好友:优先异性,再按时间倒序
|
|||
|
|
const aGender = aUser.gender !== undefined ? aUser.gender : currentGender;
|
|||
|
|
const bGender = bUser.gender !== undefined ? bUser.gender : currentGender;
|
|||
|
|
|
|||
|
|
const isAOpposite = aGender !== currentGender;
|
|||
|
|
const isBOpposite = bGender !== currentGender;
|
|||
|
|
|
|||
|
|
if (isAOpposite && !isBOpposite) return -1;
|
|||
|
|
if (!isAOpposite && isBOpposite) return 1;
|
|||
|
|
|
|||
|
|
// 同性别:按时间倒序
|
|||
|
|
return bCreateTime - aCreateTime;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 格式化时间
|
|||
|
|
formatTime(timeStr) {
|
|||
|
|
if (!timeStr) return '未知时间';
|
|||
|
|
|
|||
|
|
const createTime = new Date(timeStr);
|
|||
|
|
if (isNaN(createTime.getTime())) return '未知时间';
|
|||
|
|
|
|||
|
|
const now = new Date();
|
|||
|
|
const diffMinutes = Math.floor((now - createTime) / (1000 * 60));
|
|||
|
|
|
|||
|
|
// 5分钟内:刚刚
|
|||
|
|
if (diffMinutes < 5) return '刚刚';
|
|||
|
|
|
|||
|
|
// 格式化日期时间
|
|||
|
|
const year = createTime.getFullYear();
|
|||
|
|
const month = String(createTime.getMonth() + 1).padStart(2, '0');
|
|||
|
|
const day = String(createTime.getDate()).padStart(2, '0');
|
|||
|
|
const hour = String(createTime.getHours()).padStart(2, '0');
|
|||
|
|
const minute = String(createTime.getMinutes()).padStart(2, '0');
|
|||
|
|
|
|||
|
|
// 跨年份显示完整日期,同年份省略年份
|
|||
|
|
return year === this.data.currentYear
|
|||
|
|
? `${month}月${day}日 ${hour}:${minute}`
|
|||
|
|
: `${year}年${month}月${day}日 ${hour}:${minute}`;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 格式化评论时间
|
|||
|
|
formatCommentTime(timeStr) {
|
|||
|
|
if (!timeStr) return '';
|
|||
|
|
|
|||
|
|
const commentTime = new Date(timeStr);
|
|||
|
|
if (isNaN(commentTime.getTime())) return '';
|
|||
|
|
|
|||
|
|
const now = new Date();
|
|||
|
|
const diffMinutes = Math.floor((now - commentTime) / (1000 * 60));
|
|||
|
|
|
|||
|
|
// 5分钟内:显示"刚刚"
|
|||
|
|
if (diffMinutes < 5) return '刚刚';
|
|||
|
|
|
|||
|
|
// 格式化日期时间
|
|||
|
|
const year = commentTime.getFullYear();
|
|||
|
|
const month = commentTime.getMonth() + 1; // 不加前导0,直接显示月份
|
|||
|
|
const day = commentTime.getDate();
|
|||
|
|
const hour = String(commentTime.getHours()).padStart(2, '0');
|
|||
|
|
const minute = String(commentTime.getMinutes()).padStart(2, '0');
|
|||
|
|
|
|||
|
|
// 当前年份:显示不带年份的日期和时间,例如:8月24日 17:11
|
|||
|
|
// 非当前年份:显示带年份的日期和时间,例如:2024年 8月24日 17:11
|
|||
|
|
const currentYear = now.getFullYear();
|
|||
|
|
return year === currentYear
|
|||
|
|
? `${month}月${day}日 ${hour}:${minute}`
|
|||
|
|
: `${year}年 ${month}月${day}日 ${hour}:${minute}`;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 滚动到底部加载更多
|
|||
|
|
onReachBottom() {
|
|||
|
|
this.loadFeedList();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 返回首页功能
|
|||
|
|
navigateBackHome() {
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/map/map'
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
handlePost() {
|
|||
|
|
|
|||
|
|
// 跳转到发布页面
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: '/subpackages/media/edits/edits',
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('跳转失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '跳转发布页面失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onHide() {
|
|||
|
|
if (this.highlightTimer) {
|
|||
|
|
clearTimeout(this.highlightTimer);
|
|||
|
|
this.highlightTimer = null;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onUnload() {
|
|||
|
|
if (this.highlightTimer) {
|
|||
|
|
clearTimeout(this.highlightTimer);
|
|||
|
|
this.highlightTimer = null;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 图片预览
|
|||
|
|
previewImage(e) {
|
|||
|
|
try {
|
|||
|
|
// 获取当前点击的图片索引和动态索引
|
|||
|
|
const { index, feedIndex } = e.currentTarget.dataset;
|
|||
|
|
|
|||
|
|
// 获取当前动态的图片列表
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
if (!feed || !feed.media || !feed.media.length) {
|
|||
|
|
console.error('没有找到媒体数据');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优先使用原始图片URL或高质量图片,确保预览完整清晰的图片
|
|||
|
|
const imageUrls = feed.media
|
|||
|
|
.filter(item => item.type === 'image' && (item.originalUrl || item.url))
|
|||
|
|
.map(item => {
|
|||
|
|
// 如果有原图URL,使用原图;否则使用高质量优化URL
|
|||
|
|
if (item.originalUrl) {
|
|||
|
|
return getPreviewImageUrl(item.originalUrl);
|
|||
|
|
}
|
|||
|
|
return getPreviewImageUrl(item.url);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!imageUrls.length) {
|
|||
|
|
console.error('没有有效的图片URL');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔥 设置标志,防止预览关闭后触发页面刷新
|
|||
|
|
this._skipNextOnShowReload = true;
|
|||
|
|
|
|||
|
|
// 调用微信小程序的图片预览API
|
|||
|
|
wx.previewImage({
|
|||
|
|
current: imageUrls[index], // 当前显示图片的URL
|
|||
|
|
urls: imageUrls, // 需要预览的图片URL列表
|
|||
|
|
success: () => {
|
|||
|
|
// 预览打开成功,标志已设置,关闭时会触发 onShow
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('图片预览失败:', err);
|
|||
|
|
// 预览失败,清除标志
|
|||
|
|
this._skipNextOnShowReload = false;
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '预览图片失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('图片预览过程中出错:', error);
|
|||
|
|
// 出错时清除标志
|
|||
|
|
this._skipNextOnShowReload = false;
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '预览图片失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 图片加载成功
|
|||
|
|
onImageLoad(e) {
|
|||
|
|
const { index, feedIndex } = e.currentTarget.dataset;
|
|||
|
|
const feedList = this.data.feedList;
|
|||
|
|
|
|||
|
|
if (feedList[feedIndex] && feedList[feedIndex].media && feedList[feedIndex].media[index]) {
|
|||
|
|
// 更新对应图片的加载状态
|
|||
|
|
const updatePath = `feedList[${feedIndex}].media[${index}].loading`;
|
|||
|
|
this.setData({
|
|||
|
|
[updatePath]: false
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 图片加载失败
|
|||
|
|
onImageError(e) {
|
|||
|
|
const { index, feedIndex } = e.currentTarget.dataset;
|
|||
|
|
const feedList = this.data.feedList;
|
|||
|
|
|
|||
|
|
if (feedList[feedIndex] && feedList[feedIndex].media && feedList[feedIndex].media[index]) {
|
|||
|
|
// 加载失败也隐藏 loading
|
|||
|
|
const updatePath = `feedList[${feedIndex}].media[${index}].loading`;
|
|||
|
|
this.setData({
|
|||
|
|
[updatePath]: false
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 点击头像进入个人资料
|
|||
|
|
navigateToUserProfile(e) {
|
|||
|
|
const customId = e.currentTarget.dataset.customId;
|
|||
|
|
|
|||
|
|
if (!customId) return;
|
|||
|
|
|
|||
|
|
// 获取当前用户信息
|
|||
|
|
const currentUser = getApp().globalData.userInfo || {};
|
|||
|
|
|
|||
|
|
const currentUserId = currentUser.user.customId || '';
|
|||
|
|
|
|||
|
|
// 检查是否是当前用户自己
|
|||
|
|
if (customId === currentUserId) {
|
|||
|
|
this.navigateToSelfProfile(customId);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用friendAPI判断是否是好友关系
|
|||
|
|
this.checkFriendRelation(customId);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 跳转到个人主页
|
|||
|
|
navigateToSelfProfile(customId) {
|
|||
|
|
|
|||
|
|
const targetUrl = `/subpackages/profile/profile/profile?customId=${customId}`;
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: targetUrl,
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('跳转个人主页失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '跳转失败,请重试',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 检查好友关系
|
|||
|
|
async checkFriendRelation(customId) {
|
|||
|
|
try {
|
|||
|
|
const friendAPI = require('../../utils/friend-api.js');
|
|||
|
|
|
|||
|
|
// 显示加载提示
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '加载中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 获取好友详情
|
|||
|
|
const friendDetailResponse = await friendAPI.getFriendDetail(customId);
|
|||
|
|
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
if (friendDetailResponse && friendDetailResponse.code === 0 && friendDetailResponse.data){
|
|||
|
|
// 成功获取好友详情,说明是好友关系
|
|||
|
|
this.navigateToFriendProfile(customId);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到陌生人主页
|
|||
|
|
this.navigateToStrangerProfile(customId);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
console.error('检查好友关系失败:', error);
|
|||
|
|
|
|||
|
|
// 跳转到陌生人主页
|
|||
|
|
this.navigateToStrangerProfile(customId);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 跳转到好友主页
|
|||
|
|
navigateToFriendProfile(customId) {
|
|||
|
|
|
|||
|
|
const targetUrl = `/subpackages/social/friend-detail/friend-detail?customId=${customId}`;
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: targetUrl,
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('跳转好友主页失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '跳转失败,请重试',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 跳转到陌生人主页
|
|||
|
|
navigateToStrangerProfile(customId) {
|
|||
|
|
|
|||
|
|
const targetUrl = `/subpackages/social/user-preview/user-preview?customId=${customId}`;
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: targetUrl,
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('跳转陌生人主页失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '跳转失败,请重试',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理点赞
|
|||
|
|
async handleLike(e) {
|
|||
|
|
const feedUuid = e.currentTarget.dataset.feedUuid;
|
|||
|
|
const isLiked = e.currentTarget.dataset.isliked;
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex; // 获取当前动态索引
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查登录状态
|
|||
|
|
const app = getApp();
|
|||
|
|
if (!app.globalData.isLoggedIn) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请先登录',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: '/pages/login/login'
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TODO: 调用点赞API
|
|||
|
|
console.log('点赞动态:', feedUuid);
|
|||
|
|
|
|||
|
|
if(!isLiked){
|
|||
|
|
const response = await this.apiClient.addLikeDynamic(feedUuid);
|
|||
|
|
if(response){
|
|||
|
|
if(response.code==200){
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response.data.message,
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}else{
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response.message,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 更新点赞状态和数量
|
|||
|
|
this.updateLikeStatus(feedIndex, true);
|
|||
|
|
}else{
|
|||
|
|
const responseDelete= await this.apiClient.deleteLikeDynamic(feedUuid);
|
|||
|
|
if(responseDelete){
|
|||
|
|
if(responseDelete.code==200){
|
|||
|
|
wx.showToast({
|
|||
|
|
title: responseDelete.data.message,
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}else{
|
|||
|
|
wx.showToast({
|
|||
|
|
title: responseDelete.message,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 更新点赞状态和数量
|
|||
|
|
this.updateLikeStatus(feedIndex, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 新增更新点赞状态的方法
|
|||
|
|
updateLikeStatus(feedIndex, isLiked) {
|
|||
|
|
// 复制当前的动态列表
|
|||
|
|
const feedList = [...this.data.feedList];
|
|||
|
|
|
|||
|
|
// 检查当前动态是否存在
|
|||
|
|
if (feedList[feedIndex]) {
|
|||
|
|
// 初始化interactions对象(防止undefined错误)
|
|||
|
|
if (!feedList[feedIndex].interactions) {
|
|||
|
|
feedList[feedIndex].interactions = {
|
|||
|
|
likeCount: 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新点赞状态
|
|||
|
|
feedList[feedIndex].isLiked = isLiked;
|
|||
|
|
|
|||
|
|
// 更新点赞数量(点赞+1,取消点赞-1)
|
|||
|
|
feedList[feedIndex].interactions.likeCount =
|
|||
|
|
(feedList[feedIndex].interactions.likeCount || 0) + (isLiked ? 1 : -1);
|
|||
|
|
|
|||
|
|
// 确保数量不会小于0
|
|||
|
|
if (feedList[feedIndex].interactions.likeCount < 0) {
|
|||
|
|
feedList[feedIndex].interactions.likeCount = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新数据
|
|||
|
|
this.setData({
|
|||
|
|
feedList: feedList
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
// 处理评论 - 显示评论弹窗
|
|||
|
|
async handleComment(e) {
|
|||
|
|
const feedUuid = e.currentTarget.dataset.feedUuid;
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查登录状态
|
|||
|
|
const app = getApp();
|
|||
|
|
if (!app.globalData.isLoggedIn) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请先登录',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: '/pages/login/login'
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示弹窗并加载评论
|
|||
|
|
this.setData({
|
|||
|
|
showCommentModal: true,
|
|||
|
|
currentFeedUuid: feedUuid,
|
|||
|
|
currentFeedIndex: feedIndex,
|
|||
|
|
commentInputValue: ''
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载评论列表
|
|||
|
|
await this.loadComments(feedUuid, feedIndex);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载评论列表
|
|||
|
|
async loadComments(feedUuid, feedIndex) {
|
|||
|
|
try {
|
|||
|
|
// 先尝试从feed数据中获取评论
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
let comments = feed?.comments || [];
|
|||
|
|
|
|||
|
|
// 如果有评论数据,格式化时间
|
|||
|
|
if (comments && comments.length > 0) {
|
|||
|
|
comments = comments.map(comment => {
|
|||
|
|
return {
|
|||
|
|
...comment,
|
|||
|
|
formattedTime: comment.formattedTime || this.formatCommentTime(comment.createdAt || '')
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有评论数据,尝试从API获取(如果有获取评论的API)
|
|||
|
|
// TODO: 如果需要从API获取评论列表,在这里添加
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
currentComments: comments
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载评论失败:', error);
|
|||
|
|
this.setData({
|
|||
|
|
currentComments: []
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭评论弹窗
|
|||
|
|
closeCommentModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showCommentModal: false,
|
|||
|
|
currentFeedUuid: '',
|
|||
|
|
currentFeedIndex: -1,
|
|||
|
|
currentComments: [],
|
|||
|
|
commentInputValue: ''
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 防止点击内容区域关闭弹窗
|
|||
|
|
preventClose() {
|
|||
|
|
// 空函数,阻止事件冒泡
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 评论输入
|
|||
|
|
onCommentInput(e) {
|
|||
|
|
this.setData({
|
|||
|
|
commentInputValue: e.detail.value
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 提交评论
|
|||
|
|
async submitComment() {
|
|||
|
|
const { currentFeedUuid, currentFeedIndex, commentInputValue } = this.data;
|
|||
|
|
|
|||
|
|
if (!commentInputValue || !commentInputValue.trim()) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请输入评论内容',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前用户信息
|
|||
|
|
const app = getApp();
|
|||
|
|
const currentUser = app.globalData.userInfo?.user || {};
|
|||
|
|
const nickname = currentUser.nickname || currentUser.customId || '未知用户';
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const response = await this.apiClient.addCommentDynamic(currentFeedUuid, commentInputValue.trim(), null, nickname);
|
|||
|
|
|
|||
|
|
if (response && response.code === 200) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '评论成功',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建新评论对象(新发布的评论默认是自己的)
|
|||
|
|
const newComment = {
|
|||
|
|
id: response.data?.id || `comment_${Date.now()}`,
|
|||
|
|
content: commentInputValue.trim(),
|
|||
|
|
createdAt: response.data?.createdAt || new Date().toISOString(),
|
|||
|
|
user: {
|
|||
|
|
nickname: currentUser.nickname || currentUser.customId || '未知用户',
|
|||
|
|
avatar: currentUser.avatar || '/images/findme-logo.png',
|
|||
|
|
customId: currentUser.customId || ''
|
|||
|
|
},
|
|||
|
|
formattedTime: this.formatCommentTime(response.data?.createdAt || new Date().toISOString()),
|
|||
|
|
replies: [],
|
|||
|
|
isOwn: true // 新发布的评论默认是自己的
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 清空输入框
|
|||
|
|
this.setData({
|
|||
|
|
commentInputValue: ''
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新动态的评论列表和数量 - 使用路径更新方式避免渲染错误
|
|||
|
|
const feed = this.data.feedList[currentFeedIndex];
|
|||
|
|
if (feed) {
|
|||
|
|
// 初始化 comments 数组(如果不存在)
|
|||
|
|
const currentComments = feed.comments || [];
|
|||
|
|
const updatedComments = [newComment, ...currentComments];
|
|||
|
|
|
|||
|
|
// 更新评论数量
|
|||
|
|
const currentCommentCount = feed.interactions?.commentCount || 0;
|
|||
|
|
const visibleCommentCount = feed.visibleCommentCount || 5;
|
|||
|
|
|
|||
|
|
// 使用路径更新方式,确保小程序能正确检测数据变化
|
|||
|
|
const updatePath = {};
|
|||
|
|
updatePath[`feedList[${currentFeedIndex}].comments`] = updatedComments;
|
|||
|
|
updatePath[`feedList[${currentFeedIndex}].interactions.commentCount`] = currentCommentCount + 1;
|
|||
|
|
|
|||
|
|
// 确保 visibleCommentCount 存在且至少为20
|
|||
|
|
if (!feed.visibleCommentCount || feed.visibleCommentCount < 20) {
|
|||
|
|
updatePath[`feedList[${currentFeedIndex}].visibleCommentCount`] = 20;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.setData(updatePath);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新弹窗中的评论列表
|
|||
|
|
await this.loadComments(currentFeedUuid, currentFeedIndex);
|
|||
|
|
|
|||
|
|
// 延迟关闭评论弹窗,让用户看到成功提示
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.closeCommentModal();
|
|||
|
|
}, 800);
|
|||
|
|
} else {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response?.message || '评论失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('提交评论失败:', error);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '评论失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 删除评论
|
|||
|
|
async deleteComment(e) {
|
|||
|
|
const feedUuid = e.currentTarget.dataset.feedUuid;
|
|||
|
|
const commentId = e.currentTarget.dataset.feedCommentId;
|
|||
|
|
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查登录状态
|
|||
|
|
const app = getApp();
|
|||
|
|
if (!app.globalData.isLoggedIn) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请先登录',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: '/pages/login/login'
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
const response = await this.apiClient.deleteCommentDynamic(feedUuid,commentId);
|
|||
|
|
if(response){
|
|||
|
|
if(response.code==200){
|
|||
|
|
wx.showToast({
|
|||
|
|
title: "删除成功",
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}else{
|
|||
|
|
wx.showToast({
|
|||
|
|
title: "删除失败",
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 展开更多评论(每次增加20条,懒加载模式)
|
|||
|
|
expandComments(e) {
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
this.loadMoreComments(feedIndex);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载更多评论(懒加载,每次加载20条)
|
|||
|
|
loadMoreComments(feedIndex) {
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
|
|||
|
|
if (feed && feed.comments) {
|
|||
|
|
const currentCount = feed.visibleCommentCount || 20; // 当前显示的评论数量
|
|||
|
|
const totalCount = feed.comments.length; // 评论总数
|
|||
|
|
|
|||
|
|
// 如果已经显示全部,不继续加载
|
|||
|
|
if (currentCount >= totalCount) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 每次增加20条,但不超过总数
|
|||
|
|
// 例如:初始20条 -> 滚动后40条 -> 再滚动60条 -> 以此类推
|
|||
|
|
const newCount = Math.min(currentCount + 20, totalCount);
|
|||
|
|
|
|||
|
|
if (newCount > currentCount) {
|
|||
|
|
// 使用路径更新方式
|
|||
|
|
this.setData({
|
|||
|
|
[`feedList[${feedIndex}].visibleCommentCount`]: newCount
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 评论区域滚动到底部时触发(懒加载评论和回复)
|
|||
|
|
onCommentScrollToLower(e) {
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
if (feedIndex !== undefined && feedIndex !== null) {
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
|
|||
|
|
// 先尝试加载更多回复(优先处理回复)
|
|||
|
|
if (feed && feed.comments) {
|
|||
|
|
let hasMoreReplies = false;
|
|||
|
|
feed.comments.forEach((comment, commentIndex) => {
|
|||
|
|
if (comment.replies && comment.replies.length > 0) {
|
|||
|
|
const currentCount = comment.visibleReplyCount || 5;
|
|||
|
|
const totalCount = comment.replies.length;
|
|||
|
|
|
|||
|
|
// 如果有未显示的回复,自动加载更多
|
|||
|
|
if (currentCount < totalCount) {
|
|||
|
|
const newCount = Math.min(currentCount + 20, totalCount);
|
|||
|
|
if (newCount > currentCount) {
|
|||
|
|
this.setData({
|
|||
|
|
[`feedList[${feedIndex}].comments[${commentIndex}].visibleReplyCount`]: newCount
|
|||
|
|
});
|
|||
|
|
hasMoreReplies = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果有加载了回复,不继续加载评论(避免一次性加载太多)
|
|||
|
|
if (hasMoreReplies) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有更多回复需要加载,则加载更多评论
|
|||
|
|
this.loadMoreComments(feedIndex);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 展开更多回复(每次增加20条)
|
|||
|
|
expandReplies(e) {
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
const commentIndex = e.currentTarget.dataset.commentIndex;
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
|
|||
|
|
if (feed && feed.comments && feed.comments[commentIndex]) {
|
|||
|
|
const comment = feed.comments[commentIndex];
|
|||
|
|
const currentCount = comment.visibleReplyCount || 5; // 当前显示的回复数量(默认5条)
|
|||
|
|
const totalCount = comment.replies ? comment.replies.length : 0; // 回复总数
|
|||
|
|
|
|||
|
|
// 每次增加20条,但不超过总数
|
|||
|
|
// 例如:初始5条 -> 点击后25条 -> 再点击45条 -> 以此类推
|
|||
|
|
const newCount = Math.min(currentCount + 20, totalCount);
|
|||
|
|
|
|||
|
|
if (newCount > currentCount) {
|
|||
|
|
// 使用路径更新方式
|
|||
|
|
this.setData({
|
|||
|
|
[`feedList[${feedIndex}].comments[${commentIndex}].visibleReplyCount`]: newCount
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 点击回复按钮
|
|||
|
|
handleReplyClick(e) {
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
const commentId = e.currentTarget.dataset.commentId;
|
|||
|
|
const commentIndex = e.currentTarget.dataset.commentIndex;
|
|||
|
|
const replyId = e.currentTarget.dataset.replyId; // 如果是回复二级评论,这是被回复的二级评论ID
|
|||
|
|
const replyToUserName = e.currentTarget.dataset.replyToUser || '';
|
|||
|
|
|
|||
|
|
// 生成唯一key:如果有replyId,说明是回复二级回复,key应该包含replyId
|
|||
|
|
const replyKey = replyId
|
|||
|
|
? `feed_${feedIndex}_comment_${commentId}_reply_${replyId}`
|
|||
|
|
: `feed_${feedIndex}_comment_${commentId}`;
|
|||
|
|
|
|||
|
|
// 切换回复输入框显示状态
|
|||
|
|
const showReplyInput = { ...this.data.showReplyInput };
|
|||
|
|
|
|||
|
|
// 如果当前输入框已显示,则关闭;否则打开
|
|||
|
|
if (showReplyInput[replyKey]) {
|
|||
|
|
showReplyInput[replyKey] = false;
|
|||
|
|
this.setData({
|
|||
|
|
showReplyInput: showReplyInput,
|
|||
|
|
replyInputValue: '',
|
|||
|
|
replyingCommentId: null,
|
|||
|
|
replyingCommentIndex: null,
|
|||
|
|
replyingToCommentId: null
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 关闭其他所有输入框
|
|||
|
|
Object.keys(showReplyInput).forEach(key => {
|
|||
|
|
showReplyInput[key] = false;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 打开当前输入框
|
|||
|
|
showReplyInput[replyKey] = true;
|
|||
|
|
|
|||
|
|
// 获取feedUuid
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
const feedUuid = feed ? (feed.uuid || feed.id || feed.dynamicId) : '';
|
|||
|
|
|
|||
|
|
// 如果有replyId,说明是回复二级评论,需要设置replyToId
|
|||
|
|
this.setData({
|
|||
|
|
showReplyInput: showReplyInput,
|
|||
|
|
replyInputValue: replyToUserName ? `@${replyToUserName} ` : '',
|
|||
|
|
replyingCommentId: commentId,
|
|||
|
|
replyingCommentIndex: commentIndex,
|
|||
|
|
replyingToCommentId: replyId || null,
|
|||
|
|
currentFeedIndex: feedIndex,
|
|||
|
|
currentFeedUuid: feedUuid
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 回复输入
|
|||
|
|
onReplyInput(e) {
|
|||
|
|
this.setData({
|
|||
|
|
replyInputValue: e.detail.value
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 提交回复
|
|||
|
|
async submitReply(e) {
|
|||
|
|
// 防止重复点击
|
|||
|
|
if (this.data.submittingReply) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const feedIndex = e.currentTarget.dataset.feedIndex;
|
|||
|
|
const commentId = e.currentTarget.dataset.commentId;
|
|||
|
|
const commentIndex = e.currentTarget.dataset.commentIndex;
|
|||
|
|
const { replyInputValue, replyingToCommentId } = this.data;
|
|||
|
|
|
|||
|
|
// 移除 @用户名 前缀后,检查剩余内容是否为空
|
|||
|
|
const contentWithoutMention = replyInputValue ? replyInputValue.trim().replace(/^@[\S]+\s+/, '') : '';
|
|||
|
|
if (!replyInputValue || !replyInputValue.trim() || !contentWithoutMention) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请输入回复内容',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置提交状态
|
|||
|
|
this.setData({
|
|||
|
|
submittingReply: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 获取feedUuid
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
if (!feed) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const feedUuid = feed.uuid || feed.id || feed.dynamicId;
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查登录状态并获取当前用户信息
|
|||
|
|
const app = getApp();
|
|||
|
|
if (!app.globalData.isLoggedIn) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请先登录',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: '/pages/login/login'
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前用户信息
|
|||
|
|
const currentUser = app.globalData.userInfo?.user || {};
|
|||
|
|
const nickname = currentUser.nickname || currentUser.customId || '未知用户';
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 调用API提交回复(如果replyingToCommentId存在,说明是回复二级评论)
|
|||
|
|
const replyToId = replyingToCommentId || commentId;
|
|||
|
|
const response = await this.apiClient.addCommentDynamic(feedUuid, replyInputValue.trim(), replyToId, nickname);
|
|||
|
|
|
|||
|
|
if (response && response.code === 200) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '回复成功',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建新回复对象(新发布的回复默认是自己的)
|
|||
|
|
const newReply = {
|
|||
|
|
id: response.data?.id || `reply_${Date.now()}`,
|
|||
|
|
content: replyInputValue.trim().replace(/^@[\S]+\s+/, ''), // 移除@用户名前缀
|
|||
|
|
createdAt: response.data?.createdAt || new Date().toISOString(),
|
|||
|
|
user: {
|
|||
|
|
nickname: currentUser.nickname || currentUser.customId || '未知用户',
|
|||
|
|
avatar: currentUser.avatar || '/images/findme-logo.png',
|
|||
|
|
customId: currentUser.customId || ''
|
|||
|
|
},
|
|||
|
|
formattedTime: this.formatCommentTime(response.data?.createdAt || new Date().toISOString()),
|
|||
|
|
replyToId: replyToId,
|
|||
|
|
replyToUser: replyingToCommentId ? this.findReplyUser(this.data.feedList[feedIndex], commentId, replyingToCommentId) : null,
|
|||
|
|
isOwn: true // 新发布的回复默认是自己的
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 更新动态的评论列表 - 使用路径更新方式
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
if (feed && feed.comments && feed.comments[commentIndex]) {
|
|||
|
|
const comment = feed.comments[commentIndex];
|
|||
|
|
|
|||
|
|
// 初始化 replies 数组(如果不存在)
|
|||
|
|
const currentReplies = comment.replies || [];
|
|||
|
|
const updatedReplies = [newReply, ...currentReplies];
|
|||
|
|
|
|||
|
|
// 更新评论数量
|
|||
|
|
const currentCommentCount = feed.interactions?.commentCount || 0;
|
|||
|
|
|
|||
|
|
// 使用路径更新方式,确保小程序能正确检测数据变化
|
|||
|
|
const updatePath = {};
|
|||
|
|
updatePath[`feedList[${feedIndex}].comments[${commentIndex}].replies`] = updatedReplies;
|
|||
|
|
updatePath[`feedList[${feedIndex}].interactions.commentCount`] = currentCommentCount + 1;
|
|||
|
|
|
|||
|
|
// 如果当前显示的回复数少于5条,增加到5条以确保能看到新回复
|
|||
|
|
const currentVisibleReplyCount = comment.visibleReplyCount || 5;
|
|||
|
|
if (currentVisibleReplyCount < 5) {
|
|||
|
|
updatePath[`feedList[${feedIndex}].comments[${commentIndex}].visibleReplyCount`] = 5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清空回复输入框和状态
|
|||
|
|
// 根据是否有replyingToCommentId来生成正确的key
|
|||
|
|
const currentReplyingToCommentId = this.data.replyingToCommentId;
|
|||
|
|
const replyKey = currentReplyingToCommentId
|
|||
|
|
? `feed_${feedIndex}_comment_${commentId}_reply_${currentReplyingToCommentId}`
|
|||
|
|
: `feed_${feedIndex}_comment_${commentId}`;
|
|||
|
|
const showReplyInput = { ...this.data.showReplyInput };
|
|||
|
|
showReplyInput[replyKey] = false;
|
|||
|
|
|
|||
|
|
updatePath.showReplyInput = showReplyInput;
|
|||
|
|
updatePath.replyInputValue = '';
|
|||
|
|
updatePath.replyingCommentId = null;
|
|||
|
|
updatePath.replyingCommentIndex = null;
|
|||
|
|
updatePath.replyingToCommentId = null;
|
|||
|
|
updatePath.submittingReply = false;
|
|||
|
|
|
|||
|
|
this.setData(updatePath);
|
|||
|
|
} else {
|
|||
|
|
// 如果找不到对应数据,只更新状态
|
|||
|
|
this.setData({
|
|||
|
|
submittingReply: false
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
this.setData({
|
|||
|
|
submittingReply: false // 恢复提交状态
|
|||
|
|
});
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response?.message || '回复失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('提交回复失败:', error);
|
|||
|
|
this.setData({
|
|||
|
|
submittingReply: false // 恢复提交状态
|
|||
|
|
});
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '回复失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 查找被回复的用户信息
|
|||
|
|
findReplyUser(feed, commentId, replyId) {
|
|||
|
|
if (!feed || !feed.comments) return null;
|
|||
|
|
|
|||
|
|
const comment = feed.comments.find(c => c.id === commentId);
|
|||
|
|
if (!comment || !comment.replies) return null;
|
|||
|
|
|
|||
|
|
const reply = comment.replies.find(r => r.id === replyId);
|
|||
|
|
return reply ? reply.user : null;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 删除评论
|
|||
|
|
async deleteComment(e) {
|
|||
|
|
const { feedIndex, commentId, commentIndex } = e.currentTarget.dataset;
|
|||
|
|
|
|||
|
|
// 检查参数(feedIndex可能是0,所以不能用!feedIndex判断)
|
|||
|
|
if (feedIndex === undefined || feedIndex === null || commentId === undefined || commentId === null || commentIndex === undefined || commentIndex === null) {
|
|||
|
|
console.error('删除评论参数错误:', { feedIndex, commentId, commentIndex, dataset: e.currentTarget.dataset });
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '参数错误',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('删除评论,参数:', { feedIndex, commentId, commentIndex });
|
|||
|
|
|
|||
|
|
// 确认删除
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '删除评论',
|
|||
|
|
content: '确定要删除这条评论吗?',
|
|||
|
|
confirmText: '删除',
|
|||
|
|
confirmColor: '#ff4757',
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (!res.confirm) return;
|
|||
|
|
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
if (!feed) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const feedUuid = feed.uuid || feed.id || feed.dynamicId;
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '删除中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const response = await this.apiClient.deleteCommentDynamic(feedUuid, commentId);
|
|||
|
|
|
|||
|
|
if (response && response.code === 200) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '删除成功',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 从本地列表中删除评论(通过ID匹配,更安全)
|
|||
|
|
const currentComments = this.data.feedList[feedIndex].comments || [];
|
|||
|
|
const updatedComments = currentComments.filter((c) => c.id !== commentId);
|
|||
|
|
const currentCommentCount = (this.data.feedList[feedIndex].interactions.commentCount || 0) - 1;
|
|||
|
|
|
|||
|
|
// 使用路径更新方式
|
|||
|
|
const updatePath = {};
|
|||
|
|
updatePath[`feedList[${feedIndex}].comments`] = updatedComments;
|
|||
|
|
updatePath[`feedList[${feedIndex}].interactions.commentCount`] = Math.max(0, currentCommentCount);
|
|||
|
|
|
|||
|
|
this.setData(updatePath);
|
|||
|
|
} else {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response?.message || '删除失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('删除评论失败:', error);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '删除失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 删除回复
|
|||
|
|
async deleteReply(e) {
|
|||
|
|
const { feedIndex, commentId, commentIndex, replyId, replyIndex } = e.currentTarget.dataset;
|
|||
|
|
|
|||
|
|
// 检查参数(feedIndex可能是0,所以不能用!feedIndex判断)
|
|||
|
|
if (feedIndex === undefined || feedIndex === null || commentId === undefined || commentId === null || commentIndex === undefined || commentIndex === null || replyId === undefined || replyId === null || replyIndex === undefined || replyIndex === null) {
|
|||
|
|
console.error('删除回复参数错误:', { feedIndex, commentId, commentIndex, replyId, replyIndex, dataset: e.currentTarget.dataset });
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '参数错误',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('删除回复,参数:', { feedIndex, commentId, commentIndex, replyId, replyIndex });
|
|||
|
|
|
|||
|
|
// 确认删除
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '删除回复',
|
|||
|
|
content: '确定要删除这条回复吗?',
|
|||
|
|
confirmText: '删除',
|
|||
|
|
confirmColor: '#ff4757',
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (!res.confirm) return;
|
|||
|
|
|
|||
|
|
const feed = this.data.feedList[feedIndex];
|
|||
|
|
if (!feed) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const feedUuid = feed.uuid || feed.id || feed.dynamicId;
|
|||
|
|
if (!feedUuid) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '动态ID不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '删除中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const response = await this.apiClient.deleteCommentDynamic(feedUuid, replyId);
|
|||
|
|
|
|||
|
|
if (response && response.code === 200) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '删除成功',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 1000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 从本地列表中删除回复(通过ID匹配,更安全)
|
|||
|
|
const currentComments = this.data.feedList[feedIndex].comments || [];
|
|||
|
|
// 通过commentId查找评论,而不是使用commentIndex(更安全)
|
|||
|
|
const targetCommentIndex = currentComments.findIndex(c => c.id === commentId);
|
|||
|
|
if (targetCommentIndex >= 0) {
|
|||
|
|
const targetComment = currentComments[targetCommentIndex];
|
|||
|
|
if (targetComment && targetComment.replies) {
|
|||
|
|
const updatedReplies = targetComment.replies.filter((r) => r.id !== replyId);
|
|||
|
|
const currentCommentCount = (this.data.feedList[feedIndex].interactions.commentCount || 0) - 1;
|
|||
|
|
|
|||
|
|
// 使用路径更新方式
|
|||
|
|
const updatePath = {};
|
|||
|
|
updatePath[`feedList[${feedIndex}].comments[${targetCommentIndex}].replies`] = updatedReplies;
|
|||
|
|
updatePath[`feedList[${feedIndex}].interactions.commentCount`] = Math.max(0, currentCommentCount);
|
|||
|
|
|
|||
|
|
this.setData(updatePath);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: response?.message || '删除失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('删除回复失败:', error);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '删除失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 1500
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|