Files
cg 99723497fa rename(app): 将应用名称从"干杯日记"更改为"碰盏日记"
- 替换所有文件中的品牌名称引用,包括App.vue、pages.json、uni.scss等
- 更新全局样式、导航栏标题和组件中的文案显示
- 修改分享功能中的应用名称显示
- 调整聊天页面的输入栏键盘适配逻辑
- 在多个页面组件中添加事件发射声明以支持交互功能
2026-07-21 22:20:08 +08:00

465 lines
11 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>