完成统计分析

This commit is contained in:
bicey 2024-05-25 14:59:03 +08:00
parent 7e2ae9e565
commit a5de0b4987
7 changed files with 442 additions and 43 deletions

View File

@ -14,7 +14,7 @@
4. 健康监测与预警(前端基本完成,未接入后端)
系统应具备健康监测功能,定期收集和分析动物的健康数据,如体温、心率、呼吸频率等。同时,系统应提供预警功能,当动物的健康数据出现异常时,及时提醒管理人员采取相应措施。
5. 统计分析(未开始
5. 统计分析(前端基本完成,无需后端
系统应提供统计分析功能,对动物信息、饲养记录、健康数据等进行统计分析,生成各类报表和图表。这有助于管理人员了解动物的整体状况和饲养效果,为决策提供支持。
6. 用户权限管理(前端基本完成,未接入后端)
@ -223,7 +223,7 @@ health: {
## 统计分析
---
无需后端
## 需求分析

View File

@ -3,6 +3,7 @@
/*浏览器默认margin为8px会出现滚动条的问题因此此处设置为0*/
body {
margin: 0;
background-color: aliceblue;
}
/*相对定位*/
@ -41,7 +42,7 @@ body {
/*与pages组件相关的所有样式*/
.table {
height: 506px;
height: 516px;
margin: 0 30px;
}

View File

@ -1,8 +1,10 @@
<script>
import {mapState} from "vuex";
import {Menu, User} from "@element-plus/icons-vue";
export default {
name: "ZooAside",
components: {User, Menu},
data() {
return {}
},
@ -16,7 +18,9 @@ export default {
this.$router.push(path)
}
},
watch: {}
mounted() {
console.log('auth', this.loginUser)
}
}
</script>
@ -42,30 +46,14 @@ export default {
<el-sub-menu index="1">
<template #title="scope">
<el-icon>
<location/>
<Menu/>
</el-icon>
<span>管理员</span>
<span>用户功能</span>
</template>
<el-menu-item index="/panel/home" @click="toPath('/panel/home')">首页</el-menu-item>
<el-menu-item index="/panel/user" @click="toPath('/panel/user')">用户管理</el-menu-item>
<el-menu-item index="/panel/user" @click="toPath('/panel/user')" v-if="loginUser.auth===0">用户管理</el-menu-item>
<el-menu-item index="/panel/animal" @click="toPath('/panel/animal')">动物基本数据</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon>
<location/>
</el-icon>
<span>饲养员</span>
</template>
<el-menu-item index="/panel/breeding" @click="toPath('/panel/breeding')">饲养计划</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<el-icon>
<location/>
</el-icon>
<span>兽医</span>
</template>
<el-menu-item index="/panel/archive" @click="toPath('/panel/archive')">动物档案</el-menu-item>
<el-menu-item index="/panel/health" @click="toPath('/panel/health')">健康检测</el-menu-item>
</el-sub-menu>
@ -75,6 +63,9 @@ export default {
</template>
<style scoped>
#aside{
background-color: white;
}
.aside-icon {
width: 200px;
margin-top: 20px;

View File

@ -1,24 +1,412 @@
<script>
import {mapState} from "vuex";
import {Minus, Plus} from "@element-plus/icons-vue";
export default {
name: "ZooHome",
data() {
return {
adminCount: 0,
keeperCount: 0,
veterinaryCount: 0,
speciesCount: 0,
normalCount: 0,
abnormalCount: 0,
foodCount: Math.floor((Math.random() * 201)),//
foodCapacity: 200,//
breedingAnimalCount: 0,//
healthAnimalCount: 0,//
handleAnimalCount: 0,//
percentageColors: [
{color: '#f56c6c', percentage: 30},
{color: '#e6a23c', percentage: 60},
{color: '#5cb87a', percentage: 90},
],
childhoodCount: 0,//
adolescenceCount: 0,//
adulthoodCount: 0,//
oldCount: 0,//
}
},
methods: {
watch: {
users: {
immediate: true,
handler(value) {
this.adminCount = 0
this.keeperCount = 0
this.veterinaryCount = 0
this.users.forEach(e => {
if (e.auth === 0) {
this.adminCount++
} else if (e.auth === 1) {
this.keeperCount++
} else if (e.auth === 2) {
this.veterinaryCount++
}
})
}
},
animals: {
immediate: true,
handler(value) {
this.speciesCount = 0
const speciesSet = new Set()
this.normalCount = 0
this.abnormalCount = 0
this.childhoodCount = 0
this.adolescenceCount = 0
this.adulthoodCount = 0
this.oldCount = 0
this.animals.forEach(e => {
speciesSet.add(e.species)
if (e.state === 0) {
this.normalCount++
} else if (e.state === 1) {
this.abnormalCount++
}
if (e.phase === '幼年期') {
this.childhoodCount++
} else if (e.phase === '成长期') {
this.adolescenceCount++
} else if (e.phase === '成年期') {
this.adulthoodCount++
} else if (e.phase === '老年期') {
this.oldCount++
}
})
this.speciesCount = speciesSet.size
this.breedingAnimalCount = Number((Math.random() * this.animals.length).toFixed(0))
}
}
},
computed: {
Plus() {
return Plus
},
Minus() {
return Minus
},
...mapState(["users", 'animals']),
},
methods: {
decreaseFood() {
if (this.foodCount > 0) {
this.foodCount--
}
},
increaseFood() {
if (this.foodCount < this.foodCapacity) {
this.foodCount++
}
},
decreaseBreeding(){
if (this.breedingAnimalCount > 0) {
this.breedingAnimalCount--
}
},
increaseBreeding(){
if (this.breedingAnimalCount < this.animals.length) {
this.breedingAnimalCount++
}
},
decreaseHealth(){
if (this.healthAnimalCount > 0) {
this.healthAnimalCount--
}
},
increaseHealth(){
if (this.healthAnimalCount < this.animals.length) {
this.healthAnimalCount++
}
},
decreaseHandle(){
if (this.handleAnimalCount > 0) {
this.handleAnimalCount--
}
},
increaseHandle(){
if (this.handleAnimalCount < this.abnormalCount) {
this.handleAnimalCount++
}
}
}
}
</script>
<template>
<div>
这是home界面
<div id="home-root">
<div class="statistics" style="padding-bottom: 20px">
<el-row>
<el-col :span="24">
<div class="title">用户统计</div>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-statistic title="系统用户总数" :value="users.length"/>
</el-col>
<el-col :span="6">
<el-statistic :value="adminCount" :value-style="{color:'#409EFF'}">
<template #title>
<span style="color: #409EFF">管理员数量</span>
</template>
<template #suffix>
<span style="color: #409EFF" v-if="users.length===0">(0%)</span>
<span style="color: #409EFF" v-else>
({{ (adminCount / users.length * 100).toFixed(1) }}%)
</span>
</template>
</el-statistic>
</el-col>
<el-col :span="6">
<el-statistic :value="keeperCount" :value-style="{color:'#67C23A'}">
<template #title>
<span style="color: #67C23A">饲养员数量</span>
</template>
<template #suffix>
<span style="color: #67C23A" v-if="users.length===0">(0%)</span>
<span style="color: #67C23A" v-else>
({{ (keeperCount / users.length * 100).toFixed(1) }}%)
</span>
</template>
</el-statistic>
</el-col>
<el-col :span="6">
<el-statistic :value="keeperCount" :value-style="{color:'#E6A23C'}">
<template #title>
<span style="color: #E6A23C">兽医数量</span>
</template>
<template #suffix>
<span style="color: #E6A23C" v-if="users.length===0">(0%)</span>
<span style="color: #E6A23C" v-else>
({{ (veterinaryCount / users.length * 100).toFixed(1) }}%)
</span>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
<div class="statistics" style="margin-bottom: 0">
<el-row>
<el-col :span="24">
<div class="title">动物统计</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-row>
<el-col :span="6">
<el-statistic title="动物总数" :value="animals.length"/>
</el-col>
<el-col :span="6">
<el-statistic :value="speciesCount" title="动物种类"/>
</el-col>
<el-col :span="6">
<el-statistic :value="normalCount" :value-style="{color:'#67C23A'}">
<template #title>
<span style="color: #67C23A">健康动物</span>
</template>
<template #suffix>
<span style="color: #67C23A" v-if="animals.length===0">(0%)</span>
<span style="color: #67C23A" v-else>({{ (normalCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
<el-col :span="6">
<el-statistic :value="abnormalCount" :value-style="{color:'#f56c6c'}">
<template #title>
<span style="color: #f56c6c">异常动物</span>
</template>
<template #suffix>
<span style="color: #f56c6c" v-if="animals.length===0">(0%)</span>
<span style="color: #f56c6c" v-else>({{ (abnormalCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<div class="statistics">
<el-row>
<el-col :span="24">
<div class="title">日常统计</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-progress type="dashboard" :percentage="Number((breedingAnimalCount/animals.length*100).toFixed(1))"
:color="percentageColors">
<template #default="{ percentage }">
<span class="percentage-label">今日饲养进度</span>
<span class="percentage-value" v-if="!percentage">0%</span>
<span class="percentage-value" v-else>{{ percentage }}%</span>
<span class="percentage-label">{{ breedingAnimalCount }}/{{ animals.length }}</span>
<span v-if="percentage>=0">
<el-button :icon="Minus" @click="decreaseBreeding" size="small" style="width: 10px"/>
<el-button :icon="Plus" @click="increaseBreeding" size="small" style="width: 10px"/>
</span>
</template>
</el-progress>
</el-col>
<el-col :span="12">
<el-progress type="dashboard" :percentage="Number((foodCount/foodCapacity*100).toFixed(1))"
:color="percentageColors">
<template #default="{ percentage }">
<span class="percentage-label">食物库存</span>
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">{{ foodCount }}/{{ foodCapacity }}</span>
<span v-if="percentage>=0">
<el-button :icon="Minus" @click="decreaseFood" size="small" style="width: 10px"/>
<el-button :icon="Plus" @click="increaseFood" size="small" style="width: 10px"/>
</span>
</template>
</el-progress>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-progress type="dashboard" :percentage="Number((healthAnimalCount/animals.length*100).toFixed(1))"
:color="percentageColors">
<template #default="{ percentage }">
<span class="percentage-label">今日监测进度</span>
<span class="percentage-value" v-if="!percentage">0%</span>
<span class="percentage-value" v-else>{{ percentage }}%</span>
<span class="percentage-label">{{ healthAnimalCount }}/{{ animals.length }}</span>
<span v-if="percentage>=0">
<el-button :icon="Minus" @click="decreaseHealth" size="small" style="width: 10px"/>
<el-button :icon="Plus" @click="increaseHealth" size="small" style="width: 10px"/>
</span>
</template>
</el-progress>
</el-col>
<el-col :span="12">
<el-progress type="dashboard" :percentage="Number((handleAnimalCount/abnormalCount*100).toFixed(1))"
:color="percentageColors">
<template #default="{ percentage }">
<span class="percentage-label">异常处理进度</span>
<span class="percentage-value" v-if="!percentage">0%</span>
<span class="percentage-value" v-else>{{ percentage }}%</span>
<span class="percentage-label">{{ handleAnimalCount }}/{{ abnormalCount }}</span>
<span v-if="percentage>=0">
<el-button :icon="Minus" @click="decreaseHandle" size="small" style="width: 10px"/>
<el-button :icon="Plus" @click="increaseHandle" size="small" style="width: 10px"/>
</span>
</template>
</el-progress>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="12">
<div class="statistics">
<el-row>
<el-col :span="24">
<div class="title">生命阶段统计</div>
</el-col>
</el-row>
<el-row style="padding: 40px 0">
<el-col :span="12">
<el-statistic :value="childhoodCount" :value-style="{color:'#67C23A'}">
<template #title>
<span style="color: #67C23A">幼年期</span>
</template>
<template #suffix>
<span style="color: #67C23A" v-if="animals.length===0">(0%)</span>
<span style="color: #67C23A" v-else>({{ (childhoodCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
<el-col :span="12">
<el-statistic :value="adolescenceCount" :value-style="{color:'#409EFF'}">
<template #title>
<span style="color: #409EFF">成长期</span>
</template>
<template #suffix>
<span style="color: #409EFF" v-if="animals.length===0">(0%)</span>
<span style="color: #409EFF" v-else>({{ (adolescenceCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
</el-row>
<el-row style="padding: 40px 0">
<el-col :span="12">
<el-statistic :value="adulthoodCount" :value-style="{color:'#E6A23C'}">
<template #title>
<span style="color: #E6A23C">成年期</span>
</template>
<template #suffix>
<span style="color: #E6A23C" v-if="animals.length===0">(0%)</span>
<span style="color: #E6A23C" v-else>({{ (adulthoodCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
<el-col :span="12">
<el-statistic :value="oldCount" :value-style="{color:'#909399'}">
<template #title>
<span style="color: #909399">老年期</span>
</template>
<template #suffix>
<span style="color: #909399" v-if="animals.length===0">(0%)</span>
<span style="color: #909399" v-else>({{ (oldCount / animals.length * 100).toFixed(1) }}%)</span>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<style scoped>
#home-root {
margin: 20px;
background-color: white;
border: gray 1px solid;
}
.statistics {
border: darkgray 1px solid;
margin: 20px;
}
.title {
margin-top: 10px;
font-size: 20px;
}
.statistics, .title {
text-align: center;
}
.percentage-value {
display: block;
margin-top: 10px;
font-size: 28px;
}
.percentage-label {
display: block;
margin-top: 10px;
font-size: 12px;
}
.clear::after {
content: "";
clear: both;
display: block;
}
</style>

View File

@ -1,6 +1,6 @@
<script>
import {mapMutations,mapState,mapActions} from "vuex";
import {mapMutations, mapState, mapActions} from "vuex";
export default {
name: "ZooContainer",
@ -10,7 +10,13 @@ export default {
form: {
username: '',
password: '123456',
}
},
picture: [
'https://www4.bing.com//th?id=OHR.BambooPanda_ZH-CN8455481760_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp',
'https://www4.bing.com//th?id=OHR.PolarBearCubs_ZH-CN2913942257_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp',
'https://www4.bing.com//th?id=OHR.WhiteEyes_ZH-CN1130380430_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp',
'https://www4.bing.com//th?id=OHR.ZebraCousins_ZH-CN8159888859_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp'
]
}
},
computed: {
@ -21,7 +27,7 @@ export default {
...mapMutations(['updateLoginUser']),
//
login() {
console.log('登录',this.form)
console.log('登录', this.form)
//
if (!this.form.username || !this.form.password) {
//
@ -60,16 +66,23 @@ export default {
<template>
<div id="login-root">
<!-- 背景图片-->
<el-carousel height="400px" motion-blur>
<el-carousel-item v-for="item in 4" :key="item">
<h3 class="small justify-center" text="2xl">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
<!-- 登录表单-->
<!-- 背景图片-->
<!-- <img src="https://www4.bing.com//th?id=OHR.BambooPanda_ZH-CN8455481760_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp" width="90%" height="auto" class="center"/>-->
<div>
<el-carousel motion-blur height="700px" style="width: auto;">
<el-carousel-item v-for="(item,index) in picture" :key="index" style="text-align: center">
<img :src="item" width="auto" height="100%"/>
</el-carousel-item>
</el-carousel>
</div>
<!-- 登录表单-->
<div class="center">
<el-card style="width: 480px" shadow="always" header="登录">
<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-card style="width: 350px" shadow="always">
<template #header>
<div style="text-align: center;font-size: 25px">登录</div>
</template>
<el-form :model="form" label-width="auto" style="width: 400px">
<el-form-item label="用户名">
<el-input v-model="form.username" style="width: 240px" placeholder="请输入用户名"/>
</el-form-item>
@ -97,6 +110,10 @@ export default {
#login-root {
width: 100%;
height: 700px;
background-color: skyblue;
background-color: aliceblue;
}
.center {
opacity: 0.95;
}
</style>

View File

@ -50,15 +50,15 @@ export default {
top: 0;
}
.footer {
height: 30px;
height: 20px;
bottom: 0;
text-align: center;
line-height: 30px;
line-height: 20px;
background-color: lightgray;
}
.main {
/*有footer*/
height: 606px;
height: 616px;
/*没footer*/
/*height: 636px;*/

View File

@ -3,7 +3,7 @@ import {copy, generateAnimals, generateArchives, generateBreedingPlans, generate
const actions = {
//获取登录用户
getLoginUser(context, user) {
getLoginUser(context, value) {
//此处接入后端登录接口验证登录用户名和密码,验证通过方可通行
//密码错误
if (false) {
@ -12,6 +12,8 @@ const actions = {
type: 'error',
})
}
const user = context.state.users.find(user => user.username === value.username)
user.auth = 0
//登录成功
context.commit('updateLoginUser', user);//存储登录用户,这里拉取远程登录的用户作为参数
},