style:修改个人设置页面和藏品详细页的样式

This commit is contained in:
zheng020 2026-05-14 21:59:19 +08:00
parent 61921372fb
commit 6bc0ac71ee
5 changed files with 960 additions and 150 deletions

View File

@ -1,12 +1,12 @@
<template> <template>
<view class="detail-container"> <view class="detail-container">
<!-- 背景图片 --> <!-- 背景图片 -->
<image class="background-image" src="/static/background/starbook.jpg" mode="aspectFill"></image> <image class="background-image" src="/static/square/squearbj.png" mode="aspectFill"></image>
<!-- Header --> <!-- Header -->
<view class="header-bar"> <view class="header-bar">
<view class="back-btn" @tap="handleBack"> <view class="back-btn" @tap="handleBack">
<text class="back-icon"></text> <image class="back-icon" src="/static/starbookcontent/tuichu.png" mode="aspectFit"></image>
</view> </view>
</view> </view>
@ -38,19 +38,26 @@
<!-- 藏品卡片区域 --> <!-- 藏品卡片区域 -->
<view class="card-section"> <view class="card-section">
<view class="card-wrapper"> <view class="card-wrapper">
<NftCard :cover-image="coverUrl" :width="cardSize" :height="cardSize" <image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFit">
:custom-style="nftCardStyle" /> </image>
<image class="card-image" :src="coverUrl" mode="aspectFill"></image>
<!-- <view class="card-badge"></view> -->
</view> </view>
<!-- 点赞 + 倒计时行 --> <!-- 点赞 + 收益 + 倒计时行 -->
<view class="card-meta-row"> <view class="card-meta-row">
<!-- 点赞 --> <!-- 点赞 -->
<view class="like-area" @tap="handleLike"> <view class="like-area" @tap="handleLike">
<image :src="isLiked ? '/static/icon/like-after.png' : '/static/icon/like-before.png'" <image :src="isLiked ? '/static/icon/like-after.png' : '/static/icon/like-before.png'" class="like-icon" mode="aspectFit"></image>
class="like-icon" mode="aspectFit" />
<text class="like-num">{{ likeCount }}</text> <text class="like-num">{{ likeCount }}</text>
</view> </view>
<!-- 收益 -->
<view class="earnings-area">
<image class="crystal-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
<text class="earnings-text">{{ assetData.earnings || 0 }}/</text>
</view>
<!-- 倒计时如有 --> <!-- 倒计时如有 -->
<view v-if="showCountdown" class="countdown-area"> <view v-if="showCountdown" class="countdown-area">
<view class="countdown-pill"> <view class="countdown-pill">
@ -60,54 +67,132 @@
</view> </view>
</view> </view>
<!-- 持有人 + 获取时间 --> <!-- 信息行创作者 + 铸造时间 + 来源 -->
<view class="owner-section"> <view class="info-row">
<view class="owner-row"> <view class="info-col">
<view class="owner-info"> <view class="info-item">
<text class="owner-label">持有人</text> <text class="info-label">创作者</text>
<text class="owner-name">{{ assetData.owner_nickname || '未知' }}</text> <text class="info-value">{{ assetData.owner_nickname || '未知' }}</text>
</view> </view>
</view> </view>
<view class="acquire-row"> <view class="info-col">
<text class="acquire-label">获取时间</text> <view class="info-item">
<text class="acquire-value">{{ formattedAcquireTime }}</text> <text class="info-label">铸造时间</text>
<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 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>
<view class="creator-info">
<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>
</view>
</view> </view>
</view> </view>
<!-- 链上数据 --> <!-- 链上数据 -->
<view class="chain-section"> <view class="chain-section">
<view class="chain-row"> <image class="chain-logo" src="/static/logo/APPLOGO.png" mode="aspectFit"></image>
<text class="chain-label">数根名称</text> <view class="chain-left">
<text class="chain-value">{{ assetData.name || '未知' }}</text> <view class="chain-row">
</view> <text class="chain-label">数根名称</text>
<view class="chain-row"> <view class="chain-value-wrap">
<text class="chain-label">数根编号</text> <text class="chain-value">{{ assetData.name || '未知' }}</text>
<text class="chain-value">{{ assetData.asset_id || '未知' }}</text> </view>
</view> </view>
<view class="chain-row"> <view class="chain-row">
<text class="chain-label">数根发行方</text> <text class="chain-label">数根发行方</text>
<text class="chain-value">TOPFANS</text> <view class="chain-value-wrap">
</view> <text class="chain-value">TOPFANS</text>
<view class="chain-row"> </view>
<text class="chain-label">区块链编号</text> </view>
<text class="chain-value">{{ assetData.block_number || '未知' }}</text> <view class="chain-row">
</view> <text class="chain-label">区块链编号</text>
<view class="chain-row"> <view class="chain-value-wrap">
<text class="chain-label">交易哈希</text> <text class="chain-value">{{ showBlockNumber ? (assetData.block_number || '未知') : hiddenBlockNumber }}</text>
<text class="chain-value chain-hash" @longpress="copyHash">{{ displayTxHash }}</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>
<view class="chain-value-wrap">
<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>
</view>
</view>
</view> </view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
<!-- 点赞用户弹窗 -->
<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" >
<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>
<text class="visit-text">点击拜访</text>
</view>
</view>
</view>
</template>
</LikeUsersModal>
</view> </view>
</template> </template>
<script setup> <script setup>
import { ref, computed, onUnmounted } from 'vue'; import { ref, computed, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app'; import { onLoad, onShow } from '@dcloudio/uni-app';
import NftCard from '../components/NftCard.vue';
import { getAssetDetailApi, getMintOrderDetailApi, likeAssetApi, unlikeAssetApi } from '@/utils/api.js'; import { getAssetDetailApi, getMintOrderDetailApi, likeAssetApi, unlikeAssetApi } from '@/utils/api.js';
import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js'; import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js';
import LikeUsersModal from '@/pages/components/LikeUsersModal.vue';
// //
const assetIdParam = ref(''); const assetIdParam = ref('');
const orderIdParam = ref(''); const orderIdParam = ref('');
@ -123,6 +208,51 @@ const isLiked = ref(false);
const likeCount = ref(0); const likeCount = ref(0);
const liking = ref(false); const liking = ref(false);
// 6
// avatar: , ellipseX: X, ellipseY: Y, size:
const likedUsers = ref([
{ avatar: '/static/sucai/image-01.png', ellipseX: -40, ellipseY: -40, size: 0.85 },
{ 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 }
]);
//
const showLikeUsersModal = ref(false);
const likeUsersActiveTab = ref(0);
// /
const todayLikeUsers = ref([
{ userId: '1', nickname: '用户A', avatar: '/static/sucai/image-01.png', time: '10分钟前' },
{ userId: '2', nickname: '用户B', avatar: '/static/sucai/image-02.png', time: '30分钟前' },
{ userId: '3', nickname: '用户C', avatar: '/static/sucai/image-03.png', time: '1小时前' },
]);
const historyLikeUsers = ref([
{ userId: '4', nickname: '用户D', avatar: '/static/sucai/image-04.png', time: '昨天' },
{ userId: '5', nickname: '用户E', avatar: '/static/sucai/image-05.png', time: '3天前' },
]);
const displayedLikeUsers = computed(() => {
return likeUsersActiveTab.value === 0 ? todayLikeUsers.value : historyLikeUsers.value;
});
const handleLikeUsersTabChange = ({ tabIndex }) => {
likeUsersActiveTab.value = tabIndex;
};
// 访
const handleVisit = () => {
uni.navigateTo({
url: `/pages/profile/hisWorks?userId=${user.userId}&nickname=${encodeURIComponent(user.nickname)}`
});
};
//
const showBlockNumber = ref(false);
const showTxHash = ref(false);
// //
const remainSeconds = ref(0); const remainSeconds = ref(0);
let countdownTimer = null; let countdownTimer = null;
@ -136,16 +266,6 @@ const countdownText = computed(() => {
return `${h}:${m}:${s}`; return `${h}:${m}:${s}`;
}); });
// px
const cardSize = 280;
const nftCardStyle = {
position: 'relative',
pointerEvents: 'none',
cursor: 'default',
transition: 'none'
};
// created_at getAssetDetailApi // created_at getAssetDetailApi
const formattedAcquireTime = computed(() => { const formattedAcquireTime = computed(() => {
const raw = assetData.value.created_at || ''; const raw = assetData.value.created_at || '';
@ -166,6 +286,23 @@ const displayTxHash = computed(() => {
return `${hash.substring(0, 10)}...${hash.substring(hash.length - 8)}`; return `${hash.substring(0, 10)}...${hash.substring(hash.length - 8)}`;
}); });
// 6 + 6*
const hiddenTxHash = computed(() => {
const hash = assetData.value.tx_hash;
if (!hash) return '******';
if (hash.length <= 6) return hash + '******';
return `${hash.substring(0, 6)}******`;
});
// 6 + 6*
const hiddenBlockNumber = computed(() => {
const num = assetData.value.block_number;
if (!num) return '******';
const str = String(num);
if (str.length <= 6) return str + '******';
return `${str.substring(0, 6)}******`;
});
// //
const copyHash = () => { const copyHash = () => {
const hash = assetData.value.tx_hash; const hash = assetData.value.tx_hash;
@ -337,13 +474,8 @@ onUnmounted(() => {
/* Header */ /* Header */
.header-bar { .header-bar {
position: fixed; position: fixed;
top: 0; top: 32rpx;
left: 0; left: 32rpx;
right: 0;
height: 180rpx;
display: flex;
align-items: flex-end;
padding: 0 30rpx 20rpx;
z-index: 100; z-index: 100;
} }
@ -358,10 +490,8 @@ onUnmounted(() => {
} }
.back-icon { .back-icon {
font-size: 48rpx; width: 80rpx;
color: #1a1a1a; height: 80rpx;
line-height: 1;
text-shadow: 0 1rpx 4rpx rgba(255, 255, 255, 0.6);
} }
/* 加载/错误 */ /* 加载/错误 */
@ -506,7 +636,7 @@ onUnmounted(() => {
} }
.content-wrapper { .content-wrapper {
padding: 200rpx 40rpx 80rpx; padding: 104rpx 40rpx 80rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -519,11 +649,48 @@ onUnmounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 48rpx;
} }
.card-wrapper { .card-wrapper {
margin-bottom: 28rpx; position: relative;
width: 352rpx;
height: 520rpx;
margin-bottom: 32rpx;
}
.card-image {
width: 88%;
height: 96%;
left: 6%;
top: 1.5%;
border-radius: 48rpx;
transform-origin: center center;
position: absolute;
z-index: 3;
overflow: hidden;
transform: rotate(-10deg);
}
.card-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
transform: rotate(-10deg);
}
.card-badge {
position: absolute;
top: 40rpx;
left: 40rpx;
width: 60rpx;
height: 60rpx;
background: red;
border-radius: 20rpx;
z-index: 4;
} }
.card-meta-row { .card-meta-row {
@ -539,8 +706,14 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10rpx; gap: 10rpx;
background: rgba(0, 0, 0, 0.22); background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx; border-radius: 999rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
padding: 10rpx 28rpx; padding: 10rpx 28rpx;
backdrop-filter: blur(12rpx); backdrop-filter: blur(12rpx);
} }
@ -549,7 +722,7 @@ onUnmounted(() => {
opacity: 0.75; opacity: 0.75;
} }
.like-icon { .like-icon, .heart-icon, .crystal-icon {
width: 44rpx; width: 44rpx;
height: 44rpx; height: 44rpx;
} }
@ -562,6 +735,30 @@ onUnmounted(() => {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
/* 收益 */
.earnings-area {
display: flex;
align-items: center;
gap: 10rpx;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
padding: 10rpx 28rpx;
}
.earnings-text {
font-size: 32rpx;
font-weight: bold;
color: #e6e6e6;
font-family: 'yt', sans-serif;
font-variant-numeric: tabular-nums;
}
/* 倒计时 */ /* 倒计时 */
.countdown-area { .countdown-area {
display: flex; display: flex;
@ -583,84 +780,155 @@ onUnmounted(() => {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
/* 持有人区域 */ /* 信息行(持有人 + 铸造时间) */
.owner-section { .info-row {
width: 100%; width: 100%;
margin-bottom: 40rpx;
display: flex; display: flex;
flex-direction: column; align-items: stretch;
align-items: center; justify-content: center;
gap: 20rpx; gap: 20rpx;
padding: 24rpx 32rpx;
border-radius: 24rpx;
margin-bottom: 40rpx;
flex-direction: column;
} }
.owner-row { .info-col {
flex: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.owner-info { .info-item {
display: flex; display: flex;
flex-direction: column; gap: 8rpx;
align-items: center; width: 416rpx;
text-align: center; justify-content: space-between;
gap: 4rpx; align-items: center;
} }
.owner-label { .info-label {
font-size: 24rpx; font-size: 32rpx;
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
font-family: 'yt', sans-serif; font-family: 'yt', sans-serif;
text-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.6);
} }
.owner-name { .info-value {
font-size: 36rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
font-family: 'yt', sans-serif; font-family: 'yt', sans-serif;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7); text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7);
} }
.acquire-row { /* 点赞用户头像区域 */
.liked-users-area {
position: relative;
flex: 1;
height: 184rpx;
display: flex;
align-items: center;
justify-content: center;
}
.liked-user-avatar {
position: absolute;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: 3rpx solid #F0E4B1;
overflow: hidden;
}
.liked-user-image{
width: 100%;
height: 100%;
}
/* 创作者信息模块 */
.creator-section {
width: calc(100% - 80rpx);
display: flex;
flex-direction: row;
align-items: center;
gap: 24rpx;
padding: 16rpx;
background: url('/static/rank/activity-support-icon/beijingkuang.png') no-repeat center center;
background-size: 105% 120%;
border-radius: 24rpx;
margin-bottom: 64rpx;
}
.creator-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.creator-info {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; gap: 12rpx;
text-align: center;
gap: 4rpx;
} }
.acquire-label { .creator-title {
font-size: 24rpx; font-size: 28rpx;
color: rgba(255, 255, 255, 0.7); color: #fff;
font-family: 'yt', sans-serif; font-family: 'yt', sans-serif;
text-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.6);
} }
.acquire-value { .creator-card {
display: flex;
flex-direction: row;
align-items: center;
flex-direction: column;
gap: 16rpx;
border-radius: 16rpx;
}
.creator-tip {
font-size: 32rpx; font-size: 32rpx;
color: #ffffff; color: #ffcc00;
font-family: 'yt', sans-serif; font-family: 'yt', sans-serif;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7); text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7);
} }
.creator-preview {
width: 112rpx;
height: 80rpx;
border-radius: 12rpx;
}
/* 链上数据 */ /* 链上数据 */
.chain-section { .chain-section {
width: calc(100% - 80rpx); width: calc(100% - 80rpx);
background: rgba(0, 0, 0, 0.22); background: url('/static/rank/activity-support-icon/beijingkuang.png') no-repeat center center;
background-size: 105% 120%;
border-radius: 24rpx; border-radius: 24rpx;
padding: 30rpx 48rpx; padding: 30rpx 16rpx;
backdrop-filter: blur(12rpx); display: flex;
flex-direction: row;
align-items: center;
gap: 24rpx;
}
.chain-left {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0;
} }
.chain-row { .chain-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
padding: 18rpx 0; }
.chain-logo {
width: 156rpx;
height: 156rpx;
} }
.chain-label { .chain-label {
@ -678,9 +946,97 @@ onUnmounted(() => {
text-align: right; text-align: right;
flex: 1; flex: 1;
word-break: break-all; word-break: break-all;
display: flex
}
.chain-value-wrap {
display: flex;
align-items: center;
gap: 12rpx;
width: 240rpx;
}
.toggle-btn {
padding: 4rpx 8rpx;
}
.toggle-icon {
font-size: 28rpx;
} }
.chain-hash { .chain-hash {
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
} }
/* 点赞用户列表 */
.like-users-list {
display: flex;
flex-direction: column;
gap: 24rpx;
padding: 16rpx;
}
.like-user-item {
display: flex;
flex-direction: row;
align-items: center;
padding:0 16rpx;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}
.like-user-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.like-user-info {
flex: 1;
display: flex;
flex-direction: column;
margin: 0 24rpx;
}
.like-user-name {
font-size: 28rpx;
color: #fff;
font-family: 'yt', sans-serif;
}
.like-user-time {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
font-family: 'yt', sans-serif;
}
/* 拜访按钮 - 只显示图标 */
.visit-button {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
padding: 8rpx;
flex-direction: column;
}
.visit-icon {
width: 80rpx;
height: 80rpx;
margin-bottom: 8rpx;
/* transform: scale(1.2); */
}
.visit-text{
color: #FFFFFF;
font-size:16rpx;
}
</style> </style>

View File

@ -4,7 +4,7 @@
<image class="avatar-image" :src="avatarImage" mode="aspectFill"></image> <image class="avatar-image" :src="avatarImage" mode="aspectFill"></image>
</view> </view>
<view class="level-badge" v-if="showLevel && level" :style="badgeStyle"> <view class="level-badge" v-if="showLevel && level" :style="badgeStyle">
<text class="level-text" :style="badgeTextStyle">LV{{ level }}</text> <text class="level-text" :style="badgeTextStyle">Lv {{ level }}</text>
</view> </view>
</view> </view>
</template> </template>
@ -155,23 +155,25 @@
if (props.size >= 160) { if (props.size >= 160) {
// profile // profile
return { return {
top: '-10rpx', // top: '-10rpx',
right: '-10rpx', // right: '-10rpx',
borderRadius: '20rpx', borderRadius: '20rpx',
padding: '6rpx 16rpx', padding: '6rpx 16rpx',
border: '3rpx solid rgba(255, 255, 255, 0.8)', border: '3rpx solid rgba(255, 255, 255, 0.8)',
boxShadow: '0 4rpx 12rpx rgba(0, 0, 0, 0.3)' boxShadow: '0 4rpx 12rpx rgba(0, 0, 0, 0.3)',
bottom: '-10rpx'
}; };
} else { } else {
// //
const offset = -props.size * 0.18; // const offset = -props.size * 0.18; //
return { return {
top: `${offset}rpx`, // top: `${offset}rpx`,
right: `${offset}rpx`, // right: `${offset}rpx`,
borderRadius: '10rpx', borderRadius: '10rpx',
padding: '0 8rpx', padding: '0 8rpx',
border: '2rpx solid rgba(255, 255, 255, 0.8)', border: '2rpx solid rgba(255, 255, 255, 0.8)',
boxShadow: '0 2rpx 8rpx rgba(0, 0, 0, 0.3)' boxShadow: '0 2rpx 8rpx rgba(0, 0, 0, 0.3)',
bottom: `${offset}rpx`
}; };
} }
}); });
@ -179,7 +181,7 @@
// //
const badgeTextStyle = computed(() => { const badgeTextStyle = computed(() => {
// //
const fontSize = props.size >= 160 ? 22 : 18; const fontSize = props.size >= 160 ? 44 : 40;
return { return {
fontSize: `${fontSize}rpx` fontSize: `${fontSize}rpx`
}; };
@ -215,11 +217,18 @@
.level-badge { .level-badge {
position: absolute; position: absolute;
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%); background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
width: 88rpx;
height: 24rpx;
display: flex;
justify-content: center;
} }
.level-text { .level-text {
font-weight: bold; font-weight: bolder;
color: #e6e6e6; color: #fff;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
line-height: 1; line-height: 1;
position: relative;
top: -24rpx;
} }
</style> </style>

View File

@ -0,0 +1,332 @@
<template>
<view v-if="visible" class="modal-wrapper" @touchmove.stop.prevent @click.stop>
<transition name="fade">
<view v-if="visible" class="modal-mask" @touchstart.stop="handleMaskTouchStart"
@click.stop="handleMaskClick">
</view>
</transition>
<transition name="slide-up">
<view v-if="visible" class="modal-container" @click.stop>
<!-- 背景图片 -->
<image class="modal-background" src="/static/rank/activity-support-icon/beijingkuang1.png"
mode="aspectFill"></image>
<!-- 蒙层 -->
<view class="modal-overlay"></view>
<!-- 关闭按钮 -->
<view class="close-button" @touchstart.stop="handleCloseTouchStart" @touchend.stop="handleCloseTouchEnd"
@click="handleCloseClick">
<image class="back-icon" src="/static/starbookcontent/tuichu.png" mode="aspectFit"></image>
</view>
<!-- 内容区域 -->
<view class="modal-content" @touchstart.stop @touchend.stop @click.stop>
<!-- 标题区域 -->
<view class="title-section">
<text class="modal-title">TA们最近为你点过赞</text>
<image class="title-image" src="/static/rank/activity-support-icon/tubiao.png"
mode="aspectFill"></image>
</view>
<!-- 胶囊按钮区域 -->
<view class="tab-section">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item"
:class="{ 'active': activeTabIndex === index }" @tap="handleTabClick(index)">
<text class="tab-text">{{ tab }}</text>
</view>
</view>
<!-- 可滚动列表区域 -->
<scroll-view class="scrollable-content" scroll-y="true" :show-scrollbar="false"
:lower-threshold="100" @scrolltolower="handleScrollToLower">
<!-- 列表内容插槽 -->
<slot name="content"></slot>
</scroll-view>
</view>
</view>
</transition>
</view>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
visible: {
type: Boolean,
default: false
},
tabs: {
type: Array,
default: () => ['今日', '历史']
}
});
const emit = defineEmits(['close', 'tab-change', 'scroll-to-lower']);
//
const activeTabIndex = ref(0);
//
let maskTouchStartTime = 0;
const handleMaskTouchStart = (e) => {
maskTouchStartTime = Date.now();
e.preventDefault();
e.stopPropagation();
};
const handleMaskClick = () => {
emit('close');
};
//
let closeTouchStartTime = 0;
let closeTouchLocked = false;
const handleCloseTouchStart = (e) => {
closeTouchStartTime = Date.now();
closeTouchLocked = false;
};
const handleCloseTouchEnd = (e) => {
if (closeTouchLocked) return;
closeTouchLocked = true;
const touchDuration = Date.now() - closeTouchStartTime;
if (touchDuration < 300) {
e.preventDefault();
e.stopPropagation();
emit('close');
}
};
const handleCloseClick = (e) => {
e.preventDefault();
e.stopPropagation();
emit('close');
};
//
const handleTabClick = (index) => {
if (activeTabIndex.value === index) return;
activeTabIndex.value = index;
emit('tab-change', {
tabName: props.tabs[index],
tabIndex: index
});
};
//
const handleScrollToLower = () => {
emit('scroll-to-lower');
};
//
defineExpose({
switchToTab: (indexOrName) => {
if (typeof indexOrName === 'number') {
activeTabIndex.value = indexOrName;
} else {
const index = props.tabs.indexOf(indexOrName);
if (index !== -1) {
activeTabIndex.value = index;
}
}
},
getActiveTab: () => props.tabs[activeTabIndex.value],
getActiveTabIndex: () => activeTabIndex.value
});
</script>
<style scoped>
.modal-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.modal-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1;
}
.modal-container {
position: relative;
width: 580rpx;
max-height: 85vh;
border-radius: 40rpx;
overflow: hidden;
z-index: 2;
}
.modal-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 0;
object-fit: cover;
}
.modal-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.close-button {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
cursor: pointer;
}
.back-icon {
width: 56rpx;
height: 56rpx;
}
.close-icon {
font-size: 50rpx;
color: #e6e6e6;
line-height: 1;
font-weight: 300;
}
.modal-content {
position: relative;
z-index: 2;
padding: 60rpx 40rpx 40rpx;
display: flex;
flex-direction: column;
}
/* 标题区域 */
.title-section {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 16rpx;
margin-bottom: 32rpx;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #e6e6e6;
font-family: 'yt', sans-serif;
text-shadow: 0 4rpx 4rpx rgba(0, 0, 0, 0.3);
}
.title-image {
width: 112rpx;
height: 80rpx;
border-radius: 12rpx;
}
/* 胶囊按钮区域 */
.tab-section {
display: flex;
flex-direction: row;
justify-content: center;
gap: 32rpx;
margin-bottom: 24rpx;
padding: 12rpx 24rpx;
position: relative;
}
.tab-item {
padding: 16rpx 40rpx;
border-radius: 999rpx;
background: transparent;
transition: all 0.3s ease;
position: relative;
}
.tab-section::before {
content: '';
position: absolute;
bottom: -4rpx;
left: 96rpx;
right: 96rpx;
height: 6rpx;
background: #ccc;
border-radius: 4rpx;
}
.tab-item.active {
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}
.tab-text {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
font-family: 'yt', sans-serif;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
}
.tab-item.active .tab-text {
color: #FFFFFF;
font-weight: bold;
}
/* 可滚动内容区域 */
.scrollable-content {
flex: 1;
max-height: 500rpx;
overflow-y: auto;
}
/* 动画效果 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.3s ease;
}
.slide-up-enter-from,
.slide-up-leave-to {
opacity: 0;
transform: translateY(100rpx);
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<view class="profile-container"> <view class="profile-container">
<!-- 背景图片 - 固定在容器上 --> <!-- 背景图片 - 固定在容器上 -->
<image class="background-image" src="/static/background/starbook.png" mode="aspectFill"></image> <image class="background-image" src="/static/square/squearbj.png" mode="aspectFill"></image>
<scroll-view class="profile-scroll" scroll-y :show-scrollbar="false" :enable-back-to-top="true"> <scroll-view class="profile-scroll" scroll-y :show-scrollbar="false" :enable-back-to-top="true">
<!-- 上半部分用户信息卡片 --> <!-- 上半部分用户信息卡片 -->
@ -11,38 +11,40 @@
<view class="background-overlay"></view> <view class="background-overlay"></view>
<!-- Header组件 --> <!-- Header组件 -->
<Header :showBack="true" :showGuideIcon="false" :showTaskIcon="false" :showStarActivityIcon="false" /> <image class="close-icon-img" src="/static/starbookcontent/tuichu.png" mode="aspectFit" @tap="goBack"></image>
<!-- 用户信息卡片 --> <!-- 用户信息卡片 -->
<view class="user-info-card"> <view class="user-info-card">
<!-- 上半部分头像和用户信息左右布局 --> <!-- 上半部分头像和用户信息左右布局 -->
<view class="user-main-info"> <view class="user-main-info">
<view @tap="handleAvatarClick" class="avatar-container"> <view @tap="handleAvatarClick" class="avatar-container">
<Avatar :key="avatarKey" :userId="uid" :size="180" :borderWidth="6" :showLevel="true" <Avatar :key="avatarKey" :userId="uid" :size="260" :borderWidth="6" :showLevel="true"
:level="fanLevel" :avatarUrl="userAvatarUrl" /> :level="fanLevel" :avatarUrl="userAvatarUrl" />
</view> </view>
<view class="user-text-info"> <view class="user-text-info">
<!-- 第1行等级 + 昵称 + 粉丝标签 --> <!-- 昵称 -->
<view class="name-wrapper"> <view class="info-row">
<!-- <text class="level-badge">LV {{ fanLevel }}</text> --> <text style="font-size: 48rpx;text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);" class="info-value">{{ nickname }}</text>
<text class="user-name">{{ nickname }}</text>
<view class="fan-tag-badge" v-if="fanTag" @tap="handleFanTagClick">
<text class="fan-tag-text">{{ fanTag }}</text>
<image class="fan-tag-arrow" src="/static/icon/dropdown-arrow.png" mode="aspectFit">
</image>
</view>
</view> </view>
<!-- 第2行DID --> <!-- DID -->
<text class="user-did">数字身份DID: {{ uid }}</text> <view class="info-row">
<!-- 第3行区块链地址 --> <text class="info-label">DID</text>
<view class="blockchain-address-wrapper"> <text class="info-value uid-value">{{ uid }}</text>
<text class="blockchain-address-text">链上地址: {{ displayAddress }}</text> <view class="info-btn" @tap="copyUid">查看</view>
<view class="copy-icon" @tap="copyAddress"> </view>
<text class="copy-icon-text">📋</text> <!-- 链上地址 -->
</view> <view class="info-row">
<text class="info-label">链上地址</text>
<text class="info-value address-value">{{ displayAddress }}</text>
<view class="info-btn" @tap="copyAddress">查看</view>
</view>
<!-- 手机号 -->
<view class="info-row">
<text class="info-label">注册手机</text>
<text class="info-value" v-if="displayMobile">{{ displayMobile }}</text>
<text class="info-value" v-else>未绑定</text>
<view class="info-btn" @tap="handleViewMobile">查看</view>
</view> </view>
<!-- 第4行手机号 -->
<text class="user-mobile" v-if="displayMobile">{{ displayMobile }}</text>
</view> </view>
</view> </view>
</view> </view>
@ -56,8 +58,8 @@
<view class="section-title">我的资产</view> <view class="section-title">我的资产</view>
<view class="assets-grid"> <view class="assets-grid">
<view class="asset-card" @tap="handleAssetClick('myWorks')"> <view class="asset-card" @tap="handleAssetClick('myWorks')">
<image class="asset-icon" src="/static/icon/square.png" mode="aspectFit"></image> <image class="asset-icon" src="/static/icon/dazi.png" mode="aspectFit"></image>
<text class="asset-text">我的展馆</text> <text class="asset-text">个人中心</text>
<!-- <text class="arrow"></text> --> <!-- <text class="arrow"></text> -->
</view> </view>
<view class="asset-card" @tap="handleAssetClick('starbook')"> <view class="asset-card" @tap="handleAssetClick('starbook')">
@ -421,6 +423,41 @@ const fetchUserInfo = async (forceRefresh = false) => {
} }
}; };
// UID
const copyUid = () => {
if (!uid.value) {
uni.showToast({ title: 'UID为空', icon: 'none' });
return;
}
uni.setClipboardData({
data: uid.value,
success: () => uni.showToast({ title: '已复制UID', icon: 'success' }),
fail: () => uni.showToast({ title: '复制失败', icon: 'none' })
});
};
//
const handleViewNickname = () => {
uni.setClipboardData({
data: nickname.value,
success: () => uni.showToast({ title: '已复制昵称', icon: 'success' }),
fail: () => uni.showToast({ title: '复制失败', icon: 'none' })
});
};
//
const handleViewMobile = () => {
if (!displayMobile.value) {
uni.showToast({ title: '未绑定手机号', icon: 'none' });
return;
}
uni.setClipboardData({
data: displayMobile.value,
success: () => uni.showToast({ title: '已复制手机号', icon: 'success' }),
fail: () => uni.showToast({ title: '复制失败', icon: 'none' })
});
};
// //
const copyAddress = () => { const copyAddress = () => {
if (!blockchainAddress.value) { if (!blockchainAddress.value) {
@ -1022,6 +1059,11 @@ const confirmLogout = () => {
}); });
}; };
//
const goBack = () => {
uni.navigateBack();
};
// //
const handleAvatarClick = () => { const handleAvatarClick = () => {
showAvatarModal.value = true; showAvatarModal.value = true;
@ -1247,6 +1289,7 @@ onShow(() => {
width: 100%; width: 100%;
height: auto; height: auto;
overflow: hidden; overflow: hidden;
margin-bottom: 64rpx;
} }
.background-image { .background-image {
@ -1266,13 +1309,21 @@ onShow(() => {
display: none; display: none;
} }
.close-icon-img{
width: 80rpx;
height: 80rpx;
position: relative;
top: 32rpx;
left: 32rpx;
}
.user-info-card { .user-info-card {
position: relative; position: relative;
z-index: 2; z-index: 2;
width: 90%; width: 90%;
max-width: 600rpx; max-width: 600rpx;
margin: 0 auto; margin: 0 auto;
margin-top: 225rpx; margin-top: 64rpx;
padding: 40rpx 0; padding: 40rpx 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -1284,17 +1335,65 @@ onShow(() => {
align-items: flex-start; align-items: flex-start;
/* gap: 30rpx; */ /* gap: 30rpx; */
margin-bottom: 0; margin-bottom: 0;
}
.avatar-container {
position: absolute;
left: -56rpx;
top: 32rpx;
} }
/* 右侧用户文本信息 */ /* 右侧用户文本信息 */
.user-text-info { .user-text-info {
flex: 1; /* flex: 1; */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
/* gap: 12rpx; */ gap: 8rpx;
margin-left: 30rpx; margin-left: 8rpx;
padding-bottom: 10rpx; background-image: url(/static/rank/activity-support-icon/beijingkuang.png);
background-size: 100% 110%;
background-position: center;
padding: 24rpx 24rpx 24rpx 240rpx;
min-width: 356rpx;
}
.info-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.info-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
min-width: 112rpx;
}
.info-value {
font-size: 24rpx;
color: #ffffff;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.uid-value {
max-width: 200rpx;
}
.address-value {
max-width: 160rpx;
}
.info-btn {
font-size: 20rpx;
color: #FFD700;
padding: 4rpx 16rpx;
border: 2rpx solid rgba(255, 215, 0, 0.5);
border-radius: 16rpx;
} }
.name-wrapper { .name-wrapper {
@ -1400,6 +1499,7 @@ onShow(() => {
.section-bg { .section-bg {
border-radius: 30rpx; border-radius: 30rpx;
padding: 30rpx; padding: 30rpx;
margin-bottom: 64rpx;
} }
.section-title { .section-title {
@ -1407,7 +1507,7 @@ onShow(() => {
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
font-weight: bold; font-weight: bold;
color: #fff; color: #fff;
margin-bottom: 20rpx; margin-bottom: 8rpx;
} }
.assets-grid { .assets-grid {
@ -1415,34 +1515,40 @@ onShow(() => {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 20rpx; gap: 20rpx;
margin-bottom: 0; margin-bottom: 0;
height: 192rpx; height: 176rpx;
overflow: hidden;
} }
.asset-card { .asset-card {
background: rgba(217, 156, 180, 0.3); background-image: url(/static/rank/activity-support-icon/beijingkuang.png);
background-size: 100% 100%;
background-position: center;
border-radius: 20rpx; border-radius: 20rpx;
padding: 24rpx 16rpx; padding: 0 16rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
height: 192rpx;
} }
.asset-icon { .asset-icon {
width: 128rpx; width: 178rpx;
height: 128rpx; height: 178rpx;
margin-bottom: 12rpx; position: absolute;
top: 60%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.4;
} }
.asset-text { .asset-text {
font-size: 32rpx; font-size: 32rpx;
color: #fff; color: #fff;
margin-bottom: 8rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
position: relative;
z-index: 1;
} }
.arrow { .arrow {
@ -1457,15 +1563,17 @@ onShow(() => {
/* 服务与工具按钮 */ /* 服务与工具按钮 */
.service-buttons-container { .service-buttons-container {
background: rgba(217, 156, 180, 0.3);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: space-between;
margin-bottom: 0; margin-bottom: 0;
flex-wrap: wrap; flex-wrap: wrap;
} }
.service-button { .service-button {
background-image: url(/static/rank/activity-support-icon/djbeij.jpg);
background-size: 100% 100%;
background-position: center;
border-radius: 20rpx; border-radius: 20rpx;
width: 136rpx; width: 136rpx;
height: 160rpx; height: 160rpx;
@ -1473,12 +1581,10 @@ onShow(() => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 16rpx; padding: 16rpx;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
margin-bottom: 16rpx; margin-bottom: 16rpx;
margin-right: 16rpx;
} }
.service-icon { .service-icon {
@ -1527,7 +1633,10 @@ onShow(() => {
left: 0; left: 0;
right: 0; right: 0;
height: 8rpx; height: 8rpx;
background-color: rgba(252, 252, 252, 0.6); background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 8rpx; border-radius: 8rpx;
width: calc(100% - 60rpx); width: calc(100% - 60rpx);
transform: translateX(30rpx); transform: translateX(30rpx);
@ -1538,13 +1647,17 @@ onShow(() => {
width: 60%; width: 60%;
height: 112rpx; height: 112rpx;
line-height: 112rpx; line-height: 112rpx;
background: rgba(217, 156, 180, 0.3); background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 40rpx; border-radius: 40rpx;
font-size: 56rpx; font-size: 56rpx;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
margin-top: 20rpx; margin-top: 20rpx;
flex-shrink: 0; flex-shrink: 0;
opacity: 0.6;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 152 KiB