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
+294 -123
View File
@@ -1,17 +1,25 @@
<template>
<view class="card-root">
<!-- 顶部日期 -->
<view class="card-header">
<text class="header-date">{{ record.date }}</text>
<!-- 装饰背景层 -->
<view class="card-decor">
<view class="decor-circle decor-circle-1"></view>
<view class="decor-circle decor-circle-2"></view>
<view class="decor-circle decor-circle-3"></view>
<view class="decor-glow"></view>
</view>
<!-- 照片区域 -->
<!-- 顶部日期 -->
<view class="card-top">
<view class="date-line"></view>
<text class="header-date">{{ record.date }}</text>
<view class="date-line"></view>
</view>
<!-- 主视觉区照片或图标 -->
<view v-if="photos.length > 0" class="card-photos">
<!-- 1张: 大图 -->
<view v-if="photos.length === 1" class="photo-single">
<image :src="photos[0]" mode="aspectFill"></image>
</view>
<!-- 2-6: 网格 -->
<view v-else class="photo-grid">
<view
v-for="(photo, i) in photos"
@@ -23,43 +31,65 @@
</view>
</view>
</view>
<!-- 无照片: emoji -->
<view v-else class="card-hero">
<view class="hero-emoji-wrap">
<text class="hero-emoji">{{ mainEmoji }}</text>
<view class="hero-ring"></view>
<view class="hero-inner">
<image v-if="isIconPath(mainIcon)" :src="mainIcon" class="hero-icon-img" mode="aspectFit"></image>
<text v-else class="hero-emoji">{{ mainIcon }}</text>
</view>
</view>
<!-- 饮酒感受徽章 -->
<view v-if="feelingData" class="feeling-badge">
<text class="feeling-emoji">{{ feelingData.emoji }}</text>
<text class="feeling-name">{{ feelingData.name }}</text>
<text class="feeling-desc">{{ feelingData.desc }}</text>
</view>
<!-- 酒水清单 -->
<view class="drink-list">
<view class="list-title-row">
<view class="list-dot"></view>
<text class="list-title">今夜菜单</text>
<view class="list-dot"></view>
</view>
<view v-for="(drink, i) in record.drinks" :key="i" class="drink-row">
<text class="drink-name">{{ getCatEmoji(drink.category) }} {{ drink.brand }}</text>
<text class="drink-detail">{{ formatAmount(drink) }}</text>
<view class="drink-left">
<image v-if="isIconPath(getCatIcon(drink.category))" :src="getCatIcon(drink.category)" class="drink-icon-img" mode="aspectFit"></image>
<text v-else class="drink-emoji">{{ getCatEmoji(drink.category) }}</text>
<text class="drink-name">{{ drink.brand }}</text>
</view>
<view class="drink-dots"></view>
<text class="drink-amount">{{ formatAmount(drink) }}</text>
</view>
<view v-if="record.food" class="drink-row">
<text class="drink-name">🍲 {{ getFoodName() }}</text>
<text class="drink-detail"></text>
<view class="drink-left">
<text class="drink-emoji">🍲</text>
<text class="drink-name">{{ getFoodName() }}</text>
</view>
<view class="drink-dots"></view>
<text class="drink-amount">佐酒</text>
</view>
</view>
<!-- 饮酒感受 -->
<view v-if="feelingLabel" class="feeling-row">
<text class="feeling-text">{{ feelingLabel }}</text>
<!-- 分割装饰 -->
<view class="divider-decor">
<view class="divider-line"></view>
<text class="divider-icon">🥂</text>
<view class="divider-line"></view>
</view>
<!-- 分割线 -->
<view class="divider"></view>
<!-- 喝后心得酒言酒语 -->
<view class="card-footer">
<text class="footer-label">喝后心得</text>
<text class="footer-quote">"{{ quote }}"</text>
<!-- 酒言酒语 -->
<view class="quote-section">
<text class="quote-mark">"</text>
<text class="quote-text">{{ quote }}</text>
<text class="quote-mark quote-mark-end">"</text>
</view>
<!-- 品牌 + 二维码 -->
<!-- 品牌底栏 -->
<view class="card-brand">
<view class="brand-info">
<text class="brand-text">🍻 喝了么</text>
<view class="brand-left">
<text class="brand-name">干杯日记</text>
<text class="brand-slogan">记录每一杯的故事</text>
</view>
<view class="brand-qr"></view>
@@ -69,6 +99,7 @@
<script>
import { DRINK_CATEGORIES, FOOD_CATEGORIES, DRINK_QUOTES, FEELINGS } from '../common/constants'
import { getCatIcon, isIconPath } from '../common/utils'
const UNIT_NAMES = { ml: 'ml', liang: '两', bottle: '瓶', cup: '杯', can: '听', shot: 'shot' }
@@ -85,16 +116,17 @@ export default {
photos() {
return (this.record && this.record.photos) || []
},
mainEmoji() {
mainIcon() {
const first = (this.record.drinks || [])[0]
return first ? this.getCatEmoji(first.category) : '🥃'
return first ? getCatIcon(first.category) : '🥃'
},
feelingLabel() {
const f = FEELINGS.find(fe => fe.id === this.record.feeling)
return f ? `${f.emoji} ${f.name}` : ''
feelingData() {
return FEELINGS.find(fe => fe.id === this.record.feeling) || null
}
},
methods: {
getCatIcon,
isIconPath,
getCatEmoji(catId) {
const cat = DRINK_CATEGORIES.find(c => c.id === catId)
return cat ? cat.emoji : '🥃'
@@ -116,178 +148,317 @@ export default {
.card-root {
position: relative;
width: 620rpx;
border-radius: 32rpx;
border-radius: 40rpx;
overflow: hidden;
background: linear-gradient(170deg, var(--card-gradient-start, #1A1510) 0%, var(--card-gradient-end, #0D0B08) 100%);
border: 2rpx solid var(--border-subtle, rgba(255, 255, 255, 0.10));
box-shadow: var(--shadow-lg, 0 16rpx 48rpx rgba(0, 0, 0, 0.45)), 0 0 60rpx var(--amber-glow, rgba(232, 168, 56, 0.15));
background: linear-gradient(175deg, #1C1428 0%, #0F0A1A 40%, #0A0710 100%);
border: 2rpx solid rgba(232, 168, 56, 0.2);
box-shadow: 0 24rpx 80rpx rgba(0, 0, 0, 0.6), 0 0 80rpx rgba(232, 168, 56, 0.08), inset 0 1rpx 0 rgba(255, 255, 255, 0.06);
display: flex;
flex-direction: column;
padding: 56rpx 48rpx 40rpx;
}
/* 顶部 */
.card-header {
padding: $sp-lg $sp-xl 0;
/* === 装饰背景 === */
.card-decor {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
overflow: hidden;
}
.decor-circle {
position: absolute;
border-radius: 50%;
border: 1.5rpx solid rgba(232, 168, 56, 0.12);
}
.decor-circle-1 {
width: 300rpx; height: 300rpx;
top: -80rpx; right: -60rpx;
}
.decor-circle-2 {
width: 200rpx; height: 200rpx;
bottom: 200rpx; left: -60rpx;
border-color: rgba(139, 133, 184, 0.1);
}
.decor-circle-3 {
width: 120rpx; height: 120rpx;
top: 320rpx; right: 40rpx;
background: rgba(232, 168, 56, 0.03);
}
.decor-glow {
position: absolute;
width: 400rpx; height: 400rpx;
top: -100rpx; left: 50%;
transform: translateX(-50%);
background: radial-gradient(circle, rgba(232, 168, 56, 0.06) 0%, transparent 70%);
border-radius: 50%;
}
/* === 顶部日期 === */
.card-top {
display: flex;
align-items: center;
justify-content: center;
gap: 20rpx;
margin-bottom: 40rpx;
position: relative;
z-index: 1;
}
.date-line {
width: 60rpx;
height: 2rpx;
background: linear-gradient(90deg, transparent, rgba(232, 168, 56, 0.5));
}
.date-line:last-child {
background: linear-gradient(90deg, rgba(232, 168, 56, 0.5), transparent);
}
.header-date {
font-size: $fs-sm;
color: $text-tertiary;
font-size: 22rpx;
color: rgba(232, 168, 56, 0.8);
font-family: $font-num;
letter-spacing: 4rpx;
letter-spacing: 6rpx;
font-weight: $fw-medium;
}
/* 照片区域 */
/* === 照片区域 === */
.card-photos {
padding: $sp-lg $sp-xl 0;
position: relative;
z-index: 1;
margin-bottom: 32rpx;
}
/* 1张:大图 */
.photo-single {
width: 100%;
height: 340rpx;
border-radius: $radius-lg;
border-radius: 24rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.4);
image { width: 100%; height: 100%; }
}
/* 2-6张:网格 */
.photo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $sp-sm;
gap: 12rpx;
}
.photo-cell {
aspect-ratio: 1;
border-radius: $radius-md;
border-radius: 16rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
image { width: 100%; height: 100%; }
}
/* 奇数张时第一张占满两列 */
.photo-span {
grid-column: span 2;
aspect-ratio: 16 / 9;
}
/* emoji占位 */
/* === 主视觉图标 === */
.card-hero {
display: flex;
justify-content: center;
align-items: center;
padding: $sp-xl $sp-xl $sp-lg;
padding: 24rpx 0 32rpx;
position: relative;
z-index: 1;
}
.hero-emoji-wrap {
width: 200rpx;
height: 200rpx;
border-radius: $radius-full;
background: rgba(232, 168, 56, 0.08);
.hero-ring {
position: absolute;
width: 220rpx; height: 220rpx;
border-radius: 50%;
border: 2rpx solid rgba(232, 168, 56, 0.15);
animation: pulse-ring 3s ease-in-out infinite;
}
@keyframes pulse-ring {
0%, 100% { transform: scale(1); opacity: 0.6; }
50% { transform: scale(1.08); opacity: 1; }
}
.hero-inner {
width: 180rpx; height: 180rpx;
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, rgba(232, 168, 56, 0.15), rgba(232, 168, 56, 0.04));
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 32rpx rgba(232, 168, 56, 0.1), inset 0 0 40rpx rgba(232, 168, 56, 0.05);
}
.hero-emoji {
font-size: 100rpx;
font-size: 88rpx;
line-height: 1;
}
.hero-icon-img {
width: 110rpx;
height: 110rpx;
}
/* 酒水清单 */
.drink-list {
padding: 0 $sp-xl;
/* === 感受徽章 === */
.feeling-badge {
display: flex;
flex-direction: column;
gap: $sp-md;
align-items: center;
justify-content: center;
gap: 12rpx;
margin: 0 auto 32rpx;
padding: 14rpx 32rpx;
background: linear-gradient(135deg, rgba(232, 168, 56, 0.12), rgba(232, 168, 56, 0.04));
border: 1.5rpx solid rgba(232, 168, 56, 0.25);
border-radius: 999rpx;
position: relative;
z-index: 1;
}
.feeling-emoji {
font-size: 32rpx;
}
.feeling-name {
font-size: 26rpx;
color: #E8A838;
font-weight: $fw-bold;
letter-spacing: 2rpx;
}
.feeling-desc {
font-size: 20rpx;
color: rgba(232, 168, 56, 0.6);
}
/* === 酒水清单 === */
.drink-list {
position: relative;
z-index: 1;
padding: 32rpx;
background: rgba(255, 255, 255, 0.02);
border-radius: 24rpx;
border: 1.5rpx solid rgba(255, 255, 255, 0.05);
margin-bottom: 32rpx;
}
.list-title-row {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
margin-bottom: 28rpx;
}
.list-dot {
width: 8rpx; height: 8rpx;
border-radius: 50%;
background: rgba(232, 168, 56, 0.5);
}
.list-title {
font-size: 22rpx;
color: rgba(232, 168, 56, 0.7);
letter-spacing: 8rpx;
font-weight: $fw-medium;
}
.drink-row {
display: flex;
align-items: baseline;
justify-content: space-between;
align-items: center;
padding: 14rpx 0;
}
.drink-name {
font-size: $fs-base;
color: $text-primary;
font-weight: $fw-medium;
.drink-left {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.drink-detail {
font-size: $fs-sm;
color: $amber;
.drink-icon-img {
width: 34rpx; height: 34rpx;
flex-shrink: 0;
}
.drink-emoji {
font-size: 28rpx;
flex-shrink: 0;
}
.drink-name {
font-size: 28rpx;
color: #FFFFFF;
font-weight: $fw-medium;
}
.drink-dots {
flex: 1;
height: 2rpx;
margin: 0 16rpx;
background: repeating-linear-gradient(90deg, rgba(255,255,255,0.15) 0, rgba(255,255,255,0.15) 4rpx, transparent 4rpx, transparent 10rpx);
}
.drink-amount {
font-size: 26rpx;
color: #E8A838;
font-family: $font-num;
font-weight: $fw-bold;
flex-shrink: 0;
margin-left: $sp-md;
}
/* 饮酒感受 */
.feeling-row {
padding: $sp-sm $sp-xl 0;
/* === 分割装饰 === */
.divider-decor {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 32rpx;
position: relative;
z-index: 1;
}
.feeling-text {
font-size: $fs-base;
color: $amber;
font-weight: $fw-medium;
letter-spacing: 2rpx;
.divider-line {
flex: 1;
height: 1.5rpx;
background: linear-gradient(90deg, transparent, rgba(232, 168, 56, 0.2), transparent);
}
.divider-icon {
font-size: 28rpx;
opacity: 0.7;
}
/* 分割线 */
.divider {
margin: $sp-lg $sp-xl;
height: 2rpx;
background: linear-gradient(90deg, transparent, rgba(232, 168, 56, 0.15), transparent);
/* === 酒言酒语 === */
.quote-section {
text-align: center;
padding: 0 16rpx;
margin-bottom: 40rpx;
position: relative;
z-index: 1;
}
/* 底部 */
.card-footer {
padding: 0 $sp-xl;
display: flex;
flex-direction: column;
gap: $sp-sm;
.quote-mark {
font-size: 48rpx;
color: rgba(232, 168, 56, 0.3);
font-family: Georgia, serif;
line-height: 1;
}
.footer-label {
font-size: $fs-xs;
color: $text-tertiary;
.quote-mark-end {
display: block;
text-align: right;
}
.quote-text {
display: block;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.85);
line-height: 1.8;
letter-spacing: 2rpx;
}
.footer-quote {
font-size: $fs-sm;
color: $text-secondary;
line-height: $lh-loose;
padding: 8rpx 0;
font-style: italic;
}
/* 品牌 + 二维码 */
/* === 品牌底栏 === */
.card-brand {
padding: $sp-lg $sp-xl $sp-xl;
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 32rpx;
border-top: 1.5rpx solid rgba(255, 255, 255, 0.05);
position: relative;
z-index: 1;
}
.brand-info {
.brand-left {
display: flex;
flex-direction: column;
gap: 4rpx;
gap: 6rpx;
}
.brand-text {
font-size: $fs-sm;
color: $text-tertiary;
.brand-name {
font-size: 26rpx;
color: rgba(232, 168, 56, 0.8);
font-weight: $fw-bold;
letter-spacing: 2rpx;
letter-spacing: 4rpx;
}
.brand-slogan {
font-size: $fs-xs;
color: $text-tertiary;
font-size: 20rpx;
color: rgba(255, 255, 255, 0.3);
letter-spacing: 2rpx;
}
.brand-qr {
width: 80rpx;
height: 80rpx;
border-radius: $radius-sm;
border: 2rpx solid var(--border-subtle, rgba(255, 255, 255, 0.10));
background: var(--bg-subtle, rgba(255, 255, 255, 0.03));
border-radius: 12rpx;
border: 1.5rpx solid rgba(232, 168, 56, 0.2);
background: rgba(232, 168, 56, 0.04);
}
</style>