更新环境监测页面和报告模块

This commit is contained in:
Xiaoyu 2025-02-18 15:36:23 +08:00
parent fca3cde8bc
commit a4496ca0fa
3 changed files with 241 additions and 253 deletions

View File

@ -3664,7 +3664,7 @@ GET /api/education/knowledge
|»» created_by|integer|false|none||none| |»» created_by|integer|false|none||none|
|»» updated_by|integer|false|none||none| |»» updated_by|integer|false|none||none|
## GET 获取知识详情 ## GET
GET /api/education/knowledge/1 GET /api/education/knowledge/1

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { ElMessage } from 'element-plus'; import { ElMessage } from "element-plus";
import { Monitor, Warning, TrendCharts } from '@element-plus/icons-vue'; import { Monitor, Warning, TrendCharts } from "@element-plus/icons-vue";
interface EnvData { interface EnvData {
id: number; id: number;
@ -65,7 +65,7 @@ const envStats = ref([
]); ]);
// //
const timeRange = ref('24h'); const timeRange = ref("24h");
// //
const initChart = () => { const initChart = () => {
@ -75,167 +75,180 @@ const initChart = () => {
const myChart = echarts.init(chartDom); const myChart = echarts.init(chartDom);
const option = { const option = {
title: { title: {
text: '环境监测趋势', text: "环境监测趋势",
textStyle: { textStyle: {
fontSize: 16, fontSize: 16,
fontWeight: 500, fontWeight: 500,
color: '#303133' color: "#303133",
} },
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
backgroundColor: 'rgba(255, 255, 255, 0.95)', backgroundColor: "rgba(255, 255, 255, 0.95)",
borderColor: '#eee', borderColor: "#eee",
padding: [10, 15], padding: [10, 15],
textStyle: { textStyle: {
color: '#666' color: "#666",
}, },
formatter: function(params: any) { formatter: function (params: any) {
let result = `${params[0].axisValue}<br/>`; let result = `${params[0].axisValue}<br/>`;
params.forEach((item: any) => { params.forEach((item: any) => {
result += `${item.marker} ${item.seriesName}: ${item.value}${ result += `${item.marker} ${item.seriesName}: ${item.value}${
item.seriesName.includes('温度') ? '°C' : item.seriesName.includes("温度")
item.seriesName.includes('湿度') ? '%' : '' ? "°C"
: item.seriesName.includes("湿度")
? "%"
: ""
}<br/>`; }<br/>`;
}); });
return result; return result;
} },
}, },
legend: { legend: {
data: ['温度', '湿度', '水质指数', '空气质量'], data: ["温度", "湿度", "水质指数", "空气质量"],
right: 20, right: 20,
top: 10, top: 10,
textStyle: { textStyle: {
color: '#666' color: "#666",
}, },
itemWidth: 12, itemWidth: 12,
itemHeight: 12, itemHeight: 12,
itemGap: 20 itemGap: 20,
}, },
grid: { grid: {
left: '3%', left: "3%",
right: '4%', right: "4%",
bottom: '3%', bottom: "3%",
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'category', type: "category",
boundaryGap: false, boundaryGap: false,
data: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00', '24:00'], data: [
"00:00",
"03:00",
"06:00",
"09:00",
"12:00",
"15:00",
"18:00",
"21:00",
"24:00",
],
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#DCDFE6' color: "#DCDFE6",
} },
}, },
axisTick: { axisTick: {
show: false show: false,
}, },
axisLabel: { axisLabel: {
color: '#909399', color: "#909399",
formatter: '{value}' formatter: "{value}",
} },
}, },
yAxis: [ yAxis: [
{ {
type: 'value', type: "value",
name: '温度/湿度', name: "温度/湿度",
nameTextStyle: { nameTextStyle: {
color: '#909399', color: "#909399",
padding: [0, 30, 0, 0] padding: [0, 30, 0, 0],
}, },
splitLine: { splitLine: {
lineStyle: { lineStyle: {
color: '#EBEEF5', color: "#EBEEF5",
type: 'dashed' type: "dashed",
} },
}, },
axisLabel: { axisLabel: {
color: '#909399', color: "#909399",
formatter: '{value}' formatter: "{value}",
} },
}, },
{ {
type: 'value', type: "value",
name: '指数', name: "指数",
nameTextStyle: { nameTextStyle: {
color: '#909399', color: "#909399",
padding: [0, 0, 0, 30] padding: [0, 0, 0, 30],
}, },
splitLine: { splitLine: {
show: false show: false,
}, },
axisLabel: { axisLabel: {
color: '#909399', color: "#909399",
formatter: '{value}' formatter: "{value}",
} },
} },
], ],
series: [ series: [
{ {
name: '温度', name: "温度",
type: 'line', type: "line",
smooth: true, smooth: true,
lineStyle: { lineStyle: {
width: 3, width: 3,
color: '#409EFF' color: "#409EFF",
}, },
itemStyle: { itemStyle: {
color: '#409EFF' color: "#409EFF",
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(64, 158, 255, 0.2)' }, { offset: 0, color: "rgba(64, 158, 255, 0.2)" },
{ offset: 1, color: 'rgba(64, 158, 255, 0)' } { offset: 1, color: "rgba(64, 158, 255, 0)" },
]) ]),
}, },
data: [22, 23, 24, 25, 26, 27, 26, 25, 24] data: [22, 23, 24, 25, 26, 27, 26, 25, 24],
}, },
{ {
name: '湿度', name: "湿度",
type: 'line', type: "line",
smooth: true, smooth: true,
lineStyle: { lineStyle: {
width: 3, width: 3,
color: '#67C23A' color: "#67C23A",
}, },
itemStyle: { itemStyle: {
color: '#67C23A' color: "#67C23A",
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(103, 194, 58, 0.2)' }, { offset: 0, color: "rgba(103, 194, 58, 0.2)" },
{ offset: 1, color: 'rgba(103, 194, 58, 0)' } { offset: 1, color: "rgba(103, 194, 58, 0)" },
]) ]),
}, },
data: [60, 62, 65, 63, 65, 68, 67, 65, 64] data: [60, 62, 65, 63, 65, 68, 67, 65, 64],
}, },
{ {
name: '水质指数', name: "水质指数",
type: 'line', type: "line",
yAxisIndex: 1, yAxisIndex: 1,
smooth: true, smooth: true,
lineStyle: { lineStyle: {
width: 3, width: 3,
color: '#36CFC9' color: "#36CFC9",
}, },
itemStyle: { itemStyle: {
color: '#36CFC9' color: "#36CFC9",
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(54, 207, 201, 0.2)' }, { offset: 0, color: "rgba(54, 207, 201, 0.2)" },
{ offset: 1, color: 'rgba(54, 207, 201, 0)' } { offset: 1, color: "rgba(54, 207, 201, 0)" },
]) ]),
}, },
data: [90, 91, 92, 92, 93, 92, 92, 91, 92] data: [90, 91, 92, 92, 93, 92, 92, 91, 92],
} },
] ],
}; };
myChart.setOption(option); myChart.setOption(option);
// //
window.addEventListener('resize', () => { window.addEventListener("resize", () => {
myChart.resize(); myChart.resize();
}); });
}; };

