- 将应用名称从"喝了么"更改为"干杯日记" - 更新所有相关文件中的品牌标识和描述信息 - 升级HaveADrink依赖包从1.0.3到1.0.4版本 - 在pages.json中新增分享个人资料页面配置 - 重构DrinkCard组件UI,增加装饰背景、酒类图标支持、饮酒感受徽章等功能 - 优化API错误处理逻辑,增加静默模式支持 - 修改白酒类别图标为 urn emoji,黄酒图标为 tea emoji - 添加分享功能按钮并优化页面返回交互体验 - 引入新的工具函数用于获取酒类图标和路径判断 - 更新主题混入、常量定义等基础配置文件 - 调整全局样式和设计令牌以匹配新品牌形象
465 lines
11 KiB
Vue
465 lines
11 KiB
Vue
<template>
|
||
<view class="card-root">
|
||
<!-- 装饰背景层 -->
|
||
<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">
|
||
<view v-if="photos.length === 1" class="photo-single">
|
||
<image :src="photos[0]" mode="aspectFill"></image>
|
||
</view>
|
||
<view v-else class="photo-grid">
|
||
<view
|
||
v-for="(photo, i) in photos"
|
||
:key="i"
|
||
class="photo-cell"
|
||
:class="{ 'photo-span': i === 0 && photos.length % 2 !== 0 }"
|
||
>
|
||
<image :src="photo" mode="aspectFill"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="card-hero">
|
||
<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">
|
||
<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">
|
||
<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 class="divider-decor">
|
||
<view class="divider-line"></view>
|
||
<text class="divider-icon">🥂</text>
|
||
<view class="divider-line"></view>
|
||
</view>
|
||
|
||
<!-- 酒言酒语 -->
|
||
<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-left">
|
||
<text class="brand-name">干杯日记</text>
|
||
<text class="brand-slogan">记录每一杯的故事</text>
|
||
</view>
|
||
<view class="brand-qr"></view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<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' }
|
||
|
||
export default {
|
||
props: {
|
||
record: { type: Object, required: true }
|
||
},
|
||
data() {
|
||
return {
|
||
quote: DRINK_QUOTES[Math.floor(Math.random() * DRINK_QUOTES.length)]
|
||
}
|
||
},
|
||
computed: {
|
||
photos() {
|
||
return (this.record && this.record.photos) || []
|
||
},
|
||
mainIcon() {
|
||
const first = (this.record.drinks || [])[0]
|
||
return first ? getCatIcon(first.category) : '🥃'
|
||
},
|
||
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 : '🥃'
|
||
},
|
||
getFoodName() {
|
||
const food = FOOD_CATEGORIES.find(f => f.id === this.record.food?.category)
|
||
if (!food) return this.record.food?.name || '美食'
|
||
return this.record.food.name ? `${food.name}·${this.record.food.name}` : food.name
|
||
},
|
||
formatAmount(drink) {
|
||
const unitName = UNIT_NAMES[drink.unit] || drink.unit
|
||
return `${drink.amount}${unitName}`
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.card-root {
|
||
position: relative;
|
||
width: 620rpx;
|
||
border-radius: 40rpx;
|
||
overflow: hidden;
|
||
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-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: 22rpx;
|
||
color: rgba(232, 168, 56, 0.8);
|
||
font-family: $font-num;
|
||
letter-spacing: 6rpx;
|
||
font-weight: $fw-medium;
|
||
}
|
||
|
||
/* === 照片区域 === */
|
||
.card-photos {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
.photo-single {
|
||
width: 100%;
|
||
height: 340rpx;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.4);
|
||
image { width: 100%; height: 100%; }
|
||
}
|
||
.photo-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 12rpx;
|
||
}
|
||
.photo-cell {
|
||
aspect-ratio: 1;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
image { width: 100%; height: 100%; }
|
||
}
|
||
.photo-span {
|
||
grid-column: span 2;
|
||
aspect-ratio: 16 / 9;
|
||
}
|
||
|
||
/* === 主视觉图标 === */
|
||
.card-hero {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 24rpx 0 32rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.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: 88rpx;
|
||
line-height: 1;
|
||
}
|
||
.hero-icon-img {
|
||
width: 110rpx;
|
||
height: 110rpx;
|
||
}
|
||
|
||
/* === 感受徽章 === */
|
||
.feeling-badge {
|
||
display: flex;
|
||
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: center;
|
||
padding: 14rpx 0;
|
||
}
|
||
.drink-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.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;
|
||
}
|
||
|
||
/* === 分割装饰 === */
|
||
.divider-decor {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
margin-bottom: 32rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.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;
|
||
}
|
||
|
||
/* === 酒言酒语 === */
|
||
.quote-section {
|
||
text-align: center;
|
||
padding: 0 16rpx;
|
||
margin-bottom: 40rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.quote-mark {
|
||
font-size: 48rpx;
|
||
color: rgba(232, 168, 56, 0.3);
|
||
font-family: Georgia, serif;
|
||
line-height: 1;
|
||
}
|
||
.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;
|
||
padding: 8rpx 0;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* === 品牌底栏 === */
|
||
.card-brand {
|
||
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-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
}
|
||
.brand-name {
|
||
font-size: 26rpx;
|
||
color: rgba(232, 168, 56, 0.8);
|
||
font-weight: $fw-bold;
|
||
letter-spacing: 4rpx;
|
||
}
|
||
.brand-slogan {
|
||
font-size: 20rpx;
|
||
color: rgba(255, 255, 255, 0.3);
|
||
letter-spacing: 2rpx;
|
||
}
|
||
.brand-qr {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 12rpx;
|
||
border: 1.5rpx solid rgba(232, 168, 56, 0.2);
|
||
background: rgba(232, 168, 56, 0.04);
|
||
}
|
||
</style>
|