style: 藏品详细样式修改
This commit is contained in:
parent
f0b0df2ebf
commit
5edf153521
@ -33,7 +33,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 详情内容 -->
|
||||
<scroll-view v-else scroll-y class="content-scroll">
|
||||
<scroll-view v-else scroll-y class="content-scroll" :show-scrollbar="false">
|
||||
<view class="content-wrapper">
|
||||
<!-- 藏品卡片区域 -->
|
||||
<view class="card-section">
|
||||
@ -47,8 +47,9 @@
|
||||
<!-- 点赞 + 收益 + 倒计时行 -->
|
||||
<view class="card-meta-row">
|
||||
<!-- 点赞 -->
|
||||
<view class="like-area" @tap="handleLike">
|
||||
<image :src="isLiked ? '/static/icon/like-after.png' : '/static/icon/like-before.png'" class="like-icon" mode="aspectFit"></image>
|
||||
<view v-if="assetData.display_status === 1" class="like-area" @tap="handleLike">
|
||||
<image :src="isLiked ? '/static/icon/like-after.png' : '/static/icon/like-before.png'"
|
||||
class="like-icon" mode="aspectFit"></image>
|
||||
<text class="like-num">{{ likeCount }}</text>
|
||||
</view>
|
||||
|
||||
@ -59,7 +60,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 倒计时(如有) -->
|
||||
<view v-if="showCountdown" class="countdown-area">
|
||||
<view v-if="assetData.display_status === 1 && countdownText" class="countdown-area">
|
||||
<view class="countdown-pill">
|
||||
<text class="countdown-val">{{ countdownText }}</text>
|
||||
</view>
|
||||
@ -69,10 +70,19 @@
|
||||
|
||||
<!-- 信息行(创作者 + 铸造时间 + 来源) -->
|
||||
<view class="info-row">
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">来源</text>
|
||||
<text class="info-value">{{ assetData.source || '铸造' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">创作者</text>
|
||||
<text class="info-value">{{ assetData.owner_nickname || '未知' }}</text>
|
||||
<view class="info-nickname">
|
||||
<image v-if="userAvatarUrl" class="info-avatar" :src="userAvatarUrl" mode="aspectFill"></image>
|
||||
<text class="info-value">{{ assetData.owner_nickname || '未知' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
@ -81,27 +91,16 @@
|
||||
<text class="info-value">{{ formattedAcquireTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">来源</text>
|
||||
<text class="info-value">{{ assetData.source || '铸造' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 创作者信息模块 -->
|
||||
<view class="creator-section" @tap="showLikeUsersModal = true">
|
||||
<!-- <view v-if="likeCount > 0" class="creator-section" @tap="showLikeUsersModal = true"> -->
|
||||
<!-- 点赞用户头像区域 -->
|
||||
<view class="liked-users-area">
|
||||
<view
|
||||
v-for="(user, index) in likedUsers"
|
||||
:key="index"
|
||||
class="liked-user-avatar"
|
||||
:style="{
|
||||
transform: `translate(${user.ellipseX || 0}rpx, ${user.ellipseY || 0}rpx) scale(${user.size || 1})`,
|
||||
zIndex: index < 2 ? index + 1 : (index < 4 ? index + 3 : index + 1)
|
||||
}"
|
||||
>
|
||||
<!-- <view class="liked-users-area">
|
||||
<view v-for="(user, index) in likedUsers" :key="index" class="liked-user-avatar" :style="{
|
||||
transform: `translate(${user.ellipseX || 0}rpx, ${user.ellipseY || 0}rpx) scale(${user.size || 1})`,
|
||||
zIndex: index < 2 ? index + 1 : (index < 4 ? index + 3 : index + 1)
|
||||
}">
|
||||
<image class="liked-user-image" :src="user.avatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
@ -109,10 +108,11 @@
|
||||
<text class="creator-title">TA们最近为你点过赞</text>
|
||||
<view class="creator-card">
|
||||
<text class="creator-tip">点击查看</text>
|
||||
<image class="creator-preview" src="/static/rank/activity-support-icon/tubiao.png" mode="aspectFill"></image>
|
||||
<image class="creator-preview" src="/static/rank/activity-support-icon/tubiao.png"
|
||||
mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- </view> -->
|
||||
|
||||
<!-- 链上数据 -->
|
||||
<view class="chain-section">
|
||||
@ -133,16 +133,18 @@
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">区块链编号</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value">{{ showBlockNumber ? (assetData.block_number || '未知') : hiddenBlockNumber }}</text>
|
||||
<text class="chain-value">{{ showBlockNumber ? (assetData.block_number || '未知') :
|
||||
hiddenBlockNumber }}</text>
|
||||
<view class="toggle-btn" @tap="showBlockNumber = !showBlockNumber">
|
||||
<text class="toggle-icon">{{ showBlockNumber ? '👁' : '👁🗨' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">交易哈希</text>
|
||||
<text class="chain-label">链上哈希</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value chain-hash" @longpress="copyHash">{{ showTxHash ? displayTxHash : hiddenTxHash }}</text>
|
||||
<text class="chain-value chain-hash" @longpress="copyHash">{{ showTxHash ? displayTxHash
|
||||
: hiddenTxHash }}</text>
|
||||
<view class="toggle-btn" @tap="showTxHash = !showTxHash">
|
||||
<text class="toggle-icon">{{ showTxHash ? '👁' : '👁🗨' }}</text>
|
||||
</view>
|
||||
@ -154,30 +156,19 @@
|
||||
</scroll-view>
|
||||
|
||||
<!-- 点赞用户弹窗 -->
|
||||
<LikeUsersModal
|
||||
:visible="showLikeUsersModal"
|
||||
:tabs="['今日', '历史']"
|
||||
@close="showLikeUsersModal = false"
|
||||
@tab-change="handleLikeUsersTabChange"
|
||||
>
|
||||
<LikeUsersModal :visible="showLikeUsersModal" :tabs="['今日', '历史']" @close="showLikeUsersModal = false"
|
||||
@tab-change="handleLikeUsersTabChange">
|
||||
<template #content>
|
||||
<view class="like-users-list">
|
||||
<view v-for="(user, index) in displayedLikeUsers" :key="index" class="like-user-item" >
|
||||
<view v-for="(user, index) in displayedLikeUsers" :key="index" class="like-user-item">
|
||||
<image class="like-user-avatar" :src="user.avatar" mode="aspectFill"></image>
|
||||
<view class="like-user-info">
|
||||
<text class="like-user-name">{{ user.nickname }}</text>
|
||||
</view>
|
||||
<!-- 拜访按钮 -->
|
||||
<view
|
||||
class="visit-button"
|
||||
@tap="handleVisit"
|
||||
>
|
||||
<image
|
||||
class="visit-icon"
|
||||
src="/static/square/dianjibaifang.png"
|
||||
mode="aspectFit"
|
||||
lazy-load
|
||||
></image>
|
||||
<view class="visit-button" @tap="handleVisit">
|
||||
<image class="visit-icon" src="/static/square/dianjibaifang.png" mode="aspectFit" lazy-load>
|
||||
</image>
|
||||
<text class="visit-text">点击拜访</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -208,6 +199,22 @@ const isLiked = ref(false);
|
||||
const likeCount = ref(0);
|
||||
const liking = ref(false);
|
||||
|
||||
// 当前用户头像
|
||||
const userAvatarUrl = ref('');
|
||||
|
||||
// 加载当前用户信息
|
||||
const loadCurrentUser = () => {
|
||||
try {
|
||||
const userStr = uni.getStorageSync('user');
|
||||
if (userStr) {
|
||||
const userInfo = JSON.parse(userStr);
|
||||
userAvatarUrl.value = userInfo?.avatar_url || '';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析用户信息失败:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 点赞用户头像数据(最多6个,按椭圆排列,每个头像有独立的偏移量配置)
|
||||
// avatar: 头像地址, ellipseX: 椭圆X轴坐标, ellipseY: 椭圆Y轴坐标, size: 缩放比例
|
||||
const likedUsers = ref([
|
||||
@ -215,8 +222,8 @@ const likedUsers = ref([
|
||||
{ avatar: '/static/sucai/image-02.png', ellipseX: 40, ellipseY: -40, size: 0.85 },
|
||||
{ avatar: '/static/sucai/image-03.png', ellipseX: -96, ellipseY: 0, size: 0.9 },
|
||||
{ avatar: '/static/sucai/image-04.png', ellipseX: 120, ellipseY: -40, size: 0.9 },
|
||||
{ avatar: '/static/sucai/image-05.png', ellipseX: 64, ellipseY: 32},
|
||||
{ avatar: '/static/sucai/image-06.png', ellipseX: -16, ellipseY: 32 ,size:1.15 }
|
||||
{ avatar: '/static/sucai/image-05.png', ellipseX: 64, ellipseY: 32 },
|
||||
{ avatar: '/static/sucai/image-06.png', ellipseX: -16, ellipseY: 32, size: 1.15 }
|
||||
]);
|
||||
|
||||
// 弹窗相关
|
||||
@ -254,15 +261,48 @@ const showBlockNumber = ref(false);
|
||||
const showTxHash = ref(false);
|
||||
|
||||
// 倒计时
|
||||
const remainSeconds = ref(0);
|
||||
let countdownTimer = null;
|
||||
|
||||
const showCountdown = computed(() => remainSeconds.value > 0);
|
||||
// 倒计时状态
|
||||
const countdowns = ref({});
|
||||
|
||||
// 计算剩余时间
|
||||
const calculateRemainingTime = (item) => {
|
||||
if (item.remainSeconds !== undefined) {
|
||||
if (item.remainSeconds <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(item.remainSeconds / 3600);
|
||||
const minutes = Math.floor((item.remainSeconds % 3600) / 60);
|
||||
const seconds = Math.floor(item.remainSeconds % 60);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
if (item.exhibition_expire_at) {
|
||||
const now = Date.now();
|
||||
const expireTime = new Date(item.exhibition_expire_at).getTime();
|
||||
const remaining = expireTime - now;
|
||||
if (remaining <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(remaining / (60 * 60 * 1000));
|
||||
const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000));
|
||||
const seconds = Math.floor((remaining % (60 * 1000)) / 1000);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
};
|
||||
|
||||
// 更新倒计时
|
||||
const updateCountdowns = () => {
|
||||
countdowns.value['currentAsset'] = calculateRemainingTime({ exhibition_expire_at: assetData.value.exhibition_expire_at });
|
||||
};
|
||||
|
||||
const countdownText = computed(() => {
|
||||
const h = String(Math.floor(remainSeconds.value / 3600)).padStart(2, '0');
|
||||
const m = String(Math.floor((remainSeconds.value % 3600) / 60)).padStart(2, '0');
|
||||
const s = String(Math.floor(remainSeconds.value % 60)).padStart(2, '0');
|
||||
const countdown = countdowns.value['currentAsset'];
|
||||
if (!countdown || countdown.expired) return '';
|
||||
const h = String(countdown.hours).padStart(2, '0');
|
||||
const m = String(countdown.minutes).padStart(2, '0');
|
||||
const s = String(countdown.seconds).padStart(2, '0');
|
||||
return `${h}:${m}:${s}`;
|
||||
});
|
||||
|
||||
@ -280,10 +320,7 @@ const formattedAcquireTime = computed(() => {
|
||||
|
||||
// 截断交易哈希
|
||||
const displayTxHash = computed(() => {
|
||||
const hash = assetData.value.tx_hash;
|
||||
if (!hash) return '未知';
|
||||
if (hash.length <= 20) return hash;
|
||||
return `${hash.substring(0, 10)}...${hash.substring(hash.length - 8)}`;
|
||||
return assetData.value.tx_hash || '未知';
|
||||
});
|
||||
|
||||
// 隐藏交易哈希(保留前6位 + 6个*)
|
||||
@ -346,8 +383,7 @@ const loadData = async () => {
|
||||
isLiked.value = res.data.asset.is_liked || res.data.is_liked || false;
|
||||
likeCount.value = asset.like_count || 0;
|
||||
|
||||
if (asset.remain_time > 0) {
|
||||
remainSeconds.value = asset.remain_time;
|
||||
if (asset.exhibition_expire_at) {
|
||||
startCountdown();
|
||||
}
|
||||
console.log(res.data)
|
||||
@ -375,13 +411,10 @@ const loadData = async () => {
|
||||
|
||||
// 倒计时
|
||||
const startCountdown = () => {
|
||||
updateCountdowns(); // 立即显示,不等待1秒
|
||||
if (countdownTimer) clearInterval(countdownTimer);
|
||||
countdownTimer = setInterval(() => {
|
||||
if (remainSeconds.value > 0) {
|
||||
remainSeconds.value--;
|
||||
} else {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
updateCountdowns();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
@ -432,6 +465,7 @@ onLoad((options) => {
|
||||
assetIdParam.value = options?.asset_id || '';
|
||||
orderIdParam.value = options?.order_id || '';
|
||||
fromParam.value = options?.from || '';
|
||||
loadCurrentUser();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
@ -636,10 +670,12 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
min-height: 90%;
|
||||
padding: 104rpx 40rpx 80rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
@ -715,14 +751,15 @@ onUnmounted(() => {
|
||||
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
||||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
|
||||
padding: 10rpx 28rpx;
|
||||
backdrop-filter: blur(12rpx);
|
||||
}
|
||||
|
||||
.like-area:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.like-icon, .heart-icon, .crystal-icon {
|
||||
.like-icon,
|
||||
.heart-icon,
|
||||
.crystal-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
@ -730,9 +767,10 @@ onUnmounted(() => {
|
||||
.like-num {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #e6e6e6;
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 收益 */
|
||||
@ -754,22 +792,28 @@ onUnmounted(() => {
|
||||
.earnings-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #e6e6e6;
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 倒计时 */
|
||||
.countdown-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||||
border-radius: 999rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
}
|
||||
|
||||
.countdown-pill {
|
||||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||||
border-radius: 999rpx;
|
||||
padding: 10rpx 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.4);
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.countdown-val {
|
||||
@ -778,6 +822,7 @@ onUnmounted(() => {
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 信息行(持有人 + 铸造时间) */
|
||||
@ -804,8 +849,8 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
width: 416rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
@ -822,6 +867,20 @@ onUnmounted(() => {
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.info-avatar {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.info-nickname {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
/* 点赞用户头像区域 */
|
||||
.liked-users-area {
|
||||
position: relative;
|
||||
@ -841,7 +900,7 @@ onUnmounted(() => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.liked-user-image{
|
||||
.liked-user-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@ -910,7 +969,6 @@ onUnmounted(() => {
|
||||
padding: 30rpx 16rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
@ -943,7 +1001,6 @@ onUnmounted(() => {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-family: 'yt', sans-serif;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
display: flex
|
||||
@ -980,7 +1037,7 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding:0 16rpx;
|
||||
padding: 0 16rpx;
|
||||
background: linear-gradient(to bottom right,
|
||||
#F0E4B1 0%,
|
||||
#F08399 50%,
|
||||
@ -1034,9 +1091,9 @@ onUnmounted(() => {
|
||||
margin-bottom: 8rpx;
|
||||
/* transform: scale(1.2); */
|
||||
}
|
||||
.visit-text{
|
||||
color: #FFFFFF;
|
||||
font-size:16rpx;
|
||||
}
|
||||
|
||||
.visit-text {
|
||||
color: #FFFFFF;
|
||||
font-size: 16rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,13 +6,12 @@
|
||||
<view class="cards-container">
|
||||
<view v-for="(card, index) in cardList" :key="index" class="card-item"
|
||||
:class="{ 'card-selected': selectedIndex === index }" :style="getCardStyle(index)">
|
||||
<view
|
||||
class="card-frame"
|
||||
<view class="card-frame"
|
||||
:class="{ 'no-border': card.comingSoon, 'card-frame--tappable': !card.comingSoon }"
|
||||
@tap.stop="onCardFrameTap(index)"
|
||||
>
|
||||
@tap.stop="onCardFrameTap(index)">
|
||||
<image class="card-image" :src="card.image" mode="aspectFill" />
|
||||
<image v-if="card.comingSoon" class="coming-soon-badge" src="/static/castlove/jinqingqidai.png" mode="aspectFit" />
|
||||
<image v-if="card.comingSoon" class="coming-soon-badge" src="/static/castlove/jinqingqidai.png"
|
||||
mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -21,7 +20,8 @@
|
||||
<view class="text-panel">
|
||||
<!-- 向上按钮 -->
|
||||
<view class="arrow-btn arrow-up" @click="scrollUp">
|
||||
<image class="arrow-icon" src="/static/castlove/jiantou.png" mode="aspectFit" style="transform: rotate(180deg);" />
|
||||
<image class="arrow-icon" src="/static/castlove/jiantou.png" mode="aspectFit"
|
||||
style="transform: rotate(180deg);" />
|
||||
</view>
|
||||
|
||||
<!-- 文字列表 -->
|
||||
@ -65,7 +65,14 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedIndex: 2, // 初始选中光栅卡
|
||||
selectedIndex: 2,
|
||||
// 工艺名称 → 页面路由映射,方便扩展
|
||||
cardRoutes: {
|
||||
'光栅卡': '/pages/castlove/create',
|
||||
'拍立得': '/pages/castlove/create',
|
||||
'镭射卡': '/pages/castlove/create',
|
||||
'撕拉片': '/pages/castlove/create',
|
||||
},
|
||||
cardList: [
|
||||
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false },
|
||||
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false },
|
||||
@ -130,15 +137,24 @@ export default {
|
||||
return;
|
||||
}
|
||||
const pos = this.getCardStackPosition(index);
|
||||
const LENTICULAR_INDEX = 2;
|
||||
const goLenticular =
|
||||
this.selectedIndex === LENTICULAR_INDEX &&
|
||||
(pos === 2 || pos === 1);
|
||||
const targetName = goLenticular ? '光栅卡' : card.name;
|
||||
if (pos === 2 || goLenticular) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/castlove/create?name=${encodeURIComponent(targetName)}`,
|
||||
});
|
||||
// 只有中间位置的卡片点击才会进入创建页
|
||||
// 其他位置点击只是切换选中(把卡片移到中间),再次点击中间卡片才进入
|
||||
if (pos === 2) {
|
||||
if (card.name === '撕拉片') {
|
||||
const route = this.cardRoutes[card.name];
|
||||
if (route) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/castlove/mint/tear-card',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const route = this.cardRoutes[card.name];
|
||||
if (route) {
|
||||
uni.navigateTo({
|
||||
url: `${route}?name=${encodeURIComponent(card.name)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.selectCard(index);
|
||||
@ -164,9 +180,12 @@ export default {
|
||||
uni.showToast({ title: '请选择已开放的工艺', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/castlove/create?name=${encodeURIComponent(card.name)}`
|
||||
})
|
||||
const route = this.cardRoutes[card.name]
|
||||
if (route) {
|
||||
uni.navigateTo({
|
||||
url: `${route}?name=${encodeURIComponent(card.name)}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,6 +236,7 @@ export default {
|
||||
background-image: url('/static/square/cangpinkuang1.png');
|
||||
background-size: cover;
|
||||
box-shadow: 0 0 0 rgba(0, 0, 0, 0.5);
|
||||
|
||||
&.no-border {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="upload-box upload-box--half" @click="chooseLenticularSubject">
|
||||
<image v-if="uploadedSubject" class="uploaded-image" :src="uploadedSubject" mode="aspectFit"></image>
|
||||
<image v-if="uploadedSubject" class="uploaded-image":src="uploadedSubject" mode="aspectFit"></image>
|
||||
<view v-else class="upload-placeholder">
|
||||
<image class="upload-icon" src="/static/icon/add.png" mode="aspectFit"></image>
|
||||
<text class="upload-text">主体图</text>
|
||||
|
||||
@ -23,8 +23,7 @@
|
||||
|
||||
<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="goToAssetDetail(item.id)">
|
||||
:class="index % 2 === 0 ? 'card-tilt-left' : 'card-tilt-right'" @tap="goToAssetDetail(item.id)">
|
||||
<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">
|
||||
@ -36,6 +35,13 @@
|
||||
<text class="card-rate-text">{{ item.like_count || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 倒计时背景 -->
|
||||
<view class="countdown-background" :style="getCountdownBackgroundStyle()">
|
||||
<!-- 倒计时文字 -->
|
||||
<text class="countdown-text">
|
||||
{{ formatCountdown(item.id) }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 图片下方收益 -->
|
||||
<view class="card-income-row"
|
||||
:class="index % 2 === 0 ? 'income-tilt-right' : 'income-tilt-left'">
|
||||
@ -80,13 +86,14 @@
|
||||
<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 rank-icon-' + (index + 1)" mode="aspectFit"></image>
|
||||
<image v-if="index < 3" :src="rankIcons[index]" :class="'rank-icon rank-icon-' + (index + 1)"
|
||||
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' : ''" >
|
||||
<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"
|
||||
@ -105,7 +112,9 @@
|
||||
|
||||
<!-- 右侧奖励 -->
|
||||
<view class="liked-reward">
|
||||
<image class="reward-token-icon" :src="item.earnings > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'" mode="aspectFit">
|
||||
<image class="reward-token-icon"
|
||||
:src="item.earnings > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'"
|
||||
mode="aspectFit">
|
||||
</image>
|
||||
<text class="reward-amount">+{{ item.reward }}</text>
|
||||
</view>
|
||||
@ -123,7 +132,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { getUserGalleriesApi, getUserLikedAssetsApi, getUserProfileApi } from '@/utils/api.js';
|
||||
|
||||
@ -156,6 +165,68 @@ const formatScore = (score) => {
|
||||
return Number(score).toLocaleString();
|
||||
};
|
||||
|
||||
// 计算剩余时间
|
||||
const calculateRemainingTime = (item) => {
|
||||
if (item.remainSeconds !== undefined) {
|
||||
if (item.remainSeconds <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(item.remainSeconds / 3600);
|
||||
const minutes = Math.floor((item.remainSeconds % 3600) / 60);
|
||||
const seconds = Math.floor(item.remainSeconds % 60);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
if (item.expire_at) {
|
||||
const now = Date.now();
|
||||
const expireTime = new Date(item.expire_at).getTime();
|
||||
const remaining = expireTime - now;
|
||||
if (remaining <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(remaining / (60 * 60 * 1000));
|
||||
const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000));
|
||||
const seconds = Math.floor((remaining % (60 * 1000)) / 1000);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
};
|
||||
|
||||
// 更新所有倒计时
|
||||
const updateCountdowns = () => {
|
||||
exhibitionWorks.value.forEach(item => {
|
||||
if (item && item.id) {
|
||||
if (item.remainSeconds !== undefined && item.remainSeconds > 0) {
|
||||
item.remainSeconds--;
|
||||
}
|
||||
countdowns.value[item.id] = calculateRemainingTime(item);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 格式化倒计时显示
|
||||
const formatCountdown = (itemId) => {
|
||||
const countdown = countdowns.value[itemId];
|
||||
if (!countdown || countdown.expired) return '00:00:00';
|
||||
const h = String(countdown.hours).padStart(2, '0');
|
||||
const m = String(countdown.minutes).padStart(2, '0');
|
||||
const s = String(countdown.seconds).padStart(2, '0');
|
||||
return `${h}:${m}:${s}`;
|
||||
};
|
||||
|
||||
// 获取倒计时背景样式
|
||||
const getCountdownBackgroundStyle = () => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
bottom: '20rpx',
|
||||
right: '40%',
|
||||
transform: 'translateX(50%)',
|
||||
width: '140rpx',
|
||||
height: '36rpx',
|
||||
zIndex: 9
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const rankIcons = [
|
||||
'/static/square/icon1.png',
|
||||
'/static/square/icon2.png',
|
||||
@ -165,6 +236,9 @@ const rankIcons = [
|
||||
// 在展作品列表
|
||||
const exhibitionWorks = ref([]);
|
||||
|
||||
// 倒计时状态
|
||||
const countdowns = ref({});
|
||||
|
||||
// 点赞作品列表
|
||||
const likedWorks = ref([]);
|
||||
|
||||
@ -236,6 +310,19 @@ const loadLikedAssets = async () => {
|
||||
onMounted(() => {
|
||||
loadExhibitedAssets();
|
||||
loadLikedAssets();
|
||||
|
||||
// 启动倒计时定时器
|
||||
countdownTimer = setInterval(() => {
|
||||
updateCountdowns();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
let countdownTimer = null;
|
||||
|
||||
onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -400,8 +487,8 @@ onMounted(() => {
|
||||
|
||||
.card-rate-badge {
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
left: 40%;
|
||||
top: 16rpx;
|
||||
left: 25%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -476,6 +563,35 @@ onMounted(() => {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 倒计时背景 */
|
||||
.countdown-background {
|
||||
/* position: absolute;
|
||||
bottom: 20rpx;
|
||||
right: 30%;
|
||||
transform: translateX(-50%); */
|
||||
width: 140rpx;
|
||||
height: 36rpx;
|
||||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||||
border-radius: 999rpx;
|
||||
z-index: 9;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.4);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
/* 倒计时文字 */
|
||||
.countdown-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.8);
|
||||
z-index: 10;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-feature-settings: "tnum";
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-exhibition {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -527,7 +643,7 @@ onMounted(() => {
|
||||
flex-shrink: 0;
|
||||
/* margin-right: 8rpx; */
|
||||
position: relative;
|
||||
left: 32rpx;
|
||||
left: 32rpx;
|
||||
}
|
||||
|
||||
.rank-icon-1 {
|
||||
|
||||
@ -39,6 +39,13 @@
|
||||
<text class="card-rate-text">{{ item.like_count || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 倒计时背景 -->
|
||||
<view class="countdown-background" :style="getCountdownBackgroundStyle(index)">
|
||||
<!-- 倒计时文字 -->
|
||||
<text class="countdown-text">
|
||||
{{ formatCountdown(item.id) }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 图片下方收益 -->
|
||||
<view class="card-income-row"
|
||||
:class="index % 2 === 0 ? 'income-tilt-right' : 'income-tilt-left'">
|
||||
@ -101,13 +108,14 @@
|
||||
<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 rank-icon-' + (index + 1)" mode="aspectFit"></image>
|
||||
<image v-if="index < 3" :src="rankIcons[index]" :class="'rank-icon rank-icon-' + (index + 1)"
|
||||
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' : ''" >
|
||||
<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"
|
||||
@ -126,7 +134,9 @@
|
||||
|
||||
<!-- 右侧奖励 -->
|
||||
<view class="liked-reward">
|
||||
<image class="reward-token-icon" :src="item.reward > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'" mode="aspectFit">
|
||||
<image class="reward-token-icon"
|
||||
:src="item.reward > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'"
|
||||
mode="aspectFit">
|
||||
</image>
|
||||
<text class="reward-amount">+{{ item.reward }}</text>
|
||||
</view>
|
||||
@ -302,9 +312,73 @@ const formatScore = (score) => {
|
||||
return Number(score).toLocaleString();
|
||||
};
|
||||
|
||||
// 计算剩余时间
|
||||
const calculateRemainingTime = (item) => {
|
||||
if (item.remainSeconds !== undefined) {
|
||||
if (item.remainSeconds <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(item.remainSeconds / 3600);
|
||||
const minutes = Math.floor((item.remainSeconds % 3600) / 60);
|
||||
const seconds = Math.floor(item.remainSeconds % 60);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
if (item.expire_at) {
|
||||
const now = Date.now();
|
||||
const expireTime = new Date(item.expire_at).getTime();
|
||||
const remaining = expireTime - now;
|
||||
if (remaining <= 0) {
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
}
|
||||
const hours = Math.floor(remaining / (60 * 60 * 1000));
|
||||
const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000));
|
||||
const seconds = Math.floor((remaining % (60 * 1000)) / 1000);
|
||||
return { hours, minutes, seconds, expired: false };
|
||||
}
|
||||
return { hours: 0, minutes: 0, seconds: 0, expired: true };
|
||||
};
|
||||
|
||||
// 更新所有倒计时
|
||||
const updateCountdowns = () => {
|
||||
exhibitionWorks.value.forEach(item => {
|
||||
if (item && item.id) {
|
||||
if (item.remainSeconds !== undefined && item.remainSeconds > 0) {
|
||||
item.remainSeconds--;
|
||||
}
|
||||
countdowns.value[item.id] = calculateRemainingTime(item);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 格式化倒计时显示
|
||||
const formatCountdown = (itemId) => {
|
||||
const countdown = countdowns.value[itemId];
|
||||
if (!countdown || countdown.expired) return '00:00:00';
|
||||
const h = String(countdown.hours).padStart(2, '0');
|
||||
const m = String(countdown.minutes).padStart(2, '0');
|
||||
const s = String(countdown.seconds).padStart(2, '0');
|
||||
return `${h}:${m}:${s}`;
|
||||
};
|
||||
|
||||
// 获取倒计时背景样式
|
||||
const getCountdownBackgroundStyle = () => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
bottom: '20rpx',
|
||||
right: '40%',
|
||||
transform: 'translateX(50%)',
|
||||
width: '140rpx',
|
||||
height: '36rpx',
|
||||
zIndex: 9
|
||||
};
|
||||
};
|
||||
|
||||
// 在展作品列表
|
||||
const exhibitionWorks = ref([]);
|
||||
|
||||
// 倒计时状态
|
||||
const countdowns = ref({});
|
||||
|
||||
// 当前点赞作品列表
|
||||
const likedWorks = ref([]);
|
||||
|
||||
@ -380,6 +454,11 @@ onMounted(() => {
|
||||
loadExhibitedAssets();
|
||||
loadLikedAssets();
|
||||
|
||||
// 启动倒计时定时器
|
||||
countdownTimer = setInterval(() => {
|
||||
updateCountdowns();
|
||||
}, 1000);
|
||||
|
||||
// 监听身份切换事件,切换后刷新数据
|
||||
uni.$on('userInfoUpdated', () => {
|
||||
loadExhibitedAssets();
|
||||
@ -387,7 +466,12 @@ onMounted(() => {
|
||||
});
|
||||
});
|
||||
|
||||
let countdownTimer = null;
|
||||
|
||||
onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
uni.$off('userInfoUpdated');
|
||||
});
|
||||
|
||||
@ -560,13 +644,13 @@ onShow(() => {
|
||||
.card-tilt-left {
|
||||
transform: rotate(-4deg) translateY(10rpx);
|
||||
margin-right: 32rpx;
|
||||
border-radius: 32rpx;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: -16rpx 16rpx 16rpx rgba(229, 76, 93, 0.9);
|
||||
}
|
||||
|
||||
.card-tilt-right {
|
||||
transform: rotate(4deg) translateY(10rpx);
|
||||
border-radius: 32rpx;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 16rpx 16rpx 16rpx rgba(229, 76, 93, 0.9);
|
||||
}
|
||||
|
||||
@ -617,8 +701,8 @@ onShow(() => {
|
||||
|
||||
.card-rate-badge {
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
left: 40%;
|
||||
top: 16rpx;
|
||||
left: 25%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -697,6 +781,35 @@ onShow(() => {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 倒计时背景 */
|
||||
.countdown-background {
|
||||
/* position: absolute; */
|
||||
/* top: 20rpx;
|
||||
left: 30%; */
|
||||
/* transform: translateX(-50%); */
|
||||
width: 140rpx;
|
||||
height: 36rpx;
|
||||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||||
border-radius: 999rpx;
|
||||
z-index: 9;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.4);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
/* 倒计时文字 */
|
||||
.countdown-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-family: 'yt', sans-serif;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.8);
|
||||
z-index: 10;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-feature-settings: "tnum";
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-exhibition {
|
||||
display: flex;
|
||||
|
||||
@ -1521,7 +1521,7 @@ onShow(() => {
|
||||
|
||||
.asset-card {
|
||||
background-image: url(/static/rank/activity-support-icon/beijingkuang.png);
|
||||
background-size: 100% 100%;
|
||||
background-size: 100% 125%;
|
||||
background-position: center;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 16rpx;
|
||||
|
||||
@ -3,19 +3,19 @@
|
||||
// 不需要手动注释!
|
||||
|
||||
// #ifdef H5
|
||||
// const baseURL = 'http://localhost:8080' // H5 开发用本机
|
||||
const baseURL = 'http://101.132.250.62:8080' // H5 开发用本机
|
||||
const baseURL = 'http://192.168.110.60:8080' // H5 开发用本机
|
||||
// const baseURL = 'http://101.132.250.62:8080' // H5 开发用本机
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// 开发调试:手机和电脑同一WiFi时用这个(改成你电脑IP)
|
||||
// 上线后:改成实际服务器地址
|
||||
// const baseURL = 'http://192.168.110.60:8080'
|
||||
const baseURL = 'http://192.168.110.60:8080'
|
||||
// #endif
|
||||
|
||||
// 服务器地址(正式上线用)
|
||||
// #ifdef APP-PLUS
|
||||
const baseURL = 'http://101.132.250.62:8080'
|
||||
// const baseURL = 'http://101.132.250.62:8080'
|
||||
// #endif
|
||||
|
||||
// 是否使用模拟数据(开发调试时设为 true,后端API准备好后改为 false)
|
||||
@ -468,7 +468,9 @@ export function getBatchOssPresignedUrlsApi(files, expires = 3600, type = 'asset
|
||||
* @returns {Promise<{ imageUrl: string, orderId?: string, data: object }>}
|
||||
*/
|
||||
export function uploadLocalFileToOss(filePath, options = {}) {
|
||||
const { type = 'asset', orderId = '', fileName } = options
|
||||
const {
|
||||
type = 'asset', orderId = '', fileName
|
||||
} = options
|
||||
const objectName = fileName || `${Date.now()}.jpg`
|
||||
return new Promise((resolve, reject) => {
|
||||
getOssSignatureApi(type, orderId)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user