feat(app): 项目重命名为干杯日记并优化UI设计

- 将应用名称从"喝了么"更改为"干杯日记"
- 更新所有相关文件中的品牌标识和描述信息
- 升级HaveADrink依赖包从1.0.3到1.0.4版本
- 在pages.json中新增分享个人资料页面配置
- 重构DrinkCard组件UI,增加装饰背景、酒类图标支持、饮酒感受徽章等功能
- 优化API错误处理逻辑,增加静默模式支持
- 修改白酒类别图标为 urn emoji,黄酒图标为 tea emoji
- 添加分享功能按钮并优化页面返回交互体验
- 引入新的工具函数用于获取酒类图标和路径判断
- 更新主题混入、常量定义等基础配置文件
- 调整全局样式和设计令牌以匹配新品牌形象
This commit is contained in:
cg
2026-07-20 22:12:27 +08:00
parent adf6b684b9
commit 9f5c69e1e5
25 changed files with 2333 additions and 571 deletions
+177 -40
View File
@@ -91,24 +91,37 @@
<view v-if="selectedRecord.mode === 'drank'" class="day-record-info">
<view class="day-drinks">
<view v-for="(drink, i) in selectedRecord.drinks" :key="i" class="day-drink-item">
<text>{{ getCatEmoji(drink.category) }} {{ drink.brand }} {{ drink.product }}</text>
<view class="day-drink-row">
<view class="day-drink-name">
<image v-if="isIconPath(getCatIcon(drink.category))" :src="getCatIcon(drink.category)" class="day-drink-icon" mode="aspectFit"></image>
<text v-else>{{ getCatEmoji(drink.category) }}</text>
<text>{{ drink.brand }} {{ drink.product }}</text>
</view>
<text v-if="drink.customAmount != null" class="day-drink-amount text-num">{{ drink.customAmount }}{{ getUnitLabel(drink.customUnit) }}</text>
</view>
</view>
</view>
<view class="day-meta flex-between">
<text class="text-caption">{{ getFeelingName(selectedRecord.feeling) }}</text>
<text class="text-caption text-amber text-num">{{ selectedRecord.standardCupsTotal }} 标准杯</text>
</view>
<button class="btn-ghost day-toggle-btn" @click="toggleToAbstain">改为未饮酒</button>
<view class="day-action-row">
<button class="btn-ghost day-action-btn" @click="editRecord">修改</button>
<button class="btn-ghost day-action-btn" @click="toggleToAbstain">改为未饮酒</button>
</view>
<button class="btn-danger day-delete-btn" @click="deleteRecord">删除记录</button>
</view>
<view v-else class="day-abstain">
<text class="day-abstain-emoji">🚫</text>
<text class="text-caption">今日未饮酒好样的</text>
<button class="btn-ghost day-toggle-btn" @click="toggleToDrank">改为饮酒</button>
<button class="btn-danger day-delete-btn" @click="deleteRecord">删除记录</button>
</view>
</view>
<view v-else class="day-empty">
<text class="text-caption">当天无记录</text>
<button class="btn-ghost" @click="backfillDate">补打卡</button>
<button v-if="isSelectedToday" class="btn-primary day-record-btn" @click="goRecordToday">去记录</button>
<button v-else class="btn-ghost" @click="backfillDate">补打卡</button>
</view>
</view>
</view>
@@ -118,9 +131,9 @@
<script>
import DrinkCalendar from '../../components/DrinkCalendar.vue'
import client from '../../common/api'
import { GetCalendarReq } from 'HaveADrink'
import { getGreeting, formatDate, getWeekStart } from '../../common/utils'
import { FEELINGS, DRINK_CATEGORIES } from '../../common/constants'
import { GetCalendarReq, DeleteRecordReq, CreateRecordReq, GetRecordDetailReq } from 'HaveADrink'
import { getGreeting, formatDate, getWeekStart, getCatIcon, isIconPath } from '../../common/utils'
import { FEELINGS, DRINK_CATEGORIES, DRINK_UNITS } from '../../common/constants'
import themeMixin from '../../common/theme-mixin'
export default {
@@ -152,6 +165,9 @@ export default {
healthPercent() {
// 建议每周不超过14标准杯
return Math.min((this.stats.weekCups / 14) * 100, 100)
},
isSelectedToday() {
return this.selectedDate === formatDate(new Date(), 'YYYY-MM-DD')
}
},
onShow() {
@@ -163,6 +179,18 @@ export default {
}
this.loadData()
},
onShareAppMessage() {
return {
title: '干杯日记 - 让每一杯酒都有迹可循',
path: '/pages/index/index'
}
},
onShareTimeline() {
return {
title: '干杯日记 - 让每一杯酒都有迹可循',
query: ''
}
},
methods: {
async loadData() {
this.greeting = getGreeting()
@@ -178,15 +206,8 @@ export default {
if (userResp.status === 'fulfilled' && userResp.value) {
const user = userResp.value
this.userName = user.nickname || '酒友'
uni.setStorageSync('user_info', JSON.stringify(user))
} else {
// 降级读取本地缓存
try {
const cached = JSON.parse(uni.getStorageSync('user_info') || '{}')
this.userName = cached.nickname || '酒友'
} catch (e) {
this.userName = '酒友'
}
this.userName = '酒友'
}
// 统计概览
@@ -218,7 +239,9 @@ export default {
standardCupsTotal: d.standardCupsTotal || 0,
drinks: (d.drinks || []).map(dr => ({
...dr,
category: String(dr.category)
category: String(dr.category),
customAmount: dr.customAmount,
customUnit: dr.customUnit ? String(dr.customUnit) : ''
})),
food: d.food ? { category: String(d.food.category), name: d.food.name || '' } : null,
feeling: d.feeling ? String(d.feeling) : null,
@@ -228,13 +251,7 @@ export default {
}
} catch (e) {
console.warn('加载首页数据失败:', e)
// 降级:读取本地缓存
try {
const cached = JSON.parse(uni.getStorageSync('user_info') || '{}')
this.userName = cached.nickname || '酒友'
} catch (err) {
this.userName = '酒友'
}
this.userName = '酒友'
}
},
onMonthChange({ year, month }) {
@@ -255,7 +272,9 @@ export default {
standardCupsTotal: d.standardCupsTotal || 0,
drinks: (d.drinks || []).map(dr => ({
...dr,
category: String(dr.category)
category: String(dr.category),
customAmount: dr.customAmount,
customUnit: dr.customUnit ? String(dr.customUnit) : ''
})),
food: d.food ? { category: String(d.food.category), name: d.food.name || '' } : null,
feeling: d.feeling ? String(d.feeling) : null,
@@ -266,10 +285,37 @@ export default {
console.warn('加载月度记录失败:', e)
}
},
onDateSelect(dateStr) {
async onDateSelect(dateStr) {
this.selectedDate = dateStr
this.selectedRecord = this.records.find(r => r.date === dateStr) || null
this.showDayDetail = true
const localRecord = this.records.find(r => r.date === dateStr) || null
// 如果有记录且有id,拉取完整详情(确保拿到 customAmount/customUnit 等全量字段)
if (localRecord && localRecord.id && localRecord.mode === 'drank') {
this.selectedRecord = localRecord // 先用本地数据展示
this.showDayDetail = true
try {
const resp = await client.GetRecordDetail(new GetRecordDetailReq(localRecord.id))
const rec = resp.data || resp
if (rec) {
this.selectedRecord = {
...localRecord,
...rec,
mode: String(rec.mode || localRecord.mode),
drinks: (rec.drinks || localRecord.drinks || []).map(d => ({
...d,
category: String(d.category),
customUnit: d.customUnit ? String(d.customUnit) : ''
})),
feeling: rec.feeling ? String(rec.feeling) : localRecord.feeling,
visibility: rec.visibility ? String(rec.visibility) : localRecord.visibility
}
}
} catch (e) {
console.warn('获取记录详情失败,使用本地数据:', e)
}
} else {
this.selectedRecord = localRecord
this.showDayDetail = true
}
},
getFeelingName(feelingId) {
const f = FEELINGS.find(f => f.id === feelingId)
@@ -279,28 +325,45 @@ export default {
const cat = DRINK_CATEGORIES.find(c => c.id === catId)
return cat ? cat.emoji : '🥃'
},
getCatIcon,
isIconPath,
getUnitLabel(unitId) {
if (!unitId) return ''
const u = DRINK_UNITS.find(u => u.id === unitId)
return u ? u.name : unitId
},
backfillDate() {
this.showDayDetail = false
uni.navigateTo({
url: `/pages/record/record?date=${this.selectedDate}&mode=backfill`
})
},
// 修改记录(跳转到记录页编辑)
editRecord() {
this.showDayDetail = false
uni.navigateTo({
url: `/pages/record/record?date=${this.selectedDate}&mode=edit`
})
},
// 改为未饮酒
toggleToAbstain() {
uni.showModal({
title: '确认修改',
content: '确定将这天改为未饮酒吗?饮酒记录将被清除。',
success: (res) => {
success: async (res) => {
if (!res.confirm) return
const idx = this.records.findIndex(r => r.date === this.selectedDate)
if (idx === -1) return
this.records[idx].mode = 'abstain'
this.records[idx].drinks = []
this.records[idx].food = null
this.records[idx].feeling = null
this.records[idx].standardCupsTotal = 0
this.selectedRecord = this.records[idx]
this.saveRecords()
const recordId = this.selectedRecord && this.selectedRecord.id
try {
if (recordId) {
await client.DeleteRecord(new DeleteRecordReq(recordId))
}
uni.showToast({ title: '已修改', icon: 'success' })
this.showDayDetail = false
this.loadData()
} catch (e) {
console.warn('修改为未饮酒失败:', e)
uni.showToast({ title: '修改失败,请重试', icon: 'none' })
}
}
})
},
@@ -311,13 +374,37 @@ export default {
url: `/pages/record/record?date=${this.selectedDate}&mode=backfill`
})
},
// 保存记录到本地存储
saveRecords() {
uni.setStorageSync('drink_records', JSON.stringify(this.records))
// 删除记录
deleteRecord() {
uni.showModal({
title: '确认删除',
content: `确定删除 ${this.selectedDate} 的记录吗?删除后不可恢复。`,
confirmColor: '#e85d3a',
success: async (res) => {
if (!res.confirm) return
const recordId = this.selectedRecord && this.selectedRecord.id
try {
if (recordId) {
await client.DeleteRecord(new DeleteRecordReq(recordId))
}
this.showDayDetail = false
uni.showToast({ title: '已删除', icon: 'success' })
// 重新加载统计和日历数据
this.loadData()
} catch (e) {
console.warn('删除记录失败:', e)
uni.showToast({ title: '删除失败,请重试', icon: 'none' })
}
}
})
},
goRecord() {
uni.navigateTo({ url: '/pages/record/record' })
},
goRecordToday() {
this.showDayDetail = false
uni.navigateTo({ url: '/pages/record/record' })
},
}
}
@@ -555,6 +642,33 @@ export default {
border-bottom: 2rpx solid var(--border-micro, rgba(255, 255, 255, 0.05));
}
.day-drink-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.day-drink-name {
display: flex;
align-items: center;
gap: $sp-xs;
flex: 1;
min-width: 0;
}
.day-drink-icon {
width: 36rpx;
height: 36rpx;
flex-shrink: 0;
}
.day-drink-amount {
font-size: $fs-sm;
color: $text-secondary;
white-space: nowrap;
margin-left: $sp-sm;
}
.day-meta {
margin-top: $sp-lg;
}
@@ -574,11 +688,34 @@ export default {
text-align: center;
padding: $sp-xl 0;
.btn-ghost {
.btn-ghost, .day-record-btn {
margin-top: $sp-lg;
width: 100%;
}
}
.day-action-row {
display: flex;
gap: $sp-sm;
margin-top: $sp-lg;
}
.day-action-btn {
flex: 1;
font-size: $fs-sm;
color: $text-secondary;
border-color: var(--border-active, rgba(255, 255, 255, 0.12));
}
.day-delete-btn {
margin-top: $sp-md;
width: 100%;
font-size: $fs-sm;
color: #e85d3a;
background: rgba(232, 93, 58, 0.08);
border: 2rpx solid rgba(232, 93, 58, 0.2);
}
.day-toggle-btn {
margin-top: $sp-lg;
width: 100%;