topfans/frontend/pages/dashboard/components/DashboardHeader.vue
zheng020 6c8ecb8fda refactor(dashboard): Header .tab 改用具体属性 transition 而非 transition: all
代码评审指出 `transition: all` 是已知的 CSS 反模式——会触
发所有属性的动画,包括未预期的 layout-trigger 属性(width、
height、padding 等)。窄化为 `background` 与 `color`,符合
.tab 实际变化的状态。
2026-06-03 01:20:49 +08:00

148 lines
3.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="dashboard-header">
<!-- 装饰渐变背景 -->
<view class="header-deco-bg"></view>
<!-- 装饰光晕红粉色 -->
<view class="header-glow"></view>
<!-- 状态栏占位iPhone 44px -->
<view class="status-bar-placeholder"></view>
<view class="header-content">
<!-- 毛绒怪头像占位纯色块 + emoji -->
<view class="mascot">
<text class="mascot-emoji">🐾</text>
</view>
<!-- 渐变标题 -->
<view class="title-wrap">
<text class="header-title">数据看板</text>
</view>
<!-- Tab 胶囊 -->
<view class="header-tabs">
<view
:class="['tab', activeTab === 'crystal' ? 'tab-active' : '']"
@tap="$emit('update:activeTab', 'crystal')"
>
水晶相关
</view>
<view
:class="['tab', activeTab === 'season' ? 'tab-active' : '']"
@tap="$emit('update:activeTab', 'season')"
>
赛季总览
</view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
activeTab: { type: String, default: 'crystal' },
})
defineEmits(['update:activeTab'])
</script>
<style lang="scss" scoped>
.dashboard-header {
position: relative;
height: 360rpx;
overflow: hidden;
}
.status-bar-placeholder {
height: 44px;
}
.header-deco-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 700rpx;
background: linear-gradient(179deg, #FFE5E5 0%, #F3A0A1 50%, #FF9C9C 86%, #FF2024 100%);
filter: blur(4px);
z-index: 0;
}
.header-glow {
position: absolute;
top: 100rpx;
left: 50%;
transform: translateX(-50%);
width: 400rpx;
height: 400rpx;
background: radial-gradient(circle, rgba(255, 200, 100, 0.5) 0%, transparent 70%);
filter: blur(30px);
z-index: 1;
}
.header-content {
position: relative;
z-index: 2;
padding: 0 32rpx 32rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.mascot {
width: 104rpx;
height: 104rpx;
border-radius: 50%;
background: linear-gradient(135deg, #FF6B6B 0%, #FFB199 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 2px 2px 30px rgba(242, 21, 21, 0.47);
margin-bottom: 16rpx;
}
.mascot-emoji {
font-size: 64rpx;
}
.title-wrap {
margin-bottom: 24rpx;
}
.header-title {
font-size: 48rpx;
font-weight: 700;
background: linear-gradient(90deg, #FFE5B4 0%, #FFB199 50%, #FF8A95 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4);
}
.header-tabs {
display: flex;
background: rgba(0, 0, 0, 0.15);
border-radius: 22rpx;
padding: 6rpx;
width: 100%;
max-width: 500rpx;
}
.tab {
flex: 1;
text-align: center;
padding: 14rpx 0;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.75);
border-radius: 22rpx;
transition: background 0.25s ease, color 0.25s ease;
}
.tab-active {
background: linear-gradient(231deg, #A8A6ED 0%, #88C8D8 64%, #F380EF 100%);
color: #ffffff;
font-weight: 600;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
}
</style>