智慧养殖

This commit is contained in:
lzc 2025-03-24 17:32:57 +08:00
parent 4bd8c7222f
commit 1343293d0e
36 changed files with 2410 additions and 37 deletions

View File

@ -103,7 +103,6 @@ export default {
if (props.option) { if (props.option) {
Object.assign(option, cloneDeep(props.option)); Object.assign(option, cloneDeep(props.option));
} }
option.series[0].data = props.chartData;
setOptions(option); setOptions(option);
resize(); resize();
getInstance()?.off('click', onClick); getInstance()?.off('click', onClick);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,177 @@
<template>
<div class="inventory-charts">
<custom-echart-line-line :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
const legendData = reactive(['猪', '牛', '羊', '鸡', '其他']);
let dataItem = reactive([100, 90, 200, 250, 240, 275, 120, 300, 320, 270, 290, 120]);
let colors = reactive({
: '#3685fe',
: '#41b879',
: '#ffd500',
: '#e57373',
其他: '#46b1db',
});
let colorBg = reactive({
: [
{ offset: 0, color: 'rgba(54,161,255,0.6)' },
{ offset: 1, color: 'rgba(25,104,255,0)' },
],
: [
{ offset: 0, color: 'rgba(0,255,0,0.6)' },
{ offset: 1, color: 'rgba(25,104,255,0)' },
],
: [
{ offset: 0, color: 'rgba(255,213,0,0.6)' },
{ offset: 1, color: 'rgba(25,104,255,0)' },
],
: [
{ offset: 0, color: 'rgba(299,155,155,0.6)' },
{ offset: 1, color: 'rgba(25,104,255,0)' },
],
其他: [
{ offset: 0, color: 'rgba(0,255,0,0.6)' },
{ offset: 1, color: 'rgba(70,177,219,0)' },
],
});
const currentMonth = ref(new Date().getMonth() + 1);
const yAxisData = computed(() => {
let list = [];
for (let i = 1; i < 13; i++) {
let mouth = i < 10 ? i : i;
list.push(mouth + '月');
}
return list;
});
let seriesItem = reactive({
type: 'line',
stack: 'Total',
symbol: 'none',
smooth: true,
});
let seriesData = computed(() => {
let list = [];
if (legendData.length && legendData.length > 0) {
legendData.forEach((m) => {
let val = {
...seriesItem,
name: m,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: colorBg[m],
global: false, // false
},
},
lineStyle: {
color: colors[m],
width: 1,
type: 'solid',
},
data: dataItem,
};
list.push(val);
});
}
return list;
});
const chartsData = reactive({
option: {
backgroundColor: 'transparent',
grid: {
left: '3%',
right: '15%',
bottom: '1%',
top: '3%',
containLabel: true,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
textStyle: {
color: '#fff',
fontSize: 10,
},
confine: true, //
backgroundColor: 'rgba(17,95,182,0.5)', //
formatter: function (item) {
let params = [...item];
var res = params[0].name + '<br/>';
for (var i = 0, l = params.length; i < l; i++) {
res += params[i].value !== '-' ? params[i].marker + params[i].seriesName + ' : ' + params[i].value + ' <br/>' : '';
}
return res;
},
},
legend: {
show: true,
data: Array.from(legendData),
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
xAxis: {
type: 'category',
boundaryGap: true,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: '#94A7BD', //线
},
},
data: yAxisData,
},
yAxis: {
type: 'value',
name: ' ',
nameTextStyle: {
fontSize: 14,
color: '#94A7BD',
padding: [0, 0, 0, -45],
},
axisLine: {
lineStyle: {
color: '#94A7BD', //线
},
},
splitLine: {
show: true,
lineStyle: {
color: '#182D46',
type: [2, 3],
dashOffset: 2,
},
},
},
series: seriesData.value,
},
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.inventory-charts {
height: 100%;
width: 100%;
}
</style>

View File

@ -0,0 +1,121 @@
<template>
<div class="benefit-charts">
<custom-echart-mixin :chart-data="handelData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['成本', '收入', '繁殖率', '配种成功率']);
const chartsData = reactive({
option: {
color: ['#3685fe', '#8dcbe9', '#ffd500', '#631f9f'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
show: true,
data: legendList,
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 10,
},
dataZoom: [
// {
// type: 'slider', //
// startValue: 0, //
// endValue: 2, //
// },
// {
// type: 'inside', //
// startValue: 0,
// endValue: 2,
// },
],
yAxis: [
{
type: 'value',
name: ' ',
axisLabel: {
formatter: '{value}',
},
splitLine: {
show: true, // 线
lineStyle: {
type: 'dashed', // 线
width: 0.5, // 线
},
},
itemStyle: { fontSize: 8 },
},
],
grid: {
x: '10%',
x2: '10%',
y: '20%',
y2: '20%',
},
},
valData: [],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: i < legendList.length - 2 ? 'bar' : 'line',
type: legendList[i],
stack: num + '月',
};
if (val.seriesType == 'line') {
val.smooth = 30;
val.symbol = 'none';
}
let lastVal = {
...val,
...itemStyle,
};
list[i] = i < legendList.length - 3 ? val : lastVal;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m, indexm) => {
return { ...m, value: Number(Number(m.value) + Math.random() + indexm).toFixed(0) };
});
// console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.benefit-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<div class="demo device-charts" style="height: 90%">
<div class="list-item-header item-warp" :style="{ flex: listKeys.length }">
<template v-for="(h, indexh) in listKeys" :key="indexh">
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
</template>
</div>
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="list">
<div v-for="(item, index) in list" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
<template v-for="(b, indexb) in listKeys" :key="indexb">
<div class="item-td" :class="item.status == 1 ? 'td-title' : 'td-warn'" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
<span v-if="b != 'status'">
{{ item[b] }}
</span>
<el-icon v-else>
<Bell></Bell>
</el-icon>
</div>
</template>
</div>
</div>
</div>
</vue3ScrollSeamless>
</div>
<!-- </div> -->
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
const props = defineProps({
// items: {
// type: Array,
// default: () => [],
// },
});
let list = reactive([
{ num: '投喂机', duration: '3.7h', status: 1 },
{ num: '喂水机', duration: '10.0h', status: 1 },
{ num: '投喂机', duration: '6.4h', status: 1 },
{ num: '喂水机', duration: '3.9h', status: 1 },
{ num: '投喂机', duration: '3.6h', status: 0 },
{ num: '喂水机', duration: '4.5h', status: 1 },
{ num: '投喂机', duration: '5.6h', status: 1 },
]);
const listKeys = reactive(['num', 'status', 'duration']);
const listKeysHeader = reactive({
num: '设备编号',
status: '设备状态',
duration: '设备今日运行时长',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.device-charts {
margin-top: 8px;
.scroll-wrap {
height: 80%;
width: 100%;
margin: 4px auto;
overflow: hidden;
}
.list-item-header {
background: #144482;
font-size: 10px;
width: 100%;
.item-td {
padding: 8px 6px;
}
}
.list-item-boday {
background: transparent;
width: 100%;
.item-td {
padding: 4px 6px;
&.td-title {
color: #6beff9 !important;
}
&.td-warn {
color: red !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
}
}
.list-item {
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
line-height: 18px;
.list-item-content {
display: inline-flex;
width: 100%;
justify-content: space-around;
position: relative;
}
}
}
.demo {
// display: flex;
// align-items: center;
// justify-content: center;
// margin-top: 10px;
}
</style>

View File

@ -0,0 +1,174 @@
<template>
<div class="plant-environment-warp">
<div class="data-item-row">
<div
v-for="(n, index) in datalist"
:key="index"
:style="{
'background-image': 'url(' + getAssetsFile('images/plant/bg3.png') + ')',
width: 'calc((100% - 30px) /' + datalist.length + ')',
}"
class="data-item"
>
<div class="data-warp">
<div class="data-pos">
<div class="data-pos-center">
<div class="pos-center">
<span class="label">{{ n.label }}</span>
<div class="value">
<span>{{ n.value }}</span>
<span class="unit">{{ n.unit }}</span>
</div>
</div>
</div>
</div>
<div class="small-bg">
<img :src="getAssetsFile('images/plant/bg6.png')" />
<img :src="getAssetsFile('images/plant/' + n.icon)" class="img-icon" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useApp } from '@/hooks';
const router = useRouter();
const props = defineProps({
title: {
type: String,
default: '统计分析',
},
postion: {
type: String,
default: 'left',
},
});
let topTitle = ref('');
let pos = ref('');
const datalist = reactive([
{ label: '空气温度', value: 28.6, unit: '℃', icon: 'icon4.png' },
{ label: '空气湿度', value: 30, unit: '%', icon: 'icon3.png' },
{ label: '光照强度', value: 1000, unit: 'lux', icon: 'icon1.png' },
{ label: '降水量', value: 100, unit: 'mm', icon: 'icon2.png' },
]);
onMounted(() => {
if (datalist.length) {
datalist.forEach((m, index) => {
let num = 0;
switch (index) {
case 0:
num = 20;
break;
case 1:
num = 30;
break;
case 2:
num = 1000;
break;
case 3:
num = 100;
break;
default:
num = 10;
break;
}
m.value = (Math.random() + num).toFixed(2);
});
}
});
watch(
() => (props.title, props.postion),
() => {
topTitle.value = props.title;
pos.value = props.postion;
},
{
deep: true,
immediate: true,
}
);
</script>
<style lang="scss" scoped>
.plant-environment-warp {
height: 100%;
width: 100%;
.data-item-row {
height: 100%;
width: 100%;
display: inline-flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 10px;
}
.data-item {
height: 100%;
background-size: 100% 100%;
position: relative;
}
.data-warp {
padding: 8px 0;
text-align: center;
z-index: 1;
display: inline-flex;
justify-content: center;
.small-bg,
.data-pos {
display: inline-flex;
vertical-align: middle;
.data-pos-center {
display: inline-flex;
justify-content: space-around;
flex-direction: column;
height: 100%;
.pos-center {
}
}
}
.small-bg {
width: 38px;
height: 38px;
position: relative;
margin-top: 10%;
.img-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 38%;
height: 38%;
}
}
.data-pos {
width: calc(100% - 54px);
.label,
.value {
display: inline-block;
width: 100%;
}
.label {
color: #fff;
font-size: 13px;
}
.value {
color: #6beff9;
font-size: 16px;
font-weight: bold;
margin-top: 6px;
}
.unit {
font-size: 10px;
}
}
}
}
</style>

