topfans/frontend/pages/components/ShareReportButtons.vue

222 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="share-report-btns">
<!-- 分享按钮 -->
<view class="action-btn share-btn" @tap="handleShare">
<image class="btn-icon" src="/static/assetDetail/fenxiang.png" mode="aspectFit"></image>
<text class="btn-text">分享</text>
</view>
<!-- 举报按钮 - 只在不是自己的资产时显示 -->
<view v-if="showReport" class="action-btn report-btn" @tap="handleReport">
<image class="btn-icon" src="/static/assetDetail/jubao.png" mode="aspectFit"></image>
<text class="btn-text">举报</text>
</view>
</view>
<!-- 分享弹窗 -->
<ShareModal
:visible="showShareModal"
:coverUrl="shareCoverUrl"
:qrcodeUrl="shareQrcodeUrl"
:avatarUrl="shareAvatarUrl"
:nickname="ownerNickname"
:assetId="assetId"
:displayTxHash="displayTxHash"
:externalCanvasId="externalCanvasId"
@close="showShareModal = false"
/>
<!-- 举报弹窗 -->
<ReportModal
:visible="showReportModal"
:assetId="assetId"
@close="showReportModal = false"
@submit="handleReportSubmit"
/>
</template>
<script setup>
import { ref, computed } from 'vue';
import ShareModal from './ShareModal.vue';
import ReportModal from './ReportModal.vue';
import { getShareQrcodeApi } from '@/utils/api.js';
import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js';
const props = defineProps({
// 资产拥有者的昵称
ownerNickname: {
type: String,
default: ''
},
// 资产ID用于分享
assetId: {
type: String,
default: ''
},
// 封面图片(已解析的 CDN URL)
coverUrl: {
type: String,
default: ''
},
// 封面原始 OSS key(异步 coverUrl 还没解析完时,ShareReportButtons 自己 resolve)
coverKey: {
type: String,
default: ''
},
// 外部 page 级 canvasId(app-plus 组件内 canvas 不稳,推荐父页面提供)
externalCanvasId: {
type: String,
default: ''
},
// 链上哈希(原文,由父组件 asset-detail 提供;image-compositor 内部截断渲染)
displayTxHash: {
type: String,
default: ''
}
});
// 当前用户昵称
const currentNickname = computed(() => {
try {
const userStr = uni.getStorageSync('user');
if (userStr) {
const userInfo = JSON.parse(userStr);
return userInfo?.nickname || '';
}
} catch (e) {
console.error('获取用户信息失败:', e);
}
return '';
});
// 是否显示举报按钮(不是自己的资产时显示)
const showReport = computed(() => {
return props.ownerNickname && currentNickname.value && props.ownerNickname !== currentNickname.value;
});
// 分享弹窗状态
const showShareModal = ref(false);
const shareCoverUrl = ref('');
const shareQrcodeUrl = ref('');
const shareAvatarUrl = ref('');
// 举报弹窗状态
const showReportModal = ref(false);
// 分享
const handleShare = async () => {
// 登录门槛:未登录不打开弹窗(spec § 3.4)
const userStr = uni.getStorageSync('user');
if (!userStr || !JSON.parse(userStr)?.uid) {
uni.showToast({ title: '请先登录', icon: 'none' });
setTimeout(() => uni.navigateTo({ url: '/pages/login/login' }), 800);
return;
}
// 关键:coverUrl 是异步加载的(props.coverUrl 可能还没解析完),如果空就用 coverKey 自己 resolve
// 优先用 props.coverKey(原始 OSS key) + getAssetCoverRealUrl 拼接真实 URL,
// 因为 coverUrl 可能只缓存了短期签名 URL,share 流程需要长期可用的 URL
const sourceKey = props.coverKey || props.coverUrl;
let cover = '';
console.log('[ShareReportButtons] handleShare coverUrl =', props.coverUrl, 'coverKey =', props.coverKey);
try {
cover = await getAssetCoverRealUrl(sourceKey);
console.log('[ShareReportButtons] resolved cover =', cover);
} catch (e) {
console.warn('[ShareReportButtons] resolve cover failed, fallback raw:', e);
// 兜底:用原始值
cover = props.coverUrl || props.coverKey;
}
if (!cover) {
uni.showToast({ title: '封面加载中,请稍后再试', icon: 'none' });
return;
}
shareCoverUrl.value = cover;
// 分享者头像:从本地存储读取当前登录用户的头像
try {
const userStr2 = uni.getStorageSync('user');
if (userStr2) {
const userInfo = JSON.parse(userStr2);
shareAvatarUrl.value = userInfo?.avatar_url || '/static/square/gerenzhongxinkangpinkuang.png';
} else {
shareAvatarUrl.value = '/static/square/gerenzhongxinkangpinkuang.png';
}
} catch (e) {
console.error('获取用户头像失败:', e);
shareAvatarUrl.value = '/static/square/gerenzhongxinkangpinkuang.png';
}
// 先用 mock 占位,后端真实接口返回后会替换
shareQrcodeUrl.value = '/static/share/mock_qrcode.png';
showShareModal.value = true;
refreshQrcode();
};
// 在 showShareModal = true 后(或 watch 回调里),异步拉真实二维码 URL(spec § 3)
async function refreshQrcode() {
const userStr = uni.getStorageSync('user');
if (!userStr) return;
const user = JSON.parse(userStr);
try {
// 用异步版本(spec § 3.3 推荐 — 不阻塞 UI 线程)
const { platform } = await uni.getSystemInfo();
const data = await getShareQrcodeApi(props.assetId, user.uid, platform);
if (data?.qrcode_url) {
shareQrcodeUrl.value = data.qrcode_url;
console.log(data.qrcode_url)
}
} catch (e) {
// 失败保持 mock 占位图,不打断用户
}
}
// 举报 - 打开举报弹窗
const handleReport = () => {
if (!props.assetId) {
uni.showToast({ title: '藏品信息缺失', icon: 'none' });
return;
}
showReportModal.value = true;
};
// 举报提交完成回调(埋点/埋日志可用)
const handleReportSubmit = (payload) => {
console.log('[ShareReportButtons] report submitted', payload);
};
</script>
<style scoped>
.share-report-btns {
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100rpx;
height: 100rpx;
border-radius: 24rpx;
gap: 8rpx;
}
.action-btn:active {
opacity: 0.7;
}
.btn-icon {
width: 56rpx;
height: 56rpx;
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.5));
}
.btn-text {
font-size: 24rpx;
color: #fff;
font-family: 'yt', sans-serif;
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.6);
}
</style>