feat(theme): 实现日夜主题自动切换功能
- 添加日夜主题CSS变量定义,支持琥珀夜光和暖白琥珀两种风格 - 实现主题切换逻辑,根据时间自动切换白天(light)和夜间(dark)主题 - 在App.vue中集成主题初始化和token恢复功能 - 更新全局样式类应用主题颜色变量,包括卡片、文本、边框等 - 创建主题混入(mixin)供各页面使用,确保主题同步更新 - 实现导航栏和TabBar主题动态切换,提升用户体验 - 添加API服务层封装HaveADrink SDK,统一处理认证和请求 - 优化DrinkCard组件显示饮酒感受信息,丰富打卡记录展示 - 更新项目依赖配置,集成新的API客户端库
This commit is contained in:
+73
-12
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page-container profile-page safe-bottom">
|
||||
<view :class="themeClass" class="page-container profile-page safe-bottom">
|
||||
<!-- 用户头部 -->
|
||||
<view class="profile-header">
|
||||
<view class="profile-glow"></view>
|
||||
@@ -108,11 +108,13 @@
|
||||
|
||||
<script>
|
||||
import AchievementBadge from '../../components/AchievementBadge.vue'
|
||||
import { getRecords, getUserInfo } from '../../common/mock-data'
|
||||
import { calcStats, checkAchievements } from '../../common/utils'
|
||||
import client, { clearAuth } from '../../common/api'
|
||||
import { checkAchievements } from '../../common/utils'
|
||||
import { ACHIEVEMENTS, DRINK_CATEGORIES } from '../../common/constants'
|
||||
import themeMixin from '../../common/theme-mixin'
|
||||
|
||||
export default {
|
||||
mixins: [themeMixin],
|
||||
components: { AchievementBadge },
|
||||
data() {
|
||||
return {
|
||||
@@ -136,11 +138,67 @@ export default {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
this.user = getUserInfo()
|
||||
const records = getRecords()
|
||||
this.stats = calcStats(records)
|
||||
this.achievementList = checkAchievements(this.stats, ACHIEVEMENTS)
|
||||
async loadData() {
|
||||
try {
|
||||
const [userResp, statsResp, achieveResp] = await Promise.allSettled([
|
||||
client.GetUserProfile({}),
|
||||
client.GetStatsOverview({}),
|
||||
client.GetAchievements({})
|
||||
])
|
||||
|
||||
// 用户信息
|
||||
if (userResp.status === 'fulfilled' && userResp.value) {
|
||||
this.user = userResp.value.data || userResp.value
|
||||
} else {
|
||||
try {
|
||||
this.user = JSON.parse(uni.getStorageSync('user_info') || '{}')
|
||||
} catch (e) { this.user = {} }
|
||||
}
|
||||
|
||||
// 统计概览
|
||||
if (statsResp.status === 'fulfilled' && statsResp.value) {
|
||||
const s = statsResp.value.data || statsResp.value
|
||||
this.stats = {
|
||||
weekDrinkCount: s.weekDrinkCount || 0,
|
||||
weekCups: s.weekCups || 0,
|
||||
monthDrinkCount: s.monthDrinkCount || 0,
|
||||
monthCups: s.monthCups || 0,
|
||||
streak: s.streak || 0,
|
||||
streakType: String(s.streakType || 'drank'),
|
||||
totalDays: s.totalDays || 0,
|
||||
totalRecords: s.totalRecords || 0,
|
||||
totalCups: s.totalCups || 0,
|
||||
favCategory: s.favCategory ? String(s.favCategory) : null,
|
||||
categoryVariety: s.categoryVariety || 0
|
||||
}
|
||||
}
|
||||
|
||||
// 成就列表
|
||||
if (achieveResp.status === 'fulfilled' && achieveResp.value) {
|
||||
const list = achieveResp.value.list || achieveResp.value.data || achieveResp.value
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
this.achievementList = list.map(a => ({
|
||||
id: a.achievement_id || a.id,
|
||||
name: a.name,
|
||||
desc: a.desc,
|
||||
icon: a.icon,
|
||||
condition: a.condition || '',
|
||||
unlocked: !!a.unlocked
|
||||
}))
|
||||
} else {
|
||||
// API 返回空,降级本地计算
|
||||
this.achievementList = checkAchievements(this.stats, ACHIEVEMENTS)
|
||||
}
|
||||
} else {
|
||||
this.achievementList = checkAchievements(this.stats, ACHIEVEMENTS)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('加载个人数据失败:', e)
|
||||
try {
|
||||
this.user = JSON.parse(uni.getStorageSync('user_info') || '{}')
|
||||
} catch (err) { this.user = {} }
|
||||
this.achievementList = checkAchievements(this.stats, ACHIEVEMENTS)
|
||||
}
|
||||
},
|
||||
showAchievementDetail(a) {
|
||||
this.detailAchievement = a
|
||||
@@ -172,9 +230,7 @@ export default {
|
||||
confirmColor: '#FF6B6B',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.removeStorageSync('is_logged_in')
|
||||
uni.removeStorageSync('is_guest')
|
||||
uni.removeStorageSync('user_info')
|
||||
clearAuth()
|
||||
uni.reLaunch({ url: '/pages/login/login' })
|
||||
}
|
||||
}
|
||||
@@ -239,6 +295,7 @@ export default {
|
||||
.profile-name {
|
||||
font-size: $fs-xl;
|
||||
font-weight: $fw-bold;
|
||||
color: $text-primary;
|
||||
margin-bottom: $sp-xs;
|
||||
}
|
||||
|
||||
@@ -256,6 +313,7 @@ export default {
|
||||
display: block;
|
||||
font-size: $fs-lg;
|
||||
font-weight: $fw-bold;
|
||||
color: $text-primary;
|
||||
margin-bottom: $sp-lg;
|
||||
}
|
||||
|
||||
@@ -274,6 +332,7 @@ export default {
|
||||
display: block;
|
||||
font-size: $fs-2xl;
|
||||
font-weight: $fw-bold;
|
||||
color: $text-primary;
|
||||
margin-bottom: $sp-xs;
|
||||
}
|
||||
|
||||
@@ -299,6 +358,7 @@ export default {
|
||||
display: block;
|
||||
font-size: $fs-3xl;
|
||||
font-weight: $fw-black;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.streak-emoji {
|
||||
@@ -326,6 +386,7 @@ export default {
|
||||
align-items: center;
|
||||
gap: $sp-md;
|
||||
padding: $sp-lg 0;
|
||||
color: $text-primary;
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
@@ -364,7 +425,7 @@ export default {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.7);
|
||||
background: var(--overlay-modal, rgba(0, 0, 0, 0.7));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
Reference in New Issue
Block a user