Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev
This commit is contained in:
commit
1d983eba78
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 100%">
|
||||
<div class="basic">
|
||||
<div v-for="(item, index) in data" :key="index" class="line">
|
||||
<div class="title">{{ item.title }}</div>
|
||||
<div class="values">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
// #region
|
||||
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.basic {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
height: 100%;
|
||||
border: 2px solid #01fefd;
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(2px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
padding: 20px;
|
||||
.line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-align: left;
|
||||
line-height: 20px;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
width: 40%;
|
||||
}
|
||||
.values {
|
||||
font-size: 20px;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -13,6 +13,7 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
total: 0, // 存储所有乡镇面积总和
|
||||
option: {
|
||||
grid: {
|
||||
left: '5%',
|
||||
@ -31,11 +32,12 @@ const state = reactive({
|
||||
borderRadius: 8,
|
||||
formatter: (data) => {
|
||||
const params = data[0];
|
||||
let str = `<div class="custom-echarts-tips" >
|
||||
const percentage = ((params.value / state.total) * 100).toFixed(2);
|
||||
return `<div class="custom-echarts-tips" >
|
||||
<span>${params.name}</span><br/>
|
||||
<span>${params.marker} ${params.data} 万亩</span>
|
||||
<span>${params.marker} ${params.data} km²</span><br/>
|
||||
<span>占比 ${percentage}%</span>
|
||||
</div>`;
|
||||
return str;
|
||||
},
|
||||
extraCssText: 'backdrop-filter: blur(8px);',
|
||||
},
|
||||
@ -85,6 +87,7 @@ watch(
|
||||
(val) => {
|
||||
if (!isEmpty(val)) {
|
||||
state.data = val;
|
||||
state.total = val.reduce((sum, item) => sum + item.value, 0); // 计算总和
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div style="display: flex; justify-content: space-around; width: 100%">
|
||||
<section class="land_area_all">
|
||||
<section v-for="(item, index) in data" :key="index" class="_left">
|
||||
<section class="_circle">{{ item.value }}</section>
|
||||
<section class="_text">{{ item.title }}</section>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
// #endregion
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
// #region
|
||||
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.land_area_all {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
height: 100%;
|
||||
width: 100%; /* 限制宽度 */
|
||||
max-width: 400px; /* 可选最大值 */
|
||||
._left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
._circle {
|
||||
padding-top: 18%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: 'JinBuTi';
|
||||
width: 80%;
|
||||
aspect-ratio: 1/1;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
background: url('../../../assets/images/land/countCircleBG.png') no-repeat center center / cover;
|
||||
}
|
||||
._text {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: url('../../../assets/images/land/countTextBG.png') no-repeat center center / cover;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
text-shadow: 0 0 1px #fff;
|
||||
}
|
||||
}
|
||||
._right {
|
||||
padding-right: 4px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
.list_item {
|
||||
padding: 0 24px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
grid-template-columns: 60% 40%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
&.active {
|
||||
background: rgba(38, 122, 102, 0.3);
|
||||
border: 1px solid #35d0c0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
.spot {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
._label {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
._label {
|
||||
._spot {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-25%);
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
._value {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
._right::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2,46 +2,20 @@
|
||||
<el-row class="data-home-index">
|
||||
<el-col :span="6" class="left-charts">
|
||||
<div class="left-charts-item">
|
||||
<customBack top-title="耕地面积统计" :top-postion="'left'">
|
||||
<customBack top-title="基础地理信息" :top-postion="'left'">
|
||||
<template #back>
|
||||
<!-- <landTwo :data="state.data.two" />-->
|
||||
<basic-info :data="state.data.basic"></basic-info>
|
||||
</template>
|
||||
</customBack>
|
||||
</div>
|
||||
<div class="left-charts-item">
|
||||
<customBack top-title="土地分布数据" :top-postion="'left'">
|
||||
<template #back>
|
||||
<landOne :data="state.data.one" />
|
||||
</template>
|
||||
</customBack>
|
||||
</div>
|
||||
<div class="left-charts-item">
|
||||
<customBack top-title="农村土地资源" :top-postion="'left'">
|
||||
<template #back>
|
||||
<landTwo :data="state.data.two" />
|
||||
</template>
|
||||
</customBack>
|
||||
</div>
|
||||
<div class="left-charts-item">
|
||||
<customBack
|
||||
top-title="项目效益分析"
|
||||
:top-postion="'left'"
|
||||
:down-title="'全县'"
|
||||
:label-field="'label'"
|
||||
:value-field="'value'"
|
||||
:down-width="'100px'"
|
||||
:options="[
|
||||
{ label: '全县', value: '530926' },
|
||||
{ label: '耿马镇', value: '42611' },
|
||||
{ label: '勐撒镇', value: '9259' },
|
||||
{ label: '勐永镇', value: '17787' },
|
||||
{ label: '孟定镇', value: '42610' },
|
||||
{ label: '勐简乡', value: '17788' },
|
||||
{ label: '贺派乡', value: '40161' },
|
||||
{ label: '四排山乡', value: '40163' },
|
||||
{ label: '大兴乡', value: '40159' },
|
||||
]"
|
||||
:is-down="true"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<template #back>
|
||||
<landThere :data="state.data.there" :query="state.queryCode" />
|
||||
</template>
|
||||
</customBack>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<landMap />
|
||||
@ -79,9 +53,10 @@
|
||||
</customBack>
|
||||
</div>
|
||||
<div class="right-charts-item">
|
||||
<customBack top-title="全县作物情况" :top-postion="'right'">
|
||||
<customBack top-title="土地违规使用风险预警" :top-postion="'right'">
|
||||
<template #back>
|
||||
<landSix :data="state.data.six" />
|
||||
<warning :data="state.data.warnings"></warning>
|
||||
<!-- <landSix :data="state.data.six" />-->
|
||||
</template>
|
||||
</customBack>
|
||||
</div>
|
||||
@ -100,6 +75,8 @@ import landSix from './components/landSix.vue';
|
||||
import { useApp } from '@/hooks';
|
||||
import { sleep } from '@/utils';
|
||||
import { GetLandInfo } from '@/apis/land';
|
||||
import BasicInfo from './components/basicInfo.vue';
|
||||
import Warning from '@/views/land/components/warning.vue';
|
||||
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
@ -122,15 +99,15 @@ const loadData = async () => {
|
||||
await sleep(500);
|
||||
state.data = {
|
||||
one: [
|
||||
{ value: 20, name: '耿马镇' },
|
||||
{ value: 15, name: '勐撒镇' },
|
||||
{ value: 12, name: '勐永镇' },
|
||||
{ value: 16, name: '孟定镇' },
|
||||
{ value: 8, name: '勐简乡' },
|
||||
{ value: 12, name: '贺派乡' },
|
||||
{ value: 10, name: '四排山乡' },
|
||||
{ value: 9, name: '芒洪乡' },
|
||||
{ value: 8, name: '大兴乡' },
|
||||
{ value: 441.96, name: '耿马镇' },
|
||||
{ value: 515.11, name: '勐撒镇' },
|
||||
{ value: 399.6, name: '勐永镇' },
|
||||
{ value: 1069.15, name: '孟定镇' },
|
||||
{ value: 281.19, name: '勐简乡' },
|
||||
{ value: 251.74, name: '贺派乡' },
|
||||
{ value: 349.86, name: '四排山乡' },
|
||||
{ value: 314, name: '芒洪乡' },
|
||||
{ value: 456, name: '大兴乡' },
|
||||
],
|
||||
two: [
|
||||
{ name: '耿马镇', value: 87.84 },
|
||||
@ -200,6 +177,18 @@ const loadData = async () => {
|
||||
value: 32.1,
|
||||
},
|
||||
],
|
||||
basic: [
|
||||
{ value: '3837km²', title: '政区域面积' },
|
||||
{ value: '9个乡(镇),3个农场', title: '下辖地区' },
|
||||
{ value: '27.88万人', title: '常住人口' },
|
||||
{ value: '18.8℃ - 19.2℃', title: '年均气温' },
|
||||
{ value: '946.2毫米(山区)', title: '年降水量' },
|
||||
{ value: '优越,适宜农业发展', title: '光热水土条件' },
|
||||
],
|
||||
warnings: [
|
||||
{ value: 2, title: '当前风险预警数量' },
|
||||
{ value: 30, title: '已处理土地违规案件' },
|
||||
],
|
||||
};
|
||||
state.loading = false;
|
||||
};
|
||||
@ -240,7 +229,7 @@ const handleCommand = (data) => {
|
||||
}
|
||||
.left-charts-item {
|
||||
width: calc(100% - 5px);
|
||||
height: calc((100% - 30px) / 3);
|
||||
height: calc((100% - 30px) / 2);
|
||||
}
|
||||
|
||||
.right-charts {
|
||||
|
BIN
sub-operation-service/src/assets/images/smartFarm/demo-list.png
Normal file
BIN
sub-operation-service/src/assets/images/smartFarm/demo-list.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -140,6 +140,18 @@ export const constantRoutes = [
|
||||
name: 'intelligentFertilizer',
|
||||
meta: { title: '智能配肥' },
|
||||
},
|
||||
{
|
||||
path: '/sub-operation-service/smartFarm/IntelligentIrrigation',
|
||||
component: () => import('@/views/smartFarm/inspection/IntelligentIrrigation.vue'),
|
||||
name: 'IntelligentIrrigation',
|
||||
meta: { title: '智能灌溉' },
|
||||
},
|
||||
{
|
||||
path: '/sub-operation-service/smartFarm/IntelligentSeedSoaking',
|
||||
component: () => import('@/views/smartFarm/inspection/IntelligentSeedSoaking.vue'),
|
||||
name: 'IntelligentSeedSoaking',
|
||||
meta: { title: '智能浸种' },
|
||||
},
|
||||
{
|
||||
path: '/sub-operation-service/smartFarm/growSeedlings',
|
||||
component: () => import('@/views/smartFarm/manageControl/growSeedlings.vue'),
|
||||
|
@ -25,7 +25,7 @@ const leftMenu = reactive([
|
||||
{ name: 'agricultural', title: '农资交易', icon: 'menu2.png', path: '/sub-operation-service/ecommerce-agricultural' },
|
||||
{ name: 'supplier', title: '供应商服务', icon: 'menu1.png', path: '/sub-operation-service/ecommerce-supplier' },
|
||||
{ name: 'purchaser', title: '采购商服务', icon: 'menu3.png', path: '/sub-operation-service/ecommerce-purchaser' },
|
||||
{ name: 'land', title: '土地交易', icon: 'menu4.png', path: '/sub-operation-service/ecommerce-land' },
|
||||
// { name: 'land', title: '土地交易', icon: 'menu4.png', path: '/sub-operation-service/ecommerce-land' },
|
||||
]);
|
||||
|
||||
let currentIndex = ref(0);
|
||||
|
@ -0,0 +1,347 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, onBeforeUnmount } from 'vue';
|
||||
import { isEmpty, getAssetsFile } from '@/utils';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表 DOM 引用
|
||||
const chartRef = ref(null);
|
||||
// ECharts 实例
|
||||
let chartInstance = null;
|
||||
// 颜色列表
|
||||
const colorList = ['#3685FE', '#FFD500', '#25BF82'];
|
||||
// x轴数据
|
||||
const xData = ['1月', '2月', '3月', '4月', '5月', '6月'];
|
||||
|
||||
// 图表配置
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
title: {
|
||||
text: '',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: 400,
|
||||
},
|
||||
left: 'center',
|
||||
top: '5%',
|
||||
},
|
||||
legend: {
|
||||
icon: 'circle',
|
||||
top: '0',
|
||||
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: '20%',
|
||||
},
|
||||
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',
|
||||
show: false,
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: 'rgba(107,107,107,0.37)',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '茎秆高度',
|
||||
type: 'line',
|
||||
data: [10, 10, 30, 12, 15, 3],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#3685FE',
|
||||
shadowColor: 'rgba(158,135,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[0],
|
||||
borderColor: colorList[0],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '叶片温度',
|
||||
type: 'line',
|
||||
data: [5, 12, 11, 14, 25, 16],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#FFD500',
|
||||
shadowColor: 'rgba(115,221,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[1],
|
||||
borderColor: colorList[1],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '果实大小',
|
||||
type: 'line',
|
||||
data: [6, 14, 17, 25, 21, 10],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#25BF82',
|
||||
shadowColor: 'rgba(115,221,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[2],
|
||||
borderColor: colorList[2],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
chartsData: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: () => '',
|
||||
validator: (items) => {
|
||||
return items;
|
||||
},
|
||||
},
|
||||
showImage: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showCharts: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
imageList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
// #region
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value) {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
// 绘制图表
|
||||
chartInstance.setOption(option);
|
||||
// 自动显示最大值点的tooltip
|
||||
// showMaxValueTooltip();
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', resizeChart);
|
||||
}
|
||||
};
|
||||
|
||||
const showMaxValueTooltip = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
// 找出所有系列中的最大值点
|
||||
let maxValue = -Infinity;
|
||||
let maxSeriesIndex = 0;
|
||||
let maxDataIndex = 0;
|
||||
|
||||
option.series.forEach((series, seriesIndex) => {
|
||||
series.data.forEach((value, dataIndex) => {
|
||||
if (value > maxValue) {
|
||||
maxValue = value;
|
||||
maxSeriesIndex = seriesIndex;
|
||||
maxDataIndex = dataIndex;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 延迟执行确保图表渲染完成
|
||||
setTimeout(() => {
|
||||
chartInstance.dispatchAction({
|
||||
type: 'showTip',
|
||||
seriesIndex: maxSeriesIndex,
|
||||
dataIndex: maxDataIndex,
|
||||
});
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
// 组件卸载前销毁图表
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
window.removeEventListener('resize', resizeChart);
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
// 调整图表大小
|
||||
const resizeChart = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-card style="border-radius: 16px; min-height: 316px">
|
||||
<div class="my-card-title" style="">{{ title }}</div>
|
||||
<div v-for="(item, index) in data" :key="index" class="show-data-box">
|
||||
<div class="left-title">{{ item.title }}:</div>
|
||||
<div :style="{ color: item.status == '0' ? '#FE4066' : '#25BF82' }">{{ item.statusText }}</div>
|
||||
</div>
|
||||
<div v-if="showImage">
|
||||
<p style="font-size: 14px; text-align: left; color: #000000; margin-bottom: 10px">灌溉记录:</p>
|
||||
<el-image
|
||||
style="width: 100%"
|
||||
:src="imageList[0]"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="imageList"
|
||||
show-progress
|
||||
:initial-index="4"
|
||||
fit="cover"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showCharts">
|
||||
<div ref="chartRef" style="width: 100%; height: 200px"></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.my-card-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.show-data-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
margin: 7px 0;
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
@ -37,6 +37,8 @@ const icons = [
|
||||
{ 雨量计: 'rainGauge' },
|
||||
{ 土壤传感器: 'soilSensor' },
|
||||
{ 积水传感器: 'waterLoggingSensor' },
|
||||
{ 土壤温度: 'temperature' },
|
||||
{ 土壤湿度: 'humidity' },
|
||||
];
|
||||
|
||||
const props = defineProps({
|
||||
@ -44,19 +46,6 @@ const props = defineProps({
|
||||
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,
|
||||
@ -72,8 +61,8 @@ const props = defineProps({
|
||||
<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 style="font-size: 16px; font-weight: bold; text-align: left; color: #000; margin-bottom: 20px">{{ title }}</div>
|
||||
<div style="display: flex; justify-content: flex-start; flex-wrap: wrap; gap: 20px">
|
||||
<div v-for="(item, index) in devices" :key="index" class="device">
|
||||
<div v-if="item.status == 1" class="status" style="background-color: #25bf82">正常</div>
|
||||
<div v-else-if="item.status == 0" class="status" style="background-color: #999999">离线</div>
|
||||
@ -109,6 +98,8 @@ const props = defineProps({
|
||||
<img v-else-if="item.icon === 'rainGauge'" :src="getAssetsFile('images/smartFarm/雨量计.png')" alt="" />
|
||||
<img v-else-if="item.icon === 'soilSensor'" :src="getAssetsFile('images/smartFarm/土壤传感器.png')" alt="" />
|
||||
<img v-else-if="item.icon === 'waterLoggingSensor'" :src="getAssetsFile('images/smartFarm/积水传感器.png')" alt="" />
|
||||
<img v-else-if="item.icon === 'temperature'" :src="getAssetsFile('images/smartFarm/土壤温度.png')" alt="" />
|
||||
<img v-else-if="item.icon === 'humidity'" :src="getAssetsFile('images/smartFarm/土壤湿度.png')" alt="" />
|
||||
<div style="text-align: left; font-weight: bold; font-size: 18px">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -120,9 +111,8 @@ const props = defineProps({
|
||||
<style scoped lang="scss">
|
||||
.device {
|
||||
height: 100px;
|
||||
width: 14%;
|
||||
width: 14.6%;
|
||||
background-color: #f5f5f5;
|
||||
margin: 20px 1%;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
@ -1,19 +1,22 @@
|
||||
<template>
|
||||
<div class="smartFarm-left-menu-warp">
|
||||
<div class="left-menu">
|
||||
<div v-for="(n, index) in leftMenu" :key="index" class="left-menu-item" style="position: relative">
|
||||
<div style="display: flex; justify-content: flex-start; align-items: center" @click.stop="toLink(index)">
|
||||
<div
|
||||
v-for="(n, index) in leftMenu"
|
||||
:key="index"
|
||||
class="left-menu-item"
|
||||
style="position: relative"
|
||||
@click.stop="
|
||||
toLink(index);
|
||||
openList(index);
|
||||
"
|
||||
>
|
||||
<div style="display: flex; justify-content: flex-start; align-items: center">
|
||||
<div class="item-img">
|
||||
<img :src="getAssetsFile('images/smartFarm/' + n.icon)?.href ?? ''" alt="" />
|
||||
</div>
|
||||
<span :class="n.isOpen ? 'active' : ''" class="item-title">{{ n.title }}</span>
|
||||
<img
|
||||
v-if="n.children && n.isOpen"
|
||||
alt=""
|
||||
:src="getAssetsFile('images/smartFarm/closing.png')"
|
||||
class="isOpen"
|
||||
@click.stop="openList(index)"
|
||||
/>
|
||||
<img v-if="n.children && n.isOpen" alt="" :src="getAssetsFile('images/smartFarm/closing.png')" class="isOpen" />
|
||||
<img
|
||||
v-if="n.children && !n.isOpen"
|
||||
alt=""
|
||||
@ -25,7 +28,7 @@
|
||||
<div v-if="n.children && n.isOpen" class="item-children">
|
||||
<div v-for="(item, indexC) in n.children" :key="indexC">
|
||||
<ul style="overflow: visible; padding-left: 40px; text-align: left; list-style-type: disc !important">
|
||||
<li :class="indexC === currentCIndex ? 'active' : ''" @click.stop="toLinkSub(index, indexC)">
|
||||
<li :class="item.name === currentCIndex ? 'active' : ''" @click.stop="toLinkSub(index, item.name)">
|
||||
<div class="dot"></div>
|
||||
{{ item.title }}
|
||||
</li>
|
||||
@ -43,17 +46,13 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// const props = defineProps({
|
||||
// currentName: { type: String, default: 'dashboard' },
|
||||
// });
|
||||
|
||||
const leftMenu = reactive([
|
||||
{
|
||||
name: 'supplier',
|
||||
name: 'inspection',
|
||||
title: '农业环境监测',
|
||||
icon: 'menu1.png',
|
||||
path: '/sub-operation-service/smartFarm/main',
|
||||
isOpen: true,
|
||||
isOpen: false,
|
||||
children: [
|
||||
{
|
||||
name: 'fieldInspection',
|
||||
@ -85,6 +84,18 @@ const leftMenu = reactive([
|
||||
icon: 'menu1.png',
|
||||
path: '/sub-operation-service/smartFarm/intelligentFertilizer',
|
||||
},
|
||||
{
|
||||
name: 'IntelligentIrrigation',
|
||||
title: '智能灌溉',
|
||||
icon: 'menu1.png',
|
||||
path: '/sub-operation-service/smartFarm/IntelligentIrrigation',
|
||||
},
|
||||
{
|
||||
name: 'IntelligentSeedSoaking',
|
||||
title: '智能浸种',
|
||||
icon: 'menu1.png',
|
||||
path: '/sub-operation-service/smartFarm/IntelligentSeedSoaking',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -95,31 +106,31 @@ const leftMenu = reactive([
|
||||
isOpen: false,
|
||||
children: [
|
||||
{
|
||||
name: 'control',
|
||||
name: 'growSeedlings',
|
||||
title: '一体育苗',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/smartFarm/growSeedlings',
|
||||
},
|
||||
{
|
||||
name: 'control',
|
||||
name: 'pestPrevention',
|
||||
title: '病虫害预防',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/smartFarm/pestPrevention',
|
||||
},
|
||||
{
|
||||
name: 'control',
|
||||
name: 'irrigationSystem',
|
||||
title: '喷灌滴灌',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/smartFarm/irrigationSystem',
|
||||
},
|
||||
{
|
||||
name: 'control',
|
||||
name: 'drainageControl',
|
||||
title: '排集水控制',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/smartFarm/drainageControl',
|
||||
},
|
||||
{
|
||||
name: 'control',
|
||||
name: 'openCurtain',
|
||||
title: '开窗卷帘',
|
||||
icon: 'menu3.png',
|
||||
path: '/sub-operation-service/smartFarm/openCurtain',
|
||||
@ -129,32 +140,31 @@ const leftMenu = reactive([
|
||||
]);
|
||||
|
||||
let currentIndex = ref(0);
|
||||
let currentCIndex = ref(-1);
|
||||
|
||||
// watch(
|
||||
// () => props.currentName,
|
||||
// () => {
|
||||
// console.info('currentName', props.currentName);
|
||||
// currentIndex.value = leftMenu.findIndex((m) => {
|
||||
// return m.name === props.currentName;
|
||||
// });
|
||||
// },
|
||||
// { deep: true, immediate: true }
|
||||
// );
|
||||
let currentCIndex = ref('');
|
||||
|
||||
const toLink = (index) => {
|
||||
currentIndex.value = index;
|
||||
currentCIndex.value = -1;
|
||||
window.sessionStorage.setItem('currentOpen', index);
|
||||
if (index === 0) {
|
||||
window.sessionStorage.setItem('currentChild', 'main');
|
||||
}
|
||||
currentCIndex.value = '';
|
||||
let path = index !== undefined ? leftMenu[index].path : null;
|
||||
if (path) {
|
||||
router.push(path);
|
||||
}
|
||||
};
|
||||
const toLinkSub = (index, c) => {
|
||||
const toLinkSub = (index, name) => {
|
||||
console.info('index', index);
|
||||
console.info('c', c);
|
||||
currentCIndex.value = c;
|
||||
let path = leftMenu[index].children[c].path;
|
||||
console.info('currentChild', name);
|
||||
currentCIndex.value = name;
|
||||
window.sessionStorage.setItem('currentChild', name);
|
||||
let path;
|
||||
for (let i in leftMenu[index].children) {
|
||||
if (leftMenu[index].children[i].name === name) {
|
||||
path = leftMenu[index].children[i].path;
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
console.info('path', path);
|
||||
router.push(path);
|
||||
@ -164,6 +174,22 @@ const openList = (index) => {
|
||||
currentIndex.value = index;
|
||||
leftMenu[index].isOpen = !leftMenu[index].isOpen;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const currentMenu = window.sessionStorage.getItem('currentOpen');
|
||||
if (currentMenu) {
|
||||
for (let i in leftMenu) {
|
||||
leftMenu[i].isOpen = i === currentMenu;
|
||||
}
|
||||
}
|
||||
const currentChild = window.sessionStorage.getItem('currentChild');
|
||||
if (currentChild && currentChild === 'main') {
|
||||
currentIndex.value = 0;
|
||||
currentCIndex.value = '';
|
||||
} else if (currentChild) {
|
||||
currentCIndex.value = currentChild;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.fz {
|
||||
@ -179,7 +205,7 @@ const openList = (index) => {
|
||||
color: $color-main;
|
||||
}
|
||||
.smartFarm-left-menu-warp {
|
||||
padding: 0 30px;
|
||||
padding: 0 30px 0 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.left-menu {
|
||||
|
@ -10,21 +10,6 @@ const props = defineProps({
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: (items) => {
|
||||
return items.every((item) => {
|
||||
return (
|
||||
typeof item === 'object' &&
|
||||
item !== null &&
|
||||
typeof item.id === 'number' &&
|
||||
typeof item.isOpen === 'number' &&
|
||||
typeof item.isOperation === 'number' &&
|
||||
typeof item.name === 'string' &&
|
||||
typeof item.serial === 'string' &&
|
||||
typeof item.icon === 'string' &&
|
||||
(!item.status || typeof item.status === 'number')
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||
import { ref, watch, onMounted, onUnmounted, computed } from 'vue';
|
||||
import { isEmpty, getAssetsFile } from '@/utils';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import Hls from 'hls.js';
|
||||
@ -28,7 +28,8 @@ const props = defineProps({
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: (items) => {
|
||||
return items.every((item) => {
|
||||
const validItems = items.filter((item) => item.icon === 'camera');
|
||||
return validItems.every((item) => {
|
||||
return (
|
||||
typeof item === 'object' &&
|
||||
item !== null &&
|
||||
@ -43,6 +44,18 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['changeDevice']);
|
||||
// 显示天气详情弹窗
|
||||
const showWeatherDetail = (data) => {
|
||||
emit('changeDevice', { message: data });
|
||||
};
|
||||
|
||||
// 监听 currentDevice 的变化
|
||||
watch(currentDevice, (newValue, oldValue) => {
|
||||
showWeatherDetail(newValue);
|
||||
// console.log(`count 从 ${oldValue} 变为 ${newValue}`);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (hls.value) {
|
||||
hls.value.destroy();
|
||||
|
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<section>
|
||||
<common>
|
||||
<template #main>
|
||||
<div>
|
||||
<devices :title="'智能灌溉设备'" :devices="devices"></devices>
|
||||
</div>
|
||||
<div style="display: flex; align-items: stretch; justify-content: space-between; margin-top: 20px">
|
||||
<stream :title="'灌溉监测实时监控'" :devices="devices" style="width: 60%; margin-right: 20px; height: fit-content"></stream>
|
||||
<DataDisplay :title="'灌溉分析'" :data="rightData" :show-image="true" :image-list="srcList" style="flex: 1"></DataDisplay>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } 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 DataDisplay from '@/views/smartFarm/components/dataDisplay.vue';
|
||||
|
||||
const devices = ref([
|
||||
{
|
||||
name: 'A-001',
|
||||
icon: 'temperature',
|
||||
detail: 'A区-监控设备9',
|
||||
status: '1', // 0: 离线 1: 正常 -1: 异常
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: 'A-002',
|
||||
icon: 'temperature',
|
||||
detail: 'A区-监控设备66',
|
||||
status: '1',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'A-003',
|
||||
icon: 'humidity',
|
||||
detail: 'A区-监控设备7',
|
||||
status: '0',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'A-004',
|
||||
icon: 'humidity',
|
||||
detail: 'A区-监控设备1',
|
||||
status: '-1',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: 'A-005',
|
||||
icon: 'valve',
|
||||
detail: 'A区-监控设备5',
|
||||
status: '-1',
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: 'A-006',
|
||||
icon: 'valve',
|
||||
status: '0',
|
||||
detail: 'A区-监控设备21',
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: 'A-007',
|
||||
icon: 'sprinkler',
|
||||
status: '0',
|
||||
detail: 'A区-监控设备4',
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
name: 'A-008',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'sprinkler',
|
||||
status: '-1',
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
name: 'A-009',
|
||||
detail: 'A区-监控设备9',
|
||||
icon: 'waterSupplyValve',
|
||||
status: '-1',
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
name: 'A-010',
|
||||
detail: 'A区-监控设备10',
|
||||
icon: 'waterSupplyValve',
|
||||
status: '0',
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
name: 'A-011',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'intelligentValveControl',
|
||||
status: '1',
|
||||
id: 10,
|
||||
},
|
||||
{
|
||||
name: 'A-012',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'intelligentValveControl',
|
||||
status: '1',
|
||||
id: 11,
|
||||
},
|
||||
]);
|
||||
|
||||
const srcList = ref(['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg']);
|
||||
const rightData = ref([
|
||||
{
|
||||
title: '土壤温度', //左侧文本
|
||||
status: '1', // 0:不正常 1:正常
|
||||
statusText: '正常', //右侧文本
|
||||
},
|
||||
{
|
||||
title: '土壤养分含量',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '光照强度',
|
||||
status: '0',
|
||||
statusText: '偏高',
|
||||
},
|
||||
{
|
||||
title: '降水量',
|
||||
status: '0',
|
||||
statusText: '偏低',
|
||||
},
|
||||
{
|
||||
title: '空气湿度',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '风速',
|
||||
status: '1',
|
||||
statusText: '微风',
|
||||
},
|
||||
{
|
||||
title: '设备状态',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '用水储备',
|
||||
status: '1',
|
||||
statusText: '充足',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<section>
|
||||
<common>
|
||||
<template #main>
|
||||
<div>
|
||||
<devices :title="'智能浸种设备'" :devices="devices"></devices>
|
||||
</div>
|
||||
<div style="display: flex; align-items: stretch; justify-content: space-between; margin-top: 20px">
|
||||
<stream :title="'浸种监测实时监控'" :devices="devices" style="width: 60%; margin-right: 20px; height: fit-content"></stream>
|
||||
<DataDisplay :title="'浸种分析'" :data="rightData" :show-image="true" :image-list="srcList" style="flex: 1"></DataDisplay>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } 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 DataDisplay from '@/views/smartFarm/components/dataDisplay.vue';
|
||||
|
||||
const devices = ref([
|
||||
{
|
||||
name: 'A-001',
|
||||
icon: 'tempSensor',
|
||||
detail: 'A区-监控设备9',
|
||||
status: '1', // 0: 离线 1: 正常 -1: 异常
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: 'A-002',
|
||||
icon: 'tempSensor',
|
||||
detail: 'A区-监控设备66',
|
||||
status: '1',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'A-003',
|
||||
icon: 'heater',
|
||||
detail: 'A区-监控设备7',
|
||||
status: '1',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'A-004',
|
||||
icon: 'heater',
|
||||
detail: 'A区-监控设备1',
|
||||
status: '-1',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: 'A-005',
|
||||
icon: 'heat',
|
||||
detail: 'A区-监控设备5',
|
||||
status: '1',
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: 'A-006',
|
||||
icon: 'disinfectionLamp',
|
||||
status: '0',
|
||||
detail: 'A区-监控设备21',
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: 'A-007',
|
||||
icon: 'timer',
|
||||
status: '1',
|
||||
detail: 'A区-监控设备4',
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
name: 'A-008',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'timer',
|
||||
status: '-1',
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
name: 'A-009',
|
||||
detail: 'A区-监控设备9',
|
||||
icon: 'mixer',
|
||||
status: '1',
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
name: 'A-010',
|
||||
detail: 'A区-监控设备10',
|
||||
icon: 'liquidSensor',
|
||||
status: '1',
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
name: 'A-011',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'liquidSensor',
|
||||
status: '0',
|
||||
id: 10,
|
||||
},
|
||||
{
|
||||
name: 'A-012',
|
||||
detail: 'A区-监控设备3',
|
||||
icon: 'disinfectionLamp',
|
||||
status: '1',
|
||||
id: 11,
|
||||
},
|
||||
]);
|
||||
|
||||
const srcList = ref(['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg']);
|
||||
const rightData = ref([
|
||||
{
|
||||
title: '温度', //左侧文本
|
||||
status: '1', // 0:不正常 1:正常
|
||||
statusText: '21℃ 正常', //左侧文本
|
||||
},
|
||||
{
|
||||
title: '湿度',
|
||||
status: '1',
|
||||
statusText: '18℃ 正常',
|
||||
},
|
||||
{
|
||||
title: '光照强度',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '浸泡液位',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '溶解氧含量',
|
||||
status: '1',
|
||||
statusText: '36% 正常',
|
||||
},
|
||||
{
|
||||
title: '酸碱度',
|
||||
status: '1',
|
||||
statusText: '6.7 正常',
|
||||
},
|
||||
{
|
||||
title: '设备状态',
|
||||
status: '1',
|
||||
statusText: '正常',
|
||||
},
|
||||
{
|
||||
title: '种子状态',
|
||||
status: '1',
|
||||
statusText: '出芽',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,7 +1,227 @@
|
||||
<script setup></script>
|
||||
<script setup>
|
||||
import Devices from '@/views/smartFarm/components/devices.vue';
|
||||
import Common from '@/views/smartFarm/components/common.vue';
|
||||
import stream from '@/views/smartFarm/components/stream.vue';
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { getAssetsFile } from '@/utils/index.js';
|
||||
import * as echarts from 'echarts';
|
||||
import BugData from '@/views/smartFarm/components/bugData.vue';
|
||||
import SickData from '@/views/smartFarm/components/sickData.vue';
|
||||
|
||||
const currentDevice = ref(0);
|
||||
const devices = ref([
|
||||
{
|
||||
name: 'A-001',
|
||||
icon: 'temp',
|
||||
detail: 'A区-监控设备1',
|
||||
status: '1',
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: 'A-002',
|
||||
icon: 'temp',
|
||||
detail: 'A区-监控设备2',
|
||||
status: '1',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'A-003',
|
||||
icon: 'ph',
|
||||
detail: 'A区-监控设备3',
|
||||
status: '1',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'A-004',
|
||||
icon: 'ph',
|
||||
detail: 'A区-监控设备4',
|
||||
status: '-1',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: 'A-005',
|
||||
icon: 'O2',
|
||||
detail: 'A区-监控设备5',
|
||||
status: '1',
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: 'A-006',
|
||||
icon: 'elect',
|
||||
detail: 'A区-监控设备6',
|
||||
status: '0',
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: 'B-001',
|
||||
icon: 'light',
|
||||
detail: 'B区-监控设备1',
|
||||
status: '1',
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
name: 'B-002',
|
||||
icon: 'elect',
|
||||
detail: 'B区-监控设备2',
|
||||
status: '-1',
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
name: 'B-003',
|
||||
icon: 'dust',
|
||||
detail: 'B区-监控设备3',
|
||||
status: '1',
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
name: 'B-004',
|
||||
icon: 'dust',
|
||||
detail: 'B区-监控设备4',
|
||||
status: '1',
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
name: 'B-005',
|
||||
icon: 'float',
|
||||
detail: 'B区-监控设备5',
|
||||
status: '1',
|
||||
id: 10,
|
||||
},
|
||||
{
|
||||
name: 'B-006',
|
||||
icon: 'float',
|
||||
detail: 'B区-监控设备6',
|
||||
status: '1',
|
||||
id: 11,
|
||||
},
|
||||
]);
|
||||
const rules = ref([
|
||||
{
|
||||
name: '温度',
|
||||
icon: 'temp',
|
||||
unit: '℃',
|
||||
goat: '15',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: 'PH值',
|
||||
icon: 'ph',
|
||||
unit: '',
|
||||
goat: '6.5',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: '溶解氧',
|
||||
icon: 'O2',
|
||||
goat: '0.4',
|
||||
unit: 'mg/L',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: '色度',
|
||||
icon: 'light',
|
||||
goat: '15',
|
||||
unit: 'PCU',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: '浊度',
|
||||
icon: 'dust',
|
||||
goat: '1.5',
|
||||
unit: 'NTU',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: '导电率',
|
||||
icon: 'elect',
|
||||
goat: '3000',
|
||||
unit: 'μS/cm',
|
||||
status: '1',
|
||||
},
|
||||
{
|
||||
name: '悬浮物',
|
||||
icon: 'float',
|
||||
goat: '9000',
|
||||
unit: 'mg/L',
|
||||
status: '0',
|
||||
},
|
||||
]);
|
||||
const isFullscreen = ref(false);
|
||||
const toggleFullscreen = () => {
|
||||
isFullscreen.value = !isFullscreen.value;
|
||||
};
|
||||
|
||||
const chooseIcon = (type) => {
|
||||
switch (type) {
|
||||
case 'light':
|
||||
return '分光器.png';
|
||||
case 'float':
|
||||
return '悬浮物.png';
|
||||
case 'O2':
|
||||
return '水质溶解氧.png';
|
||||
case 'elect':
|
||||
return '水质电导率.png';
|
||||
case 'dust':
|
||||
return '浊度.png';
|
||||
case 'temp':
|
||||
return '温度.png';
|
||||
case 'ph':
|
||||
return '酸碱度.png';
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<common>
|
||||
<template #main>
|
||||
<div>
|
||||
<devices :title="'病虫害监测设备'" :devices="devices"></devices>
|
||||
</div>
|
||||
<div style="margin-top: 10px; display: flex; justify-content: space-between">
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px">
|
||||
<stream :title="'病害监测实时监控'" :devices="devices" style="width: 60%; height: fit-content"></stream>
|
||||
<div style="width: 38%">
|
||||
<el-card style="border-radius: 16px; padding: 10px">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left; color: #000">虫害数据</div>
|
||||
<sick-data style="width: 38%; min-width: 300px"></sick-data>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px; display: flex; justify-content: space-between">
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px; width: 100%">
|
||||
<stream :title="'虫害监测实时监控'" :devices="devices" style="width: 60%; height: fit-content"></stream>
|
||||
<div style="width: 38%">
|
||||
<el-card style="border-radius: 16px; padding: 10px">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left; color: #000">虫害数据</div>
|
||||
<bug-data style="width: 38%; min-width: 300px"></bug-data>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.rates {
|
||||
margin: 10px 0;
|
||||
height: 100px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-size: 80%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 25%;
|
||||
img {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -7,90 +7,65 @@
|
||||
</div>
|
||||
<div style="margin-top: 10px; display: flex; justify-content: space-between">
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px; width: 100%">
|
||||
<el-card style="width: 60%">
|
||||
<el-card style="width: 60%; margin-right: 20px; border-radius: 16px">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left">实时环境监测</div>
|
||||
<div style="color: #999999; line-height: 25px">
|
||||
当前设备
|
||||
<el-select v-model="currentDevice" placeholder="Select" size="small" style="width: 160px; margin-left: 10px">
|
||||
<el-select
|
||||
v-model="currentDevice"
|
||||
placeholder="Select"
|
||||
size="small"
|
||||
style="width: 160px; margin-left: 10px"
|
||||
@change="changeDevice($event)"
|
||||
>
|
||||
<el-option v-for="item in devices" :key="item.value" :label="item.detail" :value="item.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="envData">
|
||||
<div class="dt">
|
||||
<div class="values">21℃</div>
|
||||
<div class="values">{{ monitorData.co2 }}</div>
|
||||
<div class="points">空气温度</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">61%</div>
|
||||
<div class="values">{{ monitorData.humidity }}</div>
|
||||
<div class="points">空气湿度</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">1600mm</div>
|
||||
<div class="points">空气温度</div>
|
||||
<div class="values">{{ monitorData.rainfall }}</div>
|
||||
<div class="points">降水量</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">东南风</div>
|
||||
<div class="values">{{ monitorData.wind }}</div>
|
||||
<div class="points">风向</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="envData">
|
||||
|
||||
<div class="dt">
|
||||
<div class="values">12μg/m³</div>
|
||||
<div class="values">{{ monitorData.pm25 }}</div>
|
||||
<div class="points">PM2.5</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">679Lux</div>
|
||||
<div class="values">{{ monitorData.light }}</div>
|
||||
<div class="points">光照强度</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">440ppm</div>
|
||||
<div class="values">{{ monitorData.co2 }}</div>
|
||||
<div class="points">二氧化碳浓度</div>
|
||||
</div>
|
||||
<div class="dt">
|
||||
<div class="values">1.5m/s</div>
|
||||
<div class="values">{{ monitorData.windSpeed }}</div>
|
||||
<div class="points">风速</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notices">
|
||||
<div v-for="(item, index) in notices" :key="index">
|
||||
<img :src="getAssetsFile('images/smartFarm/bell.png')" alt="" style="height: 20px" />
|
||||
<img :src="getAssetsFile('images/smartFarm/bell.png')" alt="" style="height: 20px; margin-right: 5px" />
|
||||
<span style="font-size: 14px">{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<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">6.5 正常</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">养分含量:</div>
|
||||
<div class="errorValue">N 元素含量不足</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">重金属含量:</div>
|
||||
<div class="errorValue">Hg 金属含量超标</div>
|
||||
</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">水温:</div>
|
||||
<div class="errorValue">38℃ 偏高</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>
|
||||
</el-card>
|
||||
<DataDisplay :title="'环境分析报告'" :data="rightData" style="flex: 1"></DataDisplay>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -99,10 +74,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import Devices from '@/views/smartFarm/components/devices.vue';
|
||||
import Common from '@/views/smartFarm/components/common.vue';
|
||||
import { getAssetsFile } from '@/utils/index.js';
|
||||
import DataDisplay from '@/views/smartFarm/components/dataDisplay.vue';
|
||||
import Mock from 'mockjs';
|
||||
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
@ -195,14 +172,68 @@ const devices = ref([
|
||||
const currentDevice = ref(0);
|
||||
const notices = ref([
|
||||
{
|
||||
title: '2025年1月1日,预计将会有特大暴雨,请提前做好防护措施!',
|
||||
title: '2025年1月2日,预计将会有特大暴雨,请提前做好防护措施!',
|
||||
content: '',
|
||||
},
|
||||
{
|
||||
title: '2025年1月1日,预计将会有特大暴雨,请提前做好防护措施!',
|
||||
title: '2025年1月1日,预计将会有大雨,请提前做好防护措施!',
|
||||
content: '',
|
||||
},
|
||||
]);
|
||||
let monitorData = ref({});
|
||||
let rightData = ref([]);
|
||||
const changeDevice = (id) => {
|
||||
currentDevice.value = id;
|
||||
monitorData.value = getMockData()[0];
|
||||
rightData.value = getMockData()[1];
|
||||
};
|
||||
// 生成模拟数据
|
||||
const getMockData = () => {
|
||||
// 左侧环境监测数据
|
||||
const leftData = Mock.mock({
|
||||
co2: () => randomRange(200, 800, 0) + 'ppm', // CO₂浓度(300~800ppm)
|
||||
temperature: () => randomRange(-10, 40, 1) + '℃', // 温度(-10~40℃)
|
||||
humidity: () => randomRange(10, 100, 0) + '%', // 湿度(10%~100%)
|
||||
wind: '@pick(["东南风", "西南风", "东北风", "西北风"])',
|
||||
rainfall: () => randomRange(0, 200, 1) + 'mm', // 降水量(0~200mm,1位小数)
|
||||
pm25: () => randomRange(0, 300, 0) + 'μg/m³', // PM2.5(0~300μg/m³,整数)
|
||||
light: () => randomRange(100, 1000, 0) + 'Lux', // 光照强度(100~1000Lux,整数)
|
||||
windSpeed: () => randomRange(0, 10, 1) + 'm/s', // 风速(0~10m/s,1位小数)
|
||||
});
|
||||
// 右侧环境分析报告数据
|
||||
const rightData = Mock.mock({
|
||||
'list|6': [
|
||||
{
|
||||
// 基础字段
|
||||
'title|+1': ['酸碱度(pH)', '养分含量', '重金属含量', '水温', '水浑浊度', '盐分含量'],
|
||||
'status|1': ['0', '1'],
|
||||
'unit|+1': ['pH值', '%', 'mg/kg', '℃', 'NTU', 'dS/m'],
|
||||
|
||||
// 动态生成异常文本
|
||||
statusText: function () {
|
||||
const statusMap = {
|
||||
'酸碱度(pH)':
|
||||
this.status === '0' ? `酸碱度${Mock.mock('@float(4.0, 9.0, 1, 1)')}(${Mock.mock('@pick(["酸性过强","碱性过强"])')})` : '正常',
|
||||
养分含量: this.status === '0' ? `${Mock.mock('@pick(["N","P","K","Ca","Mg"])')}元素含量不足` : '正常',
|
||||
重金属含量: this.status === '0' ? `${Mock.mock('@pick(["镉","铅","砷","汞"])')}超标(${Mock.mock('@float(1, 5, 1, 1)')}mg/kg)` : '正常',
|
||||
水温: this.status === '0' ? `${Mock.mock('@integer(10, 40)')}℃(${Mock.mock('@pick(["低温胁迫","高温胁迫"])')})` : '正常',
|
||||
水浑浊度: this.status === '0' ? `浊度${Mock.mock('@integer(10, 50)')}NTU` : '正常',
|
||||
盐分含量: this.status === '0' ? `盐分${Mock.mock('@float(2, 5, 1, 1)')}dS/m` : '正常',
|
||||
};
|
||||
return statusMap[this.title];
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
return [leftData, rightData.list];
|
||||
};
|
||||
// 自定义随机范围函数
|
||||
const randomRange = (min, max, fixed = 0) => {
|
||||
return Mock.Random.float(min, max, fixed).toFixed(fixed);
|
||||
};
|
||||
onMounted(() => {
|
||||
changeDevice(0);
|
||||
});
|
||||
// #endregion
|
||||
|
||||
/* --------------- methods --------------- */
|
||||
@ -253,10 +284,12 @@ const chooseIcon = (type) => {
|
||||
.envData {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20px;
|
||||
.dt {
|
||||
width: 24%;
|
||||
width: 25%;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
.values {
|
||||
color: #25bf82;
|
||||
font-size: 20px;
|
||||
|
@ -6,43 +6,8 @@
|
||||
<devices :title="'田间监测设备'" :devices="devices"></devices>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px">
|
||||
<stream :title="'田间监测实时监控'" :devices="devices" style="width: 60%; height: fit-content"></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: 200px"></div>
|
||||
</el-card>
|
||||
<stream :title="'田间监测实时监控'" :devices="devices" style="width: 60%; height: fit-content" @change-device="changeDevice"></stream>
|
||||
<data-display style="width: calc(40% - 20px)" title="作物生长状态" show-charts :data="textData"></data-display>
|
||||
</div>
|
||||
</template>
|
||||
</common>
|
||||
@ -55,29 +20,62 @@ 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';
|
||||
import Mock from 'mockjs';
|
||||
import DataDisplay from '@/views/smartFarm/components/dataDisplay.vue';
|
||||
/* --------------- data --------------- */
|
||||
// #region
|
||||
// 图表 DOM 引用
|
||||
const chartRef1 = ref(null);
|
||||
// ECharts 实例
|
||||
let chartInstance = null;
|
||||
// 颜色列表
|
||||
const colorList = ['#3685FE', '#FFD500', '#25BF82'];
|
||||
// x轴数据
|
||||
const xData = ['1月', '2月', '3月', '4月', '5月', '6月'];
|
||||
|
||||
const textData = ref([
|
||||
{
|
||||
title: '作物名称', //左侧文本
|
||||
status: '1', // 0:不正常 1:正常
|
||||
statusText: '橙子', //左侧文本
|
||||
},
|
||||
{
|
||||
title: '生长状态',
|
||||
status: '1',
|
||||
statusText: '成熟期',
|
||||
},
|
||||
{
|
||||
title: '植株形态',
|
||||
status: '1',
|
||||
statusText: '果木型',
|
||||
},
|
||||
{
|
||||
title: '叶片形态',
|
||||
status: '1',
|
||||
statusText: '阔叶型',
|
||||
},
|
||||
{
|
||||
title: '生长态势',
|
||||
status: '1',
|
||||
statusText: '良好',
|
||||
},
|
||||
{
|
||||
title: '田间有机质含量',
|
||||
status: '1',
|
||||
statusText: '16%',
|
||||
},
|
||||
{
|
||||
title: '生长趋势图',
|
||||
status: '1',
|
||||
statusText: '',
|
||||
},
|
||||
]);
|
||||
|
||||
const devices = ref([
|
||||
{
|
||||
name: 'A-001',
|
||||
icon: 'camera',
|
||||
detail: 'A区-监控设备9',
|
||||
status: '0',
|
||||
status: '1',
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: 'A-002',
|
||||
icon: 'camera',
|
||||
detail: 'A区-监控设备66',
|
||||
status: '0',
|
||||
status: '1',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
@ -123,258 +121,118 @@ const devices = ref([
|
||||
id: 7,
|
||||
},
|
||||
]);
|
||||
// 图表配置
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
title: {
|
||||
text: '',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: 400,
|
||||
},
|
||||
left: 'center',
|
||||
top: '5%',
|
||||
},
|
||||
legend: {
|
||||
icon: 'circle',
|
||||
top: '0',
|
||||
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: '20%',
|
||||
},
|
||||
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',
|
||||
show: false,
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: 'rgba(107,107,107,0.37)',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '茎秆高度',
|
||||
type: 'line',
|
||||
data: [10, 10, 30, 12, 15, 3],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#3685FE',
|
||||
shadowColor: 'rgba(158,135,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[0],
|
||||
borderColor: colorList[0],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '叶片温度',
|
||||
type: 'line',
|
||||
data: [5, 12, 11, 14, 25, 16],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#FFD500',
|
||||
shadowColor: 'rgba(115,221,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[1],
|
||||
borderColor: colorList[1],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '果实大小',
|
||||
type: 'line',
|
||||
data: [6, 14, 17, 25, 21, 10],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
yAxisIndex: 0,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
color: '#25BF82',
|
||||
shadowColor: 'rgba(115,221,255, 0.3)',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetY: 20,
|
||||
},
|
||||
itemStyle: {
|
||||
color: colorList[2],
|
||||
borderColor: colorList[2],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// #endregion
|
||||
/* --------------- methods --------------- */
|
||||
// #region
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef1.value) {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
chartInstance = echarts.init(chartRef1.value);
|
||||
// 绘制图表
|
||||
chartInstance.setOption(option);
|
||||
// 自动显示最大值点的tooltip
|
||||
// showMaxValueTooltip();
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', resizeChart);
|
||||
}
|
||||
const changeDevice = (params) => {
|
||||
console.log(params);
|
||||
textData.value = generateCropReport();
|
||||
};
|
||||
|
||||
const showMaxValueTooltip = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
// 找出所有系列中的最大值点
|
||||
let maxValue = -Infinity;
|
||||
let maxSeriesIndex = 0;
|
||||
let maxDataIndex = 0;
|
||||
|
||||
option.series.forEach((series, seriesIndex) => {
|
||||
series.data.forEach((value, dataIndex) => {
|
||||
if (value > maxValue) {
|
||||
maxValue = value;
|
||||
maxSeriesIndex = seriesIndex;
|
||||
maxDataIndex = dataIndex;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 延迟执行确保图表渲染完成
|
||||
setTimeout(() => {
|
||||
chartInstance.dispatchAction({
|
||||
type: 'showTip',
|
||||
seriesIndex: maxSeriesIndex,
|
||||
dataIndex: maxDataIndex,
|
||||
});
|
||||
}, 300);
|
||||
// 生成模拟数据
|
||||
// 农业知识库
|
||||
const AGRICULTURE_KNOWLEDGE = {
|
||||
crops: ['橙子', '蓝莓', '水稻', '番茄', '小麦', '苹果', '葡萄'],
|
||||
plantTypes: {
|
||||
橙子: '乔木型',
|
||||
蓝莓: '灌木型',
|
||||
水稻: '禾本型',
|
||||
番茄: '藤本型',
|
||||
小麦: '草本型',
|
||||
苹果: '乔木型',
|
||||
葡萄: '藤本型',
|
||||
},
|
||||
leafTypes: {
|
||||
橙子: '革质叶',
|
||||
蓝莓: '卵形叶',
|
||||
水稻: '线形叶',
|
||||
番茄: '羽状叶',
|
||||
小麦: '披针叶',
|
||||
苹果: '椭圆形叶',
|
||||
葡萄: '掌状叶',
|
||||
},
|
||||
growthStages: {
|
||||
橙子: ['幼苗期', '生长期', '开花期', '结果期'],
|
||||
蓝莓: ['萌芽期', '开花期', '果实膨大期', '成熟期'],
|
||||
水稻: ['分蘖期', '拔节期', '抽穗期', '灌浆期'],
|
||||
番茄: ['育苗期', '定植期', '开花坐果期', '采收期'],
|
||||
小麦: ['出苗期', '越冬期', '返青期', '成熟期'],
|
||||
苹果: ['花芽期', '展叶期', '果实发育期', '成熟期'],
|
||||
葡萄: ['萌芽期', '新梢生长期', '果实生长期', '成熟期'],
|
||||
},
|
||||
organicMatterRange: {
|
||||
橙子: [12, 20],
|
||||
蓝莓: [8, 15],
|
||||
水稻: [15, 25],
|
||||
番茄: [10, 18],
|
||||
小麦: [18, 30],
|
||||
苹果: [10, 22],
|
||||
葡萄: [12, 18],
|
||||
},
|
||||
};
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
// 生成单组作物数据
|
||||
function generateCropReport() {
|
||||
const crop = Mock.Random.pick(AGRICULTURE_KNOWLEDGE.crops);
|
||||
const isHealthy = Mock.Random.integer(0, 10) > 1; // 80%概率健康
|
||||
const growthStage = Mock.Random.pick(AGRICULTURE_KNOWLEDGE.growthStages[crop]);
|
||||
|
||||
// 异常数据生成
|
||||
const abnormalTexts = {
|
||||
growth: ['发育迟缓', '生长停滞', '提前成熟'],
|
||||
leaf: ['枯黄', '卷曲', '病斑'],
|
||||
status: ['欠佳', '受阻', '严重滞后'],
|
||||
organic: ['过低', '严重不足', '失衡'],
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
title: '作物名称',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: crop,
|
||||
},
|
||||
{
|
||||
title: '生长状态',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: isHealthy ? growthStage : `${Mock.Random.pick(abnormalTexts.growth)}${growthStage}`,
|
||||
},
|
||||
{
|
||||
title: '植株形态',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: AGRICULTURE_KNOWLEDGE.plantTypes[crop],
|
||||
},
|
||||
{
|
||||
title: '叶片形态',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: isHealthy
|
||||
? AGRICULTURE_KNOWLEDGE.leafTypes[crop]
|
||||
: `${Mock.Random.pick(abnormalTexts.leaf)}${AGRICULTURE_KNOWLEDGE.leafTypes[crop]}`,
|
||||
},
|
||||
{
|
||||
title: '生长态势',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: isHealthy ? '良好' : Mock.Random.pick(abnormalTexts.status),
|
||||
},
|
||||
{
|
||||
title: '田间有机质含量',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: isHealthy
|
||||
? `${Mock.Random.integer(...AGRICULTURE_KNOWLEDGE.organicMatterRange[crop])}%`
|
||||
: `${Mock.Random.integer(3, 8)}% (${Mock.Random.pick(abnormalTexts.organic)})`,
|
||||
},
|
||||
{
|
||||
title: '生长趋势图',
|
||||
status: isHealthy ? 1 : 0,
|
||||
statusText: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// 生成7组不同作物数据
|
||||
const cropMonitoringData = Mock.mock({
|
||||
'data|7': [generateCropReport],
|
||||
});
|
||||
// 组件卸载前销毁图表
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
window.removeEventListener('resize', resizeChart);
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
// 调整图表大小
|
||||
const resizeChart = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px">
|
||||
<stream :title="'施肥监测实时监控'" :devices="devices" style="width: 60%; height: fit-content"></stream>
|
||||
<el-card style="width: calc(40% - 20px); border-radius: 16px; padding: 10px">
|
||||
<el-card style="width: calc(40% - 20px); border-radius: 16px">
|
||||
<div style="font-size: 16px; font-weight: bold; text-align: left">配肥分析</div>
|
||||
<div class="plantStatus">
|
||||
<div class="leftKey">土壤酸碱度:</div>
|
||||
|
@ -36,6 +36,7 @@ export default defineConfig(({ command, mode }) => {
|
||||
port: VITE_PORT,
|
||||
open: true,
|
||||
https: false,
|
||||
origin: 'http://localhost:9526',
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user