- 移除动态主题切换功能,改为全局统一深色主题 - 实现自定义TabBar组件替换原生tabBar,解决iOS闪白问题 - 创建main容器页面统一管理三个tab页面的生命周期和状态 - 将所有页面的onShow/onHide等生命周期方法迁移到子组件内部 - 更新页面路径从index改为main,并调整路由跳转逻辑 - 移除theme-mixin和相关主题工具函数 - 统一页面背景色设置,优化用户体验一致性
404 lines
8.4 KiB
Vue
404 lines
8.4 KiB
Vue
<script>
|
||
import client, { refreshAuthToken } from './common/api'
|
||
import wsManager from './common/websocket'
|
||
|
||
export default {
|
||
onLaunch() {
|
||
console.log('碰盏日记 App Launch')
|
||
// 一次性设置窗口背景色(深色主题),避免切换 tab 时露白底
|
||
try {
|
||
uni.setBackgroundColor({
|
||
backgroundColor: '#0B0B14',
|
||
backgroundColorTop: '#0B0B14',
|
||
backgroundColorBottom: '#0B0B14'
|
||
})
|
||
} catch (e) {}
|
||
// 尝试刷新 token
|
||
this.restoreAuth()
|
||
// 初始化 WebSocket 连接(私信功能)
|
||
this.initWebSocket()
|
||
// 数据迁移:清除旧版 logo.png 头像引用
|
||
this.migrateUserData()
|
||
// 启动路由守卫
|
||
this.checkLaunchRoute()
|
||
},
|
||
onShow() {
|
||
console.log('App Show')
|
||
},
|
||
onHide() {
|
||
console.log('App Hide')
|
||
},
|
||
methods: {
|
||
// 恢复登录态:从缓存恢复 token 并尝试刷新
|
||
async restoreAuth() {
|
||
const token = uni.getStorageSync('auth_token')
|
||
if (token) {
|
||
client.setToken(token)
|
||
// 后台尝试刷新 token(静默,不阻塞启动)
|
||
refreshAuthToken().catch(() => {})
|
||
}
|
||
},
|
||
// 初始化 WebSocket 连接(私信实时通信)
|
||
initWebSocket() {
|
||
const token = uni.getStorageSync('auth_token')
|
||
if (token) {
|
||
wsManager.connect(token)
|
||
}
|
||
},
|
||
// 数据迁移:清除旧版 logo.png 头像引用
|
||
migrateUserData() {
|
||
try {
|
||
const raw = uni.getStorageSync('user_info')
|
||
if (raw) {
|
||
const user = JSON.parse(raw)
|
||
if (user.avatar && user.avatar.includes('logo.png')) {
|
||
user.avatar = ''
|
||
uni.setStorageSync('user_info', JSON.stringify(user))
|
||
}
|
||
}
|
||
} catch (e) { /* ignore */ }
|
||
},
|
||
// 启动时检查引导状态,决定跳转目标
|
||
checkLaunchRoute() {
|
||
const isFirst = uni.getStorageSync('is_first_launch')
|
||
if (!isFirst || isFirst === 'true') {
|
||
// 首次使用 → 引导页
|
||
// 延迟一帧再跳:启动早期首页还未就绪,立即 reLaunch 可能报 timeout
|
||
setTimeout(() => {
|
||
uni.reLaunch({ url: '/pages/onboarding/onboarding', fail: () => {} })
|
||
}, 100)
|
||
}
|
||
// 否则正常进入主容器(main),pages.json 中 main 为第一个页面
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
/* ==========================================
|
||
* 碰盏日记 - 全局样式
|
||
* 设计概念: 琥珀夜光 Amber Night
|
||
* ========================================== */
|
||
|
||
page {
|
||
background-color: $bg-base;
|
||
color: $text-primary;
|
||
font-family: $font-base;
|
||
font-size: $fs-base;
|
||
line-height: $lh-normal;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* --- 通用工具类 --- */
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background-color: $bg-base;
|
||
color: $text-primary;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.card {
|
||
background-color: $bg-card;
|
||
color: $text-primary;
|
||
border-radius: $radius-lg;
|
||
padding: $sp-lg;
|
||
box-shadow: $shadow-sm;
|
||
}
|
||
|
||
.card-elevated {
|
||
background-color: $bg-elevated;
|
||
color: $text-primary;
|
||
border-radius: $radius-lg;
|
||
padding: $sp-lg;
|
||
box-shadow: $shadow-md;
|
||
}
|
||
|
||
/* --- 文本层级 --- */
|
||
.text-hero {
|
||
font-size: $fs-hero;
|
||
font-weight: $fw-black;
|
||
line-height: $lh-tight;
|
||
letter-spacing: -2rpx;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-display {
|
||
font-size: $fs-3xl;
|
||
font-weight: $fw-black;
|
||
line-height: $lh-tight;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-h1 {
|
||
font-size: $fs-2xl;
|
||
font-weight: $fw-bold;
|
||
line-height: $lh-tight;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-h2 {
|
||
font-size: $fs-xl;
|
||
font-weight: $fw-bold;
|
||
line-height: $lh-tight;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-h3 {
|
||
font-size: $fs-lg;
|
||
font-weight: $fw-medium;
|
||
line-height: $lh-normal;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-body {
|
||
font-size: $fs-base;
|
||
font-weight: $fw-normal;
|
||
line-height: $lh-normal;
|
||
color: $text-primary;
|
||
}
|
||
|
||
.text-caption {
|
||
font-size: $fs-sm;
|
||
color: $text-secondary;
|
||
line-height: $lh-normal;
|
||
}
|
||
|
||
.text-tiny {
|
||
font-size: $fs-xs;
|
||
color: $text-tertiary;
|
||
}
|
||
|
||
/* --- 数字专用 --- */
|
||
.text-num {
|
||
font-family: $font-num;
|
||
font-weight: $fw-bold;
|
||
letter-spacing: -1rpx;
|
||
}
|
||
|
||
/* --- 色彩文字 --- */
|
||
.text-amber {
|
||
color: $amber;
|
||
}
|
||
|
||
.text-mint {
|
||
color: $mint;
|
||
}
|
||
|
||
.text-coral {
|
||
color: $coral;
|
||
}
|
||
|
||
.text-secondary {
|
||
color: $text-secondary;
|
||
}
|
||
|
||
/* --- 按钮系统 --- */
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, $amber, $amber-deep);
|
||
color: $text-on-amber;
|
||
font-size: $fs-md;
|
||
font-weight: $fw-bold;
|
||
padding: $sp-md $sp-xl;
|
||
border-radius: $radius-full;
|
||
border: none;
|
||
text-align: center;
|
||
box-shadow: $shadow-amber;
|
||
transition: all $duration-fast $ease-out;
|
||
|
||
&:active {
|
||
transform: scale(0.97);
|
||
box-shadow: 0 4rpx 16rpx rgba(232, 168, 56, 0.2);
|
||
}
|
||
}
|
||
|
||
.btn-primary-lg {
|
||
@extend .btn-primary;
|
||
padding: $sp-lg $sp-xl;
|
||
font-size: $fs-lg;
|
||
border-radius: $radius-full;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background-color: $bg-elevated;
|
||
color: $text-primary;
|
||
font-size: $fs-base;
|
||
font-weight: $fw-medium;
|
||
padding: $sp-md $sp-xl;
|
||
border-radius: $radius-full;
|
||
border: 2rpx solid var(--border-subtle, rgba(255, 255, 255, 0.10));
|
||
transition: all $duration-fast $ease-out;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
&:active {
|
||
background-color: $bg-card-alt;
|
||
transform: scale(0.97);
|
||
}
|
||
}
|
||
|
||
.btn-ghost {
|
||
background-color: transparent;
|
||
color: $amber;
|
||
font-size: $fs-base;
|
||
font-weight: $fw-medium;
|
||
padding: $sp-sm $sp-lg;
|
||
border: 2rpx solid $amber;
|
||
border-radius: $radius-full;
|
||
text-align: center;
|
||
transition: all $duration-fast $ease-out;
|
||
|
||
&:active {
|
||
background-color: $amber-glow;
|
||
}
|
||
}
|
||
|
||
/* 添加酒水按钮(Step 1 底部) */
|
||
.btn-add-drink {
|
||
background: linear-gradient(135deg, $amber, $amber-deep);
|
||
color: $text-on-amber;
|
||
font-size: $fs-md;
|
||
font-weight: $fw-bold;
|
||
padding: $sp-md $sp-xl;
|
||
border-radius: $radius-full;
|
||
border: none;
|
||
text-align: center;
|
||
box-shadow: $shadow-amber;
|
||
transition: all $duration-fast $ease-out;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
&:active {
|
||
transform: scale(0.97);
|
||
box-shadow: 0 4rpx 16rpx rgba(232, 168, 56, 0.2);
|
||
}
|
||
|
||
&[disabled] {
|
||
background: transparent;
|
||
color: $text-tertiary;
|
||
border: 2rpx solid var(--border-subtle, rgba(255, 255, 255, 0.10));
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
|
||
.btn-text {
|
||
background: none;
|
||
border: none;
|
||
color: $text-secondary;
|
||
font-size: $fs-sm;
|
||
padding: $sp-xs $sp-sm;
|
||
}
|
||
|
||
/* --- 琥珀CTA大按钮(签名元素) --- */
|
||
.btn-cta {
|
||
background: linear-gradient(135deg, $amber-light, $amber, $amber-deep);
|
||
color: $text-on-amber;
|
||
font-size: $fs-xl;
|
||
font-weight: $fw-bold;
|
||
padding: $sp-lg 0;
|
||
border-radius: $radius-xl;
|
||
border: none;
|
||
text-align: center;
|
||
box-shadow: $shadow-amber;
|
||
width: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
right: auto;
|
||
bottom: auto;
|
||
width: 100%;
|
||
height: 100%;
|
||
border: none;
|
||
transform: none;
|
||
background: linear-gradient(90deg, transparent, var(--btn-shimmer, rgba(255, 255, 255, 0.15)), transparent);
|
||
animation: shimmer 3s infinite;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
0% { left: -100%; }
|
||
100% { left: 100%; }
|
||
}
|
||
|
||
/* --- 标签 --- */
|
||
.tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: $sp-xs $sp-md;
|
||
border-radius: $radius-full;
|
||
font-size: $fs-sm;
|
||
font-weight: $fw-medium;
|
||
background-color: $bg-elevated;
|
||
color: $text-secondary;
|
||
|
||
&.tag-active {
|
||
background-color: $amber-glow;
|
||
color: $amber;
|
||
border: 2rpx solid rgba(232, 168, 56, 0.3);
|
||
}
|
||
}
|
||
|
||
/* --- 分隔线 --- */
|
||
.divider {
|
||
height: 2rpx;
|
||
background: linear-gradient(90deg, transparent, var(--divider-color, rgba(255, 255, 255, 0.08)), transparent);
|
||
margin: $sp-lg 0;
|
||
}
|
||
|
||
/* --- 弹性布局辅助 --- */
|
||
.flex {
|
||
display: flex;
|
||
}
|
||
|
||
.flex-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.flex-center {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.flex-between {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.flex-1 {
|
||
flex: 1;
|
||
}
|
||
|
||
/* --- 安全区域 --- */
|
||
.safe-bottom {
|
||
padding-bottom: calc(env(safe-area-inset-bottom) + 120rpx);
|
||
}
|
||
|
||
/* --- 健康提示条 --- */
|
||
.health-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: $sp-sm;
|
||
font-size: $fs-xs;
|
||
color: $text-tertiary;
|
||
background-color: var(--bg-subtle, rgba(255, 255, 255, 0.03));
|
||
|
||
text {
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
</style>
|