- 添加项目配置文件(.editorconfig, .gitignore) - 创建App.vue主应用文件,包含启动时Mock数据初始化 - 添加index.html和main.js应用入口文件 - 配置manifest.json应用基本信息和权限设置 - 设置pages.json页面路由和全局样式 - 添加uni.promisify.adaptor.js适配器文件 - 创建uni.scss设计令牌系统,定义琥珀夜光主题色彩 - 新增constants.js常量定义,包含酒类分类和品牌数据
1263 lines
30 KiB
Vue
1263 lines
30 KiB
Vue
<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>
|