Files
bigscreen/src/views/Home.vue
T
2025-11-04 17:09:37 +08:00

553 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="dashboard">
<h2 class="page-title">营养监控项目运营核心数据总览</h2>
<h4 class="time-title">{{ stResourceBasicStats.updateTime || '暂无数据'}}</h4>
<div class="cards">
<!-- 基础资源 -->
<section class="card">
<header class="card-header card-header--blue"><span>基础资源</span></header>
<div class="card-body">
<ul class="list">
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>基础规模</div>
<div class="sub-row">
<div class="sub-col">
<span>单位数</span><span class="num">{{ stResourceBasicStats.unitCount || 0 }}</span><span> </span>
</div>
<div class="sub-col">
<span>食堂数</span><span class="num">{{ stResourceBasicStats.canteenCount || 0 }}</span><span>
</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>餐品资源</div>
<div class="sub-row">
<div class="sub-col">
<span>餐品总数</span><span class="num">{{ stResourceBasicStats.foodTotal || 0 }}</span><span> </span>
</div>
<div class="sub-col">
<span>食堂餐品</span><span class="num">{{ stResourceBasicStats.canteenFood || 0 }}</span><span>
</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>使用数据</div>
<div class="sub-row">
<div class="sub-col">
<span>总人数</span><span class="num">{{ stResourceBasicStats.totalPeople || 0 }}</span><span> </span>
</div>
<div class="sub-col">
<span>本周总使用人次</span><span class="num">{{ stResourceBasicStats.weekUsage || 0 }}</span><span>
较上周</span><span :class="stResourceBasicStats.weekUsageRate > 0 ? 'red' : 'green'">{{
stResourceBasicStats.weekUsageRate || 0 }}%</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>设备配置</div>
<div class="sub-row">
<div class="sub-col">
<span>餐线数</span><span class="num">{{ stResourceBasicStats.mealLine || 0 }}</span><span> </span>
</div>
</div>
<div class="sub-title">
<span>包含设备</span>
</div>
<div class="equip-grid">
<div>D款<span>{{ stResourceBasicStats.deviceD || 0 }}</span> </div>
<div>H款<span>{{ stResourceBasicStats.deviceH || 0 }}</span> </div>
<div>F款<span>{{ stResourceBasicStats.deviceF || 0 }}</span> </div>
<div>绑盘机款<span>{{ stResourceBasicStats.deviceDish || 0 }}</span> </div>
<div>落地屏<span>{{ stResourceBasicStats.deviceScreen || 0 }}</span> </div>
</div>
</div>
</li>
</ul>
</div>
</section>
<!-- 核心运营 -->
<section class="card">
<header class="card-header card-header--green"><span>核心运营</span></header>
<div class="card-body">
<ul class="list">
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>运营周期</div>
<div class="sub-row">
<div class="sub-col">
<span>总运行天数</span><span class="num">{{ stOperationCoreStats.operationDays || 0 }}</span><span>
</span>
</div>
<div class="sub-col"></div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>重量消耗</div>
<div class="sub-row">
<div class="sub-col">
<span>总称餐重量</span><span class="num">{{ fmtNum(stOperationCoreStats.totalFoodWeight) }}</span><span>
kg</span>
</div>
<div class="sub-col">
<span>总用餐重量</span><span class="num">{{ fmtNum(stOperationCoreStats.totalMealWeight) }}</span><span>
kg</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>用餐数据</div>
<div class="sub-row">
<div class="sub-col">
<span>总用餐人次数</span><span class="num">{{ fmtNum(stOperationCoreStats.mealPeople) }}</span><span>
</span>
</div>
<div class="sub-col">
<span>总用餐热量</span><span class="num">{{ fmtNum(stOperationCoreStats.mealEnergy) }}</span><span>
兆卡</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>数据资产</div>
<div class="sub-title">
<span>总数据量</span>
</div>
<div class="sub-row">
<div class="sub-col indent">
<span>存储空间</span><span class="num">{{ stOperationCoreStats.dataStorage || 0 }}</span><span> {{
stOperationCoreStats.dataStorageUnit || 'Gb' }}</span>
</div>
</div>
<div class="sub-title">
<span>总数据条</span>
</div>
<div class="sub-row">
<div class="sub-col indent">
<span>就餐记录</span><span class="num">{{ stOperationCoreStats.mealRecord || 0 }}</span><span> </span>
</div>
</div>
</div>
</li>
</ul>
</div>
</section>
<!-- 人均洞察 -->
<section class="card">
<header class="card-header card-header--orange"><span>人均洞察</span></header>
<div class="card-body">
<ul class="list">
<li class="item">
<div class="item-content">
<div class="item-title"><span class="sq"></span>人均指标<span class="unit">(单位:重量g/热量kcal)</span></div>
<div class="group">
<div class="item-sub group-title">日平均(本周/上周)</div>
<div class="sub-row indent">
<!-- <div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.dailyAvgWeight || 0 }}</span><span>
g</span><span> / </span><span
:class="stInsightPerCapitaStats.dailyAvgWeightChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.dailyAvgWeightChange || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.dailyAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span
:class="stInsightPerCapitaStats.dailyAvgEnergyChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.dailyAvgEnergyChange || 0 }}</span><span> kcal</span>
</div> -->
<div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.dailyAvgWeight || 0 }}</span><span>
g</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.dailyAvgWeightLast || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.dailyAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.dailyEnergyLast || 0 }}</span><span> kcal</span>
</div>
</div>
</div>
<div class="group">
<div class="item-sub group-title">早餐平均(本周/上周)</div>
<div class="sub-row indent">
<!-- <div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.morningAvgWeight || 0 }}</span><span>
g</span><span> / </span><span
:class="stInsightPerCapitaStats.morningAvgWeightChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.morningAvgWeightChange || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.morningAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span
:class="stInsightPerCapitaStats.morningAvgEnergyChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.morningAvgEnergyChange || 0 }}</span><span> kcal</span>
</div> -->
<div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.morningAvgWeight || 0 }}</span><span>
g</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.breakfastAvgWeightLast || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.morningAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.breakfastEnergyLast || 0 }}</span><span> kcal</span>
</div>
</div>
</div>
<div class="group">
<div class="item-sub group-title">午餐平均(本周/上周)</div>
<div class="sub-row indent">
<!-- <div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.lunchAvgWeight || 0 }}</span><span>
g</span><span> / </span><span
:class="stInsightPerCapitaStats.lunchAvgWeightChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.lunchAvgWeightChange || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.lunchAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span
:class="stInsightPerCapitaStats.lunchAvgEnergyChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.lunchAvgEnergyChange || 0 }}</span><span> kcal</span>
</div> -->
<div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.lunchAvgWeight || 0 }}</span><span>
g</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.lunchAvgAvgWeightLast || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.lunchAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.lunchEnergyLast || 0 }}</span><span> kcal</span>
</div>
</div>
</div>
<div class="group">
<div class="item-sub group-title">晚餐平均(本周/上周)</div>
<div class="sub-row indent">
<!-- <div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.dinnerAvgWeight || 0 }}</span><span>
g</span><span> / </span><span
:class="stInsightPerCapitaStats.dinnerAvgWeightChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.dinnerAvgWeightChange || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.dinnerAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span
:class="stInsightPerCapitaStats.dinnerAvgEnergyChange > 0 ? 'red' : 'green'">{{
stInsightPerCapitaStats.dinnerAvgEnergyChange || 0 }}</span><span> kcal</span>
</div> -->
<div class="sub-col">
<span>重量</span><span class="num">{{ stInsightPerCapitaStats.dinnerAvgWeight || 0 }}</span><span>
g</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.dinnerAvgWeightLast || 0 }}</span><span> g</span>
</div>
<div class="sub-col">
<span>热量</span><span class="num">{{ stInsightPerCapitaStats.dinnerAvgEnergy || 0 }}</span><span>
kcal</span><span> / </span><span class="num">{{ stInsightPerCapitaStats.dinnerEnergyLast || 0 }}</span><span> kcal</span>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
</section>
</div>
</div>
</template>
<script>
import { getStResourceBasicStats, getStOperationCoreStats, getStInsightPerCapitaStats } from '@/api/api'
export default {
name: 'Home',
data() {
return {
stResourceBasicStats: {},
stOperationCoreStats: {},
stInsightPerCapitaStats: {}
}
},
async mounted() {
await this.refreshAll()
this._hourlyTimer = setInterval(() => {
this.refreshAll()
}, 3600000)
},
methods: {
async refreshAll() {
await Promise.all([
this.loadBasicStatsData(),
this.loadCoreStatsData(),
this.loadCapitaStatsData()
])
},
async loadBasicStatsData() {
try {
const res = await getStResourceBasicStats({})
this.stResourceBasicStats = res.result || {}
} catch (err) { console.error(err) }
},
async loadCoreStatsData() {
try {
const res = await getStOperationCoreStats({})
this.stOperationCoreStats = res.result || {}
} catch (err) { console.error(err) }
},
async loadCapitaStatsData() {
try {
const res = await getStInsightPerCapitaStats({})
this.stInsightPerCapitaStats = res.result || {}
} catch (err) { console.error(err) }
},
fmtNum(n) {
if (n === undefined || n === null) return 0
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
}
}
</script>
<style scoped>
.dashboard {
min-height: 100vh;
padding: 48px 24px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.page-title {
text-align: center;
font-size: 36px;
font-weight: 600;
margin: 12px 0 20px 0;
color: #2b2b2b;
}
.time-title{
text-align: center;
font-size: 24px;
font-weight: 600;
margin: 12px 0 10px 0;
color: #2b2b2b;
}
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
}
.card {
background: #fff;
border-radius: 14px;
box-shadow: 0 6px 20px rgba(25, 55, 88, 0.08);
overflow: hidden;
}
.card-header {
height: 70px;
display: flex;
align-items: center;
padding: 0 18px;
color: #fff;
font-weight: 400;
font-size: 24px;
justify-content: center;
}
.card-header--blue {
background: linear-gradient(90deg, #3569E6 0%, #519DF9 100%);
}
.card-header--green {
background: linear-gradient(90deg, #00C27F 0%, #1AD0CE 100%);
}
.card-header--orange {
background: linear-gradient(90deg, #E26229 0%, #F29554 100%);
}
.card-body {
padding: 16px 18px 22px;
}
.list {
list-style: none;
margin: 0;
padding: 0;
}
.item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 0;
}
.item-content {
flex: 1;
}
.item-title {
font-weight: bold;
font-size: 16px;
color: #000;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.unit{
font-size: 14px;
font-weight: normal;
color: #5a6678;
}
.sq {
width: 8px;
height: 8px;
border-radius: 2px;
display: inline-block;
background: #366AE6;
}
.num {
font-weight: 700;
color: #000;
}
.item-sub {
color: #5a6678;
line-height: 22px;
padding: 0 15px;
font-size: 14px;
}
.red {
color: #E63838 !important;
font-weight: bold;
}
.green {
color: #0DA700 !important;
font-weight: bold;
}
.group-title {
padding-left: 15px;
margin-bottom: 20px;
font-weight: 400;
font-size: 14px;
color: #000;
}
.sub-row {
display: flex;
gap: 24px;
padding: 2px 15px;
margin-bottom: 15px;
}
.sub-col {
flex: 1;
text-align: left;
min-width: 0;
}
.indent {
text-indent: 1em;
}
.sub-col span {
color: #5a6678;
}
.sub-col .num {
color: #000;
}
.sub-title {
padding: 2px 15px;
}
.sub-title span {
font-weight: 400;
font-size: 14px;
color: #000;
}
.cards .card:nth-child(-n+2) .list .item:nth-child(3) .sub-row {
display: block;
}
.cards .card:nth-child(-n+2) .list .item:nth-child(3) .sub-col {
display: block;
width: 100%;
margin-bottom: 10px;
}
.equip-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
padding: 10px 2em;
gap: 6px 12px;
margin-top: 6px;
color: #5a6678;
font-size: 16px;
}
.equip-grid span {
font-weight: bold;
color: #000;
}
.trend {
font-weight: 400;
}
.trend--up {
color: #0DA700;
}
.trend--down {
color: #E63838;
}
.pos {
color: #E63838 !important;
}
.neg {
color: #0DA700 !important;
}
@media (max-width: 1024px) {
.cards {
grid-template-columns: 1fr;
}
}
</style>