feat:修改已知bug
This commit is contained in:
parent
542b1bd4f0
commit
a1bb302be8
@ -1,7 +1,7 @@
|
||||
# 开发环境配置
|
||||
# HBuilderX「运行」时自动加载;CLI 用 --mode development
|
||||
# VITE_API_BASE_URL=http://192.168.110.60:8080
|
||||
VITE_API_BASE_URL=https://api.topfans.online
|
||||
VITE_API_BASE_URL=http://192.168.110.60:8080
|
||||
# VITE_API_BASE_URL=https://api.topfans.online
|
||||
# WebSocket 地址:如与 API 同源可省略(自动从 VITE_API_BASE_URL 推导 http→ws、https→wss)
|
||||
# 独立部署时直接覆盖,例如:ws://192.168.110.60:8081
|
||||
VITE_WS_BASE_URL=ws://192.168.110.60:8080
|
||||
|
||||
@ -215,7 +215,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted } from 'vue';
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app';
|
||||
import { onLoad, onShow, onHide, onUnload } from '@dcloudio/uni-app';
|
||||
import { getAssetDetailApi, getMintOrderDetailApi, likeAssetApi, unlikeAssetApi, getAssetMaterialsApi, getAssetLikersApi } from '@/utils/api.js';
|
||||
import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js';
|
||||
import LikeUsersModal from '@/pages/components/LikeUsersModal.vue';
|
||||
@ -295,17 +295,20 @@ const isLiked = ref(false);
|
||||
const likeCount = ref(0);
|
||||
const liking = ref(false);
|
||||
|
||||
// 当前用户头像
|
||||
// 当前用户信息
|
||||
const userAvatarUrl = ref('');
|
||||
const currentUserId = ref('');
|
||||
const currentUserNickname = ref('');
|
||||
|
||||
// 加载当前用户信息
|
||||
const loadCurrentUser = () => {
|
||||
try {
|
||||
const userStr = uni.getStorageSync('user');
|
||||
if (userStr) {
|
||||
const userInfo = JSON.parse(userStr);
|
||||
if (!userStr) return;
|
||||
const userInfo = typeof userStr === 'string' ? JSON.parse(userStr) : userStr;
|
||||
userAvatarUrl.value = userInfo?.avatar_url || '';
|
||||
}
|
||||
currentUserId.value = String(userInfo?.uid || userInfo?.user_id || '');
|
||||
currentUserNickname.value = userInfo?.nickname || '';
|
||||
} catch (e) {
|
||||
console.error('解析用户信息失败:', e);
|
||||
}
|
||||
@ -325,15 +328,10 @@ const ellipseConfig = [
|
||||
// 点赞用户数据(从API加载)
|
||||
const likedUsers = ref([]);
|
||||
|
||||
// 加载点赞用户头像
|
||||
const loadLikedUsers = async (assetId) => {
|
||||
try {
|
||||
likedUsersLoading.value = true;
|
||||
// 加载点赞用户
|
||||
const res = await getAssetLikersApi(assetId, 20, 0);
|
||||
if (res.code === 200 && res.data?.users) {
|
||||
// 映射到显示格式(头像展示用)
|
||||
likedUsers.value = res.data?.users.slice(0, 6).map((user, index) => {
|
||||
// 把 API 返回的原始点赞用户映射成展示用的头像数组(最多 6 个,按椭圆排列)
|
||||
const mapLikersToDisplay = (users) => {
|
||||
if (!Array.isArray(users) || !users.length) return [];
|
||||
return users.slice(0, 6).map((user, index) => {
|
||||
const config = ellipseConfig[index] || { ellipseX: 0, ellipseY: 0, size: 1 };
|
||||
return {
|
||||
avatar: user.avatar || '/static/sucai/default-avatar.png',
|
||||
@ -342,13 +340,29 @@ const loadLikedUsers = async (assetId) => {
|
||||
size: config.size
|
||||
};
|
||||
});
|
||||
// 设置全部点赞用户用于弹窗展示
|
||||
allLikedUsers.value = res.data?.users.map(user => ({
|
||||
};
|
||||
|
||||
// 把 API 返回的原始点赞用户映射成弹窗用的完整列表
|
||||
const mapLikersToModal = (users) => {
|
||||
if (!Array.isArray(users)) return [];
|
||||
return users.map(user => ({
|
||||
userId: user.user_id,
|
||||
nickname: user.nickname || '匿名用户',
|
||||
avatar: user.avatar || '/static/sucai/default-avatar.png',
|
||||
liked_at: user.liked_at || 0
|
||||
}));
|
||||
};
|
||||
|
||||
// 加载点赞用户头像
|
||||
const loadLikedUsers = async (assetId) => {
|
||||
if (!assetId) return;
|
||||
try {
|
||||
likedUsersLoading.value = true;
|
||||
// 加载点赞用户
|
||||
const res = await getAssetLikersApi(assetId, 20, 0);
|
||||
if (res.code === 200 && res.data?.users) {
|
||||
likedUsers.value = mapLikersToDisplay(res.data.users);
|
||||
allLikedUsers.value = mapLikersToModal(res.data.users);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载点赞用户失败:', e);
|
||||
@ -540,21 +554,16 @@ const copyHash = () => {
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
console.log('loadData 开始执行');
|
||||
loading.value = true;
|
||||
loadError.value = '';
|
||||
|
||||
try {
|
||||
let assetId = assetIdParam.value;
|
||||
console.log('assetIdParam:', assetIdParam.value, 'orderIdParam:', orderIdParam.value);
|
||||
|
||||
if (!assetId && orderIdParam.value) {
|
||||
console.log('通过 order_id 获取资产信息');
|
||||
const mintRes = await getMintOrderDetailApi(orderIdParam.value);
|
||||
console.log('getMintOrderDetailApi 响应:', mintRes);
|
||||
if (mintRes.code === 200 && mintRes.data?.asset?.asset_id) {
|
||||
assetId = mintRes.data.asset.asset_id;
|
||||
console.log('获取到 assetId:', assetId);
|
||||
} else {
|
||||
throw new Error('获取铸造订单详情失败');
|
||||
}
|
||||
@ -562,9 +571,7 @@ const loadData = async () => {
|
||||
|
||||
if (!assetId) throw new Error('藏品信息不完整');
|
||||
|
||||
console.log('调用 getAssetDetailApi,assetId:', assetId);
|
||||
const res = await getAssetDetailApi(assetId);
|
||||
console.log('getAssetDetailApi 响应:', res);
|
||||
if (res.code === 200 && res.data?.asset) {
|
||||
const asset = res.data.asset;
|
||||
assetData.value = asset;
|
||||
@ -579,12 +586,9 @@ const loadData = async () => {
|
||||
if (asset.exhibition_expire_at) {
|
||||
startCountdown();
|
||||
}
|
||||
console.log(res.data)
|
||||
|
||||
// 异步加载封面图片,不阻塞页面渲染
|
||||
console.log('开始加载封面图片:', asset.cover_url);
|
||||
getAssetCoverRealUrl(asset.cover_url).then(async (url) => {
|
||||
console.log('封面图片加载成功:', url);
|
||||
coverUrl.value = url;
|
||||
if (isLenticularAsset.value) {
|
||||
// 参考 success.vue 逻辑:通过 bindAssetMaterialsApi 获取素材列表
|
||||
@ -593,7 +597,6 @@ const loadData = async () => {
|
||||
// 调用素材接口获取完整的素材列表
|
||||
try {
|
||||
const materialsRes = await getAssetMaterialsApi(assetId)
|
||||
console.log('getAssetMaterialsApi 响应:', materialsRes)
|
||||
if (materialsRes.code === 200 && materialsRes.data) {
|
||||
const materialsList = Array.isArray(materialsRes.data) ? materialsRes.data : materialsRes.data.materials || []
|
||||
// 找到 bg 和 main 素材
|
||||
@ -619,7 +622,6 @@ const loadData = async () => {
|
||||
console.error('加载封面图片失败:', err);
|
||||
coverUrl.value = ''; // 失败时不显示默认图片
|
||||
});
|
||||
console.log('loadData 执行成功');
|
||||
// 异步加载点赞用户头像
|
||||
loadLikedUsers(assetId);
|
||||
} else {
|
||||
@ -630,7 +632,6 @@ const loadData = async () => {
|
||||
loadError.value = err.message || '加载失败,请重试';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
console.log('loadData 执行完成,loading:', loading.value);
|
||||
}
|
||||
};
|
||||
|
||||
@ -643,6 +644,29 @@ const startCountdown = () => {
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 把当前用户乐观插入到点赞用户列表(让「创作者信息模块」立刻反映本次点赞)
|
||||
const prependCurrentUserToLikers = () => {
|
||||
if (!userAvatarUrl.value) return;
|
||||
const meAvatar = userAvatarUrl.value;
|
||||
// 1) 顶部头像展示列表:去重后插到最前,重新应用椭圆配置
|
||||
const nextDisplay = [
|
||||
{ avatar: meAvatar },
|
||||
...likedUsers.value.filter(u => u.avatar !== meAvatar)
|
||||
];
|
||||
likedUsers.value = mapLikersToDisplay(nextDisplay);
|
||||
// 2) 弹窗用完整列表:去重后插到最前
|
||||
const meRow = {
|
||||
userId: currentUserId.value,
|
||||
nickname: currentUserNickname.value || '我',
|
||||
avatar: meAvatar,
|
||||
liked_at: Date.now()
|
||||
};
|
||||
allLikedUsers.value = [
|
||||
meRow,
|
||||
...allLikedUsers.value.filter(u => u.avatar !== meAvatar)
|
||||
];
|
||||
};
|
||||
|
||||
// 点赞
|
||||
const handleLike = async () => {
|
||||
if (liking.value || !assetData.value.asset_id) return;
|
||||
@ -653,9 +677,15 @@ const handleLike = async () => {
|
||||
// isLiked.value = false;
|
||||
// likeCount.value = Math.max(0, likeCount.value - 1);
|
||||
// } else {
|
||||
await likeAssetApi(assetData.value.asset_id);
|
||||
const res = await likeAssetApi(assetData.value.asset_id);
|
||||
isLiked.value = true;
|
||||
likeCount.value += 1;
|
||||
// 优先用后端返回的最新 like_count;没有则本地 +1
|
||||
const serverCount = res?.data?.like_count;
|
||||
likeCount.value = typeof serverCount === 'number' ? serverCount : likeCount.value + 1;
|
||||
// 乐观更新「创作者信息模块」——立刻把当前用户头像插到第一位
|
||||
prependCurrentUserToLikers();
|
||||
// 后台静默拉取一次真实列表,保证后续顺序、昵称、avatar URL 与服务端一致
|
||||
loadLikedUsers(assetData.value.asset_id);
|
||||
// }
|
||||
// 通知展馆页面更新点赞数
|
||||
uni.$emit('assetLikeChanged', {
|
||||
@ -669,7 +699,7 @@ const handleLike = async () => {
|
||||
// 显示后端返回的实际错误信息
|
||||
const showMsg = errData || errMsg || '点赞失败';
|
||||
uni.showToast({ title: showMsg, icon: 'none', duration: 2000 });
|
||||
console.log('点赞错误:', err);
|
||||
console.error('点赞失败:', err);
|
||||
} finally {
|
||||
liking.value = false;
|
||||
}
|
||||
@ -712,12 +742,6 @@ const handleCraftMint = async () => {
|
||||
const bgImagePath = isCraftLenticular.value
|
||||
? lenticularLayers.value.find((l) => l.id === 'base')?.src || ''
|
||||
: undefined;
|
||||
console.log('[asset-detail] handleCraftMint', {
|
||||
isCraftLenticular: isCraftLenticular.value,
|
||||
bgImagePath: bgImagePath,
|
||||
lenticularLayers: lenticularLayers.value.map(l => ({ id: l.id, src: l.src ? l.src.substring(0, 30) : '' })),
|
||||
craftFormDataKeys: Object.keys(craftFormData.value || {})
|
||||
})
|
||||
craftMinting.value = true;
|
||||
uni.showLoading({ title: '铸造中…', mask: true });
|
||||
try {
|
||||
@ -749,32 +773,71 @@ const handleBack = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 点赞轮询定时器:覆盖跨用户/跨设备「实时」更新(后端暂无点赞 WS)
|
||||
let likePollingTimer = null;
|
||||
const LIKE_POLLING_INTERVAL = 30000; // 20s
|
||||
|
||||
// 启动点赞轮询
|
||||
const startLikePolling = () => {
|
||||
stopLikePolling();
|
||||
if (!assetData.value?.asset_id) return;
|
||||
likePollingTimer = setInterval(() => {
|
||||
// 页面被切到后台时轮询其实也会跑,但 uni-app 在 H5/App 下 setInterval 仍触发;
|
||||
// 定时器在 onHide 清理,onShow 重新启动,天然只在可见时拉取
|
||||
if (assetData.value?.asset_id) {
|
||||
loadLikedUsers(assetData.value.asset_id);
|
||||
}
|
||||
}, LIKE_POLLING_INTERVAL);
|
||||
};
|
||||
|
||||
const stopLikePolling = () => {
|
||||
if (likePollingTimer) {
|
||||
clearInterval(likePollingTimer);
|
||||
likePollingTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 其他页面触发的点赞变更事件:保持本页面与全局一致
|
||||
const handleAssetLikeChangedExternal = (data) => {
|
||||
if (!data || String(data.asset_id) !== String(assetData.value?.asset_id)) return;
|
||||
if (typeof data.like_count === 'number') likeCount.value = data.like_count;
|
||||
if (typeof data.is_liked === 'boolean') isLiked.value = data.is_liked;
|
||||
// 拉取最新点赞用户列表,更新「创作者信息模块」
|
||||
if (assetData.value?.asset_id) {
|
||||
loadLikedUsers(assetData.value.asset_id);
|
||||
}
|
||||
};
|
||||
|
||||
onLoad((options) => {
|
||||
console.log('onLoad 触发,参数:', options);
|
||||
assetIdParam.value = options?.asset_id || '';
|
||||
orderIdParam.value = options?.order_id || '';
|
||||
fromParam.value = options?.from || '';
|
||||
studioKindParam.value = options?.studio_kind || '';
|
||||
loadCurrentUser();
|
||||
// 订阅跨页面点赞事件
|
||||
uni.$on('assetLikeChanged', handleAssetLikeChangedExternal);
|
||||
|
||||
// craft_confirm 模式:不需要调用 loadData,而是加载本地表单数据
|
||||
if (fromParam.value === 'craft_confirm') {
|
||||
console.log('[asset-detail] craft_confirm 模式,加载本地数据');
|
||||
craftConfirmMode.value = true;
|
||||
loadCraftConfirm();
|
||||
}
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
console.log('onShow 触发');
|
||||
const currentKey = `${assetIdParam.value}_${orderIdParam.value}`;
|
||||
console.log('当前key:', currentKey, '上次key:', lastLoadKey.value);
|
||||
|
||||
// 如果参数变化了,或者是首次加载,则重新加载数据
|
||||
if ((assetIdParam.value || orderIdParam.value) && currentKey !== lastLoadKey.value) {
|
||||
lastLoadKey.value = currentKey;
|
||||
loadData();
|
||||
}
|
||||
|
||||
// 页面每次回到前台都重新拉一次点赞列表 —— 兜底「创作者信息模块」时效
|
||||
if (assetData.value?.asset_id && !craftConfirmMode.value) {
|
||||
loadLikedUsers(assetData.value.asset_id);
|
||||
// startLikePolling();
|
||||
}
|
||||
});
|
||||
|
||||
onHide(() => {
|
||||
@ -784,11 +847,20 @@ onHide(() => {
|
||||
if (isLenticularDetail.value) {
|
||||
stopTiltPreview()
|
||||
}
|
||||
stopLikePolling();
|
||||
});
|
||||
|
||||
onUnload(() => {
|
||||
// 页面被关闭/销毁:清理轮询和事件订阅,避免泄漏到下一页或后台
|
||||
stopLikePolling();
|
||||
uni.$off('assetLikeChanged', handleAssetLikeChangedExternal);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (countdownTimer) clearInterval(countdownTimer);
|
||||
stopTiltPreview();
|
||||
stopLikePolling();
|
||||
uni.$off('assetLikeChanged', handleAssetLikeChangedExternal);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -455,6 +455,7 @@ onUnmounted(() => {
|
||||
.content-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user