Files
hejiu/pages/record/record.vue
T
cg f945ccf4c7 feat(app): 初始化喝酒了么小程序基础架构
- 添加项目配置文件(.editorconfig, .gitignore)
- 创建App.vue主应用文件,包含启动时Mock数据初始化
- 添加index.html和main.js应用入口文件
- 配置manifest.json应用基本信息和权限设置
- 设置pages.json页面路由和全局样式
- 添加uni.promisify.adaptor.js适配器文件
- 创建uni.scss设计令牌系统,定义琥珀夜光主题色彩
- 新增constants.js常量定义,包含酒类分类和品牌数据
2026-07-13 22:15:16 +08:00

1263 lines
30 KiB
Vue
Raw 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="page-container record-page safe-bottom">
<!-- 顶部进度 -->
<view class="step-header">
<view class="step-back" @click="handleBack">
<text class="step-back-arrow"></text>
</view>
<view class="step-progress">
<view
v-for="i in totalSteps"
:key="i"
class="step-dot"
:class="{
'step-dot-active': i === currentStep,
'step-dot-done': i < currentStep
}"
></view>
</view>
<text class="step-label">{{ currentStep }}/{{ totalSteps }}</text>
</view>
<!-- 步骤标题 -->
<view class="step-title-area">
<text class="step-title">{{ stepTitles[currentStep - 1] }}</text>
<text class="step-subtitle">{{ stepSubtitles[currentStep - 1] }}</text>
</view>
<!-- Step 1: 选酒 -->
<view v-if="currentStep === 1" class="step-content">
<!-- 酒类选择网格 -->
<view class="category-grid">
<view
v-for="cat in drinkCategories"
:key="cat.id"
class="category-item"
:class="{ 'category-active': currentDrink.category === cat.id }"
@click="selectCategory(cat)"
>
<text class="category-emoji">{{ cat.emoji }}</text>
<text class="category-name">{{ cat.name }}</text>
</view>
</view>
<!-- 品牌选择 -->
<view v-if="currentDrink.category" class="brand-section">
<text class="section-label">品牌</text>
<view class="brand-tags">
<view
v-for="brand in brandList"
:key="brand.name"
class="tag"
:class="{ 'tag-active': currentDrink.brand === brand.name }"
@click="selectBrand(brand)"
>
<text>{{ brand.name }}</text>
</view>
</view>
<view class="input-row">
<input
class="input-field"
v-model="currentDrink.brand"
placeholder="或输入其他品牌"
placeholder-class="input-placeholder"
/>
</view>
<!-- 产品名称 -->
<text class="section-label" style="margin-top: 24rpx;">产品名称</text>
<view class="input-row">
<input
class="input-field"
v-model="currentDrink.product"
placeholder="如:飞天茅台53度"
placeholder-class="input-placeholder"
/>
</view>
</view>
</view>
<!-- Step 2: 记量 -->
<view v-if="currentStep === 2" class="step-content">
<view class="amount-section card">
<view class="amount-row">
<input
class="amount-input text-num"
type="digit"
v-model="amountStr"
placeholder="0"
placeholder-class="input-placeholder"
@input="onAmountChange"
/>
<text class="amount-unit">{{ currentDrink.unit === 'ml' ? 'ml' : currentDrink.unit }}</text>
</view>
<!-- 单位切换 -->
<view class="unit-switcher">
<view
v-for="u in units"
:key="u.id"
class="unit-btn"
:class="{ 'unit-active': currentDrink.unit === u.id }"
@click="currentDrink.unit = u.id; onAmountChange()"
>
<text>{{ u.name }}</text>
</view>
</view>
<!-- 酒精度数 -->
<view class="degree-row">
<text class="section-label">酒精度数</text>
<view class="degree-input-wrap">
<input
class="degree-input text-num"
type="digit"
v-model="degreeStr"
@input="onAmountChange"
/>
<text class="degree-symbol">°</text>
</view>
</view>
<view class="divider"></view>
<!-- 标准杯显示 -->
<view class="standard-cup-result">
<view class="standard-cup-label-row">
<text class="standard-cup-label">本次饮酒量</text>
<view class="cup-info-btn" @click="showCupInfo = true">
<text class="cup-info-icon">?</text>
</view>
</view>
<text class="standard-cup-value text-num text-amber">{{ currentCups }} 标准杯</text>
</view>
</view>
<!-- 已添加的酒水列表 -->
<view v-if="drinks.length > 0" class="added-drinks">
<text class="section-label">已添加的酒水</text>
<view v-for="(d, i) in drinks" :key="i" class="added-drink-item card">
<view class="flex-between">
<text>{{ getCatEmoji(d.category) }} {{ d.brand }} {{ d.product }}</text>
<text class="text-amber text-num">{{ d.standardCups }}</text>
</view>
</view>
</view>
<!-- 累计标准杯 -->
<view class="total-cups-bar">
<text class="text-caption">本次累计</text>
<text class="total-cups-value text-num text-amber">{{ totalCups }} 标准杯</text>
</view>
<!-- 健康提醒 -->
<view v-if="totalCups > 3" class="health-warning card">
<text class="health-warning-icon">⚠️</text>
<text class="health-warning-text">今日已饮用 {{ totalCups }} 标准杯建议每日不超过2标准杯</text>
</view>
<view v-else class="health-hint">
<text class="text-tiny">建议每日不超过2标准杯(《中国居民膳食指南》)</text>
</view>
</view>
<!-- 标准杯说明弹窗 -->
<view v-if="showCupInfo" class="cup-info-overlay" @click="showCupInfo = false">
<view class="cup-info-card card-elevated" @click.stop>
<view class="cup-info-header">
<text class="cup-info-title">什么是标准杯」?</text>
<text class="cup-info-close" @click="showCupInfo = false"></text>
</view>
<view class="cup-info-body">
<text class="cup-info-desc">标准杯是国际通用的饮酒量度量单位方便你比较不同酒类的实际酒精摄入量</text>
<view class="cup-info-formula">
<text class="cup-info-formula-text">1 标准杯 = 10g 纯酒精</text>
</view>
<text class="cup-info-subtitle">常见换算参考</text>
<view class="cup-info-table">
<view class="cup-info-row">
<text class="cup-info-cell">🥃 白酒 (52°) 1</text>
<text class="cup-info-cell cup-info-val"> 2.1 </text>
</view>
<view class="cup-info-row">
<text class="cup-info-cell">🍺 啤酒 (5°) 1</text>
<text class="cup-info-cell cup-info-val"> 2.0 </text>
</view>
<view class="cup-info-row">
<text class="cup-info-cell">🍷 红酒 (13°) 1</text>
<text class="cup-info-cell cup-info-val"> 1.5 </text>
</view>
<view class="cup-info-row">
<text class="cup-info-cell">🥂 威士忌 (40°) 1shot</text>
<text class="cup-info-cell cup-info-val"> 1.0 </text>
</view>
</view>
<view class="cup-info-tip">
<text class="cup-info-tip-text">💡 中国居民膳食指南建议成年人每日饮酒不超过 2 标准杯</text>
</view>
</view>
</view>
</view>
<!-- Step 3: 配餐 -->
<view v-if="currentStep === 3" class="step-content">
<view class="food-grid">
<view
v-for="food in foodCategories"
:key="food.id"
class="food-item"
:class="{ 'food-active': selectedFood === food.id }"
@click="selectedFood = food.id"
>
<text class="food-emoji">{{ food.emoji }}</text>
<text class="food-name">{{ food.name }}</text>
</view>
</view>
<view v-if="selectedFood && selectedFood !== 'none'" class="food-detail">
<text class="section-label">具体吃了什么</text>
<input
class="input-field"
v-model="foodName"
placeholder="如:毛肚、黄喉、肥牛..."
placeholder-class="input-placeholder"
/>
</view>
</view>
<!-- Step 4: 感受 -->
<view v-if="currentStep === 4" class="step-content">
<view class="feeling-grid">
<view
v-for="f in feelings"
:key="f.id"
class="feeling-item"
:class="{ 'feeling-active': selectedFeeling === f.id }"
@click="selectedFeeling = f.id"
>
<text class="feeling-emoji">{{ f.emoji }}</text>
<text class="feeling-name">{{ f.name }}</text>
<text class="feeling-desc">{{ f.desc }}</text>
</view>
</view>
<view class="feeling-skip">
<text class="text-caption">不想记录感受可以直接下一步</text>
</view>
</view>
<!-- Step 5: 照片 -->
<view v-if="currentStep === 5" class="step-content">
<view class="photo-grid">
<view
v-for="(photo, index) in photos"
:key="index"
class="photo-item"
>
<image :src="photo" mode="aspectFill" class="photo-img"></image>
<view class="photo-remove" @click="removePhoto(index)">
<text></text>
</view>
</view>
<view v-if="photos.length < 9" class="photo-add" @click="choosePhoto">
<text class="photo-add-icon">📷</text>
<text class="photo-add-text">拍照/相册</text>
</view>
</view>
<view class="photo-tip">
<text class="text-caption">最多选择9张照片记录今夜的精彩瞬间</text>
</view>
</view>
<!-- Step 6: 确认保存 -->
<view v-if="currentStep === 6" class="step-content">
<!-- 记录预览 -->
<view class="preview-section card">
<text class="section-label">记录预览</text>
<view class="preview-drinks">
<view v-for="(d, i) in allDrinks" :key="i" class="preview-item">
<text>{{ getCatEmoji(d.category) }} {{ d.brand }} {{ d.product }} · {{ d.amount }}{{ d.unit }}</text>
<text class="text-amber text-num">{{ d.standardCups }}</text>
</view>
</view>
<view class="divider"></view>
<view class="preview-meta">
<text v-if="selectedFood !== 'none'" class="preview-line">🍲 {{ getFoodName() }}</text>
<text v-if="selectedFeeling" class="preview-line">{{ getFeelingEmoji() }} {{ getFeelingName() }}</text>
<text class="preview-line">📍 {{ backfillDate || '今天' }}</text>
</view>
<view class="preview-total">
<text class="text-caption">总计</text>
<text class="text-amber text-num" style="font-size: 34rpx;">{{ totalCups }} 标准杯</text>
</view>
</view>
<!-- 保存后提示 -->
<view class="save-hint card">
<text class="save-hint-icon"></text>
<view class="save-hint-text">
<text class="save-hint-title">保存后可生成酒局卡片</text>
<text class="save-hint-desc">卡片可保存到相册发朋友圈或分享给好友</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-actions">
<button
v-if="currentStep > 1"
class="btn-secondary"
@click="prevStep"
>上一步</button>
<view v-else class="btn-placeholder"></view>
<button
v-if="currentStep < totalSteps"
class="btn-primary"
@click="nextStep"
:disabled="!canProceed"
>下一步</button>
<button
v-else
class="btn-cta"
@click="publishRecord"
style="flex: none; width: 55%;"
>保存并生成卡片</button>
</view>
<!-- 添加更多酒水按钮(Step 1) -->
<view v-if="currentStep === 1 && drinks.length > 0" class="add-more-bar">
<button class="btn-ghost" @click="resetCurrentDrink">+ 再添加一种酒</button>
</view>
</view>
</template>
<script>
import {
DRINK_CATEGORIES, DRINK_UNITS, BRANDS, FOOD_CATEGORIES,
FEELINGS, VISIBILITY_OPTIONS
} from '../../common/constants'
import { calcStandardCups, unitToMl, formatDate } from '../../common/utils'
import { addRecord } from '../../common/mock-data'
export default {
data() {
return {
currentStep: 1,
totalSteps: 6,
stepTitles: ['选择酒水', '记录用量', '配餐', '感受', '照片', '确认保存'],
stepSubtitles: ['喝了什么酒?', '喝了多少?', '配了什么菜?', '感觉如何?', '拍张照片吧', '保存后可生成分享卡片'],
drinkCategories: DRINK_CATEGORIES,
units: DRINK_UNITS,
foodCategories: FOOD_CATEGORIES,
feelings: FEELINGS,
visibilityOptions: VISIBILITY_OPTIONS,
// Step 1
currentDrink: { category: '', brand: '', product: '', amount: 0, unit: 'ml', degree: 0 },
drinks: [],
brandList: [],
// Step 2
amountStr: '',
degreeStr: '',
// Step 3
selectedFood: '',
foodName: '',
// Step 4
selectedFeeling: '',
// Step 5
photos: [],
// Step 6
visibility: 'friends',
// 补打卡日期
backfillDate: '',
// 标准杯说明弹窗
showCupInfo: false
}
},
computed: {
currentCups() {
const ml = unitToMl(parseFloat(this.amountStr) || 0, this.currentDrink.unit)
return calcStandardCups(ml, parseFloat(this.degreeStr) || 0)
},
totalCups() {
const addedCups = this.drinks.reduce((sum, d) => sum + d.standardCups, 0)
return Math.round((addedCups + this.currentCups) * 100) / 100
},
allDrinks() {
const all = [...this.drinks]
if (this.amountStr && this.currentDrink.category) {
const ml = unitToMl(parseFloat(this.amountStr) || 0, this.currentDrink.unit)
all.push({
...this.currentDrink,
amount: parseFloat(this.amountStr) || 0,
degree: parseFloat(this.degreeStr) || 0,
standardCups: calcStandardCups(ml, parseFloat(this.degreeStr) || 0)
})
}
return all
},
canProceed() {
switch (this.currentStep) {
case 1: return this.currentDrink.category && this.currentDrink.brand
case 2: return parseFloat(this.amountStr) > 0
case 3: return true // 配餐可选
case 4: return true // 感受可选
case 5: return true // 照片可选
case 6: return true
default: return true
}
}
},
onLoad(options) {
if (options && options.date) {
this.backfillDate = options.date
}
},
methods: {
selectCategory(cat) {
this.currentDrink.category = cat.id
this.currentDrink.degree = cat.defaultDegree
this.degreeStr = String(cat.defaultDegree)
this.brandList = BRANDS[cat.id] || []
},
selectBrand(brand) {
this.currentDrink.brand = brand.name
this.currentDrink.product = brand.product
this.currentDrink.degree = brand.degree
this.degreeStr = String(brand.degree)
},
onAmountChange() {
// 响应式计算
},
getCatEmoji(catId) {
const cat = this.drinkCategories.find(c => c.id === catId)
return cat ? cat.emoji : '🥃'
},
getFoodName() {
const food = this.foodCategories.find(f => f.id === this.selectedFood)
if (!food) return ''
return this.foodName ? `${food.name}·${this.foodName}` : food.name
},
getFeelingEmoji() {
const f = this.feelings.find(f => f.id === this.selectedFeeling)
return f ? f.emoji : ''
},
getFeelingName() {
const f = this.feelings.find(f => f.id === this.selectedFeeling)
return f ? f.name : ''
},
choosePhoto() {
uni.chooseImage({
count: 9 - this.photos.length,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.photos = [...this.photos, ...res.tempFilePaths]
}
})
},
removePhoto(index) {
this.photos.splice(index, 1)
},
resetCurrentDrink() {
// 保存当前酒水到列表
if (this.amountStr && this.currentDrink.category) {
const ml = unitToMl(parseFloat(this.amountStr) || 0, this.currentDrink.unit)
this.drinks.push({
...this.currentDrink,
amount: parseFloat(this.amountStr) || 0,
degree: parseFloat(this.degreeStr) || 0,
standardCups: calcStandardCups(ml, parseFloat(this.degreeStr) || 0)
})
}
this.currentDrink = { category: '', brand: '', product: '', amount: 0, unit: 'ml', degree: 0 }
this.amountStr = ''
this.degreeStr = ''
},
handleBack() {
if (this.currentStep > 1) {
this.prevStep()
} else {
uni.navigateBack()
}
},
prevStep() {
if (this.currentStep > 1) this.currentStep--
},
nextStep() {
// Step 1 -> 2: 保存酒水基础信息
if (this.currentStep === 1) {
if (!this.degreeStr) this.degreeStr = String(this.currentDrink.degree)
}
// Step 2 -> 3: 保存饮用量
if (this.currentStep === 2) {
const ml = unitToMl(parseFloat(this.amountStr) || 0, this.currentDrink.unit)
this.drinks.push({
...this.currentDrink,
amount: parseFloat(this.amountStr) || 0,
degree: parseFloat(this.degreeStr) || 0,
standardCups: calcStandardCups(ml, parseFloat(this.degreeStr) || 0)
})
// 重置当前输入
this.currentDrink = { ...this.currentDrink, amount: 0 }
this.amountStr = ''
}
if (this.currentStep < this.totalSteps) this.currentStep++
},
publishRecord() {
const allDrinks = this.allDrinks.length > 0 ? this.allDrinks : this.drinks
const record = {
date: this.backfillDate || formatDate(new Date(), 'YYYY-MM-DD'),
mode: 'drank',
drinks: allDrinks,
food: this.selectedFood ? { category: this.selectedFood, name: this.foodName } : null,
feeling: this.selectedFeeling || null,
photos: this.photos,
visibility: 'friends',
standardCupsTotal: this.totalCups
}
const saved = addRecord(record)
// 跳转到卡片页,传递记录ID
uni.redirectTo({
url: `/pages/card/card?recordId=${saved.id}`
})
}
}
}
</script>
<style lang="scss" scoped>
.record-page {
padding: $sp-lg;
display: flex;
flex-direction: column;
}
/* 步骤头部 */
.step-header {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-md 0;
}
.step-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: $radius-full;
background: $bg-card;
}
.step-back-arrow {
font-size: $fs-xl;
color: $text-secondary;
font-weight: $fw-bold;
}
.step-progress {
flex: 1;
display: flex;
gap: $sp-xs;
}
.step-dot {
flex: 1;
height: 6rpx;
border-radius: $radius-full;
background: $bg-elevated;
transition: all $duration-base $ease-out;
&.step-dot-done {
background: $amber;
}
&.step-dot-active {
background: $amber-light;
}
}
.step-label {
font-size: $fs-sm;
color: $text-tertiary;
font-family: $font-num;
}
.step-title-area {
margin-bottom: $sp-xl;
}
.step-title {
display: block;
font-size: $fs-2xl;
font-weight: $fw-black;
margin-bottom: $sp-xs;
}
.step-subtitle {
font-size: $fs-sm;
color: $text-secondary;
}
.step-content {
flex: 1;
}
/* Step 1: 酒类选择 */
.category-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: $sp-md;
margin-bottom: $sp-xl;
}
.category-item {
display: flex;
flex-direction: column;
align-items: center;
padding: $sp-lg $sp-sm;
background: $bg-card;
border-radius: $radius-lg;
border: 2rpx solid transparent;
transition: all $duration-fast $ease-out;
&.category-active {
border-color: $amber;
background: $amber-glow;
}
&:active {
transform: scale(0.95);
}
}
.category-emoji {
font-size: 56rpx;
margin-bottom: $sp-sm;
}
.category-name {
font-size: $fs-sm;
font-weight: $fw-medium;
}
.brand-section {
margin-top: $sp-lg;
}
.section-label {
display: block;
font-size: $fs-sm;
font-weight: $fw-medium;
color: $text-secondary;
margin-bottom: $sp-md;
}
.brand-tags {
display: flex;
flex-wrap: wrap;
gap: $sp-sm;
margin-bottom: $sp-lg;
}
.input-row {
margin-bottom: $sp-sm;
}
.input-field {
background: $bg-elevated;
border-radius: $radius-md;
padding: $sp-md $sp-lg;
color: $text-primary;
font-size: $fs-base;
border: 2rpx solid rgba(255,255,255,0.06);
}
.input-placeholder {
color: $text-tertiary;
}
/* Step 2: 记量 */
.amount-section {
margin-bottom: $sp-lg;
}
.amount-row {
display: flex;
align-items: baseline;
gap: $sp-sm;
margin-bottom: $sp-xl;
}
.amount-input {
font-size: $fs-3xl;
font-weight: $fw-black;
color: $text-primary;
flex: 1;
background: transparent;
border: none;
padding: 0;
height: 100rpx;
line-height: 100rpx;
}
.amount-unit {
font-size: $fs-lg;
color: $text-secondary;
}
.unit-switcher {
display: flex;
gap: $sp-sm;
margin-bottom: $sp-xl;
flex-wrap: wrap;
}
.unit-btn {
padding: $sp-sm $sp-lg;
background: $bg-elevated;
border-radius: $radius-full;
font-size: $fs-sm;
color: $text-secondary;
border: 2rpx solid transparent;
transition: all $duration-fast $ease-out;
&.unit-active {
background: $amber-glow;
color: $amber;
border-color: rgba(232,168,56,0.3);
}
}
.degree-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.degree-input-wrap {
display: flex;
align-items: center;
gap: $sp-xs;
}
.degree-input {
width: 120rpx;
text-align: center;
background: $bg-elevated;
border-radius: $radius-md;
padding: $sp-sm;
color: $text-primary;
font-size: $fs-lg;
}
.degree-symbol {
font-size: $fs-lg;
color: $text-secondary;
}
.standard-cup-result {
display: flex;
justify-content: space-between;
align-items: center;
}
.standard-cup-label {
font-size: $fs-base;
color: $text-secondary;
}
.standard-cup-label-row {
display: flex;
align-items: center;
gap: $sp-sm;
}
.cup-info-btn {
width: 36rpx;
height: 36rpx;
background: $bg-elevated;
border-radius: $radius-full;
display: flex;
align-items: center;
justify-content: center;
}
.cup-info-icon {
font-size: $fs-xs;
color: $text-tertiary;
font-weight: $fw-bold;
}
.standard-cup-value {
font-size: $fs-xl;
font-weight: $fw-bold;
}
/* 标准杯说明弹窗 */
.cup-info-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 200;
padding: $sp-xl;
}
.cup-info-card {
width: 100%;
max-width: 620rpx;
padding: $sp-xl;
border-radius: $radius-xl;
}
.cup-info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $sp-lg;
}
.cup-info-title {
font-size: $fs-lg;
font-weight: $fw-bold;
}
.cup-info-close {
font-size: $fs-lg;
color: $text-tertiary;
padding: $sp-xs;
}
.cup-info-body {}
.cup-info-desc {
display: block;
font-size: $fs-sm;
color: $text-secondary;
line-height: $lh-loose;
margin-bottom: $sp-lg;
}
.cup-info-formula {
background: $amber-glow;
border-radius: $radius-md;
padding: $sp-md;
text-align: center;
margin-bottom: $sp-lg;
}
.cup-info-formula-text {
font-size: $fs-md;
font-weight: $fw-bold;
color: $amber;
font-family: $font-num;
}
.cup-info-subtitle {
display: block;
font-size: $fs-sm;
font-weight: $fw-medium;
color: $text-secondary;
margin-bottom: $sp-md;
}
.cup-info-table {
margin-bottom: $sp-lg;
}
.cup-info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: $sp-sm 0;
border-bottom: 2rpx solid rgba(255, 255, 255, 0.04);
}
.cup-info-cell {
font-size: $fs-sm;
color: $text-primary;
}
.cup-info-val {
color: $amber;
font-weight: $fw-bold;
font-family: $font-num;
}
.cup-info-tip {
background: rgba(94, 198, 160, 0.1);
border-radius: $radius-md;
padding: $sp-md;
}
.cup-info-tip-text {
font-size: $fs-xs;
color: $mint;
line-height: $lh-loose;
}
.added-drinks {
margin-bottom: $sp-lg;
}
.added-drink-item {
padding: $sp-md;
margin-bottom: $sp-sm;
}
.total-cups-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: $sp-md $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
margin-bottom: $sp-md;
}
.total-cups-value {
font-size: $fs-xl;
font-weight: $fw-bold;
}
.health-warning {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-md;
background: rgba(255, 107, 107, 0.1);
border: 2rpx solid rgba(255, 107, 107, 0.2);
}
.health-warning-icon {
font-size: $fs-xl;
}
.health-warning-text {
font-size: $fs-sm;
color: $coral;
flex: 1;
}
.health-hint {
text-align: center;
padding: $sp-sm;
}
/* Step 3: 配餐 */
.food-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: $sp-md;
margin-bottom: $sp-xl;
}
.food-item {
display: flex;
flex-direction: column;
align-items: center;
padding: $sp-lg $sp-sm;
background: $bg-card;
border-radius: $radius-lg;
border: 2rpx solid transparent;
transition: all $duration-fast $ease-out;
&.food-active {
border-color: $amber;
background: $amber-glow;
}
&:active {
transform: scale(0.95);
}
}
.food-emoji {
font-size: 56rpx;
margin-bottom: $sp-sm;
}
.food-name {
font-size: $fs-sm;
font-weight: $fw-medium;
}
.food-detail {
margin-top: $sp-lg;
}
/* Step 4: 感受 */
.feeling-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: $sp-lg;
margin-bottom: $sp-xl;
}
.feeling-item {
display: flex;
flex-direction: column;
align-items: center;
padding: $sp-xl $sp-lg;
background: $bg-card;
border-radius: $radius-xl;
border: 3rpx solid transparent;
transition: all $duration-base $ease-bounce;
&.feeling-active {
border-color: $amber;
background: $amber-glow;
transform: scale(1.03);
}
&:active {
transform: scale(0.96);
}
}
.feeling-emoji {
font-size: 80rpx;
margin-bottom: $sp-md;
}
.feeling-name {
font-size: $fs-lg;
font-weight: $fw-bold;
margin-bottom: $sp-xs;
}
.feeling-desc {
font-size: $fs-xs;
color: $text-secondary;
}
.feeling-skip {
text-align: center;
padding: $sp-lg;
}
/* Step 5: 照片 */
.photo-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: $sp-md;
margin-bottom: $sp-lg;
}
.photo-item {
aspect-ratio: 1;
border-radius: $radius-lg;
overflow: hidden;
position: relative;
}
.photo-img {
width: 100%;
height: 100%;
}
.photo-remove {
position: absolute;
top: $sp-sm;
right: $sp-sm;
width: 44rpx;
height: 44rpx;
background: rgba(0,0,0,0.6);
border-radius: $radius-full;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: $fs-sm;
}
.photo-add {
aspect-ratio: 1;
background: $bg-card;
border-radius: $radius-lg;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 3rpx dashed rgba(255,255,255,0.1);
&:active {
background: $bg-card-alt;
}
}
.photo-add-icon {
font-size: $fs-3xl;
margin-bottom: $sp-sm;
opacity: 0.5;
}
.photo-add-text {
font-size: $fs-xs;
color: $text-tertiary;
}
.photo-tip {
text-align: center;
}
/* Step 6: 发布 */
.visibility-section {
margin-bottom: $sp-xl;
}
.visibility-options {
display: flex;
flex-direction: column;
gap: $sp-sm;
}
.visibility-item {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 2rpx solid transparent;
transition: all $duration-fast $ease-out;
&.visibility-active {
border-color: $amber;
background: $amber-glow;
}
}
.visibility-radio {
width: 32rpx;
height: 32rpx;
border-radius: $radius-full;
border: 3rpx solid $text-tertiary;
transition: all $duration-fast $ease-out;
&.radio-checked {
border-color: $amber;
background: $amber;
box-shadow: inset 0 0 0 6rpx $bg-base;
}
}
.visibility-name {
display: block;
font-size: $fs-base;
font-weight: $fw-medium;
}
.visibility-desc {
display: block;
font-size: $fs-xs;
color: $text-secondary;
margin-top: $sp-xs;
}
.preview-section {
margin-bottom: $sp-lg;
}
.preview-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: $sp-sm 0;
}
.preview-meta {
margin-bottom: $sp-md;
}
.preview-line {
display: block;
font-size: $fs-base;
padding: $sp-xs 0;
color: $text-secondary;
}
.preview-total {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: $sp-md;
border-top: 2rpx solid rgba(255,255,255,0.06);
}
/* 保存提示卡片 */
.save-hint {
display: flex;
align-items: flex-start;
gap: $sp-md;
margin-top: $sp-lg;
padding: $sp-lg;
background: rgba(232, 168, 56, 0.06);
border: 2rpx solid rgba(232, 168, 56, 0.15);
}
.save-hint-icon {
font-size: $fs-xl;
flex-shrink: 0;
}
.save-hint-title {
display: block;
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
margin-bottom: $sp-xs;
}
.save-hint-desc {
display: block;
font-size: $fs-sm;
color: $text-secondary;
line-height: $lh-loose;
}
/* 底部操作栏 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
gap: $sp-md;
padding: $sp-lg;
padding-bottom: calc(env(safe-area-inset-bottom) + $sp-lg);
background: linear-gradient(to top, $bg-base 80%, transparent);
z-index: 50;
.btn-secondary, .btn-primary {
flex: 1;
}
}
.btn-placeholder {
flex: 1;
}
.add-more-bar {
padding: $sp-lg 0;
text-align: center;
}
</style>