View File

@ -0,0 +1,117 @@
<template>
<div class="growth-indexes-charts">
<custom-echart-mixin :chart-data="handelData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['猪', '牛', '羊', '鸡', '其他']);
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500', '#e57373'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
show: true,
data: legendList,
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 10,
},
dataZoom: [
// {
// type: 'slider', //
// startValue: 0, //
// endValue: 2, //
// },
// {
// type: 'inside', //
// startValue: 0,
// endValue: 2,
// },
],
yAxis: [
{
type: 'value',
name: ' ',
axisLabel: {
formatter: '{value}',
},
splitLine: {
show: true, // 线
lineStyle: {
type: 'dashed', // 线
width: 0.5, // 线
},
},
itemStyle: { fontSize: 8 },
},
],
grid: {
x: '10%',
x2: '10%',
y: '20%',
y2: '20%',
},
},
valData: [],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: 'bar',
type: legendList[i],
stack: num + '月',
};
let lastVal = {
...val,
...itemStyle,
};
list[i] = i < legendList.length - 1 ? val : lastVal;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m, indexm) => {
return { ...m, value: Number(Number(m.value) + Math.random() + indexm).toFixed(0) };
});
// console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.growth-indexes-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<div class="demo health-status-charts" style="height: 90%">
<div class="list-item-header item-warp" :style="{ flex: listKeys.length }">
<template v-for="(h, indexh) in listKeys" :key="indexh">
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
</template>
</div>
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="list">
<div v-for="(item, index) in list" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
<template v-for="(b, indexb) in listKeys" :key="indexb">
<div class="item-td" :class="item.status == 1 ? 'td-title' : 'td-warn'" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
<span v-if="b != 'status'">
{{ item[b] }}
</span>
<el-icon v-else>
<Bell></Bell>
</el-icon>
</div>
</template>
</div>
</div>
</div>
</vue3ScrollSeamless>
</div>
<!-- </div> -->
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
const props = defineProps({
// items: {
// type: Array,
// default: () => [],
// },
});
let list = reactive([
{ diseaseType: '瘟疫', incidenceRate: '23%', coverage: '84%', status: 1 },
{ diseaseType: '蓝耳病', incidenceRate: '19%', coverage: '88%', status: 1 },
{ diseaseType: '口蹄疫', incidenceRate: '45%', coverage: '68%', status: 1 },
{ diseaseType: '链球病菌', incidenceRate: '35%', coverage: '88%', status: 1 },
{ diseaseType: '炎症', incidenceRate: '8%', coverage: '98%', status: 1 },
{ diseaseType: '寄生虫', incidenceRate: '11%', coverage: '99%', status: 1 },
]);
const listKeys = reactive(['diseaseType', 'incidenceRate', 'coverage', 'status']);
const listKeysHeader = reactive({
diseaseType: '疾病种类',
incidenceRate: '发病率',
coverage: '疫苗接种率',
status: '预警',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.health-status-charts {
margin-top: 8px;
.scroll-wrap {
height: 80%;
width: 100%;
margin: 4px auto;
overflow: hidden;
}
.list-item-header {
background: #144482;
font-size: 10px;
width: 100%;
.item-td {
padding: 8px 6px;
}
}
.list-item-boday {
background: transparent;
width: 100%;
.item-td {
padding: 4px 6px;
&.td-title {
color: #6beff9 !important;
}
&.td-warn {
color: red !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
}
}
.list-item {
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
line-height: 18px;
.list-item-content {
display: inline-flex;
width: 100%;
justify-content: space-around;
position: relative;
}
}
}
.demo {
// display: flex;
// align-items: center;
// justify-content: center;
// margin-top: 10px;
}
</style>

View File

@ -0,0 +1,179 @@
<template>
<div class="irrigation-charts">
<div class="charts-content">
<div class="water-droplet-bg" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg5.png') + ')' }">
<div class="water-droplet">
<custom-echart-water-droplet height="100%" :option="option" />
</div>
</div>
<div class="water-droplet-warp">
<template v-for="(n, index) in itemlist" :key="index">
<div class="water-droplet-item" :style="{ height: 'calc((100% - 20px) / ' + itemlist.length + ')' }">
<div class="title" :style="{ 'background-image': 'url(' + getAssetsFile(n.bg1) + ')' }">
<div class="title-val" :style="{ color: n.color }">{{ n.title }}</div>
</div>
<div class="tips" :style="{ 'background-image': 'url(' + getAssetsFile(n.bg2) + ')' }">
<span class="tips-val">{{ n.tips }}</span>
</div>
</div>
</template>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
let percent = ref(0.6);
let itemlist = reactive([
{ title: '智能喂水', bg1: 'images/plant/bg8.png', bg2: 'images/plant/bg7.png', tips: '去喂水', color: '#4a90e2ff' },
{ title: '智能投喂', bg1: 'images/plant/bg9.png', bg2: 'images/plant/bg7.png', tips: '去投喂', color: '#50e3c2ff' },
]);
const option = reactive({
backgroundColor: 'transparent', //
series: [
{
name: '预估量',
type: 'liquidFill',
radius: '80%',
center: ['50%', '50%'],
backgroundStyle: {
color: 'transparent',
},
data: [percent.value, percent.value],
amplitude: 12, //
label: {
//
position: ['50%', '45%'],
formatter: percent.value * 100 + '%', //,
textStyle: {
fontSize: '20px', //,
color: '#fff',
},
},
outline: {
borderDistance: 3,
itemStyle: {
borderWidth: 1,
borderColor: {
type: 'linear',
x: 1,
y: 0,
x2: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: 'rgba(255, 255, 255, 0.8)',
},
{
offset: 0.6,
color: 'rgba(255, 255, 255, 0.8)',
},
{
offset: 1,
color: 'rgba(255, 255, 255, 0.8)',
},
],
globalCoord: false,
},
},
},
itemStyle: {
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#45bfe9' },
{ offset: 1, color: '#01589c' },
],
global: false, // false
},
},
},
],
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.irrigation-charts {
height: 100%;
.charts-content {
display: inline-flex;
justify-content: space-between;
width: 100%;
height: 100%;
.water-droplet-bg {
display: inline-block;
width: 40%;
background-size: contain;
background-repeat: no-repeat;
background-position: center bottom;
position: relative;
.water-droplet {
position: absolute;
left: 50%;
top: 10%;
transform: translateX(-50%);
}
}
.water-droplet-warp {
width: 60%;
padding: 0 10px;
display: inline-flex;
vertical-align: middle;
flex-direction: column;
.water-droplet-item {
width: 100%;
}
.title,
.tips {
display: inline-block;
color: #fff;
vertical-align: top;
.tips-val {
display: inline-flex;
line-height: 42px;
text-align: center;
font-size: 14px;
font-weight: bold;
transform: skewX(-13deg) translateY(-50%);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 2px;
text-shadow: -6px 0 0 1px #add8f1;
position: absolute;
left: 0;
top: 50%;
}
}
.title {
width: 40%;
height: 100%;
background-size: contain;
background-position: left bottom;
background-repeat: no-repeat;
.title-val {
width: 100%;
text-align: center;
line-height: 32px;
}
}
.tips {
width: 60%;
height: 100%;
background-size: 100% auto;
background-repeat: no-repeat;
background-position: left center;
position: relative;
}
}
}
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<div class="monitoring-screen-warp">
<div class="monitoring-screen-content" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg2.png') + ')' }"></div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
</script>
<style lang="scss" scoped>
.monitoring-screen-warp {
height: 100%;
width: 100%;
box-sizing: border-box;
.monitoring-screen-content {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 100%;
padding: 10px 0;
div {
box-sizing: border-box;
}
}
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<div class="notice-bar-warp" :style="{ height: height }">
<div class="notice-bar-pos">
<div class="notice-icon">
<img :src="getAssetsFile('images/plant/icon5.png')" />
</div>
<div class="notice-bar" :style="{ 'line-height': height }">
<div class="scrolling-text">
<span>{{ text }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted } from 'vue';
defineProps({
text: {
type: String,
required: true,
default: '这是一条滚动通知消息,请注意查看!',
},
height: {
type: String || Number,
default: '40px',
},
});
</script>
<style scoped lang="scss">
.notice-bar-warp {
width: 100%;
display: inline-flex;
flex-direction: column;
justify-content: center;
.notice-bar-pos {
display: inline-flex;
width: 100%;
}
.notice-icon {
display: inline-block;
vertical-align: middle;
width: 30px;
img {
width: 80%;
height: 80%;
margin: 10% 0;
}
}
}
.notice-bar {
display: inline-block;
overflow: hidden;
width: calc(100% - 30px);
}
.scrolling-text {
white-space: nowrap;
animation: scroll-left 10s linear infinite;
color: #fff;
}
@keyframes scroll-left {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(-100%);
}
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<div class="plant-gs-warp">
<div class="plant-gs-content" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg1.png') + ')' }"></div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
</script>
<style lang="scss" scoped>
.plant-gs-warp {
height: 100%;
padding: 10px 0;
width: 100%;
box-sizing: border-box;
.plant-gs-content {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div class="water-intake-charts">
<custom-echart-mixin :chart-data="handelData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['饮水量', '饲料量']);
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500', '#e57373'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
show: true,
data: legendList,
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 10,
},
dataZoom: [
{
type: 'slider', //
startValue: 0, //
endValue: 5, //
},
{
type: 'inside', //
startValue: 0,
endValue: 5,
},
],
yAxis: [
{
type: 'value',
name: ' ',
axisLabel: {
formatter: '{value}',
},
splitLine: {
show: true, // 线
lineStyle: {
type: 'dashed', // 线
width: 0.5, // 线
},
},
itemStyle: { fontSize: 8 },
},
],
grid: {
x: '10%',
x2: '15%',
y: '20%',
y2: '20%',
},
},
valData: [],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: 'bar',
type: legendList[i],
...itemStyle,
};
list[i] = val;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m) => {
return { ...m, value: Number(m.value + Math.random() + 10).toFixed(2) };
});
// console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.water-intake-charts {
height: 100%;
}
</style>

