image:替换图片

This commit is contained in:
zheng020 2026-05-15 21:39:21 +08:00
parent e4ce006de3
commit 6252be4d75
7 changed files with 373 additions and 302 deletions

View File

@ -114,6 +114,19 @@
</view> --> </view> -->
<!-- </view> --> <!-- </view> -->
<!-- 完整链上哈希显示遮罩层 -->
<view v-if="showTxHash" class="txhash-mask" @tap="showTxHash = false">
<view class="txhash-popup" @tap.stop>
<view class="txhash-popup-header">
<text class="txhash-popup-title">链上哈希</text>
<view class="txhash-popup-close" @tap="showTxHash = false">
<image class="close-icon" src="/static/icon/hide.png" mode="aspectFit"></image>
</view>
</view>
<text class="txhash-popup-content" selectable @longpress="copyHash">{{ displayTxHash }}</text>
</view>
</view>
<!-- 链上数据 --> <!-- 链上数据 -->
<view class="chain-section"> <view class="chain-section">
<image class="chain-logo" src="/static/logo/APPLOGO.png" mode="aspectFit"></image> <image class="chain-logo" src="/static/logo/APPLOGO.png" mode="aspectFit"></image>
@ -143,8 +156,7 @@
<view class="chain-row"> <view class="chain-row">
<text class="chain-label">链上哈希</text> <text class="chain-label">链上哈希</text>
<view class="chain-value-wrap"> <view class="chain-value-wrap">
<text class="chain-value chain-hash" @longpress="copyHash">{{ showTxHash ? displayTxHash <text class="chain-value chain-hash" @longpress="copyHash">{{ hiddenTxHash }}</text>
: hiddenTxHash }}</text>
<view class="toggle-btn" @tap="showTxHash = !showTxHash"> <view class="toggle-btn" @tap="showTxHash = !showTxHash">
<image class="toggle-icon" :src="showTxHash ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image> <image class="toggle-icon" :src="showTxHash ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image>
</view> </view>
@ -1026,6 +1038,62 @@ onUnmounted(() => {
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
} }
/* 链上哈希遮罩层 */
.txhash-mask {
position: fixed;
left: 0;
right: 0;
bottom: 24rpx;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
padding: 0 64rpx;
}
.txhash-popup {
border-radius: 24rpx;
padding: 32rpx;
background: url('/static/rank/activity-support-icon/beijingkuang.png') no-repeat center center;
background-size: 105% 120%;
}
.txhash-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.txhash-popup-title {
font-size: 32rpx;
font-weight: bold;
color: #fff;
font-family: 'yt', sans-serif;
}
.txhash-popup-close {
padding: 8rpx;
}
.close-icon {
width: 40rpx;
height: 40rpx;
}
.txhash-popup-content {
display: block;
font-size: 26rpx;
color: #fff;
font-family: 'yt', sans-serif;
word-break: break-all;
line-height: 1.6;
background-color: rgba(0, 0, 0, 0.2);
padding: 20rpx;
border-radius: 12rpx;
}
/* 点赞用户列表 */ /* 点赞用户列表 */
.like-users-list { .like-users-list {
display: flex; display: flex;

View File

@ -56,7 +56,8 @@ const navItems = [
name: '铸爱', name: '铸爱',
icon: '/static/icon/castlove.png', icon: '/static/icon/castlove.png',
angle: 90.01, // angle: 90.01, //
path: '/pages/castlove/mall' path: '/pages/castlove/mall',
rotate: '8deg'
}, },
{ {
name: '星城', name: '星城',
@ -107,7 +108,8 @@ const getNavIconStyle = (index) => {
return { return {
'--x': `${x}rpx`, '--x': `${x}rpx`,
'--y': `${y}rpx`, '--y': `${y}rpx`,
'--delay': `${delay}s` '--delay': `${delay}s`,
'--rotate': item.rotate || '0deg'
}; };
}; };
@ -237,6 +239,7 @@ const handleNavClick = (index) => {
transition: transform 0.3s ease, filter 0.3s ease; transition: transform 0.3s ease, filter 0.3s ease;
filter: drop-shadow(0 6rpx 16rpx rgba(0, 0, 0, 0.4)) drop-shadow(0 2rpx 8rpx rgba(0, 0, 0, 0.2)); filter: drop-shadow(0 6rpx 16rpx rgba(0, 0, 0, 0.4)) drop-shadow(0 2rpx 8rpx rgba(0, 0, 0, 0.2));
position: relative; position: relative;
transform: rotate(var(--rotate));
} }
/* 底部倒影效果 */ /* 底部倒影效果 */

View File

@ -11,7 +11,8 @@
<view class="background-overlay"></view> <view class="background-overlay"></view>
<!-- Header组件 --> <!-- Header组件 -->
<image class="close-icon-img" src="/static/starbookcontent/tuichu.png" mode="aspectFit" @tap="goBack"></image> <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">
@ -24,26 +25,38 @@
<view class="user-text-info"> <view class="user-text-info">
<!-- 昵称 --> <!-- 昵称 -->
<view class="info-row"> <view class="info-row">
<text style="font-size: 48rpx;text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);" class="info-value">{{ nickname }}</text> <text style="font-size: 48rpx;text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);"
class="info-value">{{ nickname }}</text>
</view> </view>
<!-- DID --> <!-- DID -->
<view class="info-row"> <view class="info-row">
<text class="info-label">DID</text> <text class="info-label">DID</text>
<view class="address-row">
<text class="info-value uid-value">{{ uid }}</text> <text class="info-value uid-value">{{ uid }}</text>
<view class="info-btn" @tap="copyUid">查看</view> <!-- <image class="toggle-icon"
:src="showBlockNumber ? '/static/icon/show.png' : '/static/icon/hide.png'"
mode="aspectFit" @tap="copyUid"></image> -->
</view>
</view> </view>
<!-- 链上地址 --> <!-- 链上地址 -->
<view class="info-row"> <view class="info-row">
<text class="info-label">链上地址</text> <text class="info-label">链上地址</text>
<text class="info-value address-value">{{ displayAddress }}</text> <view class="address-row">
<view class="info-btn" @tap="copyAddress">查看</view> <text class="info-value address-value" @longpress="copyAddress">{{ displayAddress }}</text>
<view class="toggle-btn" @tap="showBlockNumber = !showBlockNumber">
<image class="toggle-icon" :src="showBlockNumber ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image>
</view>
</view>
</view> </view>
<!-- 手机号 --> <!-- 手机号 -->
<view class="info-row"> <view class="info-row">
<text class="info-label">注册手机</text> <text class="info-label">注册手机</text>
<text class="info-value" v-if="displayMobile">{{ displayMobile }}</text> <view class="address-row">
<text class="info-value" v-else>未绑定</text> <text class="info-value" v-if="mobile">{{ displayMobile }}</text>
<view class="info-btn" @tap="handleViewMobile">查看</view> <view class="toggle-btn" @tap="toggleMobileDisplay">
<image class="toggle-icon" :src="showMobile ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image>
</view>
</view>
</view> </view>
</view> </view>
</view> </view>
@ -78,14 +91,17 @@
<view class="service-button" @tap="handleGuideClick"> <view class="service-button" @tap="handleGuideClick">
<image class="service-icon" src="/static/icon/onboarding.png" mode="aspectFit"></image> <image class="service-icon" src="/static/icon/onboarding.png" mode="aspectFit"></image>
<text class="service-text">新手指引</text> <text class="service-text">新手指引</text>
<text v-if="guideClaimableCount > 0" class="guide-badge">{{ guideClaimableCount }}</text> <text v-if="guideClaimableCount > 0" class="guide-badge">{{ guideClaimableCount
}}</text>
</view> </view>
<view class="service-button" @tap="handleChangeNickname"> <view class="service-button" @tap="handleChangeNickname">
<image class="service-icon" src="/static/icon/edit-nickname.png" mode="aspectFit"></image> <image class="service-icon" src="/static/icon/edit-nickname.png" mode="aspectFit">
</image>
<text class="service-text">修改昵称</text> <text class="service-text">修改昵称</text>
</view> </view>
<view class="service-button" @tap="handleChangePassword"> <view class="service-button" @tap="handleChangePassword">
<image class="service-icon" src="/static/icon/edit-password.png" mode="aspectFit"></image> <image class="service-icon" src="/static/icon/edit-password.png" mode="aspectFit">
</image>
<text class="service-text">修改密码</text> <text class="service-text">修改密码</text>
</view> </view>
<!-- <view class="service-button" @tap="handleSwitchRole"> <!-- <view class="service-button" @tap="handleSwitchRole">
@ -161,7 +177,8 @@
</view> </view>
<!-- 昵称输入框 --> <!-- 昵称输入框 -->
<input class="modal-input" v-model="newIdentityNickname" placeholder="请输入您的新昵称" maxlength="20" /> <input class="modal-input" v-model="newIdentityNickname" placeholder="请输入您的新昵称"
maxlength="20" />
<view class="modal-buttons"> <view class="modal-buttons">
<button class="modal-btn-cancel" @tap="closeAddIdentityModal">取消</button> <button class="modal-btn-cancel" @tap="closeAddIdentityModal">取消</button>
@ -175,7 +192,8 @@
<view class="identity-dropdown-content" @tap.stop> <view class="identity-dropdown-content" @tap.stop>
<view class="identity-dropdown-title">切换粉丝身份</view> <view class="identity-dropdown-title">切换粉丝身份</view>
<view class="identity-list"> <view class="identity-list">
<view v-for="identity in myFanIdentities" :key="identity.star_id" class="identity-item" :class="{ <view v-for="identity in myFanIdentities" :key="identity.star_id" class="identity-item"
:class="{
'identity-item-active': identity.star_id === currentStarId, 'identity-item-active': identity.star_id === currentStarId,
'identity-item-disabled': identity.star_id === currentStarId 'identity-item-disabled': identity.star_id === currentStarId
}" @tap="handleSwitchIdentity(identity.star_id)"> }" @tap="handleSwitchIdentity(identity.star_id)">
@ -249,9 +267,8 @@
</view> </view>
</scroll-view> </scroll-view>
<!-- 新手引导列表弹窗 --> <!-- 新手引导弹窗 -->
<GuideListModal :visible="showGuideListModal" @start-guide="handleStartGuide" <GuideModal :visible="showGuideModal" @close="showGuideModal = false" @updated="handleGuideUpdated" />
@claim-success="handleClaimSuccess" @close="showGuideListModal = false" />
<!-- 全局引导遮罩 --> <!-- 全局引导遮罩 -->
<GuideOverlay /> <GuideOverlay />
@ -268,7 +285,7 @@ import Avatar from '../components/Avatar.vue';
import { getUserProfileApi, deleteAccountApi, updateNicknameApi, updatePasswordApi, getFanIdentitiesApi, addFanIdentityApi, getMyFanIdentitiesApi, switchFanIdentityApi, getOssSignatureApi, updateAvatarApi } from '@/utils/api'; import { getUserProfileApi, deleteAccountApi, updateNicknameApi, updatePasswordApi, getFanIdentitiesApi, addFanIdentityApi, getMyFanIdentitiesApi, switchFanIdentityApi, getOssSignatureApi, updateAvatarApi } from '@/utils/api';
import { validateNickname } from '@/utils/validator.js'; import { validateNickname } from '@/utils/validator.js';
import { clearAvatarCache } from '@/utils/avatarCache'; import { clearAvatarCache } from '@/utils/avatarCache';
import GuideListModal from '@/components/GuideListModal.vue'; import GuideModal from '@/pages/tasks/GuideModal.vue';
import GuideOverlay from '@/components/GuideOverlay.vue'; import GuideOverlay from '@/components/GuideOverlay.vue';
import { import {
getClaimableRewardCount getClaimableRewardCount
@ -295,8 +312,13 @@ const newPassword = ref('');
const showOldPassword = ref(false); const showOldPassword = ref(false);
// //
const showGuideListModal = ref(false); const showGuideModal = ref(false);
const guideClaimableCount = computed(() => getClaimableRewardCount()); const guideClaimableCount = computed(() => getClaimableRewardCount());
//
const handleGuideUpdated = () => {
console.log('[Profile] Guide updated');
};
const showNewPassword = ref(false); const showNewPassword = ref(false);
// //
@ -325,18 +347,33 @@ const avatarKey = ref(0); // 用于强制刷新Avatar组件
// //
const mobile = ref(''); const mobile = ref('');
// // /
const showBlockNumber = ref(false);
const showMobile = ref(false);
// showMobile
const displayMobile = computed(() => { const displayMobile = computed(() => {
return mobile.value || ''; console.log('displayMobile computed, mobile:', mobile.value, 'showMobile:', showMobile.value);
if (!mobile.value) return '';
// showMobiletruefalse
if (showMobile.value) {
return mobile.value;
}
// 34
const phone = mobile.value;
return phone.length === 11 ? phone.slice(0, 3) + '****' + phone.slice(-4) : phone;
}); });
// //
const displayAddress = computed(() => { const displayAddress = computed(() => {
const address = blockchainAddress.value; const address = blockchainAddress.value;
console.log('displayAddress computed, address:', address, 'showBlockNumber:', showBlockNumber.value);
// //
if (!address) return '0xabcd...123c'; if (!address) return '0xabcd...123c';
// showBlockNumbertruefalse
if (showBlockNumber.value) return address;
if (address.length <= 20) return address; if (address.length <= 20) return address;
// 108 // 44
return address.substring(0, 4) + '...' + address.substring(address.length - 4); return address.substring(0, 4) + '...' + address.substring(address.length - 4);
}); });
@ -392,7 +429,7 @@ const fetchUserInfo = async (forceRefresh = false) => {
fanTag.value = userForCache.fan_identity?.name || ''; fanTag.value = userForCache.fan_identity?.name || '';
blockchainAddress.value = userForCache.blockchain_address || ''; blockchainAddress.value = userForCache.blockchain_address || '';
userAvatarUrl.value = userForCache.avatar_url || ''; userAvatarUrl.value = userForCache.avatar_url || '';
mobile.value = uni.getStorageSync('login_mobile') || ''; mobile.value = userForCache.mobile || uni.getStorageSync('login_mobile') || '';
} }
} catch (error) { } catch (error) {
console.error('获取用户信息失败:', error); console.error('获取用户信息失败:', error);
@ -408,7 +445,7 @@ const fetchUserInfo = async (forceRefresh = false) => {
fanTag.value = cachedUser.fan_identity?.tag || ''; fanTag.value = cachedUser.fan_identity?.tag || '';
blockchainAddress.value = cachedUser.blockchain_address || ''; blockchainAddress.value = cachedUser.blockchain_address || '';
userAvatarUrl.value = cachedUser.avatar_url || ''; userAvatarUrl.value = cachedUser.avatar_url || '';
mobile.value = uni.getStorageSync('login_mobile') || ''; mobile.value = cachedUser.mobile || uni.getStorageSync('login_mobile') || '';
} catch (e) { } catch (e) {
console.error('解析缓存用户信息失败:', e); console.error('解析缓存用户信息失败:', e);
} }
@ -423,6 +460,12 @@ const fetchUserInfo = async (forceRefresh = false) => {
} }
}; };
//
const toggleMobileDisplay = () => {
console.log('toggleMobileDisplay called, showMobile:', showMobile.value);
showMobile.value = !showMobile.value;
};
// UID // UID
const copyUid = () => { const copyUid = () => {
if (!uid.value) { if (!uid.value) {
@ -445,19 +488,6 @@ const handleViewNickname = () => {
}); });
}; };
//
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) {
@ -917,50 +947,7 @@ const handleDeleteAccount = () => {
// //
const handleGuideClick = () => { const handleGuideClick = () => {
showGuideListModal.value = true; showGuideModal.value = true;
};
//
const handleStartGuide = (params) => {
// { key, fromStep, targetPage } key
const key = typeof params === 'string' ? params : (params?.key || '');
const fromStep = typeof params === 'object' ? (params.fromStep ?? 0) : 0;
const targetPage = typeof params === 'object' ? (params.targetPage || '') : '';
console.log('[Profile] handleStartGuide:', { key, fromStep, targetPage });
showGuideListModal.value = false;
//
if (key === 'square_home') {
// 使 targetPage
const navigateUrl = targetPage || '/pages/square/square';
uni.navigateTo({
url: navigateUrl,
success: () => {
//
setTimeout(() => {
if (fromStep === 0) {
store.dispatch("guide/startGuideFromBeginning", key);
} else {
store.dispatch("guide/resumeGuide", key);
}
}, 500);
}
});
} else {
//
if (fromStep === 0) {
store.dispatch("guide/startGuideFromBeginning", key);
} else {
store.dispatch("guide/resumeGuide", key);
}
}
};
//
const handleClaimSuccess = (reward) => {
console.log("[Profile] 领取奖励成功:", reward);
}; };
// //
@ -1371,6 +1358,11 @@ onShow(() => {
min-width: 112rpx; min-width: 112rpx;
} }
.address-row {
display: flex;
align-items: center;
}
.info-value { .info-value {
font-size: 24rpx; font-size: 24rpx;
color: #ffffff; color: #ffffff;
@ -1378,15 +1370,18 @@ onShow(() => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 200rpx;
} }
.uid-value { .uid-value {}
max-width: 200rpx;
.toggle-icon {
width: 32rpx;
height: 32rpx;
} }
.address-value { .address-value {}
max-width: 160rpx;
}
.info-btn { .info-btn {
font-size: 20rpx; font-size: 20rpx;
@ -1619,8 +1614,10 @@ onShow(() => {
.logout-section { .logout-section {
padding: 10rpx 30rpx; padding: 10rpx 30rpx;
padding-bottom: calc(40rpx + constant(safe-area-inset-bottom)); /* iOS 11.0 */ padding-bottom: calc(40rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(40rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */ /* iOS 11.0 */
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
/* iOS 11.2+ */
display: flex; display: flex;
position: relative; position: relative;
} }
@ -1686,7 +1683,10 @@ onShow(() => {
.modal-content { .modal-content {
width: 80%; width: 80%;
max-width: 600rpx; max-width: 600rpx;
background: #ffffff; /* background: #ffffff; */
background-image: url('/static/starbookcontent/beijing.png');
background-size: cover;
background-position: center bottom;
border-radius: 30rpx; border-radius: 30rpx;
padding: 60rpx 40rpx; padding: 60rpx 40rpx;
box-sizing: border-box; box-sizing: border-box;

View File

@ -17,7 +17,7 @@
<view class="modal-header"> <view class="modal-header">
<!-- 返回按钮 --> <!-- 返回按钮 -->
<view class="back-button" @click="handleClose"> <view class="back-button" @click="handleClose">
<image class="back-icon" src="/static/icon/back.png" mode="aspectFit" /> <image class="back-icon" src="/static/starbookcontent/tuichu.png" mode="aspectFit" />
</view> </view>
<!-- 标题 --> <!-- 标题 -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -96,8 +96,8 @@ const actions = {
// 缓存用户信息 // 缓存用户信息
const user = res.data.user const user = res.data.user
// 缓存登录手机号(优先使用后端返回的脱敏手机号) // 缓存登录手机号
const loginMobile = user.mobile_masked || mobile const loginMobile = mobile
uni.setStorageSync('login_mobile', loginMobile) uni.setStorageSync('login_mobile', loginMobile)
uni.setStorageSync('user', JSON.stringify(user)) uni.setStorageSync('user', JSON.stringify(user))
@ -131,8 +131,8 @@ const actions = {
// 缓存用户信息 // 缓存用户信息
const user = res.data.user const user = res.data.user
// 缓存登录手机号(优先使用后端返回的脱敏手机号) // 缓存登录手机号
const loginMobile = user.mobile_masked || mobile const loginMobile = mobile
uni.setStorageSync('login_mobile', loginMobile) uni.setStorageSync('login_mobile', loginMobile)
uni.setStorageSync('user', JSON.stringify(user)) uni.setStorageSync('user', JSON.stringify(user))