feat:增加后端接口

This commit is contained in:
2025-06-24 23:34:00 +08:00
parent 69c0bfa4c7
commit 71c107f2e5
17 changed files with 141 additions and 191 deletions

View File

@@ -89,15 +89,6 @@ const props = defineProps({
justify-self: flex-end;
}
.card-item__extra span:nth-child(1) {
font-size: 1rem;
}
.card-item__extra span:nth-child(2) {
font-size: 0.9rem;
color: #A9A9A9;
}
hr {
height: 1px;
background-color: #cccccc;

View File

@@ -44,10 +44,6 @@
</div>
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
<div class="info-item">
<i class="fa-solid fa-flag"></i>
{{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}}
</div>
<div class="info-item">
<i class="fa-regular fa-calendar-check"></i>
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
@@ -73,7 +69,6 @@ import { formatDate } from 'vue3-common/utils/dateUtil'
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
import { serviceFileRootPath } from '@/utils'
import { isMobile } from 'vue3-common/utils/layoutUtil'
const router = useRouter()
const foodStore = useFoodStore()
@@ -116,7 +111,7 @@ const viewFoodRecipeClick = async () => {
const deleteFoodRecipeClick = () => {
foodStore.foodRecipeApiType = 'DELETE'
commonElMessageBox(`请确认是否删除该菜谱: ${props.foodItem?.name}?`).then(() => {
foodStore.handleFoodRecipeApi(props.foodItem?.id as number)
foodStore.handleFoodRecipeApi(props.foodItem)
})
}

View File

@@ -20,7 +20,10 @@ const calendarRef = ref()
const foodStore = useFoodStore()
const appStore = useAppStore()
onMounted(() => {
onMounted(async () => {
foodStore.queryDateType = 'month'
await foodStore.refreshInfo()
const { month } = foodStore.queryRecord
calendarRef.value.move(new Date(month))
})

View File

@@ -6,15 +6,13 @@
<div class="daily-list">
<daily-card v-for="(item, index) in dailyItemInfo.itemList"
:key="index" @click="selectFoodRecordClick(item)"
:title="item.name" :content="item.category">
:key="index" :title="item.name" :content="item.category">
<template #icon>
<el-avatar>{{ item.category.charAt(0) }}</el-avatar>
</template>
<template #extra>
<span>{{ item.person }}</span>
<span>{{ item.date }}</span>
<el-button type="primary" :icon="CaretRight" circle @click="selectFoodRecordClick(item)"/>
</template>
</daily-card>
</div>
@@ -22,6 +20,7 @@
</template>
<script setup lang="ts">
import { CaretRight } from '@element-plus/icons-vue'
import DailySummary from '@/components/DailySummary.vue'
import DailyCard from '@/components/DailyCard.vue'
import { defineProps, onMounted, reactive, watch } from 'vue'
@@ -83,11 +82,11 @@ const getFoodDailyByDate = (date: string) => {
* @param foodRecord 美食记录
*/
const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
foodStore.foodRecordApiType = 'UPDATE'
// foodStore.foodRecordApiType = 'UPDATE'
appStore.isShowMobileBack = true
foodStore.currentFoodRecord = foodRecord
await router.push({ name: 'MobileHomeFoodRecordForm' })
// appStore.isShowMobileBack = true
// foodStore.currentFoodRecord = foodRecord
// await router.push({ name: 'MobileHomeFoodRecordForm' })
}
</script>
@@ -95,13 +94,13 @@ const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
.food-daily-item {
display: flex;
flex-direction: column;
gap: 1vh;
padding: 1vh 0.5vw;
gap: 10px;
padding: 5px;
.daily-list {
display: flex;
flex-direction: column;
gap: 1vh;
gap: 10px;
overflow: auto;
}
}

View File

@@ -3,7 +3,7 @@
<div class="query-item card-item">
<span>分类</span>
<el-segmented v-model="foodStore.queryRecipe.category"
:options="foodCategoryList"
:options="queryCategoryList"
@change="queryFoodSegmented"/>
</div>
@@ -25,29 +25,41 @@ import FoodCard from '@/components/Food/FoodCard.vue'
import ArriveBottom from '@/components/ArriveButton.vue'
import { useFoodStore } from '@/store/food.ts'
import { onBeforeUnmount, onMounted, ref } from 'vue'
import type { ICategory } from '@/types'
const foodRecipeRef = ref()
const foodStore = useFoodStore()
const foodCategoryList = [{
label: '全部',
value: ''
},
{
label: '家常菜',
value: '家常菜'
}]
const queryCategoryList = ref([] as ICategory[])
onMounted(async () => {
foodRecipeRef.value.addEventListener('scroll', handleScroll)
await foodStore.queryFoodCategory()
initCategoryList()
// foodStore.foodRecipeList = []
await foodStore.refreshInfo()
// foodRecipeRef.value.addEventListener('scroll', handleScroll)
})
onBeforeUnmount(() => {
foodRecipeRef.value.removeEventListener('scroll', handleScroll)
// foodRecipeRef.value.removeEventListener('scroll', handleScroll)
})
const initCategoryList = () => {
queryCategoryList.value = []
queryCategoryList.value.push({
label: '全部',
value: ''
})
foodStore.foodCategoryList.forEach((item) => {
queryCategoryList.value.push({
label: item,
value: item
})
})
}
const queryFoodSegmented = async () => {
foodStore.pageInfo.currentPage = 1
// foodStore.pageInfo.currentPage = 1
foodStore.foodRecipeList = []
await foodStore.queryFoodRecipeList()
}
@@ -70,6 +82,7 @@ const handleScroll = () => {
display: flex;
flex-direction: column;
gap: 10px;
overflow: auto;
.query-item {
padding: 5px 10px;

View File

@@ -12,7 +12,7 @@
:key="index" :timestamp="formatDate(item.date)" placement="top">
<div class="timeline-info card-item">
<span>{{ item.name }}</span>
<el-image :src="item.imageUrl"
<el-image :src="serviceFileRootPath + item.imageUrl"
fit="contain" alt="暂无成果">
<template #error>
<div class="image-slot">
@@ -24,30 +24,26 @@
</el-timeline-item>
</el-timeline>
<el-empty v-else description="暂无记"/>
<el-empty v-else description="暂无记"/>
</div>
</template>
<script setup lang="ts">
import { useFoodStore } from '@/store/food.ts'
import { onMounted, ref } from 'vue'
import { formatDate, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil'
import { formatDate } from 'vue3-common/utils/dateUtil'
import { serviceFileRootPath } from '@/utils'
const currentYear = ref(new Date())
const foodStore = useFoodStore()
onMounted(async () => {
const dateInfo = getStartEndDate(getCurrentYear(), 'year')
foodStore.queryDateList[0] = dateInfo[0]
foodStore.queryDateList[1] = dateInfo[1]
foodStore.queryDateType = 'year'
await foodStore.refreshInfo()
})
const changeDateEvent = async (value: Date) => {
const dateInfo = getStartEndDate(value, 'year')
foodStore.queryDateList[0] = dateInfo[0]
foodStore.queryDateList[1] = dateInfo[1]
foodStore.queryRecord.year = String(value.getFullYear())
await foodStore.refreshInfo()
}
</script>
@@ -69,6 +65,10 @@ const changeDateEvent = async (value: Date) => {
display: flex;
flex-direction: column;
gap: 10px;
.el-image {
height: 300px;
}
}
}
}

View File

@@ -9,7 +9,7 @@
</div>
<div class="chart-container card-item">
<div class="content">
<span class="title">📊类别统计</span>
<span class="title">📊菜谱统计</span>
</div>
<div class="chart" ref="foodCategoryChartRef"/>
</div>
@@ -28,22 +28,18 @@
import FoodRank from '@/components/Stats/FoodRank.vue'
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
import { lineChartOptions, barChartOptions, pieChartOptions } from 'vue3-common/utils/eChartsUtil'
import { lineChartOptions, pieChartOptions } from 'vue3-common/utils/eChartsUtil'
import { useFoodStore } from '@/store/food'
import { useAppStore } from '@/store/app.ts'
import type { IStatsChart } from 'vue3-common/types'
import { isMobile } from 'vue3-common/utils/layoutUtil'
const foodStore = useFoodStore()
const appStore = useAppStore()
const foodDailyChartRef = ref<HTMLElement>()
const foodCategoryChartRef = ref<HTMLElement>()
const billRankChartRef = ref<HTMLElement>()
let foodDailyChart: echarts.ECharts
let foodCategoryChart: echarts.ECharts
let billRankChart: echarts.ECharts
watch(() => foodStore.isRefresh, () => {
updateChart()
@@ -52,60 +48,19 @@ watch(() => foodStore.isRefresh, () => {
onMounted(() => {
foodDailyChart = echarts.init(foodDailyChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
foodCategoryChart = echarts.init(foodCategoryChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
// billRankChart = echarts.init(billRankChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
})
const updateChart = () => {
const MAX_CATEGORY_COUNT = 5
const MAX_RANK_COUNT = 10
const MAX_COUNT = 5
// 记录统计图
// const dailyXAxis = foodStore.billDateStatsList.map((item) => item.name)
// const dailyYAxis = foodStore.billDateStatsList.map((item) => item.value)
const dailyXAxis = ['2024-01', '2024-02']
const dailyYAxis = ['10', '6']
const dailyXAxis = foodStore.foodRecordStats.slice(0, MAX_COUNT).map((item) => item.name)
const dailyYAxis = foodStore.foodRecordStats.slice(0, MAX_COUNT).map((item) => item.value)
foodDailyChart.setOption(lineChartOptions('日期', dailyXAxis, '次数(次)', dailyYAxis))
//
// // 账本统计图
// const bookXAxis = foodStore.billBookStatsList.map((item) => item.name)
// const bookYAxis = foodStore.billBookStatsList.map((item) => item.value)
// billBookChart.setOption(barChartOptions('账本', bookXAxis, '金额(元)', bookYAxis))
// billBookChart.setOption({
// series: [{
// itemStyle: {
// color: foodStore.billType === 'expensive' ? '#CA6C65' : '#6FBB69'
// },
// markPoint: {
// label: {
// color: '#FFFFFF'
// }
// }
// }]
// })
//
// 类别统计图
const foodCategoryList = [
{ value: 12, name: '家常菜' },
{ value: 10, name: '鲁菜' },
{ value: 8, name: '川菜' },
{ value: 5, name: '淮扬菜' },
{ value: 2, name: '其他' }
]
foodCategoryChart.setOption(pieChartOptions('category', '次', foodCategoryList))
//
// // 排行榜统计图
// const billRankList: IStatsChart[] = []
// const resultList = foodStore.billRecordList.filter((item) => item.type === foodStore.billType)
// resultList.sort((a, b) => b.amount - a.amount).forEach((item) => {
// billRankList.push({
// name: item.content,
// value: item.amount
// })
// })
//
// if (!isMobile()) {
// billRankChart.setOption(pieChartOptions('rank', '元', billRankList.slice(0, MAX_RANK_COUNT)))
// }
const foodCategoryData = foodStore.foodCategoryStats.slice(0, MAX_COUNT)
foodCategoryChart.setOption(pieChartOptions('category', '个', foodCategoryData))
}
</script>
@@ -124,7 +79,7 @@ const updateChart = () => {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #009FA8;
border-bottom: 1px solid var(--el-color-primary);
.title {
font-size: 1em;

View File

@@ -2,8 +2,8 @@
<div v-for="(item, index) in foodRankList.slice(0, MAX_RANK_COUNT)"
:key="index" class="rank-item">
<span class="rank-index">{{ index+1 }}.</span>
<span class="rank-content">{{ item.value }}</span>
<span class="rank-amount">{{ item.count }} </span>
<span class="rank-content">{{ item.name }}</span>
<span class="rank-amount">{{ item.value }} </span>
</div>
</template>
@@ -15,19 +15,5 @@ const foodStore = useFoodStore()
const MAX_RANK_COUNT = 10
const foodRankList = computed(() => {
return [{
value: '红烧肉',
count: 10
}, {
value: '红烧鱼',
count: 8
}, {
value: '排骨汤',
count: 5
}, {
value: '红烧带鱼',
count: 2
}]
})
const foodRankList = computed(() => foodStore.foodRankStats)
</script>