|
|
@@ -1,423 +1,992 @@
|
|
|
<template>
|
|
|
- <div class="container">
|
|
|
- <!-- 中间卡片 -->
|
|
|
- <div class="add-hospital-wrapper">
|
|
|
- <div class="header-bar">
|
|
|
- <el-button @click="goBack" type="default" icon="ArrowLeft" class="back-btn">返回</el-button>
|
|
|
- <span class="title">院内膳食</span>
|
|
|
- </div>
|
|
|
- <div class="form-bar">
|
|
|
- <el-row :gutter="20" align="middle">
|
|
|
- <el-col :span="3">
|
|
|
- <span>配餐日期:</span>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-date-picker
|
|
|
- v-model="dateRange"
|
|
|
- type="daterange"
|
|
|
- range-separator="-"
|
|
|
- start-placeholder="开始日期"
|
|
|
- end-placeholder="结束日期"
|
|
|
- style="width: 100%"
|
|
|
- />
|
|
|
- </el-col>
|
|
|
- <el-col :span="3">
|
|
|
- <el-button type="primary">新增食谱</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="3">
|
|
|
- <el-button>食谱模板</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="3">
|
|
|
- <el-button type="danger">全部删除</el-button>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </div>
|
|
|
- <div class="tab-bar">
|
|
|
- <el-button type="success" plain icon="Document" class="tab-btn" style="background: #4cd964; color: #fff; border: none">食谱一</el-button>
|
|
|
- </div>
|
|
|
- <el-alert
|
|
|
- title="周食谱模板的食谱数量小于7时,食谱可循环使用"
|
|
|
- type="warning"
|
|
|
- show-icon
|
|
|
- class="mb-2 info-alert"
|
|
|
- :closable="false"
|
|
|
- style="background: transparent; border: none; color: #ff9900"
|
|
|
- />
|
|
|
-
|
|
|
- <el-table :data="tableData" class="diet-table" border>
|
|
|
- <el-table-column prop="meal" label="餐次" width="100" align="center" />
|
|
|
- <el-table-column prop="time" label="用餐时间" width="140" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <el-time-select v-model="row.time" start="06:00" step="00:30" end="22:00" placeholder="选择时间" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="menuName" label="食谱名称">
|
|
|
- <template #default="{row}">
|
|
|
- <el-select v-model="row.menuName" placeholder="请选择" style="width: 100%">
|
|
|
- <el-option label="请选择" value="" />
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="menuCategory" label="食谱分类" width="120" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <el-tag size="small" type="info" v-if="row.menuCategory">{{ row.menuCategory }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="count" label="份数" width="150" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <el-input-number v-model="row.count" :min="1" :max="99" size="small" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="weight" label="食材重量" width="120" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <span>{{ row.weight || '-' }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="calorie" label="热量" width="120" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <span>{{ row.calorie || '-' }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="amount" label="金额" width="120" align="center">
|
|
|
- <template #default="{row}">
|
|
|
- <span>{{ row.amount || '0.00' }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <div class="footer-bar">
|
|
|
- <span>费用:</span>
|
|
|
- <span class="amount">0.00元</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- 饼状图表卡片 -->
|
|
|
- <el-card>
|
|
|
- <el-tabs v-model="activeTab" class="mt-4">
|
|
|
- <el-tab-pane label="三大营养分析" name="nutrition">
|
|
|
- <div class="nutrition-analysis">
|
|
|
- <div class="nutrition-header">
|
|
|
- <div class="nutrition-target">
|
|
|
- <span>营养设定:</span>
|
|
|
- <span class="value">{{ nutritionTarget }}kcal/d</span>
|
|
|
- </div>
|
|
|
- <div class="nutrition-actual">
|
|
|
- <span>实际热量:</span>
|
|
|
- <span class="value">{{ actualCalories }}kcal/d</span>
|
|
|
- </div>
|
|
|
+ <div class="container" @keydown.enter.prevent>
|
|
|
+ <!-- 中间卡片 -->
|
|
|
+ <div class="add-hospital-wrapper">
|
|
|
+ <div class="header-bar">
|
|
|
+ <el-button @click="goBack" type="default" icon="ArrowLeft" class="back-btn">返回</el-button>
|
|
|
+ <span class="title">院内膳食</span>
|
|
|
+ </div>
|
|
|
+ <div class="form-bar">
|
|
|
+ <el-row :gutter="20" align="middle">
|
|
|
+ <el-col :span="3">
|
|
|
+ <span>配餐日期:</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-date-picker v-model="mealTimeRange" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100%" @keydown.enter.prevent />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="3">
|
|
|
+ <el-button type="primary" @click.prevent="addRecipe">新增食谱</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="3">
|
|
|
+ <el-button @click.prevent="openRecipeTemplate">食谱模板</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="3">
|
|
|
+ <el-button type="danger" @click.prevent="deleteAllRecipes">全部删除</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</div>
|
|
|
+ <div class="tab-bar">
|
|
|
+ <el-button v-for="(recipe, index) in allTableData" :key="index" :type="currentRecipe === index ? 'success' : 'info'" plain icon="Document" class="tab-btn" :class="{active: currentRecipe === index}" @click="switchRecipe(index)">
|
|
|
+ 食谱{{ index + 1 }}
|
|
|
+ <el-icon class="close-icon" @click.stop="deleteRecipe(index)" v-if="allTableData.length > 1">
|
|
|
+
|
|
|
+ <Close />
|
|
|
+ </el-icon>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-alert title="周食谱模板的食谱数量小于7时,食谱可循环使用" type="warning" show-icon class="mb-2 info-alert" :closable="false" style="background: transparent; border: none; color: #ff9900" />
|
|
|
+
|
|
|
+ <el-table :data="tableData" class="diet-table" border>
|
|
|
|
|
|
- <el-row :gutter="24">
|
|
|
- <el-col :span="14">
|
|
|
- <el-table :data="nutritionData" border class="mt-4">
|
|
|
- <el-table-column prop="name" label="三大营养素" width="180" />
|
|
|
- <el-table-column prop="weight" label="质量(g)" width="180" />
|
|
|
- <el-table-column prop="ratio" label="热量占比">
|
|
|
+ <el-table-column prop="meal" label="餐次" width="200px" align="center" />
|
|
|
+ <el-table-column prop="time" label="用餐时间" width="260px" align="center">
|
|
|
+ <template #default="{row}">
|
|
|
+ <el-time-select v-model="row.time" start="06:00" step="00:30" end="22:00" placeholder="选择时间" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="menuName" label="食谱名称" width="350px" align="center">
|
|
|
+ <template #default="{row}">
|
|
|
+ <div class="recipe-list">
|
|
|
+ <div v-if="!row.recipes?.length" class="recipe-input" @click.stop.prevent="openRecipeDialog(row)">请选择</div>
|
|
|
+ <template v-else>
|
|
|
+ <div v-for="(recipe, index) in row.recipes" :key="recipe.id" class="recipe-item">
|
|
|
+ <div class="recipe-input" @click.stop.prevent="openRecipeDialog(row, index)">
|
|
|
+ {{ recipe.name }}
|
|
|
+ </div>
|
|
|
+ <el-button type="danger" link class="delete-btn" @click.stop="removeRecipe(row, index)">
|
|
|
+ <el-icon>
|
|
|
+ <Delete />
|
|
|
+ </el-icon>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <!-- <div class="recipe-input add-more" @click.stop.prevent="openRecipeDialog(row)">请选择</div> -->
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="categoryName" label="食谱分类" width="220px" align="center">
|
|
|
+ <template #default="{row}">
|
|
|
+ <div v-for="(recipe) in row.recipes" :key="recipe.id" class="recipe-item">
|
|
|
+ <div class="recipe-input-one">
|
|
|
+ {{ recipe.categoryName }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="count" label="份数" width="220px" align="center">
|
|
|
<template #default="{row}">
|
|
|
- <span>{{ row.ratio }}%</span>
|
|
|
+ <el-input-number v-model="row.count" :min="1" :max="99" @change="updateRowData(row)" size="small" />
|
|
|
</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="reference" label="参考值">
|
|
|
+ </el-table-column>
|
|
|
+ <!-- <el-table-column prop="weight" label="食材重量" align="center">
|
|
|
<template #default="{row}">
|
|
|
- <span>{{ row.reference }}</span>
|
|
|
+ <span>{{ row.weight || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column> -->
|
|
|
+ <el-table-column label="食材重量" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.recipes && row.recipes.length">
|
|
|
+ <div v-for="(recipe) in row.recipes" :key="recipe.id">
|
|
|
+ {{ getRecipeWeight(recipe,row.count) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span v-else>-</span>
|
|
|
</template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :span="10">
|
|
|
- <div class="chart-container">
|
|
|
- <div class="chart-title">三大营养素元素质量占比</div>
|
|
|
- <div id="nutritionChart" class="chart-content"></div>
|
|
|
- <div class="chart-legend">
|
|
|
- <span class="legend-item">
|
|
|
- <span class="color-block protein"></span>
|
|
|
- 蛋白质 {{ proteinRatio }}%
|
|
|
- </span>
|
|
|
- <span class="legend-item">
|
|
|
- <span class="color-block fat"></span>
|
|
|
- 脂肪 {{ fatRatio }}%
|
|
|
- </span>
|
|
|
- <span class="legend-item">
|
|
|
- <span class="color-block carbs"></span>
|
|
|
- 碳水化合物 {{ carbsRatio }}%
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </div>
|
|
|
- </el-tab-pane>
|
|
|
-
|
|
|
- <el-tab-pane label="营养数据分析" name="data">
|
|
|
- <!-- 营养数据分析内容 -->
|
|
|
- </el-tab-pane>
|
|
|
- </el-tabs>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <!-- 底部按钮卡片 -->
|
|
|
- <div class="bottom-btn-card">
|
|
|
- <div class="btn-wrapper">
|
|
|
- <el-button type="info" plain>存为草稿</el-button>
|
|
|
- <el-button type="primary">提交</el-button>
|
|
|
- </div>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="calorie" label="热量" align="center">
|
|
|
+ <template #default="{row}">
|
|
|
+ <span>{{ row.calorie }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="amount" label="金额" align="center">
|
|
|
+ <template #default="{row}">
|
|
|
+ <span>{{ row.amount || '0.00' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="footer-bar">
|
|
|
+ <span>费用:</span>
|
|
|
+ <span class="amount">0.00元</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 饼状图表卡片 -->
|
|
|
+ <el-card>
|
|
|
+ <el-tabs v-model="activeTab" class="mt-4">
|
|
|
+ <el-tab-pane label="三大营养分析" name="nutrition">
|
|
|
+ <div class="nutrition-analysis">
|
|
|
+ <div class="nutrition-header">
|
|
|
+ <div class="nutrition-target">
|
|
|
+ <span>营养设定:</span>
|
|
|
+ <span class="value">{{ nutritionTarget }}kcal/d</span>
|
|
|
+ </div>
|
|
|
+ <div class="nutrition-actual">
|
|
|
+ <span>实际热量:</span>
|
|
|
+ <span class="value">{{ actualCalories }}kcal/d</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="14">
|
|
|
+ <el-table :data="nutritionData" border class="mt-4">
|
|
|
+ <el-table-column prop="name" label="三大营养素" width="180" />
|
|
|
+ <el-table-column prop="weight" label="质量(g)" width="180" />
|
|
|
+ <el-table-column prop="ratio" label="热量占比">
|
|
|
+ <template #default="{row}">
|
|
|
+ <span>{{ row.ratio }}%</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="reference" label="参考值">
|
|
|
+ <template #default="{row}">
|
|
|
+ <span>{{ row.reference }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="10">
|
|
|
+ <div class="chart-container">
|
|
|
+ <div class="chart-title">三大营养素元素质量占比</div>
|
|
|
+ <div id="nutritionChart" class="chart-content"></div>
|
|
|
+ <div class="chart-legend">
|
|
|
+ <span class="legend-item">
|
|
|
+ <span class="color-block protein"></span>
|
|
|
+ 蛋白质 {{ proteinRatio }}%
|
|
|
+ </span>
|
|
|
+ <span class="legend-item">
|
|
|
+ <span class="color-block fat"></span>
|
|
|
+ 脂肪 {{ fatRatio }}%
|
|
|
+ </span>
|
|
|
+ <span class="legend-item">
|
|
|
+ <span class="color-block carbs"></span>
|
|
|
+ 碳水化合物 {{ carbsRatio }}%
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="营养数据分析" name="data">
|
|
|
+ <!-- 营养数据分析内容 -->
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 底部按钮卡片 -->
|
|
|
+ <div class="bottom-btn-card">
|
|
|
+ <div class="btn-wrapper">
|
|
|
+ <el-button type="info" plain @click="saveDraft">存为草稿</el-button>
|
|
|
+ <el-button type="primary" @click="handleSubmit">提交</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+
|
|
|
+ <!-- 添加弹窗组件 -->
|
|
|
+ <CommonDialog ref="dialogRef" @confirm="handleRecipeConfirm" @cancel="handleRecipeCancel" />
|
|
|
+ <!-- 食谱模板弹窗组件 -->
|
|
|
+ <RecipeTemplate ref="recipeTemplateRef" @confirm="handleRecipeTemplateConfirm" />
|
|
|
</template>
|
|
|
<script setup lang="ts">
|
|
|
-import {ref, onMounted} from 'vue';
|
|
|
-import * as echarts from 'echarts';
|
|
|
-
|
|
|
-const dateRange = ref([]);
|
|
|
-const activeTab = ref('nutrition');
|
|
|
-const nutritionTarget = ref(897.3);
|
|
|
-const actualCalories = ref(0.0);
|
|
|
-const proteinRatio = ref(0);
|
|
|
-const fatRatio = ref(0);
|
|
|
-const carbsRatio = ref(0);
|
|
|
-
|
|
|
-const nutritionData = ref([
|
|
|
- {name: '蛋白质', weight: '', ratio: 0, reference: '10% - 15%'},
|
|
|
- {name: '脂肪', weight: '', ratio: 0, reference: '20% - 30%'},
|
|
|
- {name: '碳水化合物', weight: '', ratio: 0, reference: '50% - 65%'}
|
|
|
-]);
|
|
|
-
|
|
|
-const tableData = ref([
|
|
|
- {meal: '早餐', time: '08:00', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''},
|
|
|
- {meal: '早加', time: '08:30', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''},
|
|
|
- {meal: '中餐', time: '12:00', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''},
|
|
|
- {meal: '中加', time: '12:30', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''},
|
|
|
- {meal: '晚餐', time: '18:00', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''},
|
|
|
- {meal: '晚加', time: '18:30', menuName: '', menuCategory: '', count: 1, weight: '', calorie: '', amount: ''}
|
|
|
-]);
|
|
|
-
|
|
|
-// 初始化图表
|
|
|
-const initChart = () => {
|
|
|
- const chartDom = document.getElementById('nutritionChart');
|
|
|
- if (!chartDom) return;
|
|
|
-
|
|
|
- const myChart = echarts.init(chartDom);
|
|
|
- const option = {
|
|
|
- tooltip: {
|
|
|
- trigger: 'item'
|
|
|
- },
|
|
|
- legend: {
|
|
|
- show: false
|
|
|
- },
|
|
|
- color: ['#00D1D1', '#FFC53D', '#2F54EB'],
|
|
|
- series: [
|
|
|
- {
|
|
|
- name: '营养元素',
|
|
|
- type: 'pie',
|
|
|
- radius: ['60%', '80%'],
|
|
|
- avoidLabelOverlap: false,
|
|
|
- itemStyle: {
|
|
|
- borderRadius: 0,
|
|
|
- borderColor: '#fff',
|
|
|
- borderWidth: 2
|
|
|
- },
|
|
|
- label: {
|
|
|
- show: false
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
- disabled: true
|
|
|
- },
|
|
|
- labelLine: {
|
|
|
- show: false
|
|
|
- },
|
|
|
- data: [
|
|
|
- {value: proteinRatio.value || 1, name: '蛋白质'},
|
|
|
- {value: fatRatio.value || 1, name: '脂肪'},
|
|
|
- {value: carbsRatio.value || 1, name: '碳水化合物'}
|
|
|
- ]
|
|
|
- }
|
|
|
- ]
|
|
|
- };
|
|
|
-
|
|
|
- option && myChart.setOption(option);
|
|
|
-};
|
|
|
-
|
|
|
-const emit = defineEmits(['goBack']);
|
|
|
-// 返回上一页按钮
|
|
|
-const goBack = () => {
|
|
|
- emit('goBack');
|
|
|
-};
|
|
|
-
|
|
|
-onMounted(() => {
|
|
|
- initChart();
|
|
|
-});
|
|
|
+ import { ref, onMounted, computed, watch } from 'vue';
|
|
|
+ import * as echarts from 'echarts';
|
|
|
+ import CommonDialog from './components/CommonDialog.vue';
|
|
|
+ import RecipeTemplate from './components/RecipeTemplate.vue';
|
|
|
+ const emit = defineEmits(['goBack', 'submitHospital']);
|
|
|
+ import { MealRecipeForm } from '@/api/patients/hospitalMealRecipe/types';
|
|
|
+ import { ElMessage } from 'element-plus';
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ const props = defineProps({
|
|
|
+ patientInfo: {
|
|
|
+ type: Object,
|
|
|
+ required: true,
|
|
|
+ default: () => ({
|
|
|
+ id: '',
|
|
|
+ name: '',
|
|
|
+ age: '',
|
|
|
+ gender: ''
|
|
|
+ })
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const DRAFT_KEY = computed(() => 'addHospitalDraft_' + props.patientInfo.id);
|
|
|
+ interface Recipe {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ categoryId: number;
|
|
|
+ categoryName: string;
|
|
|
+ ingredients: string;
|
|
|
+ selected ? : boolean;
|
|
|
+ weight ? : string; // 添加可选的weight属性
|
|
|
+ calorie ? : string; // 添加可选的calorie属性
|
|
|
+ }
|
|
|
+
|
|
|
+ interface Category {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ interface TableRow {
|
|
|
+ meal: string;
|
|
|
+ time: string;
|
|
|
+ foodId: string;
|
|
|
+ menuName: string;
|
|
|
+ categoryId: string;
|
|
|
+ categoryName: string;
|
|
|
+ count: number;
|
|
|
+ weight: string;
|
|
|
+ calorie: string;
|
|
|
+ amount: string;
|
|
|
+ recipes ? : Recipe[];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 定义食谱模板数据结构
|
|
|
+ interface RecipeTemplateItem {
|
|
|
+ name: string;
|
|
|
+ categoryId: number;
|
|
|
+ categoryName: string;
|
|
|
+ meals: Array < {
|
|
|
+ meal: string;
|
|
|
+ dish: string;
|
|
|
+ weight: string;
|
|
|
+ kcal: string;
|
|
|
+ } > ;
|
|
|
+ }
|
|
|
+
|
|
|
+ const mealTimeRange = ref([new Date(), new Date()]);
|
|
|
+ const activeTab = ref('nutrition');
|
|
|
+ const nutritionTarget = ref(897.3);
|
|
|
+ const actualCalories = ref(0.0);
|
|
|
+ const proteinRatio = ref(0);
|
|
|
+ const fatRatio = ref(0);
|
|
|
+ const carbsRatio = ref(0);
|
|
|
+
|
|
|
+ const nutritionData = ref([
|
|
|
+ { name: '蛋白质', weight: '', ratio: 0, reference: '10% - 15%' },
|
|
|
+ { name: '脂肪', weight: '', ratio: 0, reference: '20% - 30%' },
|
|
|
+ { name: '碳水化合物', weight: '', ratio: 0, reference: '50% - 65%' }
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const initialTableData: TableRow[] = [
|
|
|
+ { meal: '早餐', time: '08:00', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] },
|
|
|
+ { meal: '早加', time: '08:30', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] },
|
|
|
+ { meal: '中餐', time: '12:00', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] },
|
|
|
+ { meal: '中加', time: '12:30', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] },
|
|
|
+ { meal: '晚餐', time: '18:00', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] },
|
|
|
+ { meal: '晚加', time: '18:30', foodId: '', menuName: '', categoryId: '', categoryName: '', count: 1, weight: '', calorie: '', amount: '', recipes: [] }
|
|
|
+ ];
|
|
|
+
|
|
|
+
|
|
|
+ const allTableData = ref < TableRow[][] > ([JSON.parse(JSON.stringify(initialTableData))]);
|
|
|
+ const currentRecipe = ref(0);
|
|
|
+ const tableData = computed({
|
|
|
+ get: () => allTableData.value[currentRecipe.value],
|
|
|
+ set: (val) => { allTableData.value[currentRecipe.value] = val; }
|
|
|
+ });
|
|
|
+
|
|
|
+ const recipes = ref([0]); // 食谱列表
|
|
|
+
|
|
|
+ // 添加食谱
|
|
|
+ // 新增食谱,最多7个
|
|
|
+ const addRecipe = () => {
|
|
|
+ if (allTableData.value.length >= 7) {
|
|
|
+ ElMessage.warning('最多只能添加7个食谱');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ allTableData.value.push(JSON.parse(JSON.stringify(initialTableData)));
|
|
|
+ currentRecipe.value = allTableData.value.length - 1;
|
|
|
+ };
|
|
|
+
|
|
|
+ function convertTableDataToMealRecipeList(tableData: TableRow[], tabNo: number): MealRecipeForm[] {
|
|
|
+ const result: MealRecipeForm[] = [];
|
|
|
+ tableData.forEach((row) => {
|
|
|
+ if (row.recipes && row.recipes.length > 0) {
|
|
|
+ row.recipes.forEach((recipe) => {
|
|
|
+ // 计算单个食谱的重量并乘以份数
|
|
|
+ const singleWeight = parseRecipeWeight(recipe);
|
|
|
+ result.push({
|
|
|
+ recipeNo: tabNo,
|
|
|
+ mealTime: row.meal,
|
|
|
+ eatTime: row.time,
|
|
|
+ foodId: recipe.id,
|
|
|
+ foodName: recipe.name,
|
|
|
+ foodCategoryId: recipe.categoryId,
|
|
|
+ foodWeight: singleWeight * row.count, // 这里乘以份数
|
|
|
+ calorie: recipe.calorie ? Number(recipe.calorie) : undefined,
|
|
|
+ amount: row.count,
|
|
|
+ foodCategoryName: recipe.categoryName,
|
|
|
+ price: row.amount ? Number(row.amount) : undefined,
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ function getRecipeWeight(recipe, count = 1) {
|
|
|
+ let weight = 0;
|
|
|
+ if (recipe.weight) {
|
|
|
+ weight = parseFloat(recipe.weight.replace(/[^\d.]/g, '') || '0');
|
|
|
+ } else if (recipe.ingredients) {
|
|
|
+ const matches = recipe.ingredients.match(/([\d.]+)g/g);
|
|
|
+ if (matches) {
|
|
|
+ weight = matches.reduce((sum, item) => {
|
|
|
+ const num = parseFloat(item.replace('g', ''));
|
|
|
+ return sum + (isNaN(num) ? 0 : num);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (weight * count).toFixed(4);
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseRecipeWeight(recipe) {
|
|
|
+ let weight = 0;
|
|
|
+ if (recipe.weight) {
|
|
|
+ weight = parseFloat(recipe.weight.replace(/[^\d.]/g, '') || '0');
|
|
|
+ } else if (recipe.ingredients) {
|
|
|
+ const matches = recipe.ingredients.match(/([\d.]+)g/g);
|
|
|
+ if (matches) {
|
|
|
+ weight = matches.reduce((sum, item) => {
|
|
|
+ const num = parseFloat(item.replace('g', ''));
|
|
|
+ return sum + (isNaN(num) ? 0 : num);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return weight;
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleSubmit = () => {
|
|
|
+ // 合并所有食谱tab下的明细
|
|
|
+ let mealRecipeList: MealRecipeForm[] = [];
|
|
|
+ allTableData.value.forEach((table, tabIdx) => {
|
|
|
+ mealRecipeList = mealRecipeList.concat(convertTableDataToMealRecipeList(table, tabIdx + 1));
|
|
|
+ });
|
|
|
+ const mealPlanForm = {
|
|
|
+ // ...其它表单字段
|
|
|
+ mealTimeRange: mealTimeRange.value,
|
|
|
+ mealRecipeList
|
|
|
+ };
|
|
|
+
|
|
|
+ emit('submitHospital', mealPlanForm);
|
|
|
+ emit('goBack'); // 让父组件切换回 hospitalIndex
|
|
|
+ localStorage.removeItem(DRAFT_KEY.value); // 提交成功后清除缓存
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ function saveDraft() {
|
|
|
+ const draft = {
|
|
|
+ mealTimeRange: mealTimeRange.value,
|
|
|
+ allTableData: allTableData.value,
|
|
|
+ currentRecipe: currentRecipe.value,
|
|
|
+ // 你还可以加其它需要缓存的字段
|
|
|
+ };
|
|
|
+ localStorage.setItem(DRAFT_KEY.value, JSON.stringify(draft));
|
|
|
+ ElMessage.success('操作成功');
|
|
|
+ }
|
|
|
+
|
|
|
+ function loadDraft() {
|
|
|
+ const draftStr = localStorage.getItem(DRAFT_KEY.value);
|
|
|
+ if (draftStr) {
|
|
|
+ try {
|
|
|
+ const draft = JSON.parse(draftStr);
|
|
|
+ if (draft.mealTimeRange) mealTimeRange.value = draft.mealTimeRange;
|
|
|
+ if (draft.allTableData) allTableData.value = draft.allTableData;
|
|
|
+ if (draft.currentRecipe !== undefined) currentRecipe.value = draft.currentRecipe;
|
|
|
+ } catch (e) {
|
|
|
+ // 解析失败可忽略
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 切换食谱
|
|
|
+ const switchRecipe = (index: number) => {
|
|
|
+ currentRecipe.value = index;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除食谱
|
|
|
+ const deleteRecipe = (index: number) => {
|
|
|
+ if (allTableData.value.length === 1) return;
|
|
|
+ allTableData.value.splice(index, 1);
|
|
|
+ if (currentRecipe.value >= allTableData.value.length) {
|
|
|
+ currentRecipe.value = allTableData.value.length - 1;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除所有食谱
|
|
|
+ const deleteAllRecipes = () => {
|
|
|
+
|
|
|
+ allTableData.value = [JSON.parse(JSON.stringify(initialTableData))];
|
|
|
+ currentRecipe.value = 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ const initChart = () => {
|
|
|
+ const chartDom = document.getElementById('nutritionChart');
|
|
|
+ if (!chartDom) return;
|
|
|
+
|
|
|
+ const myChart = echarts.init(chartDom);
|
|
|
+ const option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ color: ['#00D1D1', '#FFC53D', '#2F54EB'],
|
|
|
+ series: [{
|
|
|
+ name: '营养元素',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['60%', '80%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 0,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ disabled: true
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ { value: proteinRatio.value || 1, name: '蛋白质' },
|
|
|
+ { value: fatRatio.value || 1, name: '脂肪' },
|
|
|
+ { value: carbsRatio.value || 1, name: '碳水化合物' }
|
|
|
+ ]
|
|
|
+ }]
|
|
|
+ };
|
|
|
+
|
|
|
+ option && myChart.setOption(option);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 返回上一页按钮
|
|
|
+ const goBack = () => {
|
|
|
+ emit('goBack');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 食谱选择对话框
|
|
|
+
|
|
|
+ const searchKeyword = ref('');
|
|
|
+ const currentCategory = ref('');
|
|
|
+ const currentPage = ref(1);
|
|
|
+ const pageSize = ref(10);
|
|
|
+ const total = ref(1869);
|
|
|
+ const selectedRecipes = ref < Recipe[] > ([]);
|
|
|
+
|
|
|
+ const currentRow = ref < TableRow | null > (null);
|
|
|
+ const currentIndex = ref < number > (-1);
|
|
|
+ const dialogRef = ref < InstanceType < typeof CommonDialog > | null > (null);
|
|
|
+
|
|
|
+ // 处理食谱确认
|
|
|
+ const handleRecipeConfirm = (selectedRecipes: Recipe[]) => {
|
|
|
+ if (currentRow.value && selectedRecipes.length > 0) {
|
|
|
+ if (!currentRow.value.recipes) {
|
|
|
+ currentRow.value.recipes = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentIndex.value >= 0) {
|
|
|
+ // 更新已有食谱
|
|
|
+ currentRow.value.recipes[currentIndex.value] = selectedRecipes[0];
|
|
|
+ } else {
|
|
|
+ // 添加新食谱
|
|
|
+ currentRow.value.recipes.push(...selectedRecipes);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateRowData(currentRow.value);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理食谱取消
|
|
|
+ const handleRecipeCancel = () => {
|
|
|
+ };
|
|
|
+
|
|
|
+ // 移除食谱
|
|
|
+ const removeRecipe = (row: TableRow, index: number) => {
|
|
|
+ row.recipes ?.splice(index, 1);
|
|
|
+ updateRowData(row);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理食谱模板确认选择
|
|
|
+ const handleRecipeTemplateConfirm = (selectedRecipes: RecipeTemplateItem[]) => {
|
|
|
+ // 遍历选中的食谱
|
|
|
+ selectedRecipes.forEach((recipe) => {
|
|
|
+ // 遍历食谱中的每一餐
|
|
|
+ recipe.meals.forEach((mealData) => {
|
|
|
+ // 找到对应的表格行
|
|
|
+ const targetRow = tableData.value.find((row) => row.meal === mealData.meal);
|
|
|
+ if (targetRow) {
|
|
|
+ // 如果没有recipes数组,创建一个
|
|
|
+ if (!targetRow.recipes) {
|
|
|
+ targetRow.recipes = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加新的食谱项
|
|
|
+ targetRow.recipes.push({
|
|
|
+ id: Date.now(), // 临时ID
|
|
|
+ name: mealData.dish,
|
|
|
+ categoryId: mealData.categoryId,
|
|
|
+ categoryName: mealData.categoryName,
|
|
|
+ ingredients: `${mealData.weight}; ${mealData.kcal}`,
|
|
|
+ weight: mealData.weight,
|
|
|
+ calorie: mealData.kcal
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新行数据
|
|
|
+ updateRowData(targetRow);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ function sumIngredientsWeight(ingredients) {
|
|
|
+ if (!ingredients) return 0;
|
|
|
+ // 匹配所有形如 50.0000g 的数字
|
|
|
+ const matches = ingredients.match(/([\d.]+)g/g);
|
|
|
+ if (!matches) return 0;
|
|
|
+ return matches.reduce((sum, item) => {
|
|
|
+ const num = parseFloat(item.replace('g', ''));
|
|
|
+ return sum + (isNaN(num) ? 0 : num);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新行数据的逻辑优化
|
|
|
+ const updateRowData = (row: TableRow) => {
|
|
|
+
|
|
|
+ if (row.recipes ?.length) {
|
|
|
+ // 更新菜品名称
|
|
|
+ row.menuName = row.recipes.map((recipe) => recipe.name).join('、');
|
|
|
+ // row.foodId = row.recipes.map((recipe) => recipe.id).join('、');
|
|
|
+
|
|
|
+ // 计算总重量和热量
|
|
|
+ let totalCalorie = 0;
|
|
|
+
|
|
|
+ let totalWeight = 0;
|
|
|
+ row.recipes.forEach((recipe) => {
|
|
|
+ // 如果有 recipe.weight 字段优先用,否则解析 ingredients
|
|
|
+ let weight = 0;
|
|
|
+ if (recipe.weight) {
|
|
|
+ weight = parseFloat(recipe.weight.replace(/[^\d.]/g, '') || '0');
|
|
|
+ } else if (recipe.ingredients) {
|
|
|
+ weight = sumIngredientsWeight(recipe.ingredients);
|
|
|
+ }
|
|
|
+ totalWeight += weight;
|
|
|
+ });
|
|
|
+ totalWeight = totalWeight * row.count;
|
|
|
+ row.weight = totalWeight.toFixed(4) + 'g';
|
|
|
+
|
|
|
+ row.calorie = totalCalorie.toFixed(4) ;
|
|
|
+ // 金额也乘以份数
|
|
|
+ row.amount = (0.01 * row.recipes.length * row.count).toFixed(2);
|
|
|
+ } else {
|
|
|
+ // 清空数据
|
|
|
+ row.foodId = '';
|
|
|
+ row.menuName = '';
|
|
|
+ row.categoryId = '';
|
|
|
+ row.weight = '';
|
|
|
+ row.calorie = '';
|
|
|
+ row.amount = '0.00';
|
|
|
+ row.recipes = [];
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 监听患者切换自动加载草稿
|
|
|
+ watch(() => props.patientInfo.id, () => {
|
|
|
+ loadDraft();
|
|
|
+ }, { immediate: true });
|
|
|
+
|
|
|
+ // 分页相关
|
|
|
+ const handleSizeChange = (val: number) => {
|
|
|
+ pageSize.value = val;
|
|
|
+ // TODO: 重新加载数据
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCurrentChange = (val: number) => {
|
|
|
+ currentPage.value = val;
|
|
|
+ // TODO: 重新加载数据
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理 select 显示状态变化
|
|
|
+ const handleSelectVisibleChange = (visible: boolean, row: any) => {
|
|
|
+ if (visible) {}
|
|
|
+ };
|
|
|
+
|
|
|
+ // 打开食谱选择弹窗
|
|
|
+ const openRecipeDialog = (row: TableRow, index ? : number) => {
|
|
|
+ currentRow.value = row;
|
|
|
+ currentIndex.value = index ?? -1;
|
|
|
+
|
|
|
+ // 传递当前行的所有食谱给弹窗
|
|
|
+ if (row.recipes ?.length) {
|
|
|
+ dialogRef.value ?.openDialog('recipe', row.recipes);
|
|
|
+ } else {
|
|
|
+ dialogRef.value ?.openDialog('recipe');
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const recipeTemplateRef = ref < InstanceType < typeof RecipeTemplate > | null > (null);
|
|
|
+ // 打开食谱模板弹窗
|
|
|
+ const openRecipeTemplate = () => {
|
|
|
+ recipeTemplateRef.value ?.openDialog();
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ initChart();
|
|
|
+ loadDraft();
|
|
|
+ });
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
-.container {
|
|
|
- position: relative;
|
|
|
- min-height: 100vh;
|
|
|
- padding-bottom: 76px; // 底部按钮的高度 + padding
|
|
|
-
|
|
|
- .el-card {
|
|
|
- margin-top: 16px;
|
|
|
- height: 500px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.add-hospital-wrapper {
|
|
|
- background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 20px 24px 12px 24px;
|
|
|
- box-shadow: 0 2px 8px rgba(64, 158, 255, 0.04);
|
|
|
-}
|
|
|
-
|
|
|
-.header-bar {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 16px;
|
|
|
- .back-btn {
|
|
|
- margin-right: 16px;
|
|
|
- }
|
|
|
- .title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.form-bar {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 12px;
|
|
|
- .ml-2 {
|
|
|
- margin-left: 12px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.tab-bar {
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-
|
|
|
-.info-alert {
|
|
|
- margin-bottom: 8px;
|
|
|
- font-size: 14px;
|
|
|
-}
|
|
|
-
|
|
|
-.diet-table {
|
|
|
- margin-bottom: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.footer-bar {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- align-items: center;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 600;
|
|
|
- margin: 8px 0;
|
|
|
- .amount {
|
|
|
- color: #ff3a00;
|
|
|
- margin-left: 8px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.nutrition-analysis {
|
|
|
- padding: 12px;
|
|
|
-
|
|
|
- .nutrition-header {
|
|
|
- display: flex;
|
|
|
- gap: 24px;
|
|
|
- margin-bottom: 12px;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- .nutrition-target,
|
|
|
- .nutrition-actual {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
-
|
|
|
- .value {
|
|
|
- color: #409eff;
|
|
|
+ .container {
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ min-height: 90vh;
|
|
|
+ padding-bottom: 76px; // 底部按钮的高度 + padding
|
|
|
+
|
|
|
+ .el-card {
|
|
|
+ margin-top: 16px;
|
|
|
+ height: 500px;
|
|
|
+ width: 100vw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .add-hospital-wrapper {
|
|
|
+ width: 89vw;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px 24px 12px 24px;
|
|
|
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.04);
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-bar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .back-btn {
|
|
|
+ margin-right: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-bar {
|
|
|
+ width: 100vm;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .ml-2 {
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .tab-bar {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ .tab-btn {
|
|
|
+ position: relative;
|
|
|
+ padding-right: 32px;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #4cd964;
|
|
|
+ color: #fff;
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:not(.active) {
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #909399;
|
|
|
+ border-color: #dcdfe6;
|
|
|
+
|
|
|
+ .close-icon {
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-icon {
|
|
|
+ position: absolute;
|
|
|
+ right: 8px;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 2px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: rgba(0, 0, 0, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-alert {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .diet-table {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .footer-bar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 16px;
|
|
|
font-weight: 600;
|
|
|
- }
|
|
|
+ margin: 8px 0;
|
|
|
+
|
|
|
+ .amount {
|
|
|
+ color: #ff3a00;
|
|
|
+ margin-left: 8px;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.chart-container {
|
|
|
- margin-top: 16px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- text-align: center;
|
|
|
-
|
|
|
- .chart-title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 16px;
|
|
|
- color: #333;
|
|
|
- }
|
|
|
-
|
|
|
- .chart-content {
|
|
|
- height: 200px;
|
|
|
- width: 200px;
|
|
|
- }
|
|
|
-
|
|
|
- .chart-legend {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- gap: 24px;
|
|
|
- margin-top: 16px;
|
|
|
-
|
|
|
- .legend-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- color: #666;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- .color-block {
|
|
|
- width: 12px;
|
|
|
- height: 12px;
|
|
|
- border-radius: 2px;
|
|
|
-
|
|
|
- &.protein {
|
|
|
- background-color: #00d1d1;
|
|
|
+
|
|
|
+ .nutrition-analysis {
|
|
|
+ padding: 12px;
|
|
|
+
|
|
|
+ .nutrition-header {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ .nutrition-target,
|
|
|
+ .nutrition-actual {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ .value {
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container {
|
|
|
+ margin-top: 16px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ .chart-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
|
|
|
- &.fat {
|
|
|
- background-color: #ffc53d;
|
|
|
+ .chart-content {
|
|
|
+ height: 200px;
|
|
|
+ width: 200px;
|
|
|
}
|
|
|
|
|
|
- &.carbs {
|
|
|
- background-color: #2f54eb;
|
|
|
+ .chart-legend {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 24px;
|
|
|
+ margin-top: 16px;
|
|
|
+
|
|
|
+ .legend-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ color: #666;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ .color-block {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ border-radius: 2px;
|
|
|
+
|
|
|
+ &.protein {
|
|
|
+ background-color: #00d1d1;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.fat {
|
|
|
+ background-color: #ffc53d;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.carbs {
|
|
|
+ background-color: #2f54eb;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.mt-4 {
|
|
|
- margin-top: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.bottom-btn-card {
|
|
|
- position: fixed;
|
|
|
- left: 228px; // 左侧菜单的宽度
|
|
|
- right: 0;
|
|
|
- bottom: 50px;
|
|
|
- padding: 8px 16px;
|
|
|
- background: #fff;
|
|
|
- box-shadow: 0 -2px 8px rgba(64, 158, 255, 0.08);
|
|
|
- z-index: 2000;
|
|
|
-
|
|
|
- .btn-wrapper {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
-
|
|
|
- .el-button {
|
|
|
- margin-top: 30px;
|
|
|
- min-width: 120px;
|
|
|
- height: 44px;
|
|
|
- padding: 0 24px;
|
|
|
+
|
|
|
+ .mt-4 {
|
|
|
+ margin-top: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bottom-btn-card {
|
|
|
+ position: fixed;
|
|
|
+ left: 228px; // 左侧菜单的宽度
|
|
|
+ right: 0;
|
|
|
+ bottom: 50px;
|
|
|
+ padding: 8px 16px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0 -2px 8px rgba(64, 158, 255, 0.08);
|
|
|
+ z-index: 2000;
|
|
|
+
|
|
|
+ .btn-wrapper {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-top: 30px;
|
|
|
+ min-width: 120px;
|
|
|
+ height: 44px;
|
|
|
+ padding: 0 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-main) {
|
|
|
+ overflow-x: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.hidden-popper) {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .recipe-input {
|
|
|
+ width: 100%;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ padding: 0 12px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #606266;
|
|
|
+ background-color: #fff;
|
|
|
+ position: relative;
|
|
|
+ user-select: none; // 防止文本被选中
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ right: 8px;
|
|
|
+ top: 14px;
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ border: 6px solid transparent;
|
|
|
+ border-top-color: #c0c4cc;
|
|
|
+ pointer-events: none; // 防止箭头影响点击
|
|
|
+ }
|
|
|
+
|
|
|
+ &.add-more {
|
|
|
+ margin-top: 8px;
|
|
|
+ color: #909399;
|
|
|
+ border-style: dashed;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-:deep(.el-main) {
|
|
|
- overflow-x: hidden;
|
|
|
-}
|
|
|
-</style>
|
|
|
+ .recipe-list {
|
|
|
+ .recipe-input {
|
|
|
+ width: 100%;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ padding: 0 12px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #606266;
|
|
|
+ background-color: #fff;
|
|
|
+ position: relative;
|
|
|
+ user-select: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ right: 8px;
|
|
|
+ top: 14px;
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ border: 6px solid transparent;
|
|
|
+ border-top-color: #c0c4cc;
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .recipe-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .recipe-input {
|
|
|
+ flex: 1;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ padding: 0 12px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: #fff;
|
|
|
+ color: #606266;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .recipe-input-one {
|
|
|
+ flex: 1;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ padding: 0 1px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .delete-btn {
|
|
|
+ flex: none;
|
|
|
+ padding: 8px;
|
|
|
+ color: #f56c6c;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #ff4d4f;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .add-more {
|
|
|
+ margin-top: 8px;
|
|
|
+ border-style: dashed;
|
|
|
+ color: #909399;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|