This commit is contained in:
李想 2025-04-21 16:27:04 +08:00
commit 52452dd2d2
17 changed files with 708 additions and 30 deletions

BIN
src/views/dataV/img/bg4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,174 @@
<template>
<div class="land">
<div class="land-left">
<div class="land-bar"></div>
<ul class="land-node">
<li :class="`${activeIndex === 0 ? 'on' : ''}`" @click="setInfo(0)">土壤-10厘米处</li>
<li :class="`${activeIndex === 1 ? 'on' : ''}`" @click="setInfo(1)">土壤-20厘米处</li>
<li :class="`${activeIndex === 2 ? 'on' : ''}`" @click="setInfo(2)">土壤-30厘米处</li>
</ul>
</div>
<div class="land-right">
<p>
<span>温度</span>
<span>{{ row.t }}</span>
</p>
<p>
<span>温度</span>
<span>{{ row.s }}</span>
</p>
<p>
<span>EC值</span>
<span>{{ row.ec }}</span>
</p>
<p>
<span>PH值</span>
<span>{{ row.ph }}</span>
</p>
<p>
<span>氮磷钾含量</span>
<span>{{ row.ys }}</span>
</p>
</div>
<div class="land-select">
<el-select v-model="currentDevice" placeholder="请选择" size="small" @change="changeDevice">
<el-option v-for="item in deviceList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
</template>
<script>
export default {
name: 'land',
props: {
data: {
type: Object,
defalut: {},
},
},
data() {
return {
rows: [],
row: {},
currentDevice: '1',
activeIndex: 0,
deviceList: [],
};
},
watch: {
data: {
handler: function (v) {
if (v.list && v.list.length > 0) {
this.deviceList = v.list;
this.rows = v.list[0].children;
this.row = this.rows[0];
}
},
immediate: true,
deep: true,
},
},
methods: {
changeDevice(key) {
const item = this.deviceList.find((item) => item.value === key);
this.rows = item.children;
this.row = this.rows[0];
},
setInfo(key) {
this.activeIndex = key;
this.row = this.rows[key];
},
},
};
</script>
<style scoped lang="scss">
.land {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
&-select {
position: absolute;
right: 0;
top: 0;
::v-deep .el-input__inner {
background: #203c4966 !important;
border-color: #20a0ff;
color: #fff;
}
}
&-left,
&-right {
height: 100%;
}
&-left {
position: relative;
padding-left: 30px;
display: flex;
flex-direction: row;
}
&-bar {
width: 20px;
height: 100%;
background: #20a0ff;
}
&-node {
display: column;
li {
list-style: none;
color: #fff;
line-height: 70px;
font-size: 16px;
cursor: pointer;
&::before {
display: inline-block;
content: '';
width: 100px;
border-bottom: 2px dashed #fff;
margin-right: 10px;
}
&.on {
color: #20a0ff;
&::before {
border-color: #20a0ff;
}
}
}
}
&-right {
flex: 1;
display: flex;
flex-direction: column;
padding: 50px 20px 40px;
p {
margin-bottom: 0;
display: flex;
flex-direction: row;
align-items: center;
font-size: 16px;
span {
flex: 1;
text-align: right;
color: #20a0ff;
&:first-child {
color: #fff;
}
}
}
}
}
</style>

View File