View File

@ -4,15 +4,79 @@
<template #center> <template #center>
<el-row style="width: 100%; height: 100%"> <el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts"> <el-col :span="6" class="left-charts">
<div class="left-charts-item"></div> <div class="left-charts-item">
<div class="left-charts-item"></div> <customBack top-title="存栏数据分析" :top-postion="'left'">
<div class="left-charts-item"></div> <template #back>
<InventoryCharts></InventoryCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="饲料与饮水量监测" :top-postion="'left'">
<template #back>
<waterIntakeCharts></waterIntakeCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="生长指标监测" :top-postion="'left'">
<template #back>
<growthIndexesCharts></growthIndexesCharts>
</template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<el-row style="height: 67%">
<el-col :span="24" class="center-top">
<div class="notice">
<noticeBar :height="'40px'"></noticeBar>
</div>
<div class="top">
<environment></environment>
</div>
<div class="map-gis">
<plantgs></plantgs>
</div>
</el-col>
</el-row>
<el-row style="height: 33%" :gutter="30">
<el-col :span="12" style="height: 100%">
<customBack top-title="健康状况指标" :top-postion="'left'">
<template #back>
<healthStatusCharts></healthStatusCharts>
</template>
</customBack>
</el-col>
<el-col :span="12" style="height: 100%">
<customBack top-title="智慧投喂控制" :top-postion="'right'">
<template #back>
<irrigationCharts></irrigationCharts>
</template>
</customBack>
</el-col>
</el-row>
</el-col> </el-col>
<el-col :span="12"></el-col>
<el-col :span="6" class="right-charts"> <el-col :span="6" class="right-charts">
<div class="right-charts-item"></div> <div class="right-charts-item">
<div class="right-charts-item"></div> <customBack top-title="智慧监控" :top-postion="'right'">
<div class="right-charts-item"></div> <template #back>
<monitoringScreen></monitoringScreen>
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="生产性能与效益数额" :top-postion="'right'">
<template #back>
<benefitCharts></benefitCharts>
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="设备数据统计" :top-postion="'right'">
<template #back> </template>
</customBack>
</div>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
@ -21,6 +85,17 @@
</template> </template>
<script setup> <script setup>
import baseBg from '@/components/baseBg.vue'; import baseBg from '@/components/baseBg.vue';
import customBack from '@/components/customBack.vue';
import InventoryCharts from './components/InventoryCharts.vue';
import waterIntakeCharts from './components/waterIntakeCharts.vue';
import growthIndexesCharts from './components/growthIndexesCharts.vue';
import environment from './components/environment.vue';
import plantgs from './components/plantgs.vue';
import noticeBar from './components/noticeBar.vue';
import irrigationCharts from './components/irrigationCharts.vue';
import healthStatusCharts from './components/healthStatusCharts.vue';
import monitoringScreen from './components/monitoringScreen.vue';
import benefitCharts from './components/benefitCharts.vue';
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.data-home-index { .data-home-index {
@ -33,6 +108,7 @@ import baseBg from '@/components/baseBg.vue';
} }
.left-charts-item { .left-charts-item {
width: 100%; width: 100%;
height: calc((100% - 30px) / 3);
} }
.right-charts { .right-charts {
@ -46,5 +122,24 @@ import baseBg from '@/components/baseBg.vue';
width: 100%; width: 100%;
height: calc((100% - 30px) / 3); height: calc((100% - 30px) / 3);
} }
.center-top {
padding: 16px;
.notice,
.top,
.map-gis {
display: inline-block;
width: 100%;
}
.notice {
height: 40px;
}
.top {
height: 80px;
}
.map-gis {
height: calc(100% - 140px);
}
}
} }
</style> </style>

