智慧种植页面首页 田间监测页面
18532
sub-operation-service/src/assets/530926geo.json
Normal file
BIN
sub-operation-service/src/assets/images/closing.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
sub-operation-service/src/assets/images/down_1@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/bell.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/fall.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/goDown.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/goUp.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/location.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/rainy.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/rise.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/sunny.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.6 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/testPic1.png
Normal file
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 13 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/windy.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/产能预测.png
Normal file
After Width: | Height: | Size: 602 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/传感器.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/光照传感器.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/土壤温度.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/土壤湿度.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/排风.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/监控.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/空气.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
sub-operation-service/src/assets/images/smartFarm/蒸腾.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
@ -6,23 +6,36 @@
|
||||
* @LastEditTime: 2024-01-27 16:07:37
|
||||
-->
|
||||
<template>
|
||||
<template v-if="!item.hidden">
|
||||
<template v-if="!item.alwaysShow && hasOneShowingChild(item.children, item)">
|
||||
<layout-link v-if="onlyOneChild.meta" :to="onlyOneChild.path">
|
||||
<el-menu-item :index="onlyOneChild.path">
|
||||
<layout-icon :size="20" :icon="onlyOneChild?.meta?.icon" />
|
||||
<template #title>{{ onlyOneChild.meta && onlyOneChild.meta?.title }}</template>
|
||||
</el-menu-item>
|
||||
</layout-link>
|
||||
</template>
|
||||
<el-sub-menu v-else :index="item.path" teleported>
|
||||
<template v-if="item.children">
|
||||
<!-- <template v-if="!item.alwaysShow && hasOneShowingChild(item.children, item)">-->
|
||||
<!-- <layout-link :to="onlyOneChild.path">-->
|
||||
<!-- <el-menu-item :index="onlyOneChild.path">-->
|
||||
<!-- <template #title>{{ onlyOneChild.meta && onlyOneChild.meta?.title }}</template>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- </layout-link>-->
|
||||
<!-- </template>-->
|
||||
<el-sub-menu :index="item.path" teleported>
|
||||
<template #title>
|
||||
<layout-icon :size="20" :icon="item?.meta?.icon" />
|
||||
<span>{{ item.meta && item.meta?.title }}</span>
|
||||
<!-- 左侧图标 -->
|
||||
<img :src="getAssetsFile('images/smartFarm/' + item.icon)?.href ?? ''" alt="" />
|
||||
<span>{{ item.title }}</span>
|
||||
<!-- <layout-icon :size="20" :icon="item?.meta?.icon" />-->
|
||||
<!-- <span>{{ item.meta && item.meta?.title }}</span>-->
|
||||
</template>
|
||||
<!-- 右侧图标(替换默认箭头)没生效 -->
|
||||
<template #expand-icon>
|
||||
<img alt="" :src="getAssetsFile('images/smartFarm/closing.png')" class="isOpen" />
|
||||
</template>
|
||||
<sub-item v-for="child in item.children" :key="child.path" :item="child" />
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-menu-item-group :title="item.title">
|
||||
<el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
|
||||
<span style="color: black !important">{{ child.title }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup name="sub-item">
|
||||
@ -30,6 +43,7 @@ import { ref } from 'vue';
|
||||
// import { isExternal } from '@/utils/validate.js';
|
||||
import LayoutLink from './Link';
|
||||
import LayoutIcon from './Icon';
|
||||
import { getAssetsFile } from '@/utils/index.js';
|
||||
// import path from 'path-browserify';
|
||||
|
||||
defineProps({
|
||||
@ -79,3 +93,35 @@ const hasOneShowingChild = (children = [], parent) => {
|
||||
// return path.resolve(props.basePath, routePath);
|
||||
// };
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 强制覆盖默认样式 */
|
||||
.isOpen {
|
||||
width: 16px !important; /* 设置图片宽度 */
|
||||
height: 16px !important; /* 设置图片高度 */
|
||||
margin-left: auto !important; /* 让图标靠右 */
|
||||
display: block !important; /* 避免被隐藏 */
|
||||
transition: transform 0.3s; /* 添加旋转动画 */
|
||||
}
|
||||
|
||||
/* 修复父元素可能存在的样式冲突 */
|
||||
.el-sub-menu__title {
|
||||
overflow: visible !important; /* 防止图片被裁剪 */
|
||||
}
|
||||
|
||||
.item-children {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
margin-top: 8px;
|
||||
transition: transform 0.3s ease;
|
||||
.dot {
|
||||
display: inline-block;
|
||||
height: 4px;
|
||||
margin-right: 15px;
|
||||
width: 4px;
|
||||
border-radius: 90px;
|
||||
background-color: black;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 600px; height: 150px"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const chartRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
const myChart = echarts.init(chartRef.value);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
||||
axisLine: {
|
||||
show: false, // 隐藏X轴线
|
||||
},
|
||||
axisTick: {
|
||||
show: false, // 隐藏X轴刻度
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666', // 月份标签颜色
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
show: false, // 完全隐藏Y轴
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [120, 200, 150, 80, 70, 110], // 这里替换为你的实际数据
|
||||
type: 'bar',
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
color: '#4CAF50', // 绿色柱状条
|
||||
borderRadius: [6, 6, 6, 6], // 圆角设置(左上、右上、右下、左下)
|
||||
},
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: 'rgba(180, 180, 180, 0.2)', // 背景色
|
||||
borderRadius: [6, 6, 6, 6], // 背景圆角(与柱子相同)
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'top',
|
||||
formatter: '', // 你可以根据需要调整或移除
|
||||
},
|
||||
},
|
||||
],
|
||||
grid: {
|
||||
left: '-40px',
|
||||
right: '3%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true,
|
||||
},
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', function () {
|
||||
myChart.resize();
|
||||
});
|
||||
});
|
||||
</script>
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="ecommerce-common-warp">
|
||||
<div class="ecommerce-common-content">
|
||||
<div class="smartFarm-common-warp">
|
||||
<div class="smartFarm-common-content">
|
||||
<div class="left-menu">
|
||||
<slot v-if="$slots.left" name="left"></slot>
|
||||
<template v-else>
|
||||
<leftMenu :current-name="currentName"></leftMenu>
|
||||
<left-menu :menus="menus"></left-menu>
|
||||
</template>
|
||||
</div>
|
||||
<div class="common-content">
|
||||
@ -21,13 +21,48 @@ import leftMenu from './leftMenu.vue';
|
||||
const props = defineProps({
|
||||
currentName: { type: String, default: 'agricultural' },
|
||||
});
|
||||
|
||||
const menus = reactive([
|
||||
{
|
||||
name: 'supplier',
|
||||
title: '农业环境监测',
|
||||
icon: 'menu1.png',
|
||||
path: '/sub-operation-service/smartFarm/main',
|
||||
isOpen: true,
|
||||
children: [
|
||||
{
|
||||
name: 'supplier',
|
||||
title: '田间监测',
|
||||
path: '/sub-operation-service/smartFarm/fieldInspection',
|
||||
},
|
||||
{
|
||||
name: 'supplier',
|
||||
title: '水质监测',
|
||||
path: '/sub-operation-service/ecommerce-supplier',
|
||||
},
|
||||
{
|
||||
name: 'supplier',
|
||||
title: '病虫害监测',
|
||||
path: '/sub-operation-service/ecommerce-supplier',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'purchaser',
|
||||
title: '生产管理控制',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/ecommerce-purchaser',
|
||||
isOpen: false,
|
||||
children: [],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ecommerce-common-warp {
|
||||
.smartFarm-common-warp {
|
||||
width: 100%;
|
||||
height: calc(100vh - 230px);
|
||||
text-align: center;
|
||||
.ecommerce-common-content {
|
||||
.smartFarm-common-content {
|
||||
width: $width-main;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
|
@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { isEmpty, getAssetsFile } from '@/utils';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps({
|
||||
devices: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: (items) => {
|
||||
return items.every((item) => {
|
||||
return (
|
||||
typeof item === 'object' &&
|
||||
item !== null &&
|
||||
typeof item.id === 'number' &&
|
||||
typeof item.name === 'string' &&
|
||||
typeof item.detail === 'string' &&
|
||||
typeof item.icon === 'string' &&
|
||||
(!item.status || typeof item.status === 'number')
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: () => '',
|
||||
validator: (items) => {
|
||||
return items;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-card style="border-radius: 16px">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left; color: #000">{{ title }}</div>
|
||||
<div style="display: flex; justify-content: flex-start; flex-wrap: wrap">
|
||||
<div v-for="(item, index) in devices" :key="index" class="device">
|
||||
<div v-if="item.status == 0" class="status" style="background-color: #25bf82">正常</div>
|
||||
<div v-else-if="item.status == -1" class="status" style="background-color: #fe4066">异常</div>
|
||||
<div style="display: flex; flex-direction: column; justify-content: space-between; height: 100%">
|
||||
<img v-if="item.icon === 'camera'" :src="getAssetsFile('images/smartFarm/监控.png')" alt="" />
|
||||
<img v-else-if="item.icon === 'sensor'" :src="getAssetsFile('images/smartFarm/传感器.png')" alt="" />
|
||||
<div style="text-align: left; font-weight: bold; font-size: 18px">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.device {
|
||||
height: 100px;
|
||||
width: 18%;
|
||||
background-color: #f5f5f5;
|
||||
margin: 20px 1%;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
img {
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
}
|
||||
}
|
||||
.status {
|
||||
border-radius: 0 16px 0 16px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
183
sub-operation-service/src/views/smartFarm/components/mapComp.vue
Normal file
@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div ref="mapContainer" style="width: 100%; height: 300px; background: #ffffff; border: 0"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import json from '@/assets/530926geo.json';
|
||||
import { getAssetsFile } from '@/utils';
|
||||
|
||||
// 您提供的耿马县GeoJSON数据
|
||||
const gengmaGeoJSON = json;
|
||||
|
||||
// 模拟乡镇数据(根据图片中的地名)
|
||||
const towns = ref([
|
||||
{ name: '孟定镇', coord: [99.01, 23.64], weather: '晴', temp: '28℃', icon: 'sunny' },
|
||||
{ name: '勐简乡', coord: [99.24, 23.79], weather: '多云', temp: '26℃', icon: 'cloudy' },
|
||||
{ name: '四排山乡', coord: [99.5, 23.38], weather: '小雨', temp: '24℃', icon: 'rainy' },
|
||||
{ name: '大兴乡', coord: [99.8, 23.76], weather: '多云', temp: '25℃', icon: 'cloudy' },
|
||||
{ name: '耿马镇', coord: [99.42, 23.66], weather: '多云', temp: '26℃', icon: 'cloudy' },
|
||||
{ name: '贺派乡', coord: [99.21, 23.4], weather: '晴', temp: '27℃', icon: 'sunny' },
|
||||
{ name: '芒洪乡', coord: [99.73, 23.59], weather: '阴', temp: '23℃', icon: 'overcast' },
|
||||
{ name: '勐永镇', coord: [99.53, 23.99], weather: '小雨', temp: '22℃', icon: 'rainy' },
|
||||
{ name: '勐撒镇', coord: [99.47, 23.85], weather: '晴', temp: '28℃', icon: 'sunny' },
|
||||
]);
|
||||
|
||||
const mapContainer = ref(null);
|
||||
|
||||
// 获取天气图标路径
|
||||
// function getWeatherIconPath(iconType) {
|
||||
// return `/images/${iconType}.png`; // 使用绝对路径
|
||||
// }
|
||||
function getWeatherIconPath(iconType) {
|
||||
const iconMap = {
|
||||
sunny: 'images/smartFarm/sunny.png',
|
||||
cloudy: 'images/smartFarm/sunnyToCloudy.png',
|
||||
rainy: 'images/smartFarm/rainy.png',
|
||||
overcast: 'images/smartFarm/windy.png',
|
||||
};
|
||||
let path = iconMap[iconType] ? iconMap[iconType] : 'images/smartFarm/sunny.png';
|
||||
return getAssetsFile(path);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 添加防御性检查
|
||||
if (!towns.value || towns.value.some((t) => !t.weather)) {
|
||||
console.error('天气数据不完整');
|
||||
return;
|
||||
}
|
||||
const chart = echarts.init(mapContainer.value, 'dark');
|
||||
|
||||
// 添加点击事件监听
|
||||
chart.on('click', (params) => {
|
||||
if (params.componentType === 'series') {
|
||||
// 点击乡镇标记点
|
||||
console.log('点击乡镇:', params.name);
|
||||
showWeatherDetail(params.data);
|
||||
} else if (params.componentType === 'geo') {
|
||||
// 点击地图区域
|
||||
console.log('点击地图区域:', params.name);
|
||||
}
|
||||
});
|
||||
|
||||
// 显示天气详情弹窗
|
||||
const showWeatherDetail = (data) => {
|
||||
console.log(data);
|
||||
};
|
||||
|
||||
// 注册地图
|
||||
echarts.registerMap('耿马县', gengmaGeoJSON);
|
||||
|
||||
// 配置项
|
||||
const option = {
|
||||
backgroundColor: '#fff', // 白色背景
|
||||
title: {
|
||||
text: '气象数据',
|
||||
left: 20,
|
||||
top: 10,
|
||||
textStyle: {
|
||||
color: '#000', // 黑色文字
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
geo: {
|
||||
map: '耿马县',
|
||||
roam: false,
|
||||
zoom: 1.1,
|
||||
itemStyle: {
|
||||
areaColor: '#a8d8ea', // 浅蓝色填充
|
||||
borderColor: '#4682B4', // 钢蓝色边界
|
||||
borderWidth: 1.2,
|
||||
shadowColor: 'rgba(0, 100, 150, 0.3)',
|
||||
shadowBlur: 8,
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
areaColor: '#7ac5e0', // 稍深的蓝绿色
|
||||
borderWidth: 1.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 30,
|
||||
data: towns.value.map((town) => ({
|
||||
name: town.name,
|
||||
value: [...town.coord, town.temp],
|
||||
weather: town.weather,
|
||||
symbol: `image://${getWeatherIconPath(town.icon)}`,
|
||||
symbolOffset: [10, -10], // 符号向上移
|
||||
// 标签下移10px(抵消symbol偏移)
|
||||
label: { offset: [10, 20] },
|
||||
})),
|
||||
label: {
|
||||
show: true,
|
||||
formatter: (params) => {
|
||||
// 使用rich实现图文复合布局
|
||||
return `{name|${params.name}}`;
|
||||
},
|
||||
rich: {
|
||||
name: {
|
||||
color: '#333',
|
||||
fontSize: 12,
|
||||
align: 'center',
|
||||
lineHeight: 20,
|
||||
padding: [2, 0],
|
||||
},
|
||||
},
|
||||
position: function (point) {
|
||||
// 根据坐标动态调整位置(示例逻辑,需根据实际坐标微调)
|
||||
return point[1] > 23.6 ? 'top' : 'bottom';
|
||||
},
|
||||
distance: 8,
|
||||
offset: [0, 0],
|
||||
padding: [2, 5],
|
||||
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||
borderColor: '#4682B4',
|
||||
borderWidth: 0.5,
|
||||
borderRadius: 3,
|
||||
},
|
||||
tooltip: {
|
||||
formatter: (params) => `
|
||||
<div style="font-size:14px;color:#333;font-weight:bold;margin-bottom:5px;">
|
||||
${params.name}
|
||||
</div>
|
||||
<div style="margin:5px 0;">
|
||||
<span style="display:inline-block;width:70px;">天气:</span>
|
||||
<span style="color:#1E90FF;">${params.data.weather}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="display:inline-block;width:70px;">温度:</span>
|
||||
<span style="color:#1E90FF;">${params.value[2]}</span>
|
||||
</div>
|
||||
`,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#4682B4',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
graphic: {
|
||||
type: 'text',
|
||||
left: 20,
|
||||
bottom: 5,
|
||||
style: {
|
||||
text: '数据更新于: 2025.01.01 08:00:00',
|
||||
fill: '#666',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
chart.resize();
|
||||
});
|
||||
});
|
||||
</script>
|
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div ref="mapContainer" style="width: 100%; height: 300px; background: #ffffff"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import json from '@/assets/530926geo.json';
|
||||
import { getAssetsFile } from '@/utils';
|
||||
|
||||
// 您提供的耿马县GeoJSON数据
|
||||
const gengmaGeoJSON = json;
|
||||
|
||||
// 模拟乡镇数据(根据图片中的地名)
|
||||
const towns = ref([
|
||||
{ name: '孟定镇', coord: [99.01, 23.64] },
|
||||
{ name: '勐简乡', coord: [99.24, 23.79] },
|
||||
{ name: '四排山乡', coord: [99.5, 23.38] },
|
||||
{ name: '大兴乡', coord: [99.8, 23.76] },
|
||||
{ name: '耿马镇', coord: [99.42, 23.66] },
|
||||
{ name: '贺派乡', coord: [99.21, 23.4] },
|
||||
{ name: '芒洪乡', coord: [99.73, 23.59] },
|
||||
{ name: '勐永镇', coord: [99.53, 23.99] },
|
||||
{ name: '勐撒镇', coord: [99.47, 23.85] },
|
||||
]);
|
||||
|
||||
const mapContainer = ref(null);
|
||||
|
||||
// 获取天气图标路径
|
||||
// function getWeatherIconPath(iconType) {
|
||||
// return `/images/${iconType}.png`; // 使用绝对路径
|
||||
// }
|
||||
function getWeatherIconPath(iconType) {
|
||||
const iconMap = {
|
||||
sunny: 'images/smartFarm/sunny.png',
|
||||
cloudy: 'images/smartFarm/sunnyToCloudy.png',
|
||||
rainy: 'images/smartFarm/rainy.png',
|
||||
overcast: 'images/smartFarm/windy.png',
|
||||
};
|
||||
let path = iconMap[iconType] ? iconMap[iconType] : 'images/smartFarm/sunny.png';
|
||||
return getAssetsFile(path);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const chart = echarts.init(mapContainer.value, 'dark');
|
||||
|
||||
// 添加点击事件监听
|
||||
chart.on('click', (params) => {
|
||||
if (params.componentType === 'series') {
|
||||
// 点击乡镇标记点
|
||||
console.log('点击乡镇:', params.name);
|
||||
showWeatherDetail(params.data);
|
||||
} else if (params.componentType === 'geo') {
|
||||
// 点击地图区域
|
||||
console.log('点击地图区域:', params.name);
|
||||
}
|
||||
});
|
||||
|
||||
// 显示天气详情弹窗
|
||||
const showWeatherDetail = (data) => {
|
||||
console.log(data);
|
||||
};
|
||||
|
||||
// 注册地图
|
||||
echarts.registerMap('耿马县', gengmaGeoJSON);
|
||||
|
||||
// 配置项
|
||||
const option = {
|
||||
backgroundColor: '#fff', // 白色背景
|
||||
title: {
|
||||
text: '',
|
||||
left: 20,
|
||||
top: 10,
|
||||
textStyle: {
|
||||
color: '#000', // 黑色文字
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
geo: {
|
||||
map: '耿马县',
|
||||
roam: false,
|
||||
zoom: 1.1,
|
||||
label: {
|
||||
show: true, // 开启地名显示
|
||||
color: '#333',
|
||||
fontSize: 12,
|
||||
formatter: (params) => params.name, // 直接显示地名
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: '#a8d8ea', // 浅蓝色填充
|
||||
borderColor: '#4682B4', // 钢蓝色边界
|
||||
borderWidth: 1.2,
|
||||
shadowColor: 'rgba(0, 100, 150, 0.3)',
|
||||
shadowBlur: 8,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
color: '#1890FF',
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: '#BAE7FF',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 0,
|
||||
data: towns.value,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside', // 地名显示在区域内
|
||||
formatter: '{b}',
|
||||
color: '#000',
|
||||
fontSize: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
chart.resize();
|
||||
});
|
||||
});
|
||||
</script>
|
115
sub-operation-service/src/views/smartFarm/components/stream.vue
Normal file
@ -0,0 +1,115 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||
import { isEmpty, getAssetsFile } from '@/utils';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import Hls from 'hls.js';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const currentDevice = ref(0);
|
||||
const currentPicture = ref(0);
|
||||
const videoPlayer = ref(null);
|
||||
const hls = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: () => '',
|
||||
validator: (items) => {
|
||||
return items;
|
||||
},
|
||||
},
|
||||
devices: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: (items) => {
|
||||
return items.every((item) => {
|
||||
return (
|
||||
typeof item === 'object' &&
|
||||
item !== null &&
|
||||
typeof item.id === 'number' &&
|
||||
typeof item.name === 'string' &&
|
||||
typeof item.icon === 'string' &&
|
||||
typeof item.detail === 'string' &&
|
||||
(!item.status || typeof item.status === 'number')
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (hls.value) {
|
||||
hls.value.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (Hls.isSupported()) {
|
||||
hls.value = new Hls();
|
||||
hls.value.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8');
|
||||
hls.value.attachMedia(videoPlayer.value);
|
||||
hls.value.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
videoPlayer.value.play();
|
||||
});
|
||||
} else if (videoPlayer.value.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
// Safari原生支持HLS
|
||||
videoPlayer.value.src = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card style="border-radius: 16px">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left">{{ title }}</div>
|
||||
<div style="color: #999999; line-height: 25px">
|
||||
当前设备
|
||||
<el-select v-model="currentDevice" placeholder="Select" size="small" style="width: 160px; margin-left: 10px">
|
||||
<el-option v-for="item in devices" :key="item.value" :label="item.detail" :value="item.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<div class="video-wrapper">
|
||||
<video ref="videoPlayer" controls autoplay muted></video>
|
||||
<div v-if="loading" class="status-message">正在加载直播流...</div>
|
||||
<div v-if="error" class="status-message error">{{ error }}</div>
|
||||
</div>
|
||||
<div class="pictures-wrapper">
|
||||
<img :src="getAssetsFile('images/smartFarm/goUp.png')" alt="" style="width: 100px" />
|
||||
<img :src="getAssetsFile('images/smartFarm/testPic1.png')" style="width: 80%; margin: 5px 0" :alt="devices[currentDevice].detail" />
|
||||
<img :src="getAssetsFile('images/smartFarm/testPic1.png')" style="width: 80%; margin: 5px 0" :alt="devices[currentDevice].detail" />
|
||||
<img :src="getAssetsFile('images/smartFarm/goDown.png')" alt="" style="width: 100px" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.video-wrapper {
|
||||
height: 100%;
|
||||
margin-top: 30px;
|
||||
width: 58%;
|
||||
video {
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
.pictures-wrapper {
|
||||
width: 38%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2,25 +2,359 @@
|
||||
<section>
|
||||
<common>
|
||||
<template #main>
|
||||
<div>农田监测</div>
|
||||
<div>
|
||||
<devices :title="'田间监测设备'" :devices="devices"></devices>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px">
|
||||
<stream :title="'田间监测实时监控'" :devices="devices" style="width: 60%"></stream>
|
||||
<el-card style="width: calc(40% - 20px); border-radius: 16px; padding: 10px">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left">作物生长状态</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">作物名称:</div>
|
||||
<div class="rightValue">橙子</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">生长状态:</div>
|
||||
<div class="rightValue">成熟期</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">植株形态:</div>
|
||||
<div class="rightValue">果木型</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">叶片形态:</div>
|
||||
<div class="rightValue">阔叶型</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">生长态势:</div>
|
||||
<div class="rightValue">良好</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">田间有机质含量:</div>
|
||||
<div class="rightValue">橙子</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">田间有机质含量:</div>
|
||||
<div class="rightValue">16%</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">生长趋势图</div>
|
||||
<div class="rightValue"> </div>
|
||||
</div>
|
||||
<div ref="chartRef1" style="width: 100%; height: 150px"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import Common from '../components/common.vue';
|
||||
|
||||
import Devices from '@/views/smartFarm/components/devices.vue';
|
||||
import Stream from '@/views/smartFarm/components/stream.vue';
|
||||
import * as echarts from 'echarts';
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
|
||||
// 图表 DOM 引用
|
||||
const chartRef = ref(null);
|
||||
// ECharts 实例
|
||||
let chartInstance = null;
|
||||
// 颜色列表
|
||||
const colorList = ['#9E87FF', '#73DDFF'];
|
||||
// x轴数据
|
||||
const xData = ['1月', '2月', '3月', '4月', '5月', '6月'];
|
||||
const devices = ref([
|
||||
{
|
||||
name: 'A-001',
|
||||
icon: 'camera',
|
||||
detail: 'A区-监控设备9',
|
||||
status: '0',
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: 'A-002',
|
||||
icon: 'camera',
|
||||
detail: 'A区-监控设备66',
|
||||
status: '0',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'A-003',
|
||||
icon: 'sensor',
|
||||
detail: 'A区-监控设备7',
|
||||
status: '0',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'A-004',
|
||||
icon: 'sensor',
|
||||
detail: 'A区-监控设备1',
|
||||
status: '-1',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: 'A-005',
|
||||
icon: 'sensor',
|
||||
detail: 'A区-监控设备5',
|
||||
status: '-1',
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: 'A-006',
|
||||
icon: 'camera',
|
||||
status: '0',
|
||||
detail: 'A区-监控设备21',
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: 'A-007',
|
||||
icon: 'camera',
|
||||
status: '0',
|
||||
detail: 'A区-监控设备4',
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
name: 'A-008',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'camera',
|
||||
status: '0',
|
||||
id: 7,
|
||||
},
|
||||
]);
|
||||
// #endregion
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
// #region
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
// 图表配置
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
title: {
|
||||
text: '简单折线图',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: 400,
|
||||
},
|
||||
left: 'center',
|
||||
top: '5%',
|
||||
},
|
||||
legend: {
|
||||
icon: 'circle',
|
||||
top: '5%',
|
||||
right: '5%',
|
||||
itemWidth: 6,
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
color: '#556677',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
label: {
|
||||
show: true,
|
||||
backgroundColor: '#fff',
|
||||
color: '#556677',
|
||||
borderColor: 'rgba(0,0,0,0)',
|
||||
shadowColor: 'rgba(0,0,0,0)',
|
||||
shadowOffsetY: 0,
|
||||
},
|
||||
lineStyle: {
|
||||
width: 0,
|
||||
},
|
||||
},
|
||||
backgroundColor: '#fff',
|
||||
textStyle: {
|
||||
color: '#5c6c7c',
|
||||
},
|
||||
padding: [10, 10],
|
||||
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)',
|
||||
},
|
||||
grid: {
|
||||
top: '15%',
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: 'rgba(107,107,107,0.37)',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
},
|
||||
margin: 15,
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
padding: [11, 5, 7],
|
||||
backgroundColor: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#fff',
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: '#fff',
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: '#33c0cd',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#33c0cd',
|
||||
},
|
||||
],
|
||||
global: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
boundaryGap: false,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: 'rgba(107,107,107,0.37)',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Adidas',
|
||||
type: 'line',
|
||||
data: [10, 10, 30, 12, 15, 3, 7],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#9effff',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#9E87FF',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(158,135,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[0],
|
||||
borderColor: colorList[0],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Nike',
|
||||
type: 'line',
|
||||
data: [5, 12, 11, 14, 25, 16, 10],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#73DD39',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#73DDFF',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(115,221,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[1],
|
||||
borderColor: colorList[1],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
if (chartRef.value) {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
// 绘制图表
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', resizeChart);
|
||||
}
|
||||
};
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
// 组件卸载前销毁图表
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
window.removeEventListener('resize', resizeChart);
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
// 调整图表大小
|
||||
const resizeChart = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.plantStatus {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
margin: 7px 0;
|
||||
.leftKey {
|
||||
color: #000000;
|
||||
}
|
||||
.rightValue {
|
||||
color: #25bf82;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,7 +3,289 @@
|
||||
<common>
|
||||
<template #main>
|
||||
<div>
|
||||
<el-card shadow="hover"> </el-card>
|
||||
<el-card shadow="hover">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<map-comp style="height: 300px; width: 100%; border: 0"></map-comp>
|
||||
</el-col>
|
||||
<el-col :span="1"> </el-col>
|
||||
<el-col :span="13">
|
||||
<div class="location">
|
||||
耿马县·孟定镇<img :src="getAssetsFile('images/smartFarm/location.png')" height="20" style="margin-left: 8px" alt="" />
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-around">
|
||||
<el-card v-for="(item, index) in weatherData" :key="index" :body-style="{ padding: 0 }" shadow="always" class="weatherCards">
|
||||
<div>{{ item.time }}</div>
|
||||
<div><img :src="getAssetsFile('images/smartFarm/' + item.weather + '.png')" alt="" height="50" /></div>
|
||||
<div>{{ item.temp }}℃</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="details">
|
||||
<div class="details-block">
|
||||
<div class="detail">
|
||||
<div class="leftTitle">温度</div>
|
||||
<div class="rightValue">
|
||||
{{ currentData.temp }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">PM2.5</div>
|
||||
<div class="rightValue">{{ currentData.PM2 }}μg/m³</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">作物虫害</div>
|
||||
<div class="rightValue">
|
||||
{{ currentData.bugs === 0 ? '无' : '有' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">土壤温度</div>
|
||||
<div class="rightValue">{{ currentData.dustTemp }}℃</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details-block">
|
||||
<div class="detail">
|
||||
<div class="leftTitle">湿度</div>
|
||||
<div class="rightValue">{{ currentData.wet }}%</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">PM10</div>
|
||||
<div class="rightValue">{{ currentData.temp }}μg/m³</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">作物病害</div>
|
||||
<div class="rightValue">
|
||||
{{ currentData.sick === 0 ? '无' : '有' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">土壤湿度</div>
|
||||
<div class="rightValue">{{ currentData.dustWet }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details-block">
|
||||
<div class="detail">
|
||||
<div class="leftTitle">风向</div>
|
||||
<div class="rightValue">
|
||||
{{ currentData.wind }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">光照度</div>
|
||||
<div class="rightValue">{{ currentData.light }}Lux</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">风速</div>
|
||||
<div class="rightValue">{{ currentData.temp }}m/s</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="leftTitle">土壤酸碱度</div>
|
||||
<div class="rightValue">
|
||||
{{ currentData.PH }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="hover" style="margin-top: 10px">
|
||||
<div style="display: flex; padding: 20px">
|
||||
<div style="width: 30%">
|
||||
<div style="font-size: 18px; font-weight: bold; text-align: left">土壤数据</div>
|
||||
<div style="display: flex">
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/光照传感器.png')" alt="" />
|
||||
光照
|
||||
</div>
|
||||
<div class="values">2000Lux</div>
|
||||
</div>
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/排风.png')" alt="" />
|
||||
排风
|
||||
</div>
|
||||
<div class="values">15m³/h</div>
|
||||
</div>
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/蒸腾.png')" alt="" />
|
||||
蒸腾
|
||||
</div>
|
||||
<div class="values">2000Lux</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/土壤湿度.png')" alt="" />
|
||||
湿度
|
||||
</div>
|
||||
<div class="values">26%</div>
|
||||
<div class="values">34%</div>
|
||||
</div>
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/土壤温度.png')" alt="" />
|
||||
温度
|
||||
</div>
|
||||
<div class="values">32℃</div>
|
||||
<div class="values">28℃</div>
|
||||
</div>
|
||||
<div class="dustData">
|
||||
<div>
|
||||
<img :src="getAssetsFile('images/smartFarm/空气.png')" alt="" />
|
||||
空气
|
||||
</div>
|
||||
<div class="values">300ppm</div>
|
||||
<div class="values">34%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report">
|
||||
<div><img :src="getAssetsFile('images/smartFarm/bell.png')" alt="" /></div>
|
||||
<div class="warning">
|
||||
<div>温度</div>
|
||||
<div>36℃</div>
|
||||
</div>
|
||||
<div class="warning">
|
||||
<div>超高</div>
|
||||
<div>4℃</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 35%">
|
||||
<map-simple style="height: 320px; width: 100%"></map-simple>
|
||||
</div>
|
||||
<div style="width: 35%">
|
||||
<div style="margin-top: 70px; display: flex; text-align: left">
|
||||
<el-col :span="12">
|
||||
<el-row class="dataTitle">泵压管控mpa</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">输入</span>
|
||||
<span class="values">11</span>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">末端</span>
|
||||
<span class="values">2</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-row class="dataTitle">灌溉流量m²/h</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">灌溉</span>
|
||||
<span class="values">18℃</span>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">回液</span>
|
||||
<span class="values">18℃</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</div>
|
||||
<div style="display: flex; text-align: left">
|
||||
<el-col :span="12">
|
||||
<el-row class="dataTitle">水肥监测</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">PH</span>
|
||||
<span class="values">8</span>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 15px">输入</span>
|
||||
<span class="values">18℃</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="12"> </el-col>
|
||||
</div>
|
||||
<el-row class="dataTitle">灌溉流量m²/h</el-row>
|
||||
<charts-flow style="height: 150px; width: 100%"></charts-flow>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" style="margin-top: 10px">
|
||||
<div style="display: flex; padding: 20px">
|
||||
<div style="width: 50%">
|
||||
<div style="font-size: 18px; font-weight: bold; text-align: left">产能预测</div>
|
||||
<div style="display: flex; justify-content: flex-start; margin: 20px 0">
|
||||
<div class="plansBlock" style="background-color: #25bf82">
|
||||
<div style="">预计生产</div>
|
||||
<div style="font-weight: 900">300吨</div>
|
||||
</div>
|
||||
<div class="plansBlock" style="background-color: #ffbe4d; margin-left: 5%">
|
||||
<div>预计产值</div>
|
||||
<div style="font-weight: 900">1500万元</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="coins">
|
||||
<div style="width: 28%">
|
||||
<div style="text-align: left; font-size: 14px; color: #999999">水质综合评分</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 5px">
|
||||
<div style="display: flex; align-items: center; font-size: 20px">
|
||||
<div class="shu" style="background-color: #3685fe"></div>
|
||||
<div style="margin-left: 5px">87</div>
|
||||
</div>
|
||||
<div style="line-height: 30px">
|
||||
<img :src="getAssetsFile('images/smartFarm/fall.png')" alt="" style="width: 25px; height: 25px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 28%">
|
||||
<div style="text-align: left; font-size: 14px; color: #999999">病虫害管控评分</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 5px">
|
||||
<div style="display: flex; align-items: center; font-size: 20px">
|
||||
<div class="shu" style="background-color: #25bf82"></div>
|
||||
<div style="margin-left: 5px">87</div>
|
||||
</div>
|
||||
<div style="line-height: 30px">
|
||||
<img :src="getAssetsFile('images/smartFarm/rise.png')" alt="" style="width: 25px; height: 25px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 28%">
|
||||
<div style="text-align: left; font-size: 14px; color: #999999">环境综合评分</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 5px">
|
||||
<div style="display: flex; align-items: center; font-size: 20px">
|
||||
<div class="shu" style="background-color: #ffd500"></div>
|
||||
<div style="margin-left: 5px">87</div>
|
||||
</div>
|
||||
<div style="line-height: 30px">
|
||||
<img :src="getAssetsFile('images/smartFarm/rise.png')" alt="" style="width: 25px; height: 25px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vars">
|
||||
<div style="display: flex; justify-content: space-between; width: 45%">
|
||||
<div style="color: #999999">种植面积</div>
|
||||
<div style="color: #25bf82; font-weight: 900">500亩</div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; width: 45%">
|
||||
<div style="color: #999999">水质监测</div>
|
||||
<div style="color: #25bf82; font-weight: 900">1266次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vars">
|
||||
<div style="display: flex; justify-content: space-between; width: 45%">
|
||||
<div style="color: #999999">病虫害监测</div>
|
||||
<div style="color: #25bf82; font-weight: 900">367次</div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; width: 45%">
|
||||
<div style="color: #999999">环境监测</div>
|
||||
<div style="color: #25bf82; font-weight: 900">1547次</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 50%">
|
||||
<img style="width: 100%" :src="getAssetsFile('images/smartFarm/产能预测.png')" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
@ -11,12 +293,63 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import Common from './components/common.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import MapComp from '@/views/smartFarm/components/mapComp.vue';
|
||||
import { getAssetsFile } from '@/utils/index.js';
|
||||
import MapSimple from '@/views/smartFarm/components/mapSimple.vue';
|
||||
import ChartsFlow from '@/views/smartFarm/components/charts-flow.vue';
|
||||
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
const weatherData = ref([
|
||||
{
|
||||
weather: 'sunny',
|
||||
time: '15:00',
|
||||
temp: '18',
|
||||
},
|
||||
{
|
||||
weather: 'sunnyToCloudy',
|
||||
time: '16:00',
|
||||
temp: '19',
|
||||
},
|
||||
{
|
||||
weather: 'thunderRain',
|
||||
time: '17:00',
|
||||
temp: '19',
|
||||
},
|
||||
{
|
||||
weather: 'rainy',
|
||||
time: '18:00',
|
||||
temp: '18',
|
||||
},
|
||||
{
|
||||
weather: 'rainy',
|
||||
time: '19:00',
|
||||
temp: '15',
|
||||
},
|
||||
{
|
||||
weather: 'windy',
|
||||
time: '20:00',
|
||||
temp: '13',
|
||||
},
|
||||
]);
|
||||
|
||||
const currentData = ref({
|
||||
temp: 18, // 当前温度
|
||||
PM2: 80, // 空气质量 μg/m³
|
||||
bugs: 0, // 虫害
|
||||
dustTemp: 15, // 土壤温度 ℃
|
||||
wet: 56, // 湿度 %
|
||||
PM10: 120, // 空气质量 μg/m³
|
||||
sick: 0, // 病害
|
||||
dustWet: 64, // 土壤湿度%
|
||||
wind: '东南风', // 风向
|
||||
light: 500, // 光照Lux
|
||||
windSpeed: 1.5, // 风速 m/s
|
||||
PH: 6.5, // 土壤酸碱度
|
||||
});
|
||||
// #endregion
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
@ -25,4 +358,121 @@ import Common from './components/common.vue';
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.vars {
|
||||
width: 95%;
|
||||
margin: 15px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
}
|
||||
.shu {
|
||||
width: 4px;
|
||||
height: 30px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.coins {
|
||||
display: flex;
|
||||
width: 95%;
|
||||
height: 60px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.plansBlock {
|
||||
height: 90px;
|
||||
width: 45%;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
padding: 10px 20px;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
}
|
||||
.values {
|
||||
color: #25bf82;
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.dataTitle {
|
||||
margin: 4px 0;
|
||||
color: #999999;
|
||||
}
|
||||
.warning {
|
||||
div {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
.report {
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background: linear-gradient(to left, #ffc9d4 0%, #ffffff 100% /* 底部稍深青色 */);
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
.dustData {
|
||||
width: 33%;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
font-size: 15px;
|
||||
margin: 20px 0;
|
||||
.values {
|
||||
color: #25bf82;
|
||||
}
|
||||
img {
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.details {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
.details-block {
|
||||
width: 29%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.detail {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
.leftTitle {
|
||||
font-size: 15px;
|
||||
color: #999999;
|
||||
}
|
||||
.rightValue {
|
||||
font-size: 15px;
|
||||
color: #25bf82;
|
||||
}
|
||||
}
|
||||
}
|
||||
.location {
|
||||
padding: 20px 0;
|
||||
text-align: right;
|
||||
font-size: 15px;
|
||||
color: #000000;
|
||||
}
|
||||
.weatherCards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
font-size: 15px;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
height: 120px;
|
||||
width: 70px;
|
||||
background: linear-gradient(to left bottom, #cfffec 0%, /* 顶部浅青色 */ #fbfefd 70%, /* 中间渐变色 */ #ffffff 100% /* 底部稍深青色 */);
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgba(0, 150, 136, 0.15);
|
||||
}
|
||||
</style>
|
||||
|