feat:增加后端接口
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { cloudService } from './index'
|
import { cloudService } from './index'
|
||||||
import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food'
|
import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food'
|
||||||
|
import type { IStatsChart } from 'vue3-common/types'
|
||||||
|
|
||||||
export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise<boolean> =>
|
export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise<boolean> =>
|
||||||
cloudService({
|
cloudService({
|
||||||
@@ -21,13 +22,20 @@ export const deleteFoodRecipeApi = (id: number): Promise<boolean> =>
|
|||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
})
|
})
|
||||||
|
|
||||||
export const queryFoodRecipeByIdApi = (id: number): Promise<any> =>
|
export const queryFoodRecipeByIdApi = (id: number): Promise<IFoodRecipe> =>
|
||||||
cloudService({
|
cloudService({
|
||||||
url: `/food-service/food/recipe/${id}`,
|
url: `/food-service/food/recipe/${id}`,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
|
|
||||||
export const queryFoodRecipeApi = (currentPage: number, pageSize: number,
|
export const queryFoodRecipeApi = (query: IFoodQuery): Promise<IFoodRecipe[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/recipe',
|
||||||
|
method: 'get',
|
||||||
|
params: { ...query }
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodRecipeByPageApi = (currentPage: number, pageSize: number,
|
||||||
query: IFoodQuery): Promise<any> =>
|
query: IFoodQuery): Promise<any> =>
|
||||||
cloudService({
|
cloudService({
|
||||||
url: '/food-service/food/recipe/page',
|
url: '/food-service/food/recipe/page',
|
||||||
@@ -79,3 +87,21 @@ export const queryFoodStatsApi = (): Promise<IFoodStats> =>
|
|||||||
url: '/food-service/food/stats',
|
url: '/food-service/food/stats',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const queryFoodRecordStatsApi = (): Promise<IStatsChart[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/stats/record',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodCategoryStatsApi = (): Promise<IStatsChart[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/stats/category',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodRankStatsApi = (): Promise<IStatsChart[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/stats/rank',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|||||||
@@ -89,15 +89,6 @@ const props = defineProps({
|
|||||||
justify-self: flex-end;
|
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 {
|
hr {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: #cccccc;
|
background-color: #cccccc;
|
||||||
|
|||||||
@@ -44,10 +44,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
|
<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">
|
<div class="info-item">
|
||||||
<i class="fa-regular fa-calendar-check"></i>
|
<i class="fa-regular fa-calendar-check"></i>
|
||||||
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
|
{{ 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 { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||||
import { serviceFileRootPath } from '@/utils'
|
import { serviceFileRootPath } from '@/utils'
|
||||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
@@ -116,7 +111,7 @@ const viewFoodRecipeClick = async () => {
|
|||||||
const deleteFoodRecipeClick = () => {
|
const deleteFoodRecipeClick = () => {
|
||||||
foodStore.foodRecipeApiType = 'DELETE'
|
foodStore.foodRecipeApiType = 'DELETE'
|
||||||
commonElMessageBox(`请确认是否删除该菜谱: ${props.foodItem?.name}?`).then(() => {
|
commonElMessageBox(`请确认是否删除该菜谱: ${props.foodItem?.name}?`).then(() => {
|
||||||
foodStore.handleFoodRecipeApi(props.foodItem?.id as number)
|
foodStore.handleFoodRecipeApi(props.foodItem)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ const calendarRef = ref()
|
|||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
|
foodStore.queryDateType = 'month'
|
||||||
|
await foodStore.refreshInfo()
|
||||||
|
|
||||||
const { month } = foodStore.queryRecord
|
const { month } = foodStore.queryRecord
|
||||||
calendarRef.value.move(new Date(month))
|
calendarRef.value.move(new Date(month))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,15 +6,13 @@
|
|||||||
|
|
||||||
<div class="daily-list">
|
<div class="daily-list">
|
||||||
<daily-card v-for="(item, index) in dailyItemInfo.itemList"
|
<daily-card v-for="(item, index) in dailyItemInfo.itemList"
|
||||||
:key="index" @click="selectFoodRecordClick(item)"
|
:key="index" :title="item.name" :content="item.category">
|
||||||
:title="item.name" :content="item.category">
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<el-avatar>{{ item.category.charAt(0) }}</el-avatar>
|
<el-avatar>{{ item.category.charAt(0) }}</el-avatar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<span>{{ item.person }}</span>
|
<el-button type="primary" :icon="CaretRight" circle @click="selectFoodRecordClick(item)"/>
|
||||||
<span>{{ item.date }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</daily-card>
|
</daily-card>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,6 +20,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { CaretRight } from '@element-plus/icons-vue'
|
||||||
import DailySummary from '@/components/DailySummary.vue'
|
import DailySummary from '@/components/DailySummary.vue'
|
||||||
import DailyCard from '@/components/DailyCard.vue'
|
import DailyCard from '@/components/DailyCard.vue'
|
||||||
import { defineProps, onMounted, reactive, watch } from 'vue'
|
import { defineProps, onMounted, reactive, watch } from 'vue'
|
||||||
@@ -83,11 +82,11 @@ const getFoodDailyByDate = (date: string) => {
|
|||||||
* @param foodRecord 美食记录
|
* @param foodRecord 美食记录
|
||||||
*/
|
*/
|
||||||
const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
|
const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
|
||||||
foodStore.foodRecordApiType = 'UPDATE'
|
// foodStore.foodRecordApiType = 'UPDATE'
|
||||||
|
|
||||||
appStore.isShowMobileBack = true
|
// appStore.isShowMobileBack = true
|
||||||
foodStore.currentFoodRecord = foodRecord
|
// foodStore.currentFoodRecord = foodRecord
|
||||||
await router.push({ name: 'MobileHomeFoodRecordForm' })
|
// await router.push({ name: 'MobileHomeFoodRecordForm' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -95,13 +94,13 @@ const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
|
|||||||
.food-daily-item {
|
.food-daily-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1vh;
|
gap: 10px;
|
||||||
padding: 1vh 0.5vw;
|
padding: 5px;
|
||||||
|
|
||||||
.daily-list {
|
.daily-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1vh;
|
gap: 10px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="query-item card-item">
|
<div class="query-item card-item">
|
||||||
<span>分类:</span>
|
<span>分类:</span>
|
||||||
<el-segmented v-model="foodStore.queryRecipe.category"
|
<el-segmented v-model="foodStore.queryRecipe.category"
|
||||||
:options="foodCategoryList"
|
:options="queryCategoryList"
|
||||||
@change="queryFoodSegmented"/>
|
@change="queryFoodSegmented"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -25,29 +25,41 @@ import FoodCard from '@/components/Food/FoodCard.vue'
|
|||||||
import ArriveBottom from '@/components/ArriveButton.vue'
|
import ArriveBottom from '@/components/ArriveButton.vue'
|
||||||
import { useFoodStore } from '@/store/food.ts'
|
import { useFoodStore } from '@/store/food.ts'
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
|
import type { ICategory } from '@/types'
|
||||||
|
|
||||||
const foodRecipeRef = ref()
|
const foodRecipeRef = ref()
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
const foodCategoryList = [{
|
const queryCategoryList = ref([] as ICategory[])
|
||||||
label: '全部',
|
|
||||||
value: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '家常菜',
|
|
||||||
value: '家常菜'
|
|
||||||
}]
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
foodRecipeRef.value.addEventListener('scroll', handleScroll)
|
await foodStore.queryFoodCategory()
|
||||||
|
initCategoryList()
|
||||||
|
// foodStore.foodRecipeList = []
|
||||||
|
await foodStore.refreshInfo()
|
||||||
|
// foodRecipeRef.value.addEventListener('scroll', handleScroll)
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
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 () => {
|
const queryFoodSegmented = async () => {
|
||||||
foodStore.pageInfo.currentPage = 1
|
// foodStore.pageInfo.currentPage = 1
|
||||||
foodStore.foodRecipeList = []
|
foodStore.foodRecipeList = []
|
||||||
await foodStore.queryFoodRecipeList()
|
await foodStore.queryFoodRecipeList()
|
||||||
}
|
}
|
||||||
@@ -70,6 +82,7 @@ const handleScroll = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
.query-item {
|
.query-item {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:key="index" :timestamp="formatDate(item.date)" placement="top">
|
:key="index" :timestamp="formatDate(item.date)" placement="top">
|
||||||
<div class="timeline-info card-item">
|
<div class="timeline-info card-item">
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<el-image :src="item.imageUrl"
|
<el-image :src="serviceFileRootPath + item.imageUrl"
|
||||||
fit="contain" alt="暂无成果">
|
fit="contain" alt="暂无成果">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="image-slot">
|
<div class="image-slot">
|
||||||
@@ -24,30 +24,26 @@
|
|||||||
</el-timeline-item>
|
</el-timeline-item>
|
||||||
</el-timeline>
|
</el-timeline>
|
||||||
|
|
||||||
<el-empty v-else description="暂无日记"/>
|
<el-empty v-else description="暂无记录"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useFoodStore } from '@/store/food.ts'
|
import { useFoodStore } from '@/store/food.ts'
|
||||||
import { onMounted, ref } from 'vue'
|
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'
|
import { serviceFileRootPath } from '@/utils'
|
||||||
|
|
||||||
const currentYear = ref(new Date())
|
const currentYear = ref(new Date())
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const dateInfo = getStartEndDate(getCurrentYear(), 'year')
|
foodStore.queryDateType = 'year'
|
||||||
foodStore.queryDateList[0] = dateInfo[0]
|
|
||||||
foodStore.queryDateList[1] = dateInfo[1]
|
|
||||||
await foodStore.refreshInfo()
|
await foodStore.refreshInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
const changeDateEvent = async (value: Date) => {
|
const changeDateEvent = async (value: Date) => {
|
||||||
const dateInfo = getStartEndDate(value, 'year')
|
foodStore.queryRecord.year = String(value.getFullYear())
|
||||||
foodStore.queryDateList[0] = dateInfo[0]
|
|
||||||
foodStore.queryDateList[1] = dateInfo[1]
|
|
||||||
await foodStore.refreshInfo()
|
await foodStore.refreshInfo()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -69,6 +65,10 @@ const changeDateEvent = async (value: Date) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="chart-container card-item">
|
<div class="chart-container card-item">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="title">📊类别统计</span>
|
<span class="title">📊菜谱统计</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart" ref="foodCategoryChartRef"/>
|
<div class="chart" ref="foodCategoryChartRef"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,22 +28,18 @@
|
|||||||
import FoodRank from '@/components/Stats/FoodRank.vue'
|
import FoodRank from '@/components/Stats/FoodRank.vue'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import * as echarts from 'echarts'
|
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 { useFoodStore } from '@/store/food'
|
||||||
import { useAppStore } from '@/store/app.ts'
|
import { useAppStore } from '@/store/app.ts'
|
||||||
import type { IStatsChart } from 'vue3-common/types'
|
|
||||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
|
||||||
|
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const foodDailyChartRef = ref<HTMLElement>()
|
const foodDailyChartRef = ref<HTMLElement>()
|
||||||
const foodCategoryChartRef = ref<HTMLElement>()
|
const foodCategoryChartRef = ref<HTMLElement>()
|
||||||
const billRankChartRef = ref<HTMLElement>()
|
|
||||||
|
|
||||||
let foodDailyChart: echarts.ECharts
|
let foodDailyChart: echarts.ECharts
|
||||||
let foodCategoryChart: echarts.ECharts
|
let foodCategoryChart: echarts.ECharts
|
||||||
let billRankChart: echarts.ECharts
|
|
||||||
|
|
||||||
watch(() => foodStore.isRefresh, () => {
|
watch(() => foodStore.isRefresh, () => {
|
||||||
updateChart()
|
updateChart()
|
||||||
@@ -52,60 +48,19 @@ watch(() => foodStore.isRefresh, () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
foodDailyChart = echarts.init(foodDailyChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
foodDailyChart = echarts.init(foodDailyChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||||
foodCategoryChart = echarts.init(foodCategoryChartRef.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 updateChart = () => {
|
||||||
const MAX_CATEGORY_COUNT = 5
|
const MAX_COUNT = 5
|
||||||
const MAX_RANK_COUNT = 10
|
|
||||||
|
|
||||||
// 记录统计图
|
// 记录统计图
|
||||||
// const dailyXAxis = foodStore.billDateStatsList.map((item) => item.name)
|
const dailyXAxis = foodStore.foodRecordStats.slice(0, MAX_COUNT).map((item) => item.name)
|
||||||
// const dailyYAxis = foodStore.billDateStatsList.map((item) => item.value)
|
const dailyYAxis = foodStore.foodRecordStats.slice(0, MAX_COUNT).map((item) => item.value)
|
||||||
const dailyXAxis = ['2024-01', '2024-02']
|
|
||||||
const dailyYAxis = ['10', '6']
|
|
||||||
foodDailyChart.setOption(lineChartOptions('日期', dailyXAxis, '次数(次)', dailyYAxis))
|
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 = [
|
const foodCategoryData = foodStore.foodCategoryStats.slice(0, MAX_COUNT)
|
||||||
{ value: 12, name: '家常菜' },
|
foodCategoryChart.setOption(pieChartOptions('category', '个', foodCategoryData))
|
||||||
{ 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)))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -124,7 +79,7 @@ const updateChart = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid #009FA8;
|
border-bottom: 1px solid var(--el-color-primary);
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<div v-for="(item, index) in foodRankList.slice(0, MAX_RANK_COUNT)"
|
<div v-for="(item, index) in foodRankList.slice(0, MAX_RANK_COUNT)"
|
||||||
:key="index" class="rank-item">
|
:key="index" class="rank-item">
|
||||||
<span class="rank-index">{{ index+1 }}.</span>
|
<span class="rank-index">{{ index+1 }}.</span>
|
||||||
<span class="rank-content">{{ item.value }}</span>
|
<span class="rank-content">{{ item.name }}</span>
|
||||||
<span class="rank-amount">{{ item.count }} 次</span>
|
<span class="rank-amount">{{ item.value }} 次</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -15,19 +15,5 @@ const foodStore = useFoodStore()
|
|||||||
|
|
||||||
const MAX_RANK_COUNT = 10
|
const MAX_RANK_COUNT = 10
|
||||||
|
|
||||||
const foodRankList = computed(() => {
|
const foodRankList = computed(() => foodStore.foodRankStats)
|
||||||
return [{
|
|
||||||
value: '红烧肉',
|
|
||||||
count: 10
|
|
||||||
}, {
|
|
||||||
value: '红烧鱼',
|
|
||||||
count: 8
|
|
||||||
}, {
|
|
||||||
value: '排骨汤',
|
|
||||||
count: 5
|
|
||||||
}, {
|
|
||||||
value: '红烧带鱼',
|
|
||||||
count: 2
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const useAppStore = defineStore('ipp', {
|
|||||||
isDark: false,
|
isDark: false,
|
||||||
isWebSocket: false,
|
isWebSocket: false,
|
||||||
serverIp: '14.103.235.151',
|
serverIp: '14.103.235.151',
|
||||||
version: 'v1.0.0.2025062303'
|
version: 'v1.0.0.2025062401'
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
initApp() {
|
initApp() {
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ export const useAuthStore = defineStore('auth', {
|
|||||||
const { email, code: emailCode } = this.emailLoginForm
|
const { email, code: emailCode } = this.emailLoginForm
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'common':
|
case 'common':
|
||||||
console.log(username)
|
|
||||||
console.log(password)
|
|
||||||
this.homeSession = await loginApi(username, password)
|
this.homeSession = await loginApi(username, password)
|
||||||
break
|
break
|
||||||
case 'phone':
|
case 'phone':
|
||||||
|
|||||||
@@ -4,15 +4,23 @@ import type {
|
|||||||
IFoodStats, IFoodRecord
|
IFoodStats, IFoodRecord
|
||||||
} from '@/types/food'
|
} from '@/types/food'
|
||||||
import {
|
import {
|
||||||
addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi,
|
addFoodRecipeApi,
|
||||||
queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi,
|
addFoodRecordApi,
|
||||||
queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi
|
deleteFoodRecipeApi,
|
||||||
|
deleteFoodRecordApi,
|
||||||
|
queryFoodNameListApi,
|
||||||
|
queryFoodRecipeByIdApi,
|
||||||
|
queryFoodRecordApi,
|
||||||
|
queryFoodStatsApi,
|
||||||
|
updateFoodRecipeApi,
|
||||||
|
updateFoodRecordApi,
|
||||||
|
queryFoodCategoryApi,
|
||||||
|
queryFoodRecordStatsApi,
|
||||||
|
queryFoodCategoryStatsApi, queryFoodRankStatsApi, queryFoodRecipeApi
|
||||||
} from '@/apis/food.ts'
|
} from '@/apis/food.ts'
|
||||||
// import { IDateType, ImagePathEnum } from '@/types'
|
import type { IDialog, IHandleApi, IStatsChart } from 'vue3-common/types'
|
||||||
import type { IDialog, IHandleApi } from 'vue3-common/types'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getCurrentMonth, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil'
|
import { getCurrentMonth, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil'
|
||||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
|
||||||
|
|
||||||
export const useFoodStore = defineStore('food', {
|
export const useFoodStore = defineStore('food', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -44,7 +52,7 @@ export const useFoodStore = defineStore('food', {
|
|||||||
year: getCurrentYear(),
|
year: getCurrentYear(),
|
||||||
month: getCurrentMonth()
|
month: getCurrentMonth()
|
||||||
},
|
},
|
||||||
queryDateType: 'month',
|
queryDateType: 'month' as 'month' | 'year',
|
||||||
queryDateList: [] as string[],
|
queryDateList: [] as string[],
|
||||||
foodRecipeApiType: 'ADD' as IHandleApi,
|
foodRecipeApiType: 'ADD' as IHandleApi,
|
||||||
foodMaterialApiType: 'ADD' as IHandleApi,
|
foodMaterialApiType: 'ADD' as IHandleApi,
|
||||||
@@ -67,7 +75,7 @@ export const useFoodStore = defineStore('food', {
|
|||||||
rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'],
|
rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'],
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 3,
|
pageSize: 2,
|
||||||
totalSize: 1
|
totalSize: 1
|
||||||
},
|
},
|
||||||
isScrollEnd: false,
|
isScrollEnd: false,
|
||||||
@@ -77,96 +85,78 @@ export const useFoodStore = defineStore('food', {
|
|||||||
categoryCount: 0,
|
categoryCount: 0,
|
||||||
workCount: 0
|
workCount: 0
|
||||||
} as IFoodStats,
|
} as IFoodStats,
|
||||||
|
foodRecordStats: [] as IStatsChart[],
|
||||||
|
foodCategoryStats: [] as IStatsChart[],
|
||||||
|
foodRankStats: [] as IStatsChart[],
|
||||||
isRefresh: false
|
isRefresh: false
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async queryFoodRecipeList() {
|
async queryFoodRecipeList() {
|
||||||
const { currentPage, pageSize } = this.pageInfo
|
// const { currentPage, pageSize } = this.pageInfo
|
||||||
const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe)
|
// const result = await queryFoodRecipeByPageApi(currentPage, pageSize, this.queryRecipe)
|
||||||
this.foodRecipeList.push(...result.records)
|
// this.foodRecipeList.push(...result.records)
|
||||||
this.pageInfo.totalSize = result.total
|
// this.pageInfo.totalSize = result.total
|
||||||
|
this.foodRecipeList = await queryFoodRecipeApi(this.queryRecipe)
|
||||||
},
|
},
|
||||||
async queryFoodRecordList() {
|
async queryFoodRecordList() {
|
||||||
const { year, month } = this.queryRecord
|
const { year, month } = this.queryRecord
|
||||||
const date = this.queryDateType === 'year' ? year : month
|
const date = this.queryDateType === 'year' ? year : month
|
||||||
this.queryDateList = getStartEndDate(date, this.queryDateType)
|
this.queryDateList = getStartEndDate(date, this.queryDateType)
|
||||||
|
this.foodRecordList = await queryFoodRecordApi(this.queryDateList[0], this.queryDateList[1])
|
||||||
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])
|
|
||||||
},
|
},
|
||||||
async queryFoodNameList() {
|
async queryFoodNameList() {
|
||||||
// this.foodNameList = await queryFoodNameListApi()
|
this.foodNameList = await queryFoodNameListApi()
|
||||||
},
|
},
|
||||||
async queryFoodRecipe(id: number) {
|
async queryFoodRecipe(id: number) {
|
||||||
// this.currentFoodRecipe = await queryFoodRecipeByIdApi(id)
|
this.currentFoodRecipe = await queryFoodRecipeByIdApi(id)
|
||||||
},
|
},
|
||||||
async queryFoodStats() {
|
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
|
this.isRefresh = !this.isRefresh
|
||||||
},
|
},
|
||||||
async queryFoodCategory() {
|
async queryFoodCategory() {
|
||||||
// this.foodCategoryList = await queryFoodCategoryApi()
|
this.foodCategoryList = await queryFoodCategoryApi()
|
||||||
},
|
},
|
||||||
async handleFoodRecipeApi(foodRecipe: IFoodRecipe) {
|
async handleFoodRecipeApi(foodRecipe: IFoodRecipe) {
|
||||||
switch (this.foodRecipeApiType) {
|
switch (this.foodRecipeApiType) {
|
||||||
case 'ADD':
|
case 'ADD':
|
||||||
// await addFoodRecipeApi(foodRecipe)
|
await addFoodRecipeApi(foodRecipe)
|
||||||
ElMessage.success('新增菜谱成功')
|
ElMessage.success('新增菜谱成功')
|
||||||
break
|
break
|
||||||
case 'UPDATE':
|
case 'UPDATE':
|
||||||
// await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe)
|
await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe)
|
||||||
ElMessage.success('更新菜谱成功')
|
ElMessage.success('更新菜谱成功')
|
||||||
break
|
break
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
// await deleteFoodRecipeApi(foodRecipe.id as number)
|
await deleteFoodRecipeApi(foodRecipe.id as number)
|
||||||
ElMessage.success('删除菜谱成功')
|
ElMessage.success('删除菜谱成功')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.foodRecipeDialog.visible = false
|
|
||||||
await this.refreshInfo()
|
await this.refreshInfo()
|
||||||
},
|
},
|
||||||
async handleFoodRecordApi(foodRecord: IFoodRecord) {
|
async handleFoodRecordApi(foodRecord: IFoodRecord) {
|
||||||
switch (this.foodRecordApiType) {
|
switch (this.foodRecordApiType) {
|
||||||
case 'ADD':
|
case 'ADD':
|
||||||
// await addFoodRecordApi(foodRecord)
|
await addFoodRecordApi(foodRecord)
|
||||||
ElMessage.success('新增成果成功')
|
ElMessage.success('新增成果成功')
|
||||||
break
|
break
|
||||||
case 'UPDATE':
|
case 'UPDATE':
|
||||||
// await updateFoodRecordApi(foodRecord.id as number, foodRecord)
|
await updateFoodRecordApi(foodRecord.id as number, foodRecord)
|
||||||
ElMessage.success('更新成果成功')
|
ElMessage.success('更新成果成功')
|
||||||
break
|
break
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
// await deleteFoodRecordApi(foodRecord.id as number)
|
await deleteFoodRecordApi(foodRecord.id as number)
|
||||||
ElMessage.success('删除成果成功')
|
ElMessage.success('删除成果成功')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.foodRecordDialog.visible = false
|
await this.refreshInfo()
|
||||||
|
|
||||||
if (!isMobile()) {
|
|
||||||
await this.refreshInfo()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async refreshInfo() {
|
async refreshInfo() {
|
||||||
await this.queryFoodRecipeList()
|
await this.queryFoodRecipeList()
|
||||||
@@ -201,7 +191,6 @@ export const useFoodStore = defineStore('food', {
|
|||||||
getEmptyFoodRecord(): IFoodRecord {
|
getEmptyFoodRecord(): IFoodRecord {
|
||||||
return {
|
return {
|
||||||
category: '',
|
category: '',
|
||||||
person: '',
|
|
||||||
date: '',
|
date: '',
|
||||||
id: 0,
|
id: 0,
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export interface IFoodRecord {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
category: string;
|
category: string;
|
||||||
|
person: number;
|
||||||
date: string;
|
date: string;
|
||||||
person: string;
|
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
src/types/index.ts
Normal file
4
src/types/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface ICategory {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
@@ -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静态资源
|
* 获取assets静态资源
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-mobile-view food-record">
|
<div class="common-mobile-view food-record">
|
||||||
<div class="food-query card-item">
|
<div class="food-segment card-item">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="foodInfo.query"
|
v-model="foodInfo.query"
|
||||||
placeholder="请输入菜谱名称" :prefix-icon="Search" clearable
|
placeholder="请输入菜谱名称" :prefix-icon="Search" clearable
|
||||||
@@ -10,8 +10,7 @@
|
|||||||
<div class="query-item">
|
<div class="query-item">
|
||||||
<span>查看类型:</span>
|
<span>查看类型:</span>
|
||||||
<el-segmented v-model="foodInfo.recordSegment"
|
<el-segmented v-model="foodInfo.recordSegment"
|
||||||
:options="recordSegmentList"
|
:options="recordSegmentList"/>
|
||||||
@change="changeRecordSegmentEvent()"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ import FoodDaily from '@/components/Food/FoodDaily.vue'
|
|||||||
import FoodTimeline from '@/components/Food/FoodTimeline.vue'
|
import FoodTimeline from '@/components/Food/FoodTimeline.vue'
|
||||||
import DraggableButton from '@/components/DraggableButton.vue'
|
import DraggableButton from '@/components/DraggableButton.vue'
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import { onMounted, reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { useFoodStore } from '@/store/food'
|
import { useFoodStore } from '@/store/food'
|
||||||
import type { IFoodSegment } from '@/types/food'
|
import type { IFoodSegment } from '@/types/food'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -67,14 +66,6 @@ const recordSegmentList = [{
|
|||||||
value: 'timeline'
|
value: 'timeline'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
foodStore.refreshInfo()
|
|
||||||
})
|
|
||||||
|
|
||||||
const changeRecordSegmentEvent = () => {
|
|
||||||
foodStore.refreshInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
const addClick = () => {
|
const addClick = () => {
|
||||||
appStore.isShowMobileBack = true
|
appStore.isShowMobileBack = true
|
||||||
|
|
||||||
@@ -101,7 +92,7 @@ const addClick = () => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
.food-query {
|
.food-segment {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default defineConfig({
|
|||||||
port: 5175,
|
port: 5175,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/food-service': {
|
'/food-service': {
|
||||||
target: 'http://127.0.0.1:8090',
|
target: 'http://127.0.0.1:8100',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (item) => item.replace(/^\/food-service/, '')
|
rewrite: (item) => item.replace(/^\/food-service/, '')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user