372 lines
13 KiB
Vue
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> |