From 71c107f2e5da6bdd012463de52a0a73a996ef472 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Tue, 24 Jun 2025 23:34:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/food.ts | 30 ++++++++- src/components/DailyCard.vue | 9 --- src/components/Food/FoodCard.vue | 7 +-- src/components/Food/FoodDaily.vue | 5 +- src/components/Food/FoodDailyItem.vue | 21 +++---- src/components/Food/FoodRecipe.vue | 37 +++++++---- src/components/Food/FoodTimeline.vue | 18 +++--- src/components/Stats/FoodChart.vue | 63 +++---------------- src/components/Stats/FoodRank.vue | 20 +----- src/store/app.ts | 2 +- src/store/auth.ts | 2 - src/store/food.ts | 89 ++++++++++++--------------- src/types/food.ts | 2 +- src/types/index.ts | 4 ++ src/utils/index.ts | 4 +- src/views/record/index.vue | 17 ++--- vite.config.ts | 2 +- 17 files changed, 141 insertions(+), 191 deletions(-) create mode 100644 src/types/index.ts diff --git a/src/apis/food.ts b/src/apis/food.ts index 0d65886..b608e98 100644 --- a/src/apis/food.ts +++ b/src/apis/food.ts @@ -1,5 +1,6 @@ import { cloudService } from './index' import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food' +import type { IStatsChart } from 'vue3-common/types' export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise => cloudService({ @@ -21,13 +22,20 @@ export const deleteFoodRecipeApi = (id: number): Promise => method: 'DELETE' }) -export const queryFoodRecipeByIdApi = (id: number): Promise => +export const queryFoodRecipeByIdApi = (id: number): Promise => cloudService({ url: `/food-service/food/recipe/${id}`, method: 'get' }) -export const queryFoodRecipeApi = (currentPage: number, pageSize: number, +export const queryFoodRecipeApi = (query: IFoodQuery): Promise => + cloudService({ + url: '/food-service/food/recipe', + method: 'get', + params: { ...query } + }) + +export const queryFoodRecipeByPageApi = (currentPage: number, pageSize: number, query: IFoodQuery): Promise => cloudService({ url: '/food-service/food/recipe/page', @@ -79,3 +87,21 @@ export const queryFoodStatsApi = (): Promise => url: '/food-service/food/stats', method: 'get' }) + +export const queryFoodRecordStatsApi = (): Promise => + cloudService({ + url: '/food-service/food/stats/record', + method: 'get' + }) + +export const queryFoodCategoryStatsApi = (): Promise => + cloudService({ + url: '/food-service/food/stats/category', + method: 'get' + }) + +export const queryFoodRankStatsApi = (): Promise => + cloudService({ + url: '/food-service/food/stats/rank', + method: 'get' + }) diff --git a/src/components/DailyCard.vue b/src/components/DailyCard.vue index 10126c2..27c7078 100644 --- a/src/components/DailyCard.vue +++ b/src/components/DailyCard.vue @@ -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; diff --git a/src/components/Food/FoodCard.vue b/src/components/Food/FoodCard.vue index 8251f0d..977833b 100644 --- a/src/components/Food/FoodCard.vue +++ b/src/components/Food/FoodCard.vue @@ -44,10 +44,6 @@
-
- - {{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}} -
{{ 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) }) } diff --git a/src/components/Food/FoodDaily.vue b/src/components/Food/FoodDaily.vue index ca15f6c..692efe6 100644 --- a/src/components/Food/FoodDaily.vue +++ b/src/components/Food/FoodDaily.vue @@ -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)) }) diff --git a/src/components/Food/FoodDailyItem.vue b/src/components/Food/FoodDailyItem.vue index a5c5052..69bd171 100644 --- a/src/components/Food/FoodDailyItem.vue +++ b/src/components/Food/FoodDailyItem.vue @@ -6,15 +6,13 @@
+ :key="index" :title="item.name" :content="item.category">
@@ -22,6 +20,7 @@ @@ -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; } } diff --git a/src/components/Food/FoodRecipe.vue b/src/components/Food/FoodRecipe.vue index 254f6a6..adb4f7b 100644 --- a/src/components/Food/FoodRecipe.vue +++ b/src/components/Food/FoodRecipe.vue @@ -3,7 +3,7 @@
分类:
@@ -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; diff --git a/src/components/Food/FoodTimeline.vue b/src/components/Food/FoodTimeline.vue index dcaf384..882d566 100644 --- a/src/components/Food/FoodTimeline.vue +++ b/src/components/Food/FoodTimeline.vue @@ -12,7 +12,7 @@ :key="index" :timestamp="formatDate(item.date)" placement="top">
{{ item.name }} - @@ -69,6 +65,10 @@ const changeDateEvent = async (value: Date) => { display: flex; flex-direction: column; gap: 10px; + + .el-image { + height: 300px; + } } } } diff --git a/src/components/Stats/FoodChart.vue b/src/components/Stats/FoodChart.vue index c60351f..f5be311 100644 --- a/src/components/Stats/FoodChart.vue +++ b/src/components/Stats/FoodChart.vue @@ -9,7 +9,7 @@
- 📊类别统计 + 📊菜谱统计
@@ -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() const foodCategoryChartRef = ref() -const billRankChartRef = ref() 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)) } @@ -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; diff --git a/src/components/Stats/FoodRank.vue b/src/components/Stats/FoodRank.vue index 20b0e9f..2e57bdb 100644 --- a/src/components/Stats/FoodRank.vue +++ b/src/components/Stats/FoodRank.vue @@ -2,8 +2,8 @@
{{ index+1 }}. - {{ item.value }} - {{ item.count }} 次 + {{ item.name }} + {{ item.value }} 次
@@ -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) diff --git a/src/store/app.ts b/src/store/app.ts index b4e7412..c656b8e 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -14,7 +14,7 @@ export const useAppStore = defineStore('ipp', { isDark: false, isWebSocket: false, serverIp: '14.103.235.151', - version: 'v1.0.0.2025062303' + version: 'v1.0.0.2025062401' }), actions: { initApp() { diff --git a/src/store/auth.ts b/src/store/auth.ts index 51a2132..e2f676b 100644 --- a/src/store/auth.ts +++ b/src/store/auth.ts @@ -83,8 +83,6 @@ export const useAuthStore = defineStore('auth', { const { email, code: emailCode } = this.emailLoginForm switch (type) { case 'common': - console.log(username) - console.log(password) this.homeSession = await loginApi(username, password) break case 'phone': diff --git a/src/store/food.ts b/src/store/food.ts index 8eb03cb..3e19e61 100644 --- a/src/store/food.ts +++ b/src/store/food.ts @@ -4,15 +4,23 @@ import type { IFoodStats, IFoodRecord } from '@/types/food' import { - addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi, - queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi, - queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi + addFoodRecipeApi, + addFoodRecordApi, + deleteFoodRecipeApi, + deleteFoodRecordApi, + queryFoodNameListApi, + queryFoodRecipeByIdApi, + queryFoodRecordApi, + queryFoodStatsApi, + updateFoodRecipeApi, + updateFoodRecordApi, + queryFoodCategoryApi, + queryFoodRecordStatsApi, + queryFoodCategoryStatsApi, queryFoodRankStatsApi, queryFoodRecipeApi } from '@/apis/food.ts' -// import { IDateType, ImagePathEnum } from '@/types' -import type { IDialog, IHandleApi } from 'vue3-common/types' +import type { IDialog, IHandleApi, IStatsChart } from 'vue3-common/types' import { ElMessage } from 'element-plus' import { getCurrentMonth, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil' -import { isMobile } from 'vue3-common/utils/layoutUtil' export const useFoodStore = defineStore('food', { state: () => ({ @@ -44,7 +52,7 @@ export const useFoodStore = defineStore('food', { year: getCurrentYear(), month: getCurrentMonth() }, - queryDateType: 'month', + queryDateType: 'month' as 'month' | 'year', queryDateList: [] as string[], foodRecipeApiType: 'ADD' as IHandleApi, foodMaterialApiType: 'ADD' as IHandleApi, @@ -67,7 +75,7 @@ export const useFoodStore = defineStore('food', { rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'], pageInfo: { currentPage: 1, - pageSize: 3, + pageSize: 2, totalSize: 1 }, isScrollEnd: false, @@ -77,96 +85,78 @@ export const useFoodStore = defineStore('food', { categoryCount: 0, workCount: 0 } as IFoodStats, + foodRecordStats: [] as IStatsChart[], + foodCategoryStats: [] as IStatsChart[], + foodRankStats: [] as IStatsChart[], isRefresh: false }), actions: { async queryFoodRecipeList() { - const { currentPage, pageSize } = this.pageInfo - const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe) - this.foodRecipeList.push(...result.records) - this.pageInfo.totalSize = result.total + // const { currentPage, pageSize } = this.pageInfo + // const result = await queryFoodRecipeByPageApi(currentPage, pageSize, this.queryRecipe) + // this.foodRecipeList.push(...result.records) + // this.pageInfo.totalSize = result.total + this.foodRecipeList = await queryFoodRecipeApi(this.queryRecipe) }, async queryFoodRecordList() { const { year, month } = this.queryRecord const date = this.queryDateType === 'year' ? year : month this.queryDateList = getStartEndDate(date, this.queryDateType) - - const foodRecord: IFoodRecord = { - category: '家常菜', - date: '2025-06-23', - id: 0, - imageUrl: 'https://ts2.tc.mm.bing.net/th/id/OSK.277ab994364a7e09543c78379bab1d05?w=612&h=408&c=7&rs=1&qlt=80&o=6&cdv=1&cb=ircwebp1&pid=16.1', - name: '红烧鱼', - person: '哥哥' - } - const foodRecord2: IFoodRecord = { - category: '家常菜', - date: '2025-06-22', - id: 0, - imageUrl: 'https://ts2.tc.mm.bing.net/th/id/OSK.277ab994364a7e09543c78379bab1d05?w=612&h=408&c=7&rs=1&qlt=80&o=6&cdv=1&cb=ircwebp1&pid=16.1', - name: '红烧鱼', - person: '哥哥' - } - this.foodRecordList = [] - this.foodRecordList.push(foodRecord) - this.foodRecordList.push(foodRecord2) - // this.foodRecordList = await queryFoodRecordApi(this.queryDateList[0], this.queryDateList[1]) + this.foodRecordList = await queryFoodRecordApi(this.queryDateList[0], this.queryDateList[1]) }, async queryFoodNameList() { - // this.foodNameList = await queryFoodNameListApi() + this.foodNameList = await queryFoodNameListApi() }, async queryFoodRecipe(id: number) { - // this.currentFoodRecipe = await queryFoodRecipeByIdApi(id) + this.currentFoodRecipe = await queryFoodRecipeByIdApi(id) }, async queryFoodStats() { - // this.foodStats = await queryFoodStatsApi() + this.foodStats = await queryFoodStatsApi() + this.foodRecordStats = await queryFoodRecordStatsApi() + this.foodCategoryStats = await queryFoodCategoryStatsApi() + this.foodRankStats = await queryFoodRankStatsApi() this.isRefresh = !this.isRefresh }, async queryFoodCategory() { - // this.foodCategoryList = await queryFoodCategoryApi() + this.foodCategoryList = await queryFoodCategoryApi() }, async handleFoodRecipeApi(foodRecipe: IFoodRecipe) { switch (this.foodRecipeApiType) { case 'ADD': - // await addFoodRecipeApi(foodRecipe) + await addFoodRecipeApi(foodRecipe) ElMessage.success('新增菜谱成功') break case 'UPDATE': - // await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe) + await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe) ElMessage.success('更新菜谱成功') break case 'DELETE': - // await deleteFoodRecipeApi(foodRecipe.id as number) + await deleteFoodRecipeApi(foodRecipe.id as number) ElMessage.success('删除菜谱成功') break default: break } - this.foodRecipeDialog.visible = false await this.refreshInfo() }, async handleFoodRecordApi(foodRecord: IFoodRecord) { switch (this.foodRecordApiType) { case 'ADD': - // await addFoodRecordApi(foodRecord) + await addFoodRecordApi(foodRecord) ElMessage.success('新增成果成功') break case 'UPDATE': - // await updateFoodRecordApi(foodRecord.id as number, foodRecord) + await updateFoodRecordApi(foodRecord.id as number, foodRecord) ElMessage.success('更新成果成功') break case 'DELETE': - // await deleteFoodRecordApi(foodRecord.id as number) + await deleteFoodRecordApi(foodRecord.id as number) ElMessage.success('删除成果成功') break default: break } - this.foodRecordDialog.visible = false - - if (!isMobile()) { - await this.refreshInfo() - } + await this.refreshInfo() }, async refreshInfo() { await this.queryFoodRecipeList() @@ -201,7 +191,6 @@ export const useFoodStore = defineStore('food', { getEmptyFoodRecord(): IFoodRecord { return { category: '', - person: '', date: '', id: 0, imageUrl: '', diff --git a/src/types/food.ts b/src/types/food.ts index 3424dd7..72dc468 100644 --- a/src/types/food.ts +++ b/src/types/food.ts @@ -25,8 +25,8 @@ export interface IFoodRecord { id: number; name: string; category: string; + person: number; date: string; - person: string; imageUrl: string; } diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..9fe91d7 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,4 @@ +export interface ICategory { + label: string; + value: string; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index f0c58b6..be604d6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,12 +1,12 @@ /** * 文件服务器地址 */ -export const fileServiceUrl = '/home-service/file' +export const fileServiceUrl = '/food-service/file' /** * 服务器文件路径根地址 */ -export const serviceFileRootPath = '/home-service/' +export const serviceFileRootPath = '/food-service/' /** * 获取assets静态资源 diff --git a/src/views/record/index.vue b/src/views/record/index.vue index 7fc8375..4a824db 100644 --- a/src/views/record/index.vue +++ b/src/views/record/index.vue @@ -1,6 +1,6 @@