topfans/frontend/pages/profile/myWorks.vue

847 lines
18 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="page-container">
<!-- 背景图片 -->
<image class="bg-image" src="/static/square/beijingban.png" mode="aspectFill"></image>
<!-- 顶部导航 -->
<view class="nav-bar ">
<view class="nav-back" @tap="goBack">
<image class="nav-back-icon" src="/static/icon/back.png" mode="aspectFit"></image>
</view>
<!-- <text class="nav-title">我的作品</text> -->
<view class="nav-placeholder"></view>
<view class="nav-settings" @tap="goToSettings">
<text class="nav-settings-text">个人设置</text>
</view>
</view>
<view class="scroll-content">
<!-- 我的在展作品 -->
<view class="section-block section-1">
<view class="section-label">
<image class="section-label-bg" src="/static/nft/dingbutubiao_liang.png" mode="aspectFill"></image>
<text class="section-label-text">我的在展作品</text>
</view>
<view class="exhibition-grid">
<view v-for="(item, index) in exhibitionWorks" :key="item.id" class="exhibition-card"
:class="index % 2 === 0 ? 'card-tilt-left' : 'card-tilt-right'"
@tap="handleExhibitionCardTap(item, index)">
<image class="card-image" :src="item.cover_url || '/static/nft/placeholder.png'"
mode="aspectFill"></image>
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFill">
</image>
<!-- 点赞数 -->
<view class="card-rate-badge">
<image class="heart-icon" src="/static/icon/heart-icon.png" mode="aspectFit"></image>
<view class="card-rate-text-wrap">
<text class="card-rate-text">{{ item.like_count || 0 }}</text>
</view>
</view>
<!-- 图片下方收益 -->
<view class="card-income-row"
:class="index % 2 === 0 ? 'income-tilt-right' : 'income-tilt-left'">
<image class="topfans-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
<view class="card-income-text-wrap">
<text class="card-income-text">{{ item.earnings || 0 }}/时</text>
</view>
</view>
</view>
<!-- 空状态占位:显示剩余空展位卡片 -->
<view v-if="exhibitionWorks.length < 2" class="empty-exhibition">
<view class="empty-card empty-card-left" @tap="openAssetSelector(0)">
<image class="empty-cover" src="/static/nft/placeholder.png" mode="aspectFill"></image>
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png"
mode="aspectFill"></image>
<view class="empty-add-btn">
<text class="empty-add-icon">+</text>
</view>
</view>
<view class="empty-card empty-card-right" @tap="openAssetSelector(1)">
<image class="empty-cover" src="/static/nft/placeholder.png" mode="aspectFill"></image>
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png"
mode="aspectFill"></image>
<view class="empty-add-btn">
<text class="empty-add-icon">+</text>
</view>
</view>
</view>
</view>
</view>
<!-- 当前点赞作品 -->
<view class="section-block">
<view class="section-label">
<image class="section-label-bg" src="/static/nft/dingbutubiao_liang.png" mode="aspectFill"></image>
<text class="section-label-text">点赞作品</text>
</view>
<scroll-view class="liked-list" scroll-y="true" :show-scrollbar="false">
<view v-for="(item, index) in likedWorks" :key="item.id" class="liked-row"
@tap="goToAssetDetail(item.id)">
<!-- 排名图标,绝对定位在卡片左侧 -->
<image v-if="index < 3" :src="rankIcons[index]" class="rank-icon-img" mode="aspectFit"></image>
<!-- 卡片主体 -->
<view class="liked-item" :class="index === 0 ? 'liked-item-first' : ''">
<!-- 作品封面 -->
<view class="liked-cover-wrap" :class="index === 0 ? 'liked-cover-wrap-first' : ''">
<image class="liked-cover" :src="item.cover_url || '/static/nft/placeholder.png'"
mode="aspectFill"></image>
<image class="liked-cover-frame" src="/static/square/cangpinkuang1.png"
mode="aspectFill"></image>
</view>
<!-- 作品信息 -->
<view class="liked-info">
<text class="liked-status">{{ item.status_text }}</text>
<view class="liked-score-row">
<text class="liked-score">{{ formatScore(item.score) }}</text>
<image class="fire-icon" src="/static/square/rementubiao.png" mode="aspectFit">
</image>
</view>
</view>
<!-- 右侧奖励 -->
<view class="liked-reward">
<image class="reward-token-icon" src="/static/icon/crystal.png" mode="aspectFit">
</image>
<text class="reward-amount">+{{ item.reward }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="likedWorks.length === 0" class="empty-liked">
<text class="empty-text">当前暂无点赞作品</text>
</view>
</scroll-view>
</view>
<!-- <view style="height: 60rpx;"></view> -->
</view>
<!-- 藏品选择器组件 -->
<AssetSelector :visible="showAssetSelector" :replace-asset="assetToReplace" @close="closeAssetSelector"
@select="handleAssetSelect" />
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { getMyExhibitedAssetsApi, getMyLikedAssetsApi, getMyGalleriesApi, placeAssetToGalleryApi } from '@/utils/api.js';
import AssetSelector from '../components/AssetSelector.vue';
import { onShow } from '@dcloudio/uni-app';
import { doubleTapLike } from '@/utils/likeHelper.js';
const goBack = () => {
// 获取页面栈
const pages = getCurrentPages();
if (pages.length > 1) {
// 有上一页,执行返回
uni.navigateBack();
} else {
// 没有上一页跳转到square页面
uni.reLaunch({
url: '/pages/square/square'
});
}
};
const goToSettings = () => {
uni.navigateTo({
url: '/pages/profile/profile'
});
};
const goToCastlove = () => {
uni.navigateTo({
url: '/pages/castlove/mall'
});
};
// 藏品选择器相关
const showAssetSelector = ref(false);
const assetToReplace = ref(null);
const currentSlotIndex = ref(0);
const openAssetSelector = (slotIndex = 0) => {
currentSlotIndex.value = slotIndex;
showAssetSelector.value = true;
};
const closeAssetSelector = () => {
showAssetSelector.value = false;
assetToReplace.value = null;
};
const handleAssetSelect = async ({ asset, isReplace, oldAsset }) => {
console.log('选中藏品:', asset, '替换模式:', isReplace, '槽位:', currentSlotIndex.value);
uni.showLoading({ title: '加载中...' });
try {
const galleriesRes = await getMyGalleriesApi();
console.log('展馆API返回:', galleriesRes);
const slots = galleriesRes.data?.slots || [];
const ownerId = galleriesRes.data?.gallery_owner_id;
console.log('槽位列表:', slots, 'ownerId:', ownerId);
// 过滤出可操作的槽位can_operate: true
const operatableSlots = slots.filter(s => s.can_operate);
console.log('可操作槽位:', operatableSlots);
if (operatableSlots.length === 0 || !ownerId) {
uni.showToast({ title: '暂无可用展馆', icon: 'none' });
return;
}
let targetSlotId = null;
if (isReplace && oldAsset) {
const slot = slots.find(s => s.asset_id === oldAsset.asset_id);
targetSlotId = slot?.slot_id;
} else {
// 使用 currentSlotIndex 对应可操作槽位列表中的槽位
const targetSlot = operatableSlots[currentSlotIndex.value];
targetSlotId = targetSlot?.slot_id;
}
if (!targetSlotId) {
uni.showToast({ title: '展馆已满', icon: 'none' });
return;
}
console.log('调用展出接口: asset_id=', asset.asset_id, 'ownerId=', ownerId, 'slotId=', targetSlotId);
await placeAssetToGalleryApi(asset.asset_id, ownerId, targetSlotId);
uni.showToast({ title: '展出成功', icon: 'success' });
await loadExhibitedAssets();
} catch (err) {
console.error('展出失败:', err);
uni.showToast({ title: err.message || '展出失败', icon: 'none' });
} finally {
uni.hideLoading();
}
};
const goToAssetDetail = (id) => {
if (!id) return;
uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${id}` });
};
// 双击点赞处理
const cardTapTimers = {};
const handleExhibitionCardTap = (item, index) => {
if (cardTapTimers[item.id]) {
// 第二次点击,双击点赞
clearTimeout(cardTapTimers[item.id]);
delete cardTapTimers[item.id];
doubleTapLike(item.id, (success) => {
if (success) {
// 更新在展作品的点赞数
exhibitionWorks.value[index].like_count = (exhibitionWorks.value[index].like_count || 0) + 1;
// 将作品添加到当前点赞列表
const likedItem = {
id: item.id,
cover_url: item.cover_url,
like_count: exhibitionWorks.value[index].like_count,
earnings: item.earnings,
name: item.name,
status_text: '潜力待挖',
score: exhibitionWorks.value[index].like_count,
reward: 0,
};
likedWorks.value.unshift(likedItem);
uni.showToast({ title: '点赞成功', icon: 'success' });
}
});
} else {
// 第一次点击,单击跳转
cardTapTimers[item.id] = setTimeout(() => {
delete cardTapTimers[item.id];
goToAssetDetail(item.id);
}, 300);
}
};
const rankIcons = [
'/static/rank/rank-icon1.png',
'/static/rank/rank-icon2.png',
'/static/rank/rank-icon3.png',
];
const formatScore = (score) => {
if (!score && score !== 0) return '0';
return Number(score).toLocaleString();
};
// 在展作品列表
const exhibitionWorks = ref([]);
// 当前点赞作品列表
const likedWorks = ref([]);
// 加载我的展出作品
const loadExhibitedAssets = async () => {
try {
const res = await getMyExhibitedAssetsApi(1, 20);
if (res.data && res.data.items) {
exhibitionWorks.value = res.data.items.map(item => ({
id: item.asset_id,
cover_url: item.cover_url,
like_count: item.like_count,
earnings: item.earnings,
exhibited_at: item.exhibited_at,
expire_at: item.expire_at,
name: item.name,
}));
}
} catch (err) {
console.error('加载展出作品失败:', err);
}
};
// 加载我的点赞作品
const loadLikedAssets = async () => {
try {
const res = await getMyLikedAssetsApi(1, 20);
if (res.data && res.data.items) {
likedWorks.value = res.data.items.map((item, index) => ({
id: item.asset_id,
cover_url: item.cover_url,
like_count: item.like_count,
earnings: item.earnings,
liked_at: item.liked_at,
name: item.name,
// 暂时用排名模拟状态文字
status_text: index < 3 ? '排名进榜' : '潜力待挖',
score: item.like_count,
reward: Math.floor(item.earnings || 0),
}));
}
} catch (err) {
console.error('加载点赞作品失败:', err);
}
};
onMounted(() => {
loadExhibitedAssets();
loadLikedAssets();
});
onShow(() => {
loadLikedAssets();
});
</script>
<style scoped>
.page-container {
min-height: 100vh;
position: relative;
}
/* 背景图片 */
.bg-image {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
/* 导航栏 */
.nav-bar {
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 80rpx 32rpx 16rpx;
}
.nav-back {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
/* background: rgba(255,255,255,0.5);
border-radius: 50%; */
}
.nav-back-icon {
width: 80rpx;
height: 80rpx;
}
.nav-title {
font-size: 36rpx;
font-weight: 700;
color: #5a2d82;
letter-spacing: 2rpx;
}
.nav-placeholder {
width: 64rpx;
}
.nav-settings {
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 24rpx;
padding: 8rpx 20rpx 8rpx 20rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}
.nav-settings-text {
font-size: 24rpx;
color: #fff;
font-weight: 400;
}
/* 内容区域 */
.scroll-content {
position: relative;
z-index: 1;
/* padding: 0 32rpx; */
}
.section-block {
background: rgb(249 159 192 / 45%);
border-radius: 48rpx;
padding: 16rpx;
}
/* 区块 */
.section-1 {
margin-bottom: 8rpx;
/* border-radius: 48rpx;
padding: 16rpx; */
}
/* 区块标签 */
.section-label {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
height: 80rpx;
min-width: 232rpx;
}
.section-label-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.section-label-text {
position: relative;
z-index: 1;
font-size: 26rpx;
color: #fff;
font-weight: 600;
padding: 0 28rpx;
}
/* 在展作品网格 */
.exhibition-grid {
display: flex;
flex-direction: row;
/* gap: 24rpx; */
padding: 10rpx 10rpx 80rpx;
justify-content: center;
}
.exhibition-card {
width: 248rpx;
height: 380rpx;
border-radius: 20rpx;
overflow: visible;
position: relative;
}
.card-tilt-left {
transform: rotate(-4deg) translateY(10rpx);
margin-right: 32rpx;
}
.card-tilt-right {
transform: rotate(4deg) translateY(10rpx);
}
.card-income-row.income-tilt-right {
transform: translateX(-50%) rotate(4deg);
}
.card-income-row.income-tilt-left {
transform: translateX(-50%) rotate(-4deg);
}
.card-image {
width: 88%;
height: 92%;
border-radius: 80rpx;
transform-origin: center center;
position: relative;
z-index: 3;
padding: 16rpx;
overflow: hidden;
}
.card-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
}
.card-user-tag {
position: absolute;
bottom: 56rpx;
left: 0;
right: 0;
display: flex;
justify-content: center;
}
.card-user-text {
font-size: 20rpx;
color: #fff;
background: rgba(0, 0, 0, 0.45);
padding: 4rpx 14rpx;
border-radius: 20rpx;
}
.card-rate-badge {
position: absolute;
bottom: 16rpx;
left: 40%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 6rpx;
padding: 6rpx 16rpx;
z-index: 9;
}
/* 图片下方收益 */
.card-income-row {
position: absolute;
bottom: -52rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
white-space: nowrap;
overflow: visible;
}
.topfans-icon {
width: 52rpx;
height: 52rpx;
position: relative;
z-index: 2;
margin-right: -16rpx;
left: 20rpx;
top: 8rpx
}
.card-income-text-wrap {
width: 64rpx;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
padding: 8rpx 20rpx 8rpx 40rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
display: flex;
align-items: center;
justify-content: center;
}
.card-income-text {
font-size: 16rpx;
color: #fff;
font-weight: 700;
text-align: center;
}
.heart-icon {
width: 28rpx;
height: 28rpx;
}
.card-rate-text-wrap {
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
padding: 2rpx 16rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
display: flex;
}
.card-rate-text {
font-size: 22rpx;
color: #fff;
font-weight: 700;
}
/* 空状态 */
.empty-exhibition {
display: flex;
align-items: center;
justify-content: center;
padding: 80rpx 0;
gap: 24rpx;
}
.empty-card {
width: 248rpx;
height: 380rpx;
border-radius: 20rpx;
overflow: visible;
position: relative;
}
.empty-card-left {
transform: rotate(-4deg) translateY(10rpx);
}
.empty-card-right {
transform: rotate(4deg) translateY(10rpx);
}
.empty-cover {
width: 88%;
height: 92%;
border-radius: 80rpx;
position: relative;
z-index: 3;
padding: 16rpx;
opacity: 0.5;
}
/* 卡片内的添加按钮 */
.empty-add-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
background: linear-gradient(135deg, #F0E4B1 0%, #F08399 50%, #B94E73 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
box-shadow: 0 4rpx 16rpx rgba(185, 78, 115, 0.4);
}
.empty-add-icon {
font-size: 48rpx;
color: #fff;
font-weight: 700;
line-height: 1;
}
.empty-text {
font-size: 28rpx;
color: #b09cc0;
}
/* 当前点赞列表 */
.liked-list {
max-height: 732rpx;
}
.liked-row {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 16rpx;
}
.liked-item {
display: flex;
align-items: center;
background: #ffffff50;
border-radius: 32rpx;
padding: 16rpx 20rpx;
gap: 16rpx;
overflow: hidden;
box-sizing: border-box;
width: 80%;
padding-left: 10%;
}
.liked-item-first {
padding: 28rpx 20rpx;
width: 90%;
padding-left: 20%;
}
/* 排名徽章 */
.rank-icon-img {
width: 56rpx;
height: 68rpx;
flex-shrink: 0;
margin-right: 8rpx;
}
.rank-number-badge {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
background: rgba(180, 140, 220, 0.3);
display: flex;
align-items: center;
justify-content: center;
}
.rank-number-text {
font-size: 24rpx;
color: #fff;
font-weight: 700;
}
/* 作品封面 */
.liked-cover-wrap {
width: 88rpx;
height: 88rpx;
flex-shrink: 0;
margin-left: -18rpx;
margin-right: 48rpx;
position: relative;
}
/* .liked-cover-wrap-first {
width: 88rpx;
height: 110rpx;
} */
.liked-cover {
width: 90%;
height: 90%;
border-radius: 24rpx;
transform: rotate(-22deg);
transform-origin: center center;
position: relative;
z-index: 3;
padding: 0.25rem;
}
.liked-cover-frame {
position: absolute;
top: 0;
left: 0;
width: 110%;
height: 110%;
z-index: 2;
transform: rotate(-22deg);
transform-origin: center center;
}
/* 作品信息 */
.liked-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
/* gap: 8rpx; */
overflow: hidden;
}
.liked-status {
font-size: 28rpx;
color: #fff;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 16rpx;
}
.liked-score-row {
display: flex;
align-items: center;
/* gap: 6rpx; */
}
.liked-score {
font-size: 26rpx;
color: #fff;
font-weight: 700;
margin-right: 8rpx;
}
.fire-icon {
width: 32rpx;
height: 32rpx;
align-self: flex-end;
margin-top: 4rpx;
}
/* 右侧奖励 */
.liked-reward {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 8rpx;
flex-shrink: 0;
}
.reward-token-icon {
width: 56rpx;
height: 56rpx;
}
.reward-amount {
font-size: 28rpx;
color: #c060e0;
font-weight: 700;
}
/* 空状态 */
.empty-liked {
padding: 60rpx 0;
display: flex;
align-items: center;
justify-content: center;
}
</style>