| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- <template>
- <view class="meal-container">
- <view class="nav-bar">
- <text class="nav-back" @click="goBack">‹ 返回</text>
- <text class="nav-title">饮食记录</text>
- <view class="nav-placeholder"></view>
- </view>
- <!-- 成员选择 -->
- <view class="member-selector-card">
- <text class="member-label">记录人</text>
- <picker mode="selector" :range="memberOptions" :value="selectedMemberIndex" @change="onMemberChange">
- <view class="picker-display">{{ memberOptions[selectedMemberIndex] }}</view>
- </picker>
- </view>
- <view class="section-card">
- <view class="section-title">今日饮食</view>
- <view class="meal-types">
- <view class="meal-type-item" v-for="type in mealTypes" :key="type.value"
- :class="{ 'meal-type-active': activeMealType === type.value }"
- @click="activeMealType = type.value">
- <text>{{ type.label }}</text>
- </view>
- </view>
- <view class="food-input-area">
- <view class="input-row">
- <text class="input-label">食物名称</text>
- <input class="input-field" v-model="foodName" placeholder="输入食物名称" />
- </view>
- <view class="input-row">
- <text class="input-label">份量</text>
- <input class="input-field" v-model="foodAmount" placeholder="如:1碗、200g" />
- </view>
- <view class="input-row" v-if="foodName">
- <text class="input-label">预估热量</text>
- <input class="input-field" v-model="calories" type="number" placeholder="可选" />
- </view>
- <view class="add-food-btn" @click="addFood">添加食物</view>
- </view>
- <view class="food-list">
- <view class="food-item" v-for="(food, idx) in todayFoods" :key="idx">
- <view class="food-info">
- <text class="food-name">{{ food.foodName }}</text>
- <text class="food-amount">{{ food.foodAmount }}</text>
- </view>
- <text class="food-cal" v-if="food.calories">{{ food.calories }} kcal</text>
- <text class="food-del" @click="todayFoods.splice(idx, 1)">✕</text>
- </view>
- <view class="food-empty" v-if="todayFoods.length === 0">
- <text>暂未添加食物</text>
- </view>
- </view>
- <view class="submit-btn" @click="submitMeal">
- <text>保存记录</text>
- </view>
- </view>
- <view class="section-card">
- <view class="section-title">历史记录</view>
- <view class="history-list">
- <view class="history-item" v-for="item in mealHistory" :key="item.id">
- <text class="history-date">{{ item.eatenAt ? item.eatenAt.substring(0,10) : '' }}</text>
- <text class="history-type">{{ item.mealType }}</text>
- <text class="history-food">{{ item.foodName }} {{ item.foodAmount }}</text>
- </view>
- <view class="history-empty" v-if="mealHistory.length === 0">
- <text>暂无饮食记录</text>
- </view>
- </view>
- </view>
- <view class="bottom-spacer"></view>
- </view>
- </template>
- <script>
- import config from '@/config.js'
- import { getChildren } from '@/utils/api.js'
- export default {
- data() {
- return {
- childId: null,
- selectedMemberIndex: 0,
- memberOptions: ['我自己'],
- memberIds: [],
- activeMealType: 'breakfast',
- foodName: '',
- foodAmount: '',
- calories: '',
- todayFoods: [],
- mealHistory: [],
- mealTypes: [
- { label: '早餐', value: 'breakfast' },
- { label: '午餐', value: 'lunch' },
- { label: '晚餐', value: 'dinner' },
- { label: '加餐', value: 'snack' }
- ]
- }
- },
- onLoad: function(options) {
- this.childId = options.childId ? parseInt(options.childId) : null
- this.loadChildren()
- if (this.childId) this.loadHistory()
- },
- methods: {
- loadChildren: function() {
- var self = this
- getChildren().then(function(res) {
- if (res.code === 200 && res.data && res.data.length > 0) {
- self.memberIds = res.data.map(function(c) { return c.childId })
- self.memberOptions = ['我自己'].concat(res.data.map(function(c) { return c.name || c.nickname || '孩子' }))
- }
- }).catch(function() {})
- },
- onMemberChange: function(e) {
- this.selectedMemberIndex = parseInt(e.detail.value)
- if (this.selectedMemberIndex === 0) {
- this.childId = null
- } else {
- this.childId = this.memberIds[this.selectedMemberIndex - 1]
- }
- if (this.childId) this.loadHistory()
- },
- addFood: function() {
- if (!this.foodName.trim()) {
- uni.showToast({ title: '请输入食物名称', icon: 'none' })
- return
- }
- this.todayFoods.push({
- mealType: this.activeMealType,
- foodName: this.foodName.trim(),
- foodAmount: this.foodAmount.trim() || '1份',
- calories: this.calories ? parseInt(this.calories) : null
- })
- this.foodName = ''
- this.foodAmount = ''
- this.calories = ''
- },
- submitMeal: function() {
- if (this.todayFoods.length === 0) {
- uni.showToast({ title: '请至少添加一个食物', icon: 'none' })
- return
- }
- uni.showLoading({ title: '保存中...' })
- var self = this
- var promises = this.todayFoods.map(function(food) {
- var data = {
- mealType: food.mealType,
- foodName: food.foodName,
- foodAmount: food.foodAmount,
- calories: food.calories
- }
- if (self.childId) {
- data.childId = self.childId
- data.memberId = self.childId
- }
- return healthRequest('/api/health/meal/create', data)
- })
- Promise.all(promises).then(function() {
- uni.hideLoading()
- uni.showToast({ title: '保存成功', icon: 'success' })
- this.todayFoods = []
- this.loadHistory()
- }.bind(this)).catch(function() {
- uni.hideLoading()
- uni.showToast({ title: '保存失败,请在后续版本中使用', icon: 'none' })
- })
- },
- loadHistory: function() {
- var self = this
- var params = {}
- if (this.childId) params.childId = this.childId
- healthRequest('/api/health/meal/list', params).then(function(res) {
- if (res.data) self.mealHistory = res.data
- }).catch(function() { self.mealHistory = [] })
- },
- goBack: function() { uni.navigateBack() }
- }
- }
- function healthRequest(url, data) {
- var token = uni.getStorageSync('token')
- return new Promise(function(resolve, reject) {
- uni.request({
- url: config.API_BASE_URL + url,
- method: 'POST',
- data: data,
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': token ? 'Bearer ' + token : ''
- },
- success: function(res) {
- if (res.data.code === 200) resolve(res.data)
- else reject(res.data)
- },
- fail: function(err) { reject(err) }
- })
- })
- }
- </script>
- <style scoped>
- .meal-container { min-height: 100vh; background: #f5f7fa; padding-bottom: 60rpx; }
- .nav-bar { position: sticky; top: 0; background: #fff; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 20rpx 30rpx; box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06); z-index: 100; }
- .nav-back { font-size: 32rpx; color: #5B9BD5; }
- .nav-title { font-size: 32rpx; font-weight: bold; color: #333; }
- .nav-placeholder { width: 80rpx; }
- .member-selector-card { margin: 20rpx 30rpx; background: #fff; border-radius: 20rpx; padding: 20rpx 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); display: flex; flex-direction: row; align-items: center; justify-content: space-between; }
- .member-label { font-size: 28rpx; color: #666; }
- .picker-display { font-size: 28rpx; color: #333; font-weight: 500; }
- .section-card { margin: 20rpx 30rpx; background: #fff; border-radius: 20rpx; padding: 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
- .section-title { font-size: 30rpx; font-weight: bold; color: #333; margin-bottom: 20rpx; }
- .meal-types { display: flex; flex-direction: row; margin-bottom: 20rpx; }
- .meal-type-item { padding: 10rpx 24rpx; border-radius: 20rpx; font-size: 26rpx; color: #666; background: #f0f0f0; margin-right: 16rpx; }
- .meal-type-active { background: #FFF3E0; color: #E65100; font-weight: 500; }
- .input-row { display: flex; flex-direction: row; align-items: center; margin-bottom: 16rpx; }
- .input-label { font-size: 26rpx; color: #666; width: 140rpx; flex-shrink: 0; }
- .input-field { flex: 1; background: #f5f7fa; border-radius: 12rpx; padding: 14rpx 16rpx; font-size: 28rpx; }
- .add-food-btn { background: #E3F2FD; color: #1565C0; font-size: 26rpx; text-align: center; padding: 14rpx; border-radius: 12rpx; margin-top: 8rpx; }
- .food-list { margin-top: 16rpx; }
- .food-item { display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 12rpx 0; border-bottom: 1rpx solid #f0f0f0; }
- .food-info { flex: 1; }
- .food-name { font-size: 28rpx; color: #333; }
- .food-amount { font-size: 24rpx; color: #999; margin-left: 12rpx; }
- .food-cal { font-size: 24rpx; color: #E65100; margin-right: 16rpx; }
- .food-del { font-size: 28rpx; color: #ccc; }
- .food-empty { text-align: center; padding: 30rpx; color: #999; font-size: 26rpx; }
- .submit-btn { background: linear-gradient(135deg, #4CAF50, #66BB6A); color: #fff; font-size: 32rpx; font-weight: bold; text-align: center; padding: 22rpx; border-radius: 40rpx; margin-top: 16rpx; }
- .history-list { display: flex; flex-direction: column; }
- .history-item { display: flex; flex-direction: row; align-items: center; padding: 14rpx 0; border-bottom: 1rpx solid #f5f5f5; }
- .history-date { font-size: 24rpx; color: #999; width: 160rpx; }
- .history-type { font-size: 24rpx; color: #1565C0; background: #E3F2FD; border-radius: 8rpx; padding: 2rpx 12rpx; margin-right: 12rpx; }
- .history-food { font-size: 26rpx; color: #333; flex: 1; }
- .history-empty { text-align: center; padding: 30rpx; color: #999; font-size: 26rpx; }
- .bottom-spacer { height: 60rpx; }
- </style>
|