Files
pj_plan_cloud/src/views/nutrition.vue
T
2025-10-17 13:01:55 +08:00

372 lines
13 KiB
Vue

<template>
<div class="container">
<el-aside class="aside">
<AsideBlock>
<template #header>
<span>营养分析</span>
</template>
<NutritionAnalyse :data="timerObj.nutritionAnalyse.data.data"
:labels="timerObj.nutritionAnalyse.data.labels" @changeDateType="nutritionAnalyseChangeDateType" />
</AsideBlock>
<AsideBlock>
<template #header>
<span>今日就餐人数统计</span>
</template>
</AsideBlock>
<AsideBlock>
<template #header>
<span>今日复购排行榜(top5)</span>
</template>
<PersonThroughRank :data="timerObj.repurchase.data" />
</AsideBlock>
</el-aside>
<div class="main">
<AsideBlock height="calc(100% * 2 / 3)" titleBgWidth="50%">
<template #header>
<span>营养分析</span>
<div class="report-search">
<el-date-picker style="width: 20vw;" v-model="queryParams.createTimeList" type="datetimerange"
size="small" placeholder="请选择日期" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" @change="changeQueryDate" />
</div>
</template>
<NutritionReport ref="nutritionReportRef" />
</AsideBlock>
<AsideBlock titleBgWidth="50%">
<template #header>
<span>营养分析趋势</span>
</template>
<PersonAbnormalEventLineChart type="nutrition" yTitle="购买次数"
:data="timerObj.nutritionAnalyseTrend.data" />
</AsideBlock>
</div>
<el-aside class="aside">
<AsideBlock>
<template #header>
<span>菜品分类购买数量</span>
</template>
<AbnormalEventTypeStatus title="购买数量" type="shop" :data="timerObj.classBuyCount.data"
@changeDateType="alarmTotalByTimerChnageDateType" />
</AsideBlock>
<AsideBlock height="calc(100% * 2 / 3)">
<template #header>
<span>菜品喜好排行</span>
</template>
<NutritionTop :data="timerObj.cuisineTop.data" @change-date-type="changeCuisineTopDateType" />
</AsideBlock>
</el-aside>
</div>
</template>
<script setup>
import { onMounted, onBeforeUnmount, reactive, ref } from "vue";
import { nutritionApi } from "@/api/index"
import useNotification from "@/components/notification/index";
import { simplePoll } from "@/utils/poll"
import { displayPrice } from "@/utils/format-money"
import AsideBlock from "@/components/aside-block.vue";
import NutritionReport from "@/components/nutrition-report.vue";
import AbnormalEventTypeStatus from "@/components/abnormal-event-type-status.vue";
import NutritionAnalyse from "@/components/nutrition-analyse/index.vue"
import PersonThroughRank from "@/components/person-through-rank.vue";
import PersonAbnormalEventLineChart from "@/components/person-abnormal-event-line-chart.vue";
import NutritionTop from "@/components/nutrition-top.vue"
const { notificationAdd, notificationClear } = useNotification();
const nutritionReportRef = ref()
const queryParams = reactive({
createTimeList: null,
})
const timerObj = reactive({
// 复购排行榜
repurchase: {
initial: null,
interval: 10 * 1000,
data: null,
queryParams: {
dateRangeType: "YESTERDAY"
},
},
// 营养分析
nutritionAnalyse: {
initial: null,
interval: 10 * 1000,
data: {
data: null,
labels: null,
},
queryParams: {
dateRangeType: "YESTERDAY"
},
},
// 营养分析趋势
nutritionAnalyseTrend: {
initial: null,
interval: 10 * 1000,
data: {
data: null,
labels: null,
},
queryParams: {},
},
// 菜品分类购买数量
classBuyCount: {
initial: null,
interval: 10 * 1000,
data: null,
queryParams: {
dateRangeType: "YESTERDAY"
},
},
// 菜品喜好排行
cuisineTop: {
initial: null,
interval: 10 * 1000,
data: null,
queryParams: {
dateRangeType: "YESTERDAY"
},
}
})
onMounted(() => {
getRepurchase()
getNutritionAnalyse()
getClassBuyCount()
getCuisineTop()
getNutritionAnalyseTrend()
})
onBeforeUnmount(() => {
notificationClear();
for (let key of Object.keys(timerObj)) {
const value = timerObj[key];
if (value && value.initial) {
value.initial.destroy();
}
}
})
// 复购排行榜
const getRepurchase = () => {
timerObj.repurchase.initial = simplePoll(async () => {
try {
const { code, data } = await nutritionApi.repurchase({ ...timerObj.repurchase.queryParams });
if (code != 0) throw new Error("code!=0");
console.log("复购排行榜:", data);
if (!data) return;
timerObj.repurchase.data = data
} catch (error) {
console.error("|复购排行榜|意外的错误:", error);
}
}, timerObj.repurchase.interval)
}
// 营养分析趋势
const getNutritionAnalyseTrend = () => {
timerObj.nutritionAnalyseTrend.initial = simplePoll(async () => {
try {
const { code, data } = await nutritionApi.nutritionTrend({ ...timerObj.nutritionAnalyseTrend.queryParams });
if (code != 0) throw new Error("code!=0");
console.log("营养分析趋势:", data);
if (!data) return;
timerObj.nutritionAnalyseTrend.data = data;
} catch (error) {
console.error("|营养分析趋势|意外的错误:", error);
}
}, timerObj.nutritionAnalyseTrend.interval)
}
// 营养分析
const getNutritionAnalyse = () => {
timerObj.nutritionAnalyse.initial = simplePoll(async () => {
try {
const { code, data } = await nutritionApi.nutritionByTime({ ...timerObj.nutritionAnalyse.queryParams });
if (code != 0) throw new Error("code!=0");
console.log("营养分析:", data);
if (!data) return;
const dataKeys = Object.keys(data)
const { dateRangeType } = timerObj.nutritionAnalyse.queryParams;
// 昨天
if (dateRangeType === "YESTERDAY") {
timerObj.nutritionAnalyse.data.data = {
YESTERDAY: Object.keys(data).map(v => {
const _entity = data[v]
return {
title: _entity.typeStr,
total: _entity?.list[0]?.buyProductCount ?? 0
}
}),
}
}
// 今天
if (dateRangeType === "TODAY") {
timerObj.nutritionAnalyse.data.data = {
// 今日
TODAY: Object.keys(data).map(v => {
const _entity = data[v]
return {
title: _entity.typeStr,
total: _entity?.list[0]?.buyProductCount ?? 0
}
}),
}
}
// 本周
if (dateRangeType === "THIS_WEEK") {
timerObj.nutritionAnalyse.data.data = { THIS_WEEK: disposeBeLateTotal(data) };
timerObj.nutritionAnalyse.data.labels = {
THIS_WEEK: data[dataKeys[0]]?.list?.map((v) => {
return v.time + "|" + v.weekStr;
}),
};
}
// 本月
if (dateRangeType === "THIS_MONTH") {
timerObj.nutritionAnalyse.data.data = {
THIS_MONTH: data[dataKeys[0]]?.list?.map((v) => {
const hcCount = data.hc?.list.find((item) => item.time === item.time)?.buyProductCount ?? 0
const scCount = data.sc?.list.find((item) => item.time === item.time)?.buyProductCount ?? 0
const zsCount = data.zs?.list.find((item) => item.time === item.time)?.buyProductCount ?? 0
return {
day: v.time.slice(-2),
type: 0,
hcCount: Number(hcCount),
scCount: Number(scCount),
zsCount: Number(zsCount),
};
}),
}
timerObj.nutritionAnalyse.data.labels = {
THIS_MONTH: [],
};
}
// 本年
if (dateRangeType === "THIS_YEAR") {
timerObj.nutritionAnalyse.data.data = {
THIS_YEAR: disposeBeLateTotal(data),
};
timerObj.nutritionAnalyse.data.labels = {
THIS_YEAR: data[dataKeys[0]]?.list?.map((v) => {
return v.time.slice(0, -3);
}),
};
}
} catch (error) {
console.error("|营养分析|意外的错误:", error);
}
}, timerObj.nutritionAnalyse.interval);
}
const disposeBeLateTotal = (obj) => {
if (!obj) return;
return Object.keys(obj).map(v => {
const _entity = obj[v]
return {
text: _entity.typeStr,
name: _entity.type,
values:
_entity?.list?.map((v) => {
return Number(v.buyProductCount);
}) ?? [],
}
})
}
const nutritionAnalyseChangeDateType = (type) => {
timerObj.nutritionAnalyse.queryParams.dateRangeType = type;
timerObj.nutritionAnalyse.initial.executeOnce(type);
}
// 菜品分类购买数量
const getClassBuyCount = () => {
timerObj.classBuyCount.initial = simplePoll(async () => {
try {
const { code, data } = await nutritionApi.classCount({ ...timerObj.classBuyCount.queryParams });
if (code != 0) throw new Error("code!=0");
console.log("菜品分类购买数量:", data);
if (!data) {
return;
}
timerObj.classBuyCount.data =
data.map((v) => {
return {
title: v.classifyName,
type: v.classifyId,
total: v.buyCount,
};
}) ?? [];
console.log("timerObj.classBuyCount.data:", timerObj.classBuyCount.data);
} catch (error) {
console.error("|菜品分类购买数量|意外的错误:", error);
}
}, timerObj.classBuyCount.interval);
};
const alarmTotalByTimerChnageDateType = async (type) => {
timerObj.classBuyCount.queryParams.dateRangeType = type;
timerObj.classBuyCount.initial.executeOnce();
}
// 菜品喜好排行
const getCuisineTop = () => {
timerObj.cuisineTop.initial = simplePoll(async () => {
try {
const { code, data } = await nutritionApi.cuisineTop({ ...timerObj.cuisineTop.queryParams });
if (code != 0) throw new Error("code!=0");
console.log("菜品喜好排行:", data);
if (!data) {
return;
}
timerObj.cuisineTop.data =
data.map((v) => {
return {
title: v.productName,
type: v.productId,
total: v.buyProductCount,
};
}) ?? [];
} catch (error) {
console.error("|菜品分类购买数量|意外的错误:", error);
}
}, timerObj.cuisineTop.interval);
}
const changeCuisineTopDateType = (type) => {
timerObj.cuisineTop.queryParams.dateRangeType = type;
timerObj.cuisineTop.initial.executeOnce();
}
</script>
<style scoped lang="less">
.container {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
color: @text-color;
.aside {
height: 100%;
width: 24%;
z-index: 10;
box-sizing: border-box;
overflow: hidden;
}
.main {
height: 100%;
flex: 1;
box-sizing: border-box;
overflow: hidden;
margin: 0 1vw;
}
}
</style>