View File

@ -5,6 +5,17 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
let seriesData = reactive([
{ value: 205.6 },
{ value: 308.7 },
{ value: 359.6 },
{ value: 452.6 },
{ value: 388.9 },
{ value: 508.7 },
{ value: 369.5 },
{ value: 610.8 },
{ value: 754.3 },
]);
const chartsData = reactive({ const chartsData = reactive({
option: { option: {
@ -53,18 +64,46 @@ const chartsData = reactive({
}, },
}, },
}, },
series: [
{
type: 'pictorialBar',
barCategoryGap: '40%',
barWidth: '100%',
symbol: 'path://M0,10 L10,10 C5.5,10 5.5,5 5,0 C4.5,5 4.5,10 0,10 z',
data: seriesData,
labelLine: { show: true },
z: 10,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#000001', //
},
{
offset: 1,
color: '#0175b6', //
},
],
global: false, // false
},
},
label: {
show: false,
position: 'top',
formatter: '{c}',
color: 'white',
fontSize: 14,
},
},
],
}, },
valData: [ valData: seriesData,
{ value: 205.6 },
{ value: 308.7 },
{ value: 359.6 },
{ value: 452.6 },
{ value: 388.9 },
{ value: 508.7 },
{ value: 369.5 },
{ value: 610.8 },
{ value: 754.3 },
],
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,174 @@
<template>
<div class="plant-environment-warp">
<div class="data-item-row">
<div
v-for="(n, index) in datalist"
:key="index"
:style="{
'background-image': 'url(' + getAssetsFile('images/plant/bg3.png') + ')',
width: 'calc((100% - 30px) /' + datalist.length + ')',
}"
class="data-item"
>
<div class="data-warp">
<div class="data-pos">
<div class="data-pos-center">
<div class="pos-center">
<span class="label">{{ n.label }}</span>
<div class="value">
<span>{{ n.value }}</span>
<span class="unit">{{ n.unit }}</span>
</div>
</div>
</div>
</div>
<div class="small-bg">
<img :src="getAssetsFile('images/plant/bg6.png')" />
<img :src="getAssetsFile('images/plant/' + n.icon)" class="img-icon" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useApp } from '@/hooks';
const router = useRouter();
const props = defineProps({
title: {
type: String,
default: '统计分析',
},
postion: {
type: String,
default: 'left',
},
});
let topTitle = ref('');
let pos = ref('');
const datalist = reactive([
{ label: '空气温度', value: 28.6, unit: '℃', icon: 'icon4.png' },
{ label: '空气湿度', value: 30, unit: '%', icon: 'icon3.png' },
{ label: '光照强度', value: 1000, unit: 'lux', icon: 'icon1.png' },
{ label: '降水量', value: 100, unit: 'mm', icon: 'icon2.png' },
]);
onMounted(() => {
if (datalist.length) {
datalist.forEach((m, index) => {
let num = 0;
switch (index) {
case 0:
num = 20;
break;
case 1:
num = 30;
break;
case 2:
num = 1000;
break;
case 3:
num = 100;
break;
default:
num = 10;
break;
}
m.value = (Math.random() + num).toFixed(2);
});
}
});
watch(
() => (props.title, props.postion),
() => {
topTitle.value = props.title;
pos.value = props.postion;
},
{
deep: true,
immediate: true,
}
);
</script>
<style lang="scss" scoped>
.plant-environment-warp {
height: 100%;
width: 100%;
.data-item-row {
height: 100%;
width: 100%;
display: inline-flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 10px;
}
.data-item {
height: 100%;
background-size: 100% 100%;
position: relative;
}
.data-warp {
padding: 8px 0;
text-align: center;
z-index: 1;
display: inline-flex;
justify-content: center;
.small-bg,
.data-pos {
display: inline-flex;
vertical-align: middle;
.data-pos-center {
display: inline-flex;
justify-content: space-around;
flex-direction: column;
height: 100%;
.pos-center {
}
}
}
.small-bg {
width: 38px;
height: 38px;
position: relative;
margin-top: 10%;
.img-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 38%;
height: 38%;
}
}
.data-pos {
width: calc(100% - 54px);
.label,
.value {
display: inline-block;
width: 100%;
}
.label {
color: #fff;
font-size: 13px;
}
.value {
color: #6beff9;
font-size: 16px;
font-weight: bold;
margin-top: 6px;
}
.unit {
font-size: 10px;
}
}
}
}
</style>

