|
@@ -1,71 +1,173 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <view class="records-page">
|
|
|
|
|
|
|
+ <view class="page">
|
|
|
|
|
+ <view class="member-bar" v-if="memberList.length > 1">
|
|
|
|
|
+ <view class="member-tab" :class="{'active': currentMemberId === (item.id || item.userId)}"
|
|
|
|
|
+ v-for="item in memberList" :key="item.id || item.userId"
|
|
|
|
|
+ @tap="switchMember(item)">
|
|
|
|
|
+ <text class="member-name">{{ item.nickname || item.roleLabel || '全部' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
|
|
|
- <!-- 日期选择 -->
|
|
|
|
|
- <view class="date-picker">
|
|
|
|
|
- <view class="date-item" :class="{'date-active': selectedDate === item}"
|
|
|
|
|
- v-for="item in dateRange" :key="item" @tap="selectDate(item)">
|
|
|
|
|
- <text class="date-num">{{ getDayNum(item) }}</text>
|
|
|
|
|
- <text class="date-week">{{ getWeekday(item) }}</text>
|
|
|
|
|
|
|
+ <!-- 周历 -->
|
|
|
|
|
+ <view class="week-strip">
|
|
|
|
|
+ <view class="week-day" v-for="(d, i) in weekDays" :key="d.date"
|
|
|
|
|
+ :class="{'week-day-today': d.today, 'week-day-active': d.date === selectedDate, 'week-day-has': d.hasRecords}">
|
|
|
|
|
+ <text class="week-day-label">{{ d.label }}</text>
|
|
|
|
|
+ <text class="week-day-num">{{ d.num }}</text>
|
|
|
|
|
+ <view class="week-dot-wrap" v-if="d.hasRecords">
|
|
|
|
|
+ <view class="week-dot"></view>
|
|
|
|
|
+ <text class="week-day-count">{{ d.count }}</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<scroll-view class="content" scroll-y>
|
|
<scroll-view class="content" scroll-y>
|
|
|
- <!-- 统计卡片 -->
|
|
|
|
|
- <view class="card stats-card">
|
|
|
|
|
|
|
+ <!-- 食材摄入提示 -->
|
|
|
|
|
+ <view class="alert-card" v-if="alertData.overConsumed.length > 0 || alertData.underConsumed.length > 0">
|
|
|
|
|
+ <view class="alert-header">
|
|
|
|
|
+ <text class="alert-icon">⚠️</text>
|
|
|
|
|
+ <text class="alert-title">饮食提示</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="alert-section" v-if="alertData.overConsumed.length > 0">
|
|
|
|
|
+ <text class="alert-label over">摄入偏多 / 需戒备</text>
|
|
|
|
|
+ <view class="alert-tags">
|
|
|
|
|
+ <view class="alert-tag tag-over" v-for="(item, i) in alertData.overConsumed" :key="i">
|
|
|
|
|
+ <text>{{ item.foodName }}</text>
|
|
|
|
|
+ <text class="tag-count">{{ item.count }}次</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="alert-hint">{{ alertData.overConsumed[0] && alertData.overConsumed[0].reason || '' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="alert-section" v-if="alertData.underConsumed.length > 0">
|
|
|
|
|
+ <text class="alert-label under">摄入偏少</text>
|
|
|
|
|
+ <view class="alert-tags">
|
|
|
|
|
+ <view class="alert-tag tag-under" v-for="(item, i) in alertData.underConsumed" :key="i">
|
|
|
|
|
+ <text>{{ item.foodName }}</text>
|
|
|
|
|
+ <text class="tag-count">{{ item.count }}次</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 本周营养汇总 -->
|
|
|
|
|
+ <view class="card" v-if="weeklySummary.recordCount > 0">
|
|
|
<view class="card-accent"></view>
|
|
<view class="card-accent"></view>
|
|
|
<view class="card-body">
|
|
<view class="card-body">
|
|
|
- <text class="stat-title">{{ selectedDate }} 饮食统计</text>
|
|
|
|
|
- <view class="stat-grid">
|
|
|
|
|
|
|
+ <text class="card-title">本周饮食汇总({{ weeklySummary.startDate }} ~ {{ weeklySummary.endDate }})</text>
|
|
|
|
|
+ <view class="stat-row">
|
|
|
<view class="stat-item">
|
|
<view class="stat-item">
|
|
|
- <text class="stat-value">{{ stats.calories || 0 }}</text>
|
|
|
|
|
|
|
+ <text class="stat-val">{{ weeklySummary.totalCalories || 0 }}</text>
|
|
|
<text class="stat-unit">kcal</text>
|
|
<text class="stat-unit">kcal</text>
|
|
|
<text class="stat-label">总热量</text>
|
|
<text class="stat-label">总热量</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="stat-item">
|
|
<view class="stat-item">
|
|
|
- <text class="stat-value">{{ stats.records || 0 }}</text>
|
|
|
|
|
- <text class="stat-unit">次</text>
|
|
|
|
|
- <text class="stat-label">记录次数</text>
|
|
|
|
|
|
|
+ <text class="stat-val">{{ weeklySummary.activeDays || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">天</text>
|
|
|
|
|
+ <text class="stat-label">打卡天数</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="stat-item">
|
|
|
|
|
+ <text class="stat-val">{{ weeklySummary.totalProtein || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">g</text>
|
|
|
|
|
+ <text class="stat-label">蛋白质</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="stat-item">
|
|
<view class="stat-item">
|
|
|
- <text class="stat-value">{{ stats.photos || 0 }}</text>
|
|
|
|
|
- <text class="stat-unit">张</text>
|
|
|
|
|
- <text class="stat-label">拍照记录</text>
|
|
|
|
|
|
|
+ <text class="stat-val">{{ weeklySummary.totalFat || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">g</text>
|
|
|
|
|
+ <text class="stat-label">脂肪</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 记录列表 -->
|
|
|
|
|
- <view class="record-list" v-if="records.length > 0">
|
|
|
|
|
- <view class="record-item" v-for="record in records" :key="record.id" @tap="goDetail(record)">
|
|
|
|
|
- <view class="record-header">
|
|
|
|
|
- <text class="record-meal">{{ record.mealType }}</text>
|
|
|
|
|
- <text class="record-time">{{ formatTime(record.createdAt) }}</text>
|
|
|
|
|
|
|
+ <!-- 高频食材 -->
|
|
|
|
|
+ <view class="card" v-if="weeklySummary.topFoods.length > 0">
|
|
|
|
|
+ <view class="card-accent" style="background: linear-gradient(180deg, #FF8C42, #FFB366);"></view>
|
|
|
|
|
+ <view class="card-body">
|
|
|
|
|
+ <text class="card-title">本周高频食材(前10)</text>
|
|
|
|
|
+ <view class="top-foods-list">
|
|
|
|
|
+ <view class="top-food-item" v-for="(f, i) in weeklySummary.topFoods" :key="f.foodName">
|
|
|
|
|
+ <text class="top-food-rank">{{ i + 1 }}</text>
|
|
|
|
|
+ <text class="top-food-name">{{ f.foodName }}</text>
|
|
|
|
|
+ <text class="top-food-count">{{ f.count }}次</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="record-body">
|
|
|
|
|
- <view class="record-photo" v-if="record.imageUrl">
|
|
|
|
|
- <image :src="record.imageUrl" mode="aspectFill" class="photo-img" />
|
|
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 日期切换 -->
|
|
|
|
|
+ <view class="date-bar">
|
|
|
|
|
+ <view class="date-prev" @tap="prevDay">‹ 前一天</view>
|
|
|
|
|
+ <text class="date-display">{{ selectedDate }}</text>
|
|
|
|
|
+ <view class="date-next" @tap="nextDay">下一天 ›</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 当日统计 -->
|
|
|
|
|
+ <view class="card daily-summary-card" v-if="dailyStats.totalCalories > 0 || dailyStats.totalProtein > 0">
|
|
|
|
|
+ <view class="card-accent"></view>
|
|
|
|
|
+ <view class="card-body">
|
|
|
|
|
+ <text class="card-title">{{ selectedDate }} 当日统计</text>
|
|
|
|
|
+ <view class="stat-row">
|
|
|
|
|
+ <view class="stat-item">
|
|
|
|
|
+ <text class="stat-val">{{ dailyStats.totalCalories || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">kcal</text>
|
|
|
|
|
+ <text class="stat-label">热量</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="record-info">
|
|
|
|
|
- <text class="record-foods">{{ getFoodsText(record.userConfirmedFoods) }}</text>
|
|
|
|
|
- <text class="record-source" v-if="record.recordMethod === 'photo'">📷 拍照识别</text>
|
|
|
|
|
- <text class="record-source" v-else>✏️ 手动记录</text>
|
|
|
|
|
|
|
+ <view class="stat-item">
|
|
|
|
|
+ <text class="stat-val">{{ dailyStats.totalProtein || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">g</text>
|
|
|
|
|
+ <text class="stat-label">蛋白</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="stat-item">
|
|
|
|
|
+ <text class="stat-val">{{ dailyStats.totalFat || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">g</text>
|
|
|
|
|
+ <text class="stat-label">脂肪</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="stat-item">
|
|
|
|
|
+ <text class="stat-val">{{ dailyStats.totalCarbs || 0 }}</text>
|
|
|
|
|
+ <text class="stat-unit">g</text>
|
|
|
|
|
+ <text class="stat-label">碳水</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <view class="empty-state" v-else>
|
|
|
|
|
- <text class="empty-icon">📝</text>
|
|
|
|
|
- <text class="empty-text">今天还没有饮食记录</text>
|
|
|
|
|
- <view class="add-btn" @tap="addRecord">
|
|
|
|
|
- <text>+ 添加记录</text>
|
|
|
|
|
|
|
+ <!-- 记录列表(按日期分组) -->
|
|
|
|
|
+ <view class="records-section" v-if="records.length > 0">
|
|
|
|
|
+ <view class="records-title">
|
|
|
|
|
+ <text class="records-label">{{ selectedDate }} 饮食记录</text>
|
|
|
|
|
+ <text class="records-count">{{ records.length }} 条</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <view class="record-card" v-for="r in records" :key="r.id" @tap="goDetail(r)">
|
|
|
|
|
+ <view class="record-card-header">
|
|
|
|
|
+ <view class="meal-badge" :class="'meal-' + r.mealType">
|
|
|
|
|
+ <text>{{ mealLabel(r.mealType) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="record-meta">
|
|
|
|
|
+ <text class="record-time">{{ formatTime(r.createdAt) }}</text>
|
|
|
|
|
+ <text class="record-backfill" v-if="r.isBackfill">补打</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="record-body">
|
|
|
|
|
+ <text class="record-foods">{{ formatFoodItems(r.foodItems) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="record-footer" v-if="r.totalCalories > 0 || r.proteinG > 0">
|
|
|
|
|
+ <text class="record-cal">🔥 {{ r.totalCalories }}kcal</text>
|
|
|
|
|
+ <text class="record-nut" v-if="r.proteinG > 0">蛋白 {{ r.proteinG }}g</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="record-remark" v-if="r.remark">{{ r.remark }}</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="empty-state" v-else-if="!loading">
|
|
|
|
|
+ <text class="empty-icon">🍽️</text>
|
|
|
|
|
+ <text class="empty-text">当天暂无饮食记录</text>
|
|
|
|
|
+ <text class="empty-hint">点击右下角「+」打卡记录</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <view class="loading-state" v-if="loading">
|
|
|
|
|
+ <text class="loading-text">加载中...</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="bottom-spacer"></view>
|
|
|
</scroll-view>
|
|
</scroll-view>
|
|
|
|
|
|
|
|
- <!-- 底部浮动按钮 -->
|
|
|
|
|
<view class="fab" @tap="addRecord">
|
|
<view class="fab" @tap="addRecord">
|
|
|
<text class="fab-icon">+</text>
|
|
<text class="fab-icon">+</text>
|
|
|
</view>
|
|
</view>
|
|
@@ -73,285 +175,332 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { getDietDailyRecords } from '@/utils/api.js'
|
|
|
|
|
-import { parseDate } from '@/utils/format.js'
|
|
|
|
|
|
|
+import { getMealList, getMealDaily, getMealWeekly, getMealAlerts, getCautionFamilyMembers, createMealRecord } from '../../utils/api.js'
|
|
|
|
|
+import { parseDate } from '../../utils/format.js'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
|
|
+ memberId: null,
|
|
|
|
|
+ currentMemberId: null,
|
|
|
|
|
+ memberList: [],
|
|
|
selectedDate: this.formatDate(new Date()),
|
|
selectedDate: this.formatDate(new Date()),
|
|
|
- dateRange: [],
|
|
|
|
|
|
|
+ weekDays: [],
|
|
|
records: [],
|
|
records: [],
|
|
|
- stats: {}
|
|
|
|
|
|
|
+ dailyStats: {},
|
|
|
|
|
+ weeklySummary: {},
|
|
|
|
|
+ alertData: { overConsumed: [], underConsumed: [] },
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ _lastLoadDate: null
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- onLoad: function() {
|
|
|
|
|
- this.initDateRange()
|
|
|
|
|
- this.loadRecords()
|
|
|
|
|
|
|
+ onLoad: function(options) {
|
|
|
|
|
+ this.memberId = options.memberId ? parseInt(options.memberId) : null
|
|
|
|
|
+ this.currentMemberId = this.memberId
|
|
|
|
|
+ this.loadMemberList()
|
|
|
|
|
+ this.initWeekDays()
|
|
|
|
|
+ this.loadWeekly()
|
|
|
|
|
+ this.loadAlerts()
|
|
|
|
|
+ this.loadDayRecords()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
- goBack: function() {
|
|
|
|
|
- uni.navigateBack()
|
|
|
|
|
|
|
+ goBack: function() { uni.navigateBack() },
|
|
|
|
|
+ mealLabel: function(t) {
|
|
|
|
|
+ var m = { breakfast: '早餐', lunch: '午餐', dinner: '晚餐', snack: '加餐' }
|
|
|
|
|
+ return m[t] || t || '饮食'
|
|
|
|
|
+ },
|
|
|
|
|
+ switchMember: function(item) {
|
|
|
|
|
+ this.currentMemberId = item.id || item.userId
|
|
|
|
|
+ this.loadWeekly()
|
|
|
|
|
+ this.loadAlerts()
|
|
|
|
|
+ this.loadDayRecords()
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadMemberList() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ var res = await getCautionFamilyMembers()
|
|
|
|
|
+ if (res && res.code === 200 && res.data && res.data.length > 0) {
|
|
|
|
|
+ var allItem = { id: -1, userId: -1, nickname: '全部', roleLabel: '全部' }
|
|
|
|
|
+ this.memberList = [allItem].concat(res.data)
|
|
|
|
|
+ if (!this.currentMemberId) this.currentMemberId = -1
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch(e) {}
|
|
|
},
|
|
},
|
|
|
- initDateRange: function() {
|
|
|
|
|
|
|
+ initWeekDays: function() {
|
|
|
var self = this
|
|
var self = this
|
|
|
var today = new Date()
|
|
var today = new Date()
|
|
|
- for (var i = 6; i >= 0; i--) {
|
|
|
|
|
- var d = new Date(today)
|
|
|
|
|
- d.setDate(d.getDate() - i)
|
|
|
|
|
- self.dateRange.push(self.formatDate(d))
|
|
|
|
|
|
|
+ var dayOfWeek = today.getDay() || 7 // 1=周一 7=周日
|
|
|
|
|
+ var monday = new Date(today)
|
|
|
|
|
+ monday.setDate(today.getDate() - (dayOfWeek - 1))
|
|
|
|
|
+ var days = []
|
|
|
|
|
+ var labels = ['一', '二', '三', '四', '五', '六', '日']
|
|
|
|
|
+ for (var i = 0; i < 7; i++) {
|
|
|
|
|
+ var d = new Date(monday)
|
|
|
|
|
+ d.setDate(monday.getDate() + i)
|
|
|
|
|
+ days.push({
|
|
|
|
|
+ date: self.formatDate(d),
|
|
|
|
|
+ label: '周' + labels[i],
|
|
|
|
|
+ num: d.getDate(),
|
|
|
|
|
+ today: self.formatDate(today) === self.formatDate(d),
|
|
|
|
|
+ hasRecords: false,
|
|
|
|
|
+ count: 0
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
- this.selectedDate = self.dateRange[6]
|
|
|
|
|
- },
|
|
|
|
|
- selectDate: function(date) {
|
|
|
|
|
- this.selectedDate = date
|
|
|
|
|
- this.loadRecords()
|
|
|
|
|
|
|
+ this.weekDays = days
|
|
|
},
|
|
},
|
|
|
- getDayNum: function(dateStr) {
|
|
|
|
|
- var d = parseDate(dateStr)
|
|
|
|
|
- return d ? d.getDate() : ''
|
|
|
|
|
|
|
+ updateWeekDots: function(records) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var countByDate = {}
|
|
|
|
|
+ for (var i = 0; i < records.length; i++) {
|
|
|
|
|
+ var r = records[i]
|
|
|
|
|
+ var d = r.recordDate || self.formatDate(r.createdAt)
|
|
|
|
|
+ countByDate[d] = (countByDate[d] || 0) + 1
|
|
|
|
|
+ }
|
|
|
|
|
+ for (var j = 0; j < self.weekDays.length; j++) {
|
|
|
|
|
+ var wd = self.weekDays[j]
|
|
|
|
|
+ wd.hasRecords = (countByDate[wd.date] || 0) > 0
|
|
|
|
|
+ wd.count = countByDate[wd.date] || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ self.$forceUpdate()
|
|
|
},
|
|
},
|
|
|
- getWeekday: function(dateStr) {
|
|
|
|
|
- var days = ['日', '一', '二', '三', '四', '五', '六']
|
|
|
|
|
- var d = parseDate(dateStr)
|
|
|
|
|
- return d ? '周' + days[d.getDay()] : ''
|
|
|
|
|
|
|
+ loadWeekly: function() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var mid = self.currentMemberId > 0 ? self.currentMemberId : null
|
|
|
|
|
+ getMealWeekly({ memberId: mid, startDate: self.weekDays[0].date, endDate: self.weekDays[6].date })
|
|
|
|
|
+ .then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ self.weeklySummary = res.data
|
|
|
|
|
+ self.updateWeekDots([])
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
},
|
|
},
|
|
|
- loadRecords: function() {
|
|
|
|
|
|
|
+ loadAlerts: function() {
|
|
|
var self = this
|
|
var self = this
|
|
|
- var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId')
|
|
|
|
|
- if (!memberId) return
|
|
|
|
|
-
|
|
|
|
|
- getDietDailyRecords({ memberId: memberId, date: this.selectedDate }).then(function(res) {
|
|
|
|
|
- if (res && res.records) {
|
|
|
|
|
- self.records = res.records
|
|
|
|
|
- self.stats = res
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ var mid = self.currentMemberId > 0 ? self.currentMemberId : null
|
|
|
|
|
+ getMealAlerts({ memberId: mid, startDate: self.weekDays[0].date, endDate: self.weekDays[6].date })
|
|
|
|
|
+ .then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) self.alertData = res.data
|
|
|
|
|
+ }).catch(function() {})
|
|
|
},
|
|
},
|
|
|
- formatTime: function(timeStr) {
|
|
|
|
|
- if (!timeStr) return ''
|
|
|
|
|
- var d = parseDate(timeStr)
|
|
|
|
|
- if (!d) return ''
|
|
|
|
|
- var h = d.getHours().toString().padStart(2, '0')
|
|
|
|
|
- var m = d.getMinutes().toString().padStart(2, '0')
|
|
|
|
|
- return h + ':' + m
|
|
|
|
|
|
|
+ loadDayRecords: function() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ self.loading = true
|
|
|
|
|
+ var mid = self.currentMemberId > 0 ? self.currentMemberId : null
|
|
|
|
|
+ getMealDaily({ memberId: mid, date: self.selectedDate })
|
|
|
|
|
+ .then(function(res) {
|
|
|
|
|
+ self.loading = false
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ self.records = (res.data || [])
|
|
|
|
|
+ var stats = self._calcDailyStats(self.records)
|
|
|
|
|
+ self.dailyStats = stats
|
|
|
|
|
+ } else {
|
|
|
|
|
+ self.records = []
|
|
|
|
|
+ self.dailyStats = {}
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(function() { self.loading = false })
|
|
|
},
|
|
},
|
|
|
- getFoodsText: function(foodsJson) {
|
|
|
|
|
- try {
|
|
|
|
|
- var foods = JSON.parse(foodsJson || '[]')
|
|
|
|
|
- return foods.map(function(f) { return f.name }).join('、')
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- return ''
|
|
|
|
|
|
|
+ _calcDailyStats: function(records) {
|
|
|
|
|
+ var s = { totalCalories: 0, totalProtein: 0, totalFat: 0, totalCarbs: 0, totalFiber: 0 }
|
|
|
|
|
+ for (var i = 0; i < records.length; i++) {
|
|
|
|
|
+ var r = records[i]
|
|
|
|
|
+ if (r.totalCalories != null) s.totalCalories += r.totalCalories
|
|
|
|
|
+ if (r.proteinG != null) s.totalProtein += r.proteinG
|
|
|
|
|
+ if (r.fatG != null) s.totalFat += r.fatG
|
|
|
|
|
+ if (r.carbsG != null) s.totalCarbs += r.carbsG
|
|
|
|
|
+ if (r.fiberG != null) s.totalFiber += r.fiberG
|
|
|
}
|
|
}
|
|
|
|
|
+ return s
|
|
|
|
|
+ },
|
|
|
|
|
+ prevDay: function() {
|
|
|
|
|
+ var d = parseDate(this.selectedDate)
|
|
|
|
|
+ if (!d) return
|
|
|
|
|
+ d.setDate(d.getDate() - 1)
|
|
|
|
|
+ this.selectedDate = this.formatDate(d)
|
|
|
|
|
+ this.loadDayRecords()
|
|
|
|
|
+ },
|
|
|
|
|
+ nextDay: function() {
|
|
|
|
|
+ var d = parseDate(this.selectedDate)
|
|
|
|
|
+ if (!d) return
|
|
|
|
|
+ d.setDate(d.getDate() + 1)
|
|
|
|
|
+ this.selectedDate = this.formatDate(d)
|
|
|
|
|
+ this.loadDayRecords()
|
|
|
},
|
|
},
|
|
|
addRecord: function() {
|
|
addRecord: function() {
|
|
|
- uni.navigateTo({ url: 'pages/diet/record-detail' })
|
|
|
|
|
|
|
+ var url = '/pages/growth/diet-checkin'
|
|
|
|
|
+ if (this.selectedDate) url += '?date=' + this.selectedDate
|
|
|
|
|
+ uni.navigateTo({ url: url })
|
|
|
},
|
|
},
|
|
|
goDetail: function(record) {
|
|
goDetail: function(record) {
|
|
|
- var id = record.id
|
|
|
|
|
- uni.navigateTo({ url: 'pages/diet/record-detail?id=' + id })
|
|
|
|
|
|
|
+ // 饮食记录详情暂时直接展示,不跳转
|
|
|
|
|
+ var foods = this.formatFoodItems(record.foodItems)
|
|
|
|
|
+ var msg = (this.mealLabel(record.mealType) || '') + ' · ' + this.formatTime(record.createdAt)
|
|
|
|
|
+ if (foods) msg += '\n' + foods
|
|
|
|
|
+ if (record.remark) msg += '\n' + record.remark
|
|
|
|
|
+ if (record.isBackfill) msg += '\n(补打,无能量值)'
|
|
|
|
|
+ uni.showModal({ title: msg, content: '', showCancel: false })
|
|
|
|
|
+ },
|
|
|
|
|
+ formatFoodItems: function(txt) {
|
|
|
|
|
+ if (!txt) return ''
|
|
|
|
|
+ var clean = txt.replace(/^\[|\]$/g, '').replace(/"/g, '')
|
|
|
|
|
+ // 尝试解析 JSON
|
|
|
|
|
+ try {
|
|
|
|
|
+ var arr = JSON.parse(txt)
|
|
|
|
|
+ if (Array.isArray(arr)) {
|
|
|
|
|
+ return arr.map(function(f) { return f.name || f }).join('、')
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch(e) {}
|
|
|
|
|
+ // 纯文本:去掉数量单位
|
|
|
|
|
+ return clean.replace(/\d+[克斤两碗份盘个]/g, '').replace(/[、,,;;]/g, '、').replace(/\s+/g, ' ').trim()
|
|
|
},
|
|
},
|
|
|
- formatDate: function(date) {
|
|
|
|
|
- var d = parseDate(date)
|
|
|
|
|
|
|
+ formatTime: function(ts) {
|
|
|
|
|
+ if (!ts) return ''
|
|
|
|
|
+ var d = parseDate(ts)
|
|
|
if (!d) return ''
|
|
if (!d) return ''
|
|
|
- var m = (d.getMonth() + 1).toString().padStart(2, '0')
|
|
|
|
|
- var day = d.getDate().toString().padStart(2, '0')
|
|
|
|
|
- return d.getFullYear() + '-' + m + '-' + day
|
|
|
|
|
|
|
+ return (d.getHours() || 0).toString().padStart(2, '0') + ':' + (d.getMinutes() || 0).toString().padStart(2, '0')
|
|
|
|
|
+ },
|
|
|
|
|
+ formatDate: function(d) {
|
|
|
|
|
+ if (!d) return ''
|
|
|
|
|
+ var date = typeof d === 'string' ? parseDate(d) : d
|
|
|
|
|
+ if (!date) return ''
|
|
|
|
|
+ var m = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
|
|
|
+ var day = date.getDate().toString().padStart(2, '0')
|
|
|
|
|
+ return date.getFullYear() + '-' + m + '-' + day
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
-.records-page {
|
|
|
|
|
- min-height: 100vh;
|
|
|
|
|
- background-color: #F5F7FA;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
-}
|
|
|
|
|
-.nav-placeholder {
|
|
|
|
|
- width: 80rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.date-picker {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- background-color: #FFFFFF;
|
|
|
|
|
- padding: 20rpx 0;
|
|
|
|
|
- overflow-x: auto;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
-}
|
|
|
|
|
-.date-item {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- width: 120rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 16rpx 0;
|
|
|
|
|
- margin: 0 10rpx;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.date-active {
|
|
|
|
|
- background-color: #FF8C42;
|
|
|
|
|
- color: #FFFFFF;
|
|
|
|
|
-}
|
|
|
|
|
-.date-num {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-.date-week {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- opacity: 0.8;
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-top: 4rpx;
|
|
|
|
|
|
|
+.page { min-height: 100vh; background: #FFF7ED; display: flex; flex-direction: column; }
|
|
|
|
|
+
|
|
|
|
|
+/* 成员切换栏 */
|
|
|
|
|
+.member-bar {
|
|
|
|
|
+ display: flex; overflow-x: auto; padding: 16rpx 30rpx 8rpx; background: #fff;
|
|
|
|
|
+ border-bottom: 1rpx solid #f0f0f0; gap: 16rpx; -webkit-overflow-scrolling: touch;
|
|
|
}
|
|
}
|
|
|
-.content {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- padding: 30rpx;
|
|
|
|
|
- padding-bottom: 160rpx;
|
|
|
|
|
|
|
+.member-tab {
|
|
|
|
|
+ flex-shrink: 0; padding: 8rpx 20rpx; border-radius: 20rpx; background: #f5f5f5; color: #666;
|
|
|
|
|
+ font-size: 26rpx; font-weight: 500;
|
|
|
}
|
|
}
|
|
|
|
|
+.member-tab.active { background: linear-gradient(135deg, #FF8C42, #FFB366); color: #fff; }
|
|
|
|
|
+
|
|
|
|
|
+/* 周历条 */
|
|
|
|
|
+.week-strip {
|
|
|
|
|
+ display: flex; align-items: center; justify-content: space-around;
|
|
|
|
|
+ background: #fff; padding: 12rpx 20rpx 16rpx; border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+.week-day {
|
|
|
|
|
+ display: flex; flex-direction: column; align-items: center;
|
|
|
|
|
+ padding: 8rpx 12rpx; border-radius: 16rpx; min-width: 68rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.week-day-today .week-day-label { color: #FF8C42; font-weight: 700; }
|
|
|
|
|
+.week-day-active { background: linear-gradient(135deg, #FF8C42, #FFB366); }
|
|
|
|
|
+.week-day-active .week-day-label,
|
|
|
|
|
+.week-day-active .week-day-num { color: #fff; }
|
|
|
|
|
+.week-day-has .week-dot-wrap { margin-top: 6rpx; }
|
|
|
|
|
+.week-day-label { font-size: 20rpx; color: #999; }
|
|
|
|
|
+.week-day-num { font-size: 28rpx; font-weight: 600; color: #333; }
|
|
|
|
|
+.week-dot-wrap { display: flex; align-items: center; gap: 6rpx; }
|
|
|
|
|
+.week-dot { width: 10rpx; height: 10rpx; border-radius: 50%; background: #FF8C42; }
|
|
|
|
|
+.week-day-count { font-size: 18rpx; color: #C2410C; }
|
|
|
|
|
+
|
|
|
|
|
+/* 内容区 */
|
|
|
|
|
+.content { flex: 1; padding: 24rpx 24rpx 180rpx; overflow-y: auto; }
|
|
|
|
|
+
|
|
|
|
|
+/* 通用卡片 */
|
|
|
.card {
|
|
.card {
|
|
|
- display: flex;
|
|
|
|
|
- background-color: #FFFFFF;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- margin-bottom: 30rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-}
|
|
|
|
|
-.card-accent {
|
|
|
|
|
- width: 8rpx;
|
|
|
|
|
- background-color: #FF8C42;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.card-body {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- padding: 30rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-title {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-grid {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-around;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-item {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-value {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #FF8C42;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-unit {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.stat-label {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #666;
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-top: 4rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-list {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-item {
|
|
|
|
|
- background-color: #FFFFFF;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 24rpx 30rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
|
|
|
|
-}
|
|
|
|
|
-.record-header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- margin-bottom: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-meal {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- padding: 4rpx 16rpx;
|
|
|
|
|
- background-color: #FFF5EB;
|
|
|
|
|
- color: #FF8C42;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-time {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.record-body {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-photo {
|
|
|
|
|
- width: 120rpx;
|
|
|
|
|
- height: 120rpx;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-img {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
-}
|
|
|
|
|
-.record-info {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- gap: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.record-foods {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- line-height: 1.5;
|
|
|
|
|
-}
|
|
|
|
|
-.record-source {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.empty-state {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 80rpx 40rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.empty-icon {
|
|
|
|
|
- font-size: 100rpx;
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.empty-text {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-bottom: 40rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.add-btn {
|
|
|
|
|
- background-color: #FF8C42;
|
|
|
|
|
- color: #FFFFFF;
|
|
|
|
|
- padding: 24rpx 60rpx;
|
|
|
|
|
- border-radius: 30rpx;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
|
|
+ display: flex; background: #fff; border-radius: 20rpx; margin-bottom: 24rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05); overflow: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
+.card-accent { width: 8rpx; background: linear-gradient(180deg, #FF8C42, #FFB366); flex-shrink: 0; }
|
|
|
|
|
+.card-body { flex: 1; padding: 24rpx 28rpx; }
|
|
|
|
|
+.card-title { font-size: 28rpx; font-weight: 600; color: #333; margin-bottom: 16rpx; display: block; }
|
|
|
|
|
+
|
|
|
|
|
+/* 统计行 */
|
|
|
|
|
+.stat-row { display: flex; justify-content: space-around; }
|
|
|
|
|
+.stat-item { text-align: center; }
|
|
|
|
|
+.stat-val { font-size: 36rpx; font-weight: 700; color: #FF8C42; display: block; }
|
|
|
|
|
+.stat-unit { font-size: 20rpx; color: #999; }
|
|
|
|
|
+.stat-label { font-size: 22rpx; color: #666; display: block; margin-top: 4rpx; }
|
|
|
|
|
+
|
|
|
|
|
+/* 食材提示 */
|
|
|
|
|
+.alert-card {
|
|
|
|
|
+ background: #fff; border-radius: 20rpx; margin-bottom: 24rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05); overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+.alert-header { display: flex; align-items: center; gap: 12rpx; padding: 24rpx 28rpx 16rpx; border-bottom: 1rpx solid #FFF3E0; }
|
|
|
|
|
+.alert-icon { font-size: 36rpx; }
|
|
|
|
|
+.alert-title { font-size: 28rpx; font-weight: 600; color: #333; }
|
|
|
|
|
+.alert-section { padding: 16rpx 28rpx; }
|
|
|
|
|
+.alert-label { font-size: 24rpx; font-weight: 600; display: block; margin-bottom: 12rpx; }
|
|
|
|
|
+.alert-label.over { color: #C62828; }
|
|
|
|
|
+.alert-label.under { color: #1565C0; }
|
|
|
|
|
+.alert-tags { display: flex; flex-wrap: wrap; gap: 12rpx; }
|
|
|
|
|
+.alert-tag { display: flex; align-items: center; gap: 6rpx; padding: 8rpx 16rpx; border-radius: 20rpx; font-size: 24rpx; }
|
|
|
|
|
+.tag-over { background: #FFEBEE; color: #C62828; }
|
|
|
|
|
+.tag-under { background: #E3F2FD; color: #1565C0; }
|
|
|
|
|
+.tag-count { font-size: 20rpx; opacity: 0.8; }
|
|
|
|
|
+.alert-hint { font-size: 22rpx; color: #888; margin-top: 8rpx; display: block; }
|
|
|
|
|
+
|
|
|
|
|
+/* 高频食材 */
|
|
|
|
|
+.top-foods-list { display: flex; flex-direction: column; gap: 12rpx; }
|
|
|
|
|
+.top-food-item { display: flex; align-items: center; gap: 16rpx; }
|
|
|
|
|
+.top-food-rank {
|
|
|
|
|
+ width: 36rpx; height: 36rpx; border-radius: 50%; background: #FFF1E6; color: #FF8C42;
|
|
|
|
|
+ font-size: 22rpx; font-weight: 700; display: flex; align-items: center; justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+.top-food-name { flex: 1; font-size: 26rpx; color: #333; }
|
|
|
|
|
+.top-food-count { font-size: 24rpx; color: #999; }
|
|
|
|
|
+
|
|
|
|
|
+/* 日期切换栏 */
|
|
|
|
|
+.date-bar {
|
|
|
|
|
+ display: flex; align-items: center; justify-content: space-between;
|
|
|
|
|
+ background: #fff; border-radius: 16rpx; padding: 16rpx 24rpx; margin-bottom: 20rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
+}
|
|
|
|
|
+.date-prev, .date-next { font-size: 26rpx; color: #FF8C42; font-weight: 600; padding: 8rpx 16rpx; }
|
|
|
|
|
+.date-display { font-size: 28rpx; font-weight: 600; color: #333; }
|
|
|
|
|
+
|
|
|
|
|
+/* 记录卡片 */
|
|
|
|
|
+.records-title { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx; }
|
|
|
|
|
+.records-label { font-size: 28rpx; font-weight: 600; color: #333; }
|
|
|
|
|
+.records-count { font-size: 24rpx; color: #999; }
|
|
|
|
|
+
|
|
|
|
|
+.record-card {
|
|
|
|
|
+ background: #fff; border-radius: 16rpx; padding: 20rpx 24rpx; margin-bottom: 16rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(255,140,66,0.06); border-left: 6rpx solid #FF8C42;
|
|
|
|
|
+}
|
|
|
|
|
+.record-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10rpx; }
|
|
|
|
|
+.meal-badge { padding: 6rpx 18rpx; border-radius: 20rpx; font-size: 24rpx; font-weight: 600; }
|
|
|
|
|
+.meal-breakfast { background: #FFF3E0; color: #E65100; }
|
|
|
|
|
+.meal-lunch { background: #FFF8E1; color: #F57C00; }
|
|
|
|
|
+.meal-dinner { background: #F3E5F5; color: #7B1FA2; }
|
|
|
|
|
+.meal-snack { background: #E8F5E9; color: #2E7D32; }
|
|
|
|
|
+.record-meta { display: flex; align-items: center; gap: 12rpx; }
|
|
|
|
|
+.record-time { font-size: 24rpx; color: #999; }
|
|
|
|
|
+.record-backfill { font-size: 20rpx; padding: 2rpx 10rpx; background: #FFF3E0; color: #E65100; border-radius: 8rpx; }
|
|
|
|
|
+.record-body { margin-bottom: 8rpx; }
|
|
|
|
|
+.record-foods { font-size: 26rpx; color: #333; line-height: 1.5; }
|
|
|
|
|
+.record-footer { display: flex; gap: 20rpx; margin-bottom: 6rpx; }
|
|
|
|
|
+.record-cal { font-size: 22rpx; color: #FF8C42; font-weight: 600; }
|
|
|
|
|
+.record-nut { font-size: 22rpx; color: #999; }
|
|
|
|
|
+.record-remark { font-size: 24rpx; color: #888; font-style: italic; }
|
|
|
|
|
+
|
|
|
|
|
+/* 空态 */
|
|
|
|
|
+.empty-state { text-align: center; padding: 80rpx 40rpx; }
|
|
|
|
|
+.empty-icon { font-size: 80rpx; display: block; margin-bottom: 20rpx; }
|
|
|
|
|
+.empty-text { font-size: 28rpx; color: #333; font-weight: 500; display: block; margin-bottom: 8rpx; }
|
|
|
|
|
+.empty-hint { font-size: 24rpx; color: #999; display: block; }
|
|
|
|
|
+.loading-state { text-align: center; padding: 80rpx 0; }
|
|
|
|
|
+.loading-text { font-size: 26rpx; color: #999; }
|
|
|
|
|
+.bottom-spacer { height: 40rpx; }
|
|
|
|
|
+
|
|
|
|
|
+/* 浮动按钮 */
|
|
|
.fab {
|
|
.fab {
|
|
|
- position: fixed;
|
|
|
|
|
- right: 40rpx;
|
|
|
|
|
- bottom: 60rpx;
|
|
|
|
|
- width: 100rpx;
|
|
|
|
|
- height: 100rpx;
|
|
|
|
|
- background-color: #FF8C42;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- box-shadow: 0 4rpx 20rpx rgba(255, 140, 66, 0.4);
|
|
|
|
|
-}
|
|
|
|
|
-.fab-icon {
|
|
|
|
|
- font-size: 48rpx;
|
|
|
|
|
- color: #FFFFFF;
|
|
|
|
|
- font-weight: 300;
|
|
|
|
|
|
|
+ position: fixed; right: 40rpx; bottom: 60rpx; width: 100rpx; height: 100rpx;
|
|
|
|
|
+ background: linear-gradient(135deg, #FF8C42, #FFB366); border-radius: 50%;
|
|
|
|
|
+ display: flex; align-items: center; justify-content: center;
|
|
|
|
|
+ box-shadow: 0 4rpx 20rpx rgba(255,140,66,0.4);
|
|
|
}
|
|
}
|
|
|
-</style>
|
|
|
|
|
|
|
+.fab-icon { font-size: 48rpx; color: #fff; font-weight: 300; }
|
|
|
|
|
+</style>
|