View File

@ -12,14 +12,14 @@ import {
Cpu, Cpu,
Link, Link,
Cellphone, Cellphone,
Share Share,
} from "@element-plus/icons-vue"; } from "@element-plus/icons-vue";
const projectInfo = ref({ const projectInfo = ref({
title: "智慧湿地管理系统", title: "智慧湿地管理系统",
version: "v2.0.0", version: "v2.0.0",
description: `智慧湿地管理系统是一个基于现代信息技术的综合性湿地生态监测和管理平台。系统采用"前后端分离+小程序"的技术架构, description: `智慧湿地管理系统是一个基于现代信息技术的综合性湿地生态监测和管理平台。系统采用"Web管理端+微信小程序"的架构,
实现了Web端管理和移动端监测的完整生态系统通过实时数据采集可视化展示等手段为湿地生态保护提供智能化解决方案`, 通过多端协同为湿地生态保护提供智能化解决方案`,
// //
stats: [ stats: [
@ -49,7 +49,7 @@ const projectInfo = ref({
change: "+15%", change: "+15%",
color: "#064E3B", color: "#064E3B",
bgColor: "linear-gradient(120deg, #4ADE80 0%, #22C55E 100%)", bgColor: "linear-gradient(120deg, #4ADE80 0%, #22C55E 100%)",
textColor: "#FFFFFF" textColor: "#FFFFFF",
}, },
{ {
title: "设备在线", title: "设备在线",
@ -59,7 +59,7 @@ const projectInfo = ref({
change: "stable", change: "stable",
color: "#EDE9FE", color: "#EDE9FE",
bgColor: "linear-gradient(120deg, #A78BFA 0%, #8B5CF6 100%)", bgColor: "linear-gradient(120deg, #A78BFA 0%, #8B5CF6 100%)",
textColor: "#FFFFFF" textColor: "#FFFFFF",
}, },
], ],
@ -73,40 +73,28 @@ const projectInfo = ref({
{ name: "Element Plus", desc: "组件库" }, { name: "Element Plus", desc: "组件库" },
{ name: "TypeScript", desc: "开发语言" }, { name: "TypeScript", desc: "开发语言" },
{ name: "ECharts", desc: "数据可视化" }, { name: "ECharts", desc: "数据可视化" },
{ name: "Vite", desc: "构建工具" } { name: "Vite", desc: "构建工具" },
] ],
}, },
backend: { backend: {
title: "后端服务", title: "后端服务",
icon: Cpu, icon: Cpu,
items: [ items: [
{ name: "Node.js", desc: "运行环境" }, { name: "Node.js", desc: "运行环境" },
{ name: "MySQL", desc: "数据存储" }, { name: "MySQL", desc: "数据库" },
{ name: "Redis", desc: "缓存服务" }, { name: "Apifox", desc: "接口管理" },
{ name: "RESTful API", desc: "接口规范" }, ],
{ name: "WebSocket", desc: "实时通信" }
]
}, },
miniapp: { miniapp: {
title: "小程序端", title: "小程序端",
icon: Cellphone, icon: Cellphone,
items: [ items: [
{ name: "微信小程序", desc: "WXML & WXSS" }, { name: "微信小程序", desc: "原生开发" },
{ name: "Vant Weapp", desc: "UI组件库" }, { name: "Vant Weapp", desc: "UI组件库" },
{ name: "wx-charts", desc: "图表库" }, { name: "wx-charts", desc: "轻量图表库" },
{ name: "WebSocket", desc: "实时通信" } { name: "ec-canvas", desc: "ECharts适配" },
] ],
}, },
deployment: {
title: "开发部署",
icon: Share,
items: [
{ name: "Git", desc: "版本控制" },
{ name: "Docker", desc: "容器化部署" },
{ name: "Nginx", desc: "Web服务器" },
{ name: "PM2", desc: "进程管理" }
]
}
}, },
// //
@ -115,33 +103,27 @@ const projectInfo = ref({
title: "Web管理端", title: "Web管理端",
icon: Monitor, icon: Monitor,
desc: "提供完整的湿地生态系统管理功能", desc: "提供完整的湿地生态系统管理功能",
features: ["数据看板", "环境监测", "物种管理", "系统配置"] features: ["数据看板", "环境监测", "物种管理", "系统配置"],
},
{
title: "环境监测",
icon: DataAnalysis,
desc: "实时采集和分析环境数据",
features: ["水质监测", "空气监测", "土壤监测", "气象监测"]
}, },
{ {
title: "小程序端", title: "小程序端",
icon: Cellphone, icon: Cellphone,
desc: "面向公众的湿地生态互动平台", desc: "面向公众的湿地生态互动平台",
features: [ features: [
"实时数据查看", "实时生态数据监测",
"生态科普", "湿地生态知识科普",
"环保打卡", "最新公告通知",
"在线咨询", "环保行为打卡",
"消息通知" "AI生态问答助手",
] ],
}, },
{ {
title: "巡护管理", title: "数据服务",
icon: Location, icon: DataAnalysis,
desc: "智能化的巡护任务管理系统", desc: "提供数据存储和分析能力",
features: ["任务分配", "轨迹记录", "实时通讯", "数据采集"] features: ["数据采集", "实时监测", "数据分析", "预警提醒"],
} },
] ],
}); });
</script> </script>
@ -160,18 +142,19 @@ const projectInfo = ref({
</div> </div>
</div> </div>
<!-- 系统架构 --> <!-- 系统架构部分 -->
<div class="architecture-section"> <div class="section architecture-section">
<h2>系统架构</h2> <h2>系统架构</h2>
<div class="arch-grid"> <div class="card-grid">
<el-card v-for="(arch, key) in projectInfo.architecture" <el-card
:key="key" v-for="(arch, key) in projectInfo.architecture"
class="arch-card" :key="key"
shadow="hover"> class="arch-card"
<div class="arch-header"> >
<el-icon :size="24" :class="key"> <div class="card-header">
<component :is="arch.icon" /> <div :class="['icon-wrapper', key]">
</el-icon> <el-icon><component :is="arch.icon" /></el-icon>
</div>
<h3>{{ arch.title }}</h3> <h3>{{ arch.title }}</h3>
</div> </div>
<ul class="tech-list"> <ul class="tech-list">
@ -184,14 +167,15 @@ const projectInfo = ref({
</div> </div>
</div> </div>
<!-- 应用场景 --> <!-- 应用场景部分 -->
<div class="scenarios-section"> <div class="section scenarios-section">
<h2>应用场景</h2> <h2>应用场景</h2>
<div class="scenario-grid"> <div class="card-grid">
<el-card v-for="scenario in projectInfo.scenarios" <el-card
:key="scenario.title" v-for="scenario in projectInfo.scenarios"
class="scenario-card" :key="scenario.title"
shadow="hover"> class="scenario-card"
>
<div class="scenario-icon"> <div class="scenario-icon">
<el-icon :size="32"> <el-icon :size="32">
<component :is="scenario.icon" /> <component :is="scenario.icon" />
@ -200,9 +184,7 @@ const projectInfo = ref({
<h3>{{ scenario.title }}</h3> <h3>{{ scenario.title }}</h3>
<p>{{ scenario.desc }}</p> <p>{{ scenario.desc }}</p>
<div class="feature-tags"> <div class="feature-tags">
<el-tag v-for="feature in scenario.features" <el-tag v-for="feature in scenario.features" :key="feature" size="small">
:key="feature"
size="small">
{{ feature }} {{ feature }}
</el-tag> </el-tag>
</div> </div>
@ -214,6 +196,108 @@ const projectInfo = ref({
<style lang="scss" scoped> <style lang="scss" scoped>
.about-container { .about-container {
padding: 20px;
.section {
margin-bottom: 60px;
h2 {
font-size: 24px;
margin-bottom: 30px;
text-align: center;
color: #303133;
}
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
max-width: 1200px;
margin: 0 auto;
padding: 0 24px; //
}
}
//
.el-card {
border: none;
border-radius: 12px;
transition: all 0.3s;
height: 100%;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
.icon-wrapper {
width: 64px;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16px;
font-size: 24px;
&.frontend {
background: rgba(24, 144, 255, 0.1);
color: #1890ff;
}
&.backend {
background: rgba(82, 196, 26, 0.1);
color: #52c41a;
}
&.miniapp {
background: rgba(250, 84, 28, 0.1);
color: #fa541c;
}
}
h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
}
}
//
.tech-list {
list-style: none;
padding: 0;
margin: 0;
li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.tech-name {
font-weight: 500;
color: #303133;
}
.tech-desc {
font-size: 13px;
color: #909399;
}
}
}
.hero-section { .hero-section {
padding: 40px; padding: 40px;
margin: -20px -20px 20px; margin: -20px -20px 20px;
@ -264,105 +348,7 @@ const projectInfo = ref({
} }
} }
.architecture-section {
margin-bottom: 60px;
h2 {
font-size: 24px;
margin-bottom: 30px;
text-align: center;
color: #303133;
}
.arch-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.arch-card {
.arch-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
.el-icon {
padding: 12px;
border-radius: 8px;
&.frontend {
background: rgba(24, 144, 255, 0.1);
color: #1890FF;
}
&.backend {
background: rgba(82, 196, 26, 0.1);
color: #52C41A;
}
&.miniapp {
background: rgba(250, 84, 28, 0.1);
color: #FA541C;
}
&.tools {
background: rgba(114, 46, 209, 0.1);
color: #722ED1;
}
}
h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
}
.tech-list {
list-style: none;
padding: 0;
margin: 0;
li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.tech-name {
font-weight: 500;
color: #303133;
}
.tech-desc {
font-size: 13px;
color: #909399;
}
}
}
}
}
.scenarios-section { .scenarios-section {
h2 {
font-size: 24px;
margin-bottom: 30px;
text-align: center;
color: #303133;
}
.scenario-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.scenario-card { .scenario-card {
text-align: center; text-align: center;
padding: 30px; padding: 30px;
@ -376,7 +362,7 @@ const projectInfo = ref({
justify-content: center; justify-content: center;
border-radius: 16px; border-radius: 16px;
background: rgba(24, 144, 255, 0.1); background: rgba(24, 144, 255, 0.1);
color: #1890FF; color: #1890ff;
} }
h3 { h3 {
@ -403,16 +389,5 @@ const projectInfo = ref({
} }
} }
} }
.el-card {
border: none;
border-radius: 12px;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
}
} }
</style> </style>