@ -1,9 +1,65 @@
<template>
<div class="w100 h100">
<div class="tree" ref="tree">
<div v-for="(item, index) in productList" class="product" ref="product" :key="index" @click="openDialog(item)">
<img class="width-60 height-60" :src="item.imgUrl" alt="" />
<div>{{ item.productName }}</div>
<div class="tree-date">
<span>{{ currentDate }}</span>
<span> <i class="el-icon-location"></i>凤庆县 </span>
</div>
<div class="weather-container">
<div class="temperature">
<div class="temperature-left">
<span class="temp-value">11</span>
<span class="temp-unit"></span>
</div>
<div class="weather-summary">
<span>多云</span>
<span class="temperature-range">12/-1</span>
</div>
</div>
<div class="weather-info-grid">
<div class="weather-item">
<span class="value">东风</span>
<span class="label">3</span>
</div>
<div class="weather-item">
<span class="value">光照</span>
<span class="label"></span>
</div>
<div class="weather-item">
<span class="value">湿度</span>
<span class="label">85%</span>
</div>
<div class="weather-item">
<span class="value">降雨 </span>
<span class="label">0mm</span>
</div>
<div class="weather-item">
<span class="value">炉温</span>
<span class="label">1201</span>
</div>
</div>
</div>
<div class="tree-date"></div>
<div
v-for="(item, index) in productList"
:class="`product product-${index + 1} ${currentBase === item.id ? 'on' : ''}`"
ref="product"
:key="index"
@click="openDialog(item)"
>
<div class="radiating-point">
<div class="point"></div>
<div class="wave"></div>
<div class="wave"></div>
<div class="wave"></div>
</div>
<div class="product-tips">
<img class="width-60 height-60" :src="item.imgUrl" alt="" />
<span class="product-info">
{{ item.productName }}
<!-- <em>点击查看地块信息</em> -->
</span>
</div>
</div>
</div>
@ -84,6 +140,8 @@ export default {
},
data() {
return {
currentDate: '',
currentBase: 'base1',
//
productList: [],
//
@ -133,9 +191,9 @@ export default {
if (n) {
await this.getProductList();
await this.getDeviceList();
this.$nextTick(() => {
this.changePosition();
});
// this.$nextTick(() => {
// this.changePosition();
// });
}
},
immediate: true,
@ -152,24 +210,188 @@ export default {
}
},
},
created() {
this.getDate();
},
methods: {
getDate() {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
this.currentDate = `${year}-${month}-${day}`;
},
//
async getProductList() {
//
//
const { rows } = await listDevice({ baseId: this.baseId, isCamera: 0 });
//
let p = rows.map((item) => ({ ...item, imgUrl: item.productImgUrl.split(',')[1] }));
this.productList = this._.uniqBy(p, 'productId');
// //
// let p = rows.map((item) => ({ ...item, imgUrl: item.productImgUrl.split(',')[1] }));
// this.productList = this._.uniqBy(p, 'productId');
const list = [
{
id: 'base1',
productName: '#8010地块',
imgUrl: require('../img/land-icon.png'),
url: require('../video/ht1.mp4'),
videos: [
require('../video/ht1.mp4'),
require('../video/ht2.mp4'),
require('../video/ht3.mp4'),
require('../video/ht4.mp4'),
require('../video/ht5.mp4'),
require('../video/ht6.mp4'),
],
type: 'ht',
list: [
{
label: '1号土壤传感器',
value: '1',
children: [
{
label: '10cm',
t: '13℃',
s: '22%',
ec: '1.2mS/cm',
ph: 5.5,
ys: '15,22,150 mg/kg',
},
{
label: '20cm',
t: '15℃',
s: '26%',
ec: '1.3mS/cm',
ph: 5.5,
ys: '15,18,145 mg/kg',
},
{
label: '30cm',
t: '16℃',
s: '33%',
ec: '1.7mS/cm',
ph: 5.5,
ys: '11,10,100 mg/kg',
},
],
},
{
label: '2号土壤传感器',
value: '2',
children: [
{
label: '10cm',
t: '10℃',
s: '28%',
ec: '0.6mS/cm',
ph: 6.3,
ys: '15,22,150 mg/kg',
},
{
label: '20cm',
t: '11℃',
s: '22%',
ec: '0.9mS/cm',
ph: 6.3,
ys: '12,18,145 mg/kg',
},
{
label: '30cm',
t: '9℃',
s: '15%',
ec: '1.1mS/cm',
ph: 6.3,
ys: '11,10,110 mg/kg',
},
],
},
],
},
{
id: 'base2',
productName: '#8023地块',
imgUrl: require('../img/land-icon.png'),
url: require('../video/cy1.mp4'),
videos: [require('../video/cy1.mp4'), require('../video/cy2.mp4'), require('../video/cy3.mp4')],
type: 'cy',
list: [
{
label: '3号土壤传感器',
value: '1',
children: [
{
label: '10cm',
t: '17℃',
s: '25%',
ec: '1.0mS/cm',
ph: 5.9,
ys: '15,22,139 mg/kg',
},
{
label: '20cm',
t: '15℃',
s: '27%',
ec: '1.2mS/cm',
ph: 5.9,
ys: '12,18,126 mg/kg',
},
{
label: '30cm',
t: '16℃',
s: '33%',
ec: '1.3mS/cm',
ph: 5.9,
ys: '11,16,111 mg/kg',
},
],
},
{
label: '5号土壤传感器',
value: '2',
children: [
{
label: '10cm',
t: '13℃',
s: '18%',
ec: '0.8mS/cm',
ph: 6.2,
ys: '19,28,180 mg/kg',
},
{
label: '20cm',
t: '12℃',
s: '22%',
ec: '0.9mS/cm',
ph: 6.2,
ys: '15,25,170 mg/kg',
},
{
label: '30cm',
t: '9℃',
s: '15%',
ec: '1.1mS/cm',
ph: 6.2,
ys: '14,20,150 mg/kg',
},
],
},
],
},
];
this.productList = list;
this.openDialog(list[0]);
},
//
async openDialog(product) {
this.deviceDialogVisiable = true;
//ID
await this.getDeviceList(product.productId);
//
this.curentDeviceId = this.deviceList[0].deviceId;
this.getDevice(this.curentDeviceId);
// this.deviceDialogVisiable = true;
// //ID
// await this.getDeviceList(product.productId);
// //
// this.curentDeviceId = this.deviceList[0].deviceId;
// this.getDevice(this.curentDeviceId);
this.currentBase = product.id;
this.$emit('switch', product);
},
//
async getDeviceList(productId) {
@ -347,30 +569,236 @@ export default {
};
</script>
<style lang="scss" scoped>
.weather-container {
width: 280px;
position: absolute;
right: 0;
background: rgba(0, 0, 0, 0.5);
color: white;
padding: 10px;
border-radius: 10px;
}
.temperature {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 20px;
&-left {
font-size: 48px;
margin-right: 50px;
}
}
.temp-value {
font-weight: bold;
}
.temp-unit {
font-size: 24px;
margin-left: 5px;
}
.weather-info-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin-bottom: 16px;
}
.weather-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.weather-item .value {
font-size: 14px;
font-weight: bold;
margin-bottom: 5px;
white-space: nowrap;
}
.weather-item .label {
font-size: 12px;
opacity: 0.8;
}
.weather-summary {
display: flex;
justify-content: space-between;
font-size: 14px;
}
.temperature-range {
opacity: 0.8;
}
//
.tree {
width: 100%;
height: 100%;
background: url('../img/tree.png') no-repeat;
background: url('../img/land-bg.png') no-repeat;
background-size: 100% auto;
background-position: center center;
position: relative;
.product {
width: 100px;
height: 100px;
&-date {
position: absolute;
border-radius: 50%;
left: 20px;
top: -20px;
display: flex;
flex-direction: row;
background-color: rgba(0, 0, 0, 0.5);
height: 40px;
line-height: 40px;
font-size: 16px;
color: #fff;
span {
margin: 0 20px;
}
i {
margin-right: 3px;
}
}
.product {
width: 300px;
height: 300px;
position: absolute;
// border-radius: 50%;
cursor: pointer;
text-align: center;
color: #fff;
font-size: 14px;
font-weight: 900;
// background: #fff;
&-tips {
// display: none;
opacity: 0;
position: absolute;
left: 50%;
transition: all 0.2s ease;
// &:hover {
// .product-info {
// display: block;
// }
// }
}
&.on,
&:hover {
.product-tips {
// display: block;
opacity: 1;
transform: scale(1.1);
}
}
&-1 {
left: 230px;
top: 380px;
.product-tips {
top: 12%;
}
.radiating-point {
position: absolute;
left: 166px;
top: 20px;
}
}
&-2 {
left: 700px;
top: 500px;
.product-tips {
top: -6%;
}
.radiating-point {
position: absolute;
left: 166px;
top: -30px;
}
}
&-info {
position: absolute;
left: 80px;
top: 0px;
display: inline-block;
padding: 10px 20px;
white-space: nowrap;
background: rgba(255, 255, 255, 0.8);
border-radius: 10px;
font-size: 16px;
color: #333;
}
img {
height: 70%;
width: auto;
}
}
}
.radiating-point {
position: relative;
width: 20px;
height: 20px;
margin: 100px auto;
}
.point {
width: 20px;
height: 20px;
background-color: #007bff;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
}
.wave {
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
border: 2px solid #007bff;
border-radius: 50%;
opacity: 0;
animation: radiate 2s infinite;
}
.wave:nth-child(2) {
animation-delay: 0.5s;
}
.wave:nth-child(3) {
animation-delay: 1s;
}
@keyframes radiate {
0% {
transform: scale(0.1);
opacity: 1;
}
100% {
transform: scale(3);
opacity: 0;
}
}
//
$colorL1: #0c2438;
$colorL2: #092944;

View File

@ -0,0 +1,71 @@
<template>
<div class="w100 h100">
<video ref="videoPlayer" controls width="100%" height="100%" @ended="pauseVideo">
<source :src="videoUrl" type="video/mp4" />
你的浏览器不支持视频播放
</video>
</div>
</template>
<script>
export default {
name: 'Video',
mixins: [],
props: {
data: {
type: Object,
defalut: {},
},
},
data() {
return {
videoUrl: '',
currentIndex: 0,
};
},
computed: {},
watch: {
data: {
handler: async function (row) {
this.videoUrl = row.url;
await this.sleep(500);
this.playVideo();
},
immediate: true,
deep: true,
},
},
async created() {},
methods: {
sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
},
playVideo() {
this.$nextTick(() => {
if (this.$refs.videoPlayer) {
this.$refs.videoPlayer.load();
this.$refs.videoPlayer.play();
}
});
},
pauseVideo() {
if (this.$refs.videoPlayer) {
this.$refs.videoPlayer.pause();
if (this.currentIndex === this.data.videos.length - 1) {
this.currentIndex = 0;
} else {
this.currentIndex = this.currentIndex + 1;
}
this.nextVideo();
}
},
async nextVideo() {
this.videoUrl = this.data.videos[this.currentIndex];
console.log(this.videoUrl, this.currentIndex);
await this.sleep(200);
this.playVideo();
},
},
};
</script>

View File

@ -3,11 +3,11 @@
<LayOut :bg="bg" :title="title">
<!-- 左边 大树 -->
<template v-slot:seven>
<tree :baseId="baseId"></tree>
<tree :baseId="baseId" @switch="onSwitch"></tree>
</template>
<!-- 右上 视频播放 -->
<template v-slot:two>
<camera :baseId="baseId"></camera>
<video-mp4 :data="data" />
</template>
<!-- 右中 害虫监测 -->
<template v-slot:three>
@ -15,7 +15,8 @@
</template>
<!-- 下右 设备统计 -->
<template v-slot:six>
<device-chart :baseId="baseId"></device-chart>
<!-- <device-chart :baseId="baseId"></device-chart> -->
<land :data="data" />
</template>
</LayOut>
</div>
@ -23,9 +24,9 @@
<script>
import LayOut from '../layOut.vue';
import Camera from './Camera.vue';
import VideoMp4 from './Video.vue';
import Land from './Land.vue';
import BugChart from './BugChart.vue';
import DeviceChart from './DeviceChart.vue';
import Tree from './Tree.vue';
import SinglePlayer from '@/views/components/player/SinglePlayer';
@ -33,14 +34,18 @@ export default {
props: {
baseId: Number,
},
components: { LayOut, Camera, BugChart, DeviceChart, Tree, SinglePlayer },
components: { LayOut, VideoMp4, BugChart, Land, Tree, SinglePlayer },
data() {
return {
bg: require('../img/bg.png'),
title: ['视频监控', '害虫监测数据', '气象站环境检测', '气象站图表统计', '设备统计'],
title: ['视频监控', '害虫监测数据', '气象站环境检测', '气象站图表统计', '土壤监测数据'],
data: {},
};
},
methods: {
onSwitch(val) {
this.data = val;
},
},
};
</script>
<style lang="scss" scoped></style>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB