feat: UI redesign with uv-ui color system and landscape imagery

- Replace all colors with uv-ui design tokens (primary #3c9cff, warning #f9ae3d, etc.)
- Hero section: Wugong Mountain landscape photo with dark overlay
- Super Player card: photo background, large typography, warning-color price/CTA, value comparison (¥198 vs ¥3800+)
- Spots section: landscape thumbnails instead of CSS gradients, warning-color prices
- Profile page: landscape hero header, CSS menu icons, floating card layout
- Card list page: landscape hero header, filter pills, empty state design
- TabBar: rename '入园码' to '旅游卡', Lucide-style PNG icons (home/card/profile)
- Typography: corrected font weights (semibold for prices, medium for labels)
- Remove broken font-loader (lxgw-wen-kai CDN 404)
- Skip wx.getPrivacySetting in devtools to avoid framework timeout
- Replace all inline SVG with CSS-only icons for mini-program compatibility
- Add design system files (styles/, components/, animations)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-06-25 01:07:21 +08:00
co-authored by Claude
parent c062dee3e6
commit 5f1ab737e9
25 changed files with 2110 additions and 390 deletions
+296 -44
View File
@@ -1,34 +1,57 @@
<template>
<view class="page">
<view class="user-card">
<view class="avatar-wrap">
<image class="avatar" :src="userStore.userInfo?.avatarUrl || defaultAvatar" mode="aspectFill" />
</view>
<view class="user-info">
<text class="nickname">{{ userStore.userInfo?.nickname || '未登录' }}</text>
<text class="user-desc" v-if="userStore.isLogin">欢迎使用爱上萍乡</text>
<text class="user-desc" v-else @click="goLogin">点击登录</text>
<!-- 头部:风景图 + 用户信息 -->
<view class="profile-hero">
<image
class="hero-bg"
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?fm=jpg&q=80&w=750&h=400&fit=crop"
mode="aspectFill"
/>
<view class="hero-overlay" />
<view class="hero-content">
<view class="avatar-wrap">
<image class="avatar" :src="userStore.userInfo?.avatarUrl || defaultAvatar" mode="aspectFill" />
</view>
<view class="user-info">
<text class="nickname">{{ userStore.userInfo?.nickname || '未登录' }}</text>
<text class="user-desc" v-if="userStore.isLogin">欢迎使用爱上萍乡</text>
<text class="user-desc user-desc--link" v-else @click="goLogin">点击登录 →</text>
</view>
</view>
</view>
<view class="menu-card">
<view class="menu-item" @click="goTo('/packages/user/pages/card/bind')">
<text class="menu-icon">🎫</text>
<text class="menu-text">我的年卡</text>
<text class="menu-arrow">›</text>
</view>
<view class="menu-item" @click="showComingSoon">
<text class="menu-icon">📦</text>
<text class="menu-text">订单管理</text>
<text class="menu-arrow">›</text>
</view>
<view class="menu-item" @click="showComingSoon">
<text class="menu-icon">📋</text>
<text class="menu-text">使用记录</text>
<text class="menu-arrow">›</text>
<!-- 功能菜单 -->
<view class="menu-section">
<view class="menu-card">
<view class="menu-item" @click="goTo('/packages/user/pages/card/bind')">
<view class="menu-icon-wrap menu-icon-wrap--primary">
<view class="icon-card" />
</view>
<text class="menu-text">我的年卡</text>
<view class="menu-chevron" />
</view>
<view class="menu-item" @click="showComingSoon">
<view class="menu-icon-wrap menu-icon-wrap--warning">
<view class="icon-order" />
</view>
<text class="menu-text">订单管理</text>
<view class="menu-chevron" />
</view>
<view class="menu-item menu-item--last" @click="showComingSoon">
<view class="menu-icon-wrap menu-icon-wrap--success">
<view class="icon-history" />
</view>
<text class="menu-text">使用记录</text>
<view class="menu-chevron" />
</view>
</view>
</view>
<!-- 退出登录 -->
<view class="btn-area" v-if="userStore.isLogin">
<button class="btn-logout" @click="handleLogout">退出登录</button>
<view class="btn-logout" @click="handleLogout">
<text>退出登录</text>
</view>
</view>
</view>
</template>
@@ -39,16 +62,13 @@ import { useUserStore } from '../../store/user'
const userStore = useUserStore()
const defaultAvatar = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
/** 跳转登录页 */
function goLogin() { uni.navigateTo({ url: '/pages/login/login' }) }
/** 需要登录的页面跳转 */
function goTo(url: string) {
if (!userStore.isLogin) { uni.navigateTo({ url: '/pages/login/login' }); return }
uni.navigateTo({ url })
}
/** 退出登录 */
function handleLogout() {
uni.showModal({
title: '提示', content: '确定要退出登录吗?',
@@ -62,24 +82,256 @@ function handleLogout() {
})
}
/** 即将上线提示 */
function showComingSoon() { uni.showToast({ title: '即将上线,敬请期待', icon: 'none' }) }
</script>
<style scoped>
.page { min-height: 100vh; background: #f5f5f5; }
.user-card { background: linear-gradient(135deg, #4A90D9, #357ABD); padding: 50rpx 30rpx 40rpx; display: flex; align-items: center; }
.avatar-wrap { margin-right: 24rpx; }
.avatar { width: 120rpx; height: 120rpx; border-radius: 50%; border: 4rpx solid rgba(255,255,255,0.5); background: #eee; }
.user-info { display: flex; flex-direction: column; }
.nickname { font-size: 34rpx; font-weight: bold; color: #fff; margin-bottom: 8rpx; }
.user-desc { font-size: 24rpx; color: rgba(255,255,255,0.8); }
.menu-card { background: #fff; border-radius: 16rpx; margin: 20rpx; overflow: hidden; }
.menu-item { display: flex; align-items: center; padding: 30rpx; border-bottom: 1rpx solid #f0f0f0; }
.menu-item:last-child { border-bottom: none; }
.menu-icon { font-size: 36rpx; margin-right: 20rpx; }
.menu-text { flex: 1; font-size: 28rpx; color: #333; }
.menu-arrow { font-size: 36rpx; color: #ccc; }
.btn-area { padding: 40rpx 20rpx; }
.btn-logout { width: 100%; height: 88rpx; line-height: 88rpx; background: #fff; color: #E74C3C; border-radius: 44rpx; font-size: 30rpx; border: 1rpx solid #f0f0f0; }
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: $uv-bg-color;
}
/* Hero header */
.profile-hero {
position: relative;
height: 360rpx;
overflow: hidden;
}
.hero-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.hero-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
180deg,
rgba(48, 49, 51, 0.5) 0%,
rgba(48, 49, 51, 0.7) 100%
);
}
.hero-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
padding: $spacing-12 $spacing-4 $spacing-4;
}
.avatar-wrap {
margin-right: $spacing-3;
flex-shrink: 0;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 4rpx solid rgba(255, 255, 255, 0.4);
background: $uv-info-light;
}
.user-info {
display: flex;
flex-direction: column;
}
.nickname {
font-family: $font-family-display;
font-size: $font-size-xl;
font-weight: $font-weight-bold;
color: #fff;
margin-bottom: $spacing-1;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
}
.user-desc {
font-size: $font-size-sm;
color: rgba(255, 255, 255, 0.7);
&--link {
color: $uv-warning;
}
}
/* Menu section */
.menu-section {
padding: $spacing-3;
margin-top: -40rpx;
position: relative;
z-index: 3;
}
.menu-card {
background: $color-card;
border-radius: $radius-xl;
box-shadow: $shadow-base;
overflow: hidden;
}
.menu-item {
display: flex;
align-items: center;
padding: $spacing-3 $spacing-4;
border-bottom: $border-width solid $uv-border-color;
transition: background $transition-fast;
&:active {
background: $uv-info-light;
}
&--last {
border-bottom: none;
}
}
.menu-icon-wrap {
width: 64rpx;
height: 64rpx;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
margin-right: $spacing-3;
flex-shrink: 0;
&--primary {
background: $uv-primary-light;
}
&--warning {
background: $uv-warning-light;
}
&--success {
background: $uv-success-light;
}
}
/* CSS icons */
.icon-card {
width: 28rpx;
height: 20rpx;
border: 3rpx solid $uv-primary;
border-radius: 4rpx;
position: relative;
&::after {
content: '';
position: absolute;
top: 6rpx;
left: 0;
width: 100%;
height: 3rpx;
background: $uv-primary;
}
}
.icon-order {
width: 22rpx;
height: 26rpx;
border: 3rpx solid $uv-warning;
border-radius: 4rpx;
position: relative;
&::before {
content: '';
position: absolute;
top: 6rpx;
left: 4rpx;
width: 10rpx;
height: 3rpx;
background: $uv-warning;
}
&::after {
content: '';
position: absolute;
top: 14rpx;
left: 4rpx;
width: 8rpx;
height: 3rpx;
background: $uv-warning;
}
}
.icon-history {
width: 24rpx;
height: 24rpx;
border: 3rpx solid $uv-success;
border-radius: 50%;
position: relative;
&::before {
content: '';
position: absolute;
top: 4rpx;
left: 8rpx;
width: 3rpx;
height: 8rpx;
background: $uv-success;
}
&::after {
content: '';
position: absolute;
top: 9rpx;
left: 8rpx;
width: 6rpx;
height: 3rpx;
background: $uv-success;
}
}
.menu-text {
flex: 1;
font-size: $font-size-base;
color: $uv-main-color;
font-weight: $font-weight-medium;
}
.menu-chevron {
width: 10rpx;
height: 10rpx;
border-top: 2rpx solid $uv-light-color;
border-right: 2rpx solid $uv-light-color;
transform: rotate(45deg);
flex-shrink: 0;
}
/* Logout button */
.btn-area {
padding: $spacing-5 $spacing-3;
}
.btn-logout {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 88rpx;
background: $color-card;
border-radius: $radius-full;
box-shadow: $shadow-sm;
transition: transform $transition-fast;
&:active {
transform: scale(0.98);
}
text {
font-size: $font-size-base;
color: $uv-error;
font-weight: $font-weight-medium;
}
}
</style>