View File

@ -1,18 +1,41 @@
<template> <template>
<div class="irrigation-charts"> <div class="irrigation-charts">
<custom-echart-water-droplet height="100%" :option="option" /> <div class="charts-content">
<div class="water-droplet-bg" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg5.png') + ')' }">
<div class="water-droplet">
<custom-echart-water-droplet height="100%" :option="option" />
</div>
</div>
<div class="water-droplet-warp">
<template v-for="(n, index) in itemlist" :key="index">
<div class="water-droplet-item" :style="{ height: 'calc((100% - 20px) / ' + itemlist.length + ')' }">
<div class="title" :style="{ 'background-image': 'url(' + getAssetsFile(n.bg1) + ')' }">
<div class="title-val" :style="{ color: n.color }">{{ n.title }}</div>
</div>
<div class="tips" :style="{ 'background-image': 'url(' + getAssetsFile(n.bg2) + ')' }">
<span class="tips-val">{{ n.tips }}</span>
</div>
</div>
</template>
</div>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
let percent = ref(0.6); let percent = ref(0.6);
let itemlist = reactive([
{ title: '智能灌溉', bg1: 'images/plant/bg8.png', bg2: 'images/plant/bg7.png', tips: '去浇水', color: '#4a90e2ff' },
{ title: '智能施肥', bg1: 'images/plant/bg9.png', bg2: 'images/plant/bg7.png', tips: '去施肥', color: '#50e3c2ff' },
]);
const option = reactive({ const option = reactive({
backgroundColor: 'transparent', // backgroundColor: 'transparent', //
series: [ series: [
{ {
name: '预估量', name: '预估量',
type: 'liquidFill', type: 'liquidFill',
radius: '42%', radius: '80%',
center: ['50%', '50%'], center: ['50%', '50%'],
backgroundStyle: { backgroundStyle: {
color: 'transparent', color: 'transparent',
@ -24,14 +47,14 @@ const option = reactive({
position: ['50%', '45%'], position: ['50%', '45%'],
formatter: percent.value * 100 + '%', //, formatter: percent.value * 100 + '%', //,
textStyle: { textStyle: {
fontSize: '26px', //, fontSize: '20px', //,
color: '#fff', color: '#fff',
}, },
}, },
outline: { outline: {
borderDistance: 3, borderDistance: 3,
itemStyle: { itemStyle: {
borderWidth: 2, borderWidth: 1,
borderColor: { borderColor: {
type: 'linear', type: 'linear',
x: 1, x: 1,
@ -41,15 +64,15 @@ const option = reactive({
colorStops: [ colorStops: [
{ {
offset: 0, offset: 0,
color: '#007DFF', color: 'rgba(255, 255, 255, 0.8)',
}, },
{ {
offset: 0.6, offset: 0.6,
color: 'rgba(45, 67, 114, 1)', color: 'rgba(255, 255, 255, 0.8)',
}, },
{ {
offset: 1, offset: 1,
color: 'rgba(45, 67, 114, 1)', color: 'rgba(255, 255, 255, 0.8)',
}, },
], ],
globalCoord: false, globalCoord: false,
@ -79,5 +102,78 @@ onMounted(() => {});
<style lang="scss" scoped> <style lang="scss" scoped>
.irrigation-charts { .irrigation-charts {
height: 100%; height: 100%;
.charts-content {
display: inline-flex;
justify-content: space-between;
width: 100%;
height: 100%;
.water-droplet-bg {
display: inline-block;
width: 40%;
background-size: contain;
background-repeat: no-repeat;
background-position: center bottom;
position: relative;
.water-droplet {
position: absolute;
left: 50%;
top: 10%;
transform: translateX(-50%);
}
}
.water-droplet-warp {
width: 60%;
padding: 0 10px;
display: inline-flex;
vertical-align: middle;
flex-direction: column;
.water-droplet-item {
width: 100%;
}
.title,
.tips {
display: inline-block;
color: #fff;
vertical-align: top;
.tips-val {
display: inline-flex;
line-height: 42px;
text-align: center;
font-size: 14px;
font-weight: bold;
transform: skewX(-13deg) translateY(-50%);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 2px;
text-shadow: -6px 0 0 1px #add8f1;
position: absolute;
left: 0;
top: 50%;
}
}
.title {
width: 40%;
height: 100%;
background-size: contain;
background-position: left bottom;
background-repeat: no-repeat;
.title-val {
width: 100%;
text-align: center;
line-height: 32px;
}
}
.tips {
width: 60%;
height: 100%;
background-size: 100% auto;
background-repeat: no-repeat;
background-position: left center;
position: relative;
}
}
}
} }
</style> </style>

View File

@ -0,0 +1,25 @@
<template>
<div class="monitoring-screen-warp">
<div class="monitoring-screen-content" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg2.png') + ')' }"></div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
</script>
<style lang="scss" scoped>
.monitoring-screen-warp {
height: 100%;
width: 100%;
box-sizing: border-box;
.monitoring-screen-content {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 100%;
padding: 10px 0;
div {
box-sizing: border-box;
}
}
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<div class="notice-bar-warp" :style="{ height: height }">
<div class="notice-bar-pos">
<div class="notice-icon">
<img :src="getAssetsFile('images/plant/icon5.png')" />
</div>
<div class="notice-bar" :style="{ 'line-height': height }">
<div class="scrolling-text">
<span>{{ text }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted } from 'vue';
defineProps({
text: {
type: String,
required: true,
default: '这是一条滚动通知消息,请注意查看!',
},
height: {
type: String || Number,
default: '40px',
},
});
</script>
<style scoped lang="scss">
.notice-bar-warp {
width: 100%;
display: inline-flex;
flex-direction: column;
justify-content: center;
.notice-bar-pos {
display: inline-flex;
width: 100%;
}
.notice-icon {
display: inline-block;
vertical-align: middle;
width: 30px;
img {
width: 80%;
height: 80%;
margin: 10% 0;
}
}
}
.notice-bar {
display: inline-block;
overflow: hidden;
width: calc(100% - 30px);
}
.scrolling-text {
white-space: nowrap;
animation: scroll-left 10s linear infinite;
color: #fff;
}
@keyframes scroll-left {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(-100%);
}
}
</style>

View File

@ -113,7 +113,7 @@ let handelData = computed(() => {
list.map((m, indexm) => { list.map((m, indexm) => {
return { ...m, value: Number(Number(m.value) + Math.random() + indexm).toFixed(0) }; return { ...m, value: Number(Number(m.value) + Math.random() + indexm).toFixed(0) };
}); });
console.info('handelData', list); // console.info('handelData', list);
return list; return list;
}); });

View File

@ -0,0 +1,22 @@
<template>
<div class="plant-gs-warp">
<div class="plant-gs-content" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg1.png') + ')' }"></div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
</script>
<style lang="scss" scoped>
.plant-gs-warp {
height: 100%;
padding: 10px 0;
width: 100%;
box-sizing: border-box;
.plant-gs-content {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,274 @@
<template>
<div class="water-detection-charts">
<custom-echart-line-line :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
const legendData = ['矿物质', '钠元素'];
let series = reactive([
{
name: legendData[0],
type: 'line',
symbol: 'none', //
smooth: true,
lineStyle: {
normal: {
width: 1,
color: '#00e6fb', // 线
},
},
areaStyle: {
normal: {
//线4x0,y0,x2,y2(0~1);true
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(2, 113, 146,1)' },
{ offset: 0.4, color: 'rgba(2, 83, 111,1)' },
{ offset: 0.7, color: 'rgba(2, 64, 77,1)' },
{ offset: 1, color: 'rgba(1, 18, 43)' },
],
global: false, // false
},
},
},
data: [0.64, 0.34, 0.7, 0.44, 0.74, 0.12, 0.65, 0.41, 0.85, 0.96, 0.23, 0.16],
markLine: {
symbol: 'none',
silent: true,
lineStyle: {
color: 'red', //
width: 2, // 线
type: 'solid',
},
data: [
{ yAxis: 0.5, name: '基准线1' },
{ yAxis: 0.7, name: '基准线2' },
],
label: {
show: true,
position: 'end',
formatter: '{b}: {c}',
color: '#fff',
},
},
},
{
name: legendData[1],
type: 'line',
symbol: 'none', //
smooth: true,
lineStyle: {
normal: {
width: 1,
color: 'rgb(0, 254, 150)', // 线
},
},
areaStyle: {
normal: {
//线4x0,y0,x2,y2(0~1);true
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgb(42, 140, 102)' },
{ offset: 0.6, color: 'rgb(14, 82, 63)' },
{ offset: 1, color: 'rgb(1, 18, 43)' },
],
global: false, // false
},
},
},
data: [0.14, 0.73, 0.38, 0.46, 0.65, 0.17, 0.89, 0.95, 0.47, 0.19, 0.86, 0.58],
},
]);
const currentMonth = ref(new Date().getMonth() + 1);
const xPoint = computed(() => {
let list = [];
console.info('111', currentMonth.value);
for (let i = 1; i < 13; i++) {
let mouth = i < 10 ? i : i;
list.push(mouth + '月');
}
return list;
});
const chartsData = reactive({
option: {
backgroundColor: '#00001b',
grid: {
left: 20,
right: 20,
bottom: '5%',
top: '20%',
containLabel: true,
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(33, 85, 154, .6)',
borderWidth: 1,
borderColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(85, 149, 233, .6)', // 0%
},
{
offset: 1,
color: 'rgba(85, 149, 233, 0)', // 100%
},
],
global: false, // false
},
padding: 8,
textStyle: {
color: '#fff',
},
axisPointer: {
lineStyle: {
type: 'dashed',
color: 'rgba(255, 255, 255, .6)',
},
},
extraCssText: 'box-shadow: 2px 2px 16px 1px rgba(0, 39, 102, 0.16)',
formatter: function (params) {
let content = `<div style='font-size: 14px; color: #fff;'>${params[0].name}</div>`;
if (Array.isArray(params)) {
for (let i = 0; i < params.length; i++) {
content += `
<div style='display: flex; align-items: center; padding: 4px; background: #21559A; margin-top: 4px; color: #fff;'>
<div style='width: 10px; height: 10px; background: ${params[i].color}; margin-right: 8px;'></div>
<div style='font-size: 12px; margin-right: 32px;'>${params[i].seriesName}</div>
<div style='font-size: 14px;'>${params[i].value}</div>
</div>
`;
}
}
return content;
},
},
legend: {
show: true,
data: Array.from(legendData),
left: 'center', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
// legend: [
// {
// data: [
// {
// name: legendData[0],
// icon: 'rect',
// },
// ],
// itemWidth: 10,
// itemHeight: 2,
// top: '2%',
// left: '40%',
// textStyle: {
// color: '#ADD7FF',
// fontSize: 12,
// },
// },
// {
// top: '2%',
// left: '55%',
// textStyle: {
// color: '#ADD7FF',
// fontSize: 14,
// },
// itemWidth: 18,
// itemHeight: 2,
// data: [
// {
// name: legendData[1],
// icon: 'rect',
// },
// ],
// },
// ],
xAxis: {
type: 'category',
boundaryGap: false,
axisLabel: {
interval: 0, // 1
textStyle: {
color: '#DEEBFF',
fontStyle: 'normal',
fontSize: 10,
},
},
axisTick: {
show: false,
},
axisLine: {
// 线
lineStyle: {
color: 'rgba(77, 128, 254, 0.2)',
},
},
splitLine: {
show: false,
},
data: xPoint.value,
},
yAxis: {
type: 'value',
name: ' ',
nameTextStyle: {
color: '#DEEBFF',
fontSize: 14,
padding: [0, 0, 0, -30],
},
axisLabel: {
interval: 0, // 1
textStyle: {
color: '#DEEBFF',
fontStyle: 'normal',
fontSize: 12,
},
},
axisTick: {
show: false,
},
axisLine: {
// 线
lineStyle: {
color: 'rgba(77, 128, 254, 0.2)',
},
},
splitLine: {
show: false,
},
},
series: series,
},
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.water-detection-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,290 @@
<template>
<div class="yield-charts">
<custom-echart-pictorial-bar :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['总产量', '种植面积']);
const payload = {
id: '',
data: {
title: legendList,
unit: ['%'],
xAxis: ['耿马镇', '勐撒镇', '勐永镇', '孟定镇', '勐简乡', '贺派乡', '四排山乡', '芒洪乡', '大兴乡'],
data1: [20, 80, 100, 40, 34, 90, 60, 20, 80],
data2: [10, 100, 80, 30, 50, 100, 50, 10, 90],
},
};
const unit = payload.data.unit || [];
const xAxis = payload.data.xAxis || [];
const data1 = payload.data.data1 || [];
const data2 = payload.data.data2 || [];
const title = payload.data.title || [];
const chartsData = reactive({
option: {
backgroundColor: 'transparent',
grid: {
left: '3%',
right: '4%',
bottom: '1%',
top: '15%',
containLabel: true,
},
legend: {
show: true,
data: Array.from(legendList),
left: 'center', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
tooltip: {
// axis item
trigger: 'axis',
backgroundColor: 'rgba(9, 30, 60, 0.6)',
extraCssText: 'box-shadow: 0 0 8px rgba(0, 128, 255, 0.27) inset;',
borderWidth: 0,
confine: false,
appendToBody: true,
textStyle: {
color: '#fff',
fontSize: 10,
},
//
axisPointer: {
type: 'shadow',
},
shadowStyle: {
color: 'rgba(157, 168, 245, 0.1)',
},
formatter: (data) => {
var tip = '<h5 class="echarts-tip-h5">' + data[0].name + '</h5>';
data.forEach((item) => {
let unit = '';
if (item.seriesType === 'bar') {
tip += '<div class="echarts-tip-div">';
// tip += '<div class="left">' + item.marker + item.seriesName + '</div>';
tip += '<div class="right">' + item.seriesName + '' + item.value + unit + '</div>';
tip += '</div>';
}
});
return tip;
},
},
xAxis: {
data: xAxis,
axisLine: {
lineStyle: {
type: 'solid',
color: '#4176a3',
width: '0.5', //线
},
},
axisLabel: {
textStyle: {
color: '#fff', //
fontSize: 12,
},
},
},
dataZoom: [
{
type: 'slider', //
startValue: 0, //
endValue: 5, //
},
{
type: 'inside', //
startValue: 0,
endValue: 5,
},
],
yAxis: [
{
name: ' ',
nameTextStyle: {
align: 'left',
fontSize: 11,
color: '#4176a3',
},
type: 'value',
axisLine: {
show: false,
lineStyle: {
color: 'transparent', //
},
},
splitLine: { show: false },
axisTick: { show: false },
axisLabel: {
show: true,
fontSize: 12,
textStyle: {
color: '#ADD6FF', //
},
},
},
],
series: [
{
name: title[0],
type: 'bar',
barWidth: 10,
showBackground: true,
backgroundStyle: {
color: 'rgba(21,136,209,0.1)',
},
itemStyle: {
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#45bfe9' },
{ offset: 1, color: '#01589c' },
],
global: false, // false
},
},
data: data1,
z: 0,
zlevel: 0,
},
{
type: 'pictorialBar',
barWidth: 10,
itemStyle: {
color: '#021C46', //
},
symbolRepeat: 'true',
symbolMargin: 3,
symbol: 'rect',
symbolSize: [30, 4],
data: data1,
z: 1,
zlevel: 0,
label: {
show: false,
position: 'top',
fontSize: 14,
color: '#fff', //
formatter: function (params) {
return params.data;
},
},
},
{
name: title[1],
type: 'bar',
barWidth: 10,
showBackground: true,
backgroundStyle: {
color: 'rgba(21,136,209,0.1)',
},
itemStyle: {
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#6beff9' },
{ offset: 1, color: '#2f888f' },
],
global: false, // false
},
},
data: data2,
z: 0,
zlevel: 0,
},
{
type: 'pictorialBar',
barWidth: 10,
itemStyle: {
color: '#021C46', //
},
symbolRepeat: 'true',
symbolMargin: 3,
symbol: 'rect',
symbolSize: [30, 4],
data: data2,
z: 1,
zlevel: 0,
label: {
show: false,
position: 'top',
fontSize: 14,
color: '#fff', //
formatter: function (params) {
return params.data;
},
},
},
],
},
valData: [
{ name: '1月', value: 40, type: '蝗虫', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 30, type: '飞蛾', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 100, type: '其他', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 60, type: '蚜虫', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 20, type: '蝗虫', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 20, type: '飞蛾', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 80, type: '其他', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 40, type: '蚜虫', seriesType: 'bar', ...itemStyle },
],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: 'bar',
type: legendList[i],
...itemStyle,
};
list[i] = val;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m) => {
return { ...m, value: Number(m.value + Math.random() + 10).toFixed(2) };
});
// console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.yield-charts {
height: 100%;
width: 100%;
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="data-home-index"> <div class="data-plant-index">
<baseBg :name-val="'plant'" top-title="智慧种植管理系统"> <baseBg :name-val="'plant'" top-title="智慧种植管理系统">
<template #center> <template #center>
<el-row style="width: 100%; height: 100%"> <el-row style="width: 100%; height: 100%">
@ -27,18 +27,28 @@
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-row style="height: 60%"> <el-row style="height: 67%">
<el-col :span="24"></el-col> <el-col :span="24" class="center-top">
<div class="notice">
<noticeBar :height="'40px'"></noticeBar>
</div>
<div class="top">
<environment></environment>
</div>
<div class="map-gis">
<plantgs></plantgs>
</div>
</el-col>
</el-row> </el-row>
<el-row style="height: 40%"> <el-row style="height: 33%" :gutter="30">
<el-col :span="12"> <el-col :span="12" style="height: 100%">
<customBack top-title="水肥检测分析" :top-postion="'left'"> <customBack top-title="水肥检测分析" :top-postion="'left'">
<template #back> <template #back>
<waterfertilizerCharts></waterfertilizerCharts> <waterfertilizerCharts></waterfertilizerCharts>
</template> </template>
</customBack> </customBack>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12" style="height: 100%">
<customBack top-title="智慧水肥灌溉" :top-postion="'right'"> <customBack top-title="智慧水肥灌溉" :top-postion="'right'">
<template #back> <template #back>
<irrigationCharts></irrigationCharts> <irrigationCharts></irrigationCharts>
@ -50,17 +60,23 @@
<el-col :span="6" class="right-charts"> <el-col :span="6" class="right-charts">
<div class="right-charts-item"> <div class="right-charts-item">
<customBack top-title="智慧监控 A区 QQI" :top-postion="'right'"> <customBack top-title="智慧监控 A区 QQI" :top-postion="'right'">
<template #back></template> <template #back>
<monitoringScreen></monitoringScreen>
</template>
</customBack> </customBack>
</div> </div>
<div class="right-charts-item"> <div class="right-charts-item">
<customBack top-title="种植产量分析" :top-postion="'right'"> <customBack top-title="种植产量分析" :top-postion="'right'">
<template #back></template> <template #back>
<yieldCharts></yieldCharts>
</template>
</customBack> </customBack>
</div> </div>
<div class="right-charts-item"> <div class="right-charts-item">
<customBack top-title="水质检测分析" :top-postion="'right'"> <customBack top-title="水质检测分析" :top-postion="'right'">
<template #back></template> <template #back>
<waterdetectionCharts></waterdetectionCharts>
</template>
</customBack> </customBack>
</div> </div>
</el-col> </el-col>
@ -77,9 +93,15 @@ import insectPestsCharts from './components/insectPestsCharts';
import pathologyCharts from './components/pathologyCharts.vue'; import pathologyCharts from './components/pathologyCharts.vue';
import waterfertilizerCharts from './components/waterfertilizerCharts.vue'; import waterfertilizerCharts from './components/waterfertilizerCharts.vue';
import irrigationCharts from './components/irrigationCharts.vue'; import irrigationCharts from './components/irrigationCharts.vue';
import yieldCharts from './components/yieldCharts.vue';
import waterdetectionCharts from './components/waterdetectionCharts.vue';
import environment from './components/environment.vue';
import plantgs from './components/plantgs.vue';
import monitoringScreen from './components/monitoringScreen.vue';
import noticeBar from './components/noticeBar.vue';
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.data-home-index { .data-plant-index {
.left-charts { .left-charts {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
@ -103,5 +125,24 @@ import irrigationCharts from './components/irrigationCharts.vue';
width: 100%; width: 100%;
height: calc((100% - 30px) / 3); height: calc((100% - 30px) / 3);
} }
.center-top {
padding: 16px;
.notice,
.top,
.map-gis {
display: inline-block;
width: 100%;
}
.notice {
height: 40px;
}
.top {
height: 80px;
}
.map-gis {
height: calc(100% - 140px);
}
}
} }
</style> </style>