This commit is contained in:
13713575202 2025-04-23 17:17:07 +08:00
parent 1fb5b814d8
commit e1644d1181
135 changed files with 10996 additions and 6 deletions

8
components.d.ts vendored
View File

@ -7,7 +7,13 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
BaseBg: typeof import('./src/components/baseBg.vue')['default']
CenterMap: typeof import('./src/components/centerMap.vue')['default']
CodeDialog: typeof import('./src/components/code-dialog/index.vue')['default']
Components: typeof import('./src/components/index.js')['default']
copy: typeof import('./src/components/baseBg copy.vue')['default']
CurrentTime: typeof import('./src/components/currentTime.vue')['default']
CustomBack: typeof import('./src/components/customBack.vue')['default']
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
CustomEchartBar: typeof import('./src/components/custom-echart-bar/index.vue')['default']
CustomEchartBubble: typeof import('./src/components/custom-echart-bubble/index.vue')['default']
@ -25,6 +31,7 @@ declare module 'vue' {
CustomEchartWordCloud: typeof import('./src/components/custom-echart-word-cloud/index.vue')['default']
CustomIframe: typeof import('./src/components/custom-iframe/index.vue')['default']
CustomImportExcel: typeof import('./src/components/custom-import-excel/index.vue')['default']
CustomProgress: typeof import('./src/components/customProgress.vue')['default']
CustomRankList: typeof import('./src/components/custom-rank-list/index.vue')['default']
CustomRichEditor: typeof import('./src/components/custom-rich-editor/index.vue')['default']
CustomScrollBoard: typeof import('./src/components/custom-scroll-board/index.vue')['default']
@ -32,6 +39,7 @@ declare module 'vue' {
CustomTableTree: typeof import('./src/components/custom-table-tree/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SubTop: typeof import('./src/components/subTop.vue')['default']
SvgIcon: typeof import('./src/components/svg-icon/index.vue')['default']
UpFile: typeof import('./src/components/custom-rich-editor/upFile.js')['default']
}

View File

@ -23,6 +23,10 @@
"axios": "^1.6.5",
"echarts": "^5.6.0",
"echarts-gl": "^2.0.9",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0",
"@vuemap/vue-amap": "^2.0",
"@vuemap/vue-amap-loca": "^2.0",
"element-plus": "^2.7.2",
"js-base64": "^3.7.6",
"lodash": "^4.17.21",

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 717 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

File diff suppressed because it is too large Load Diff

267
src/components/baseBg.vue Normal file
View File

@ -0,0 +1,267 @@
<template>
<!-- :style="{ 'background-image': 'url(' + getAssetsFile('images/vsualized/screenbg.png') + ')' }" -->
<div class="data-warp">
<div class="chart-content">
<div class="top">
<slot v-if="$slots.top" name="top"></slot>
<!--:style="{ 'background-image': 'url(' + getAssetsFile('images/vsualized/hraderbg.png') + ')' }" -->
<div v-else class="top-content-warp">
<div class="top-left"></div>
<div class="top-content">
<div class="top-content-p">
<div class="b-nav-l">
<template v-for="(n, indexn) in navlist" :key="n.name">
<div
v-if="indexn <= 3"
class="b-nav-item"
:style="{
'background-image':
'url(' +
(currentName == n.name ? getAssetsFile('images/vsualized/home/nav-on.png') : getAssetsFile('images/vsualized/home/nav.png')) +
')',
}"
:class="currentName == n.name ? 'nav-act' : 'nav-normal'"
@click="itemAct(n.name)"
>
<span>{{ n.title }}</span>
</div>
</template>
</div>
<!-- <div class="title txt-ellipsis clamp1">{{ topTitle }}</div> -->
<div class="title txt-ellipsis clamp1">{{ '农业产业政务云平台' }}</div>
<div class="b-nav-r">
<template v-for="(m, indexm) in navlist" :key="m.name">
<div
v-if="indexm > 3"
class="b-nav-item"
:style="{
'background-image':
'url(' +
(currentName == m.name ? getAssetsFile('images/vsualized/home/nav-on.png') : getAssetsFile('images/vsualized/home/nav.png')) +
')',
}"
:class="currentName == m.name ? 'nav-act' : 'nav-normal'"
@click="itemAct(m.name)"
>
<span>{{ m.title }}</span>
</div>
</template>
</div>
</div>
</div>
<!-- <div class="top-right">{{ currentTime }}</div> -->
</div>
</div>
<div class="content">
<slot name="center"></slot>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { useApp } from '@/hooks';
const router = useRouter();
const props = defineProps({
nameVal: {
type: String,
default: 'home',
},
topTitle: {
type: String,
default: '系统',
},
});
const navlist = ref([
{ title: '首页', name: 'home' },
{ title: '土地资源', name: 'land' },
{ title: '投入品', name: 'inputs' },
{ title: '生产经营主体', name: 'entities' },
// { title: '', name: 'plant' },
// { title: '', name: 'breed' },
{ title: '全流程溯源', name: 'trace' },
{ title: '产业预警决策', name: 'early' },
]);
let currentName = ref('home');
watch(
() => props.nameVal,
() => {
currentName.value = props.nameVal;
},
{
deep: true,
immediate: true,
}
);
const itemAct = (name) => {
currentName.value = name;
router.push({ name: name });
};
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.data-warp {
width: 100%;
height: 100vh;
background-size: 100% 100%;
.chart-content {
width: 100%;
height: 100vh;
z-index: 1;
position: absolute;
left: 0;
top: 0;
.top,
.content,
.bottom {
width: 100%;
padding: 0 16px;
background-size: cover;
display: inline-flex;
flex-direction: column;
justify-content: center;
}
.top {
height: 55px;
.top-content-warp {
width: 100%;
height: 100%;
background-size: 100% 100%;
background-repeat: no-repeat;
.top-left,
.top-content,
.top-right {
display: inline-block;
vertical-align: middle;
}
.top-content {
width: calc(100% - 400px);
height: 100%;
display: inline-flex;
justify-content: center;
flex-direction: column;
.top-content-p {
width: 100%;
}
.title,
.b-nav-l,
.b-nav-r {
display: inline-block;
vertical-align: middle;
}
.b-nav-l,
.b-nav-r {
width: calc((100% - 300px) / 2);
}
.b-nav-r {
text-align: right;
}
.title {
width: 300px;
line-height: 38px;
text-align: center;
font-size: 22px;
font-weight: bold;
transform: skewX(-8deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 8px;
text-shadow: -6px 0 0 1px #add8f1;
max-height: unset !important;
}
}
.b-nav-l,
.b-nav-r {
margin: auto;
display: inline-flex;
gap: 20px;
.b-nav-item {
display: inline-block;
cursor: pointer;
min-width: 132px;
height: 38px;
text-align: center;
line-height: 38px;
span {
font-size: 14px;
font-weight: bold;
display: inline-flex;
transform: skewX(-8deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
letter-spacing: 4px;
text-shadow: -2px 0 0 1px #add8f1;
}
&.nav-act {
color: rgba(255, 255, 255, 1);
}
&.nav-normal {
color: rgba(255, 255, 255, 0.6);
}
}
}
.top-left {
width: 200px;
}
.top-right {
text-align: right;
width: 200px;
color: #add8f1;
}
}
}
.bottom {
height: 80px;
text-align: center;
.b-nav {
margin: auto;
display: inline-flex;
gap: 20px;
.b-nav-item {
display: inline-block;
cursor: pointer;
min-width: 132px;
height: 42px;
text-align: center;
line-height: 38px;
span {
font-size: 14px;
font-weight: bold;
display: inline-flex;
transform: skewX(-8deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
letter-spacing: 4px;
text-shadow: -2px 0 0 1px #add8f1;
}
&.nav-act {
color: rgba(255, 255, 255, 1);
}
&.nav-normal {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.content {
height: calc(100% - 60px);
}
}
}
</style>

View File

@ -0,0 +1,212 @@
<template>
<div class="map-center-warp">
<img :src="getAssetsFile('images/vsualized/gmmap.png')" class="map-img" />
<div class="map-pos">
<custom-echart-maps :chart-data="chartsData.valData" height="100%" :option="chartsData.option" :geo="geoData" :name="mapName" />
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted } from 'vue';
var aspectScale = 0.8807505292367753;
// var iconUrl = 'http://localhost:9529/sub-government-screen-service/src/assets/images/vsualized/home/partbg.png';
// var iconUrl = getAssetsFile('images/vsualized/gmmap.png').href;
var iconUrl = getAssetsFile('images/vsualized/gmmap2.png').href;
import geoJsonData from '../components/530926geo.json'; //
let geoData = geoJsonData;
let mapName = ref('ZJ');
const chartsData = reactive({
option: {
title: {
text: '',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: function (params) {
if (params.seriesType === 'effectScatter') {
return `${params.name}: (${params.value[0]}, ${params.value[1]})`;
}
return params.name;
},
},
toolbox: {
show: false,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
// dataView: { readOnly: false },
// restore: {},
// saveAsImage: {},
},
},
geo: {
map: mapName.value,
roam: true,
zoom: 0.9,
show: false,
itemStyle: {
normal: {
areaColor: '#1f77b4', //
borderColor: '#000', //
},
emphasis: {
areaColor: '#ff7f0e', //
borderColor: '#fff', //
},
},
},
series: [
// {
// name: '',
// type: 'map',
// mapType: mapName.value,
// roam: true,
// zoom: 1.2,
// itemStyle: {
// normal: {
// borderColor: '#000', //
// borderWidth: 1, //
// areaColor: 'transparent', //
// },
// emphasis: {
// borderColor: '#fff',
// borderWidth: 2,
// },
// },
// },
//
{
name: '闪烁散点',
type: 'effectScatter', // 使 effectScatter
coordinateSystem: 'geo',
data: [
//
{
name: '孟定镇',
value: [99.081993, 23.554045, 100], // , ,
itemStyle: {
color: '#FF0000', //
},
},
{
name: '勐永镇',
value: [99.406653, 23.534142, 80], // , ,
itemStyle: {
color: '#00FF00', //
},
},
],
symbolSize: function (val) {
return val[2] ? val[2] / 10 : 10; //
},
label: {
formatter: '{b}',
position: 'right',
show: false,
},
rippleEffect: {
period: 4, //
scale: 3, //
brushType: 'stroke', // 'stroke' 'fill'
},
hoverAnimation: false,
},
],
},
valData: [
{
name: '镇边界',
type: 'map',
mapType: mapName.value,
roam: true,
zoom: 1.2,
itemStyle: {
normal: {
borderColor: '#000', //
borderWidth: 1, //
areaColor: 'transparent', //
},
emphasis: {
borderColor: '#fff',
borderWidth: 2,
},
},
},
//
{
name: '闪烁散点',
type: 'effectScatter', // 使 effectScatter
coordinateSystem: 'geo',
data: [
//
{
name: '孟定镇',
value: [99.081993, 23.554045, 100], // , ,
itemStyle: {
color: '#FF0000', //
},
},
{
name: '贺派乡',
value: [99.406653, 23.534142, 80], // , ,
itemStyle: {
color: '#00FF00', //
},
},
],
symbolSize: function (val) {
return val[2] ? val[2] / 10 : 10; //
},
label: {
formatter: '{b}',
position: 'right',
show: false,
},
rippleEffect: {
period: 4, //
scale: 3, //
brushType: 'stroke', // 'stroke' 'fill'
},
hoverAnimation: false,
},
],
});
onMounted(() => {
console.info('iconUrl', iconUrl);
});
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.map-center-warp {
width: 100%;
text-align: center;
position: relative;
height: 90%;
.map-img {
width: 80%;
height: 80%;
position: absolute;
bottom: 0;
left: 50%;
object-fit: contain;
transform: translateX(-50%);
max-width: 1000px;
max-height: 1000px;
}
.map-pos {
width: 80%;
height: 80%;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<el-dialog
v-model="state.visible"
draggable
title="溯源码"
width="40%"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="onClose"
>
<div class="code-panel">
<div class="code-panel-picture">
<el-image style="width: 200px; height: 200px" :src="row.orCodeUrl" fit="cover" lazy />
</div>
<el-button type="primary" @click="downloadFile(row.orCodeUrl, `${row.productName}-溯源码.png`, 'image')"> 下载溯源码</el-button>
</div>
</el-dialog>
</template>
<script setup name="code-dialog">
import { reactive } from 'vue';
import { downloadFile } from '@/utils';
const props = defineProps({
row: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['on-close']);
const state = reactive({
visible: false,
});
const onClose = () => {
state.visible = false;
};
defineExpose({
show: () => {
state.visible = true;
},
hide: () => {
onClose();
},
});
</script>
<style lang="scss" scoped>
.code {
&-panel {
padding-bottom: 40px;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,65 @@
<template>
<div class="current-time-warp">{{ currentTime }}</div>
</template>
<script setup>
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue';
let currentTime = ref('');
const formatDateTime = () => {
const now = new Date();
//
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // JavaScript01
const day = String(now.getDate()).padStart(2, '0');
// 06
const weekDay = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][now.getDay()];
const hour = String(now.getHours()).padStart(2, '0');
const minute = String(now.getMinutes()).padStart(2, '0');
const second = String(now.getSeconds()).padStart(2, '0');
//
const formattedDate = `${year}-${month}-${day}\u00A0\u00A0\u00A0 ${weekDay}\u00A0\u00A0\u00A0 ${hour}:${minute}:${second}`;
// console.log(formattedDate);
return formattedDate;
};
let interval = ref(null);
const startTime = () => {
interval.value = setInterval(() => {
currentTime.value = formatDateTime();
}, 1000);
};
const chearTime = () => {
if (interval.value) {
clearInterval(interval.value);
interval.value = null;
}
};
onMounted(() => {
startTime();
});
onUnmounted(() => {
chearTime();
});
defineExpose({
startTime,
chearTime,
});
</script>
<style lang="scss" scoped>
.current-time-warp {
position: fixed;
right: 16px;
top: 24px;
color: #add8f1;
z-index: 2;
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div class="custom-back-warp">
<subTop :title="topTitle" :postion="topPostion"></subTop>
<div class="custom-back-content">
<slot name="back"></slot>
</div>
</div>
</template>
<script setup>
import subTop from '@/components/subTop.vue';
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({
topTitle: {
type: String,
default: '统计',
},
topPostion: {
type: String,
default: 'left',
},
});
</script>
<style lang="scss" scoped>
.custom-back-warp {
height: 100%;
width: 100%;
.custom-back-content {
height: calc(100% - 38px);
width: 100%;
padding: 10px 0;
box-sizing: border-box;
}
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div ref="progress" class="custom-progress-val" :style="{ background: inactiveBg }">
<div class="progress-warp" :style="{ height: height }">
<div class="progress" :style="{ height: height, width: pWidth + 'px', background: activateBg }"></div>
</div>
{{ width }}
</div>
</template>
<script setup>
import { ref, onMounted, watch, reactive, computed } from 'vue';
const props = defineProps({
height: {
type: String,
default: '10px',
},
inactiveBg: {
type: String,
default: 'transparent',
},
activateBg: {
type: String,
default: 'linear-gradient(90deg, #45bfe9 0%, #01589c 100%)',
},
percent: {
type: Number,
default: 20,
},
width: {
type: String,
default: null,
},
});
let progress = ref(null);
let maxwidth = computed(() => {
return progress.value && progress.value.clientWidth;
});
let pWidth = computed(() => {
let num = 0;
num = Number(((maxwidth.value * props.percent) / 100).toFixed(0));
return num;
});
</script>
<style lang="scss" scoped>
.custom-progress-val {
width: calc(100%);
border-radius: 6px;
.progress-warp {
width: 100%;
.progress {
height: 100%;
border-radius: 6px;
animation: expandWidth 1s ease-in-out forwards;
}
@keyframes expandWidth {
from {
width: 0;
}
to {
width: maxwidth + 'px';
}
}
}
}
</style>

81
src/components/subTop.vue Normal file
View File

@ -0,0 +1,81 @@
<template>
<div class="title-top-warp">
<div
:style="{
'background-image': 'url(' + getAssetsFile('images/vsualized/home/titlebg.png') + ')',
transform: pos == 'right' ? 'rotateY(180deg)' : '',
}"
class="title-top-bg"
></div>
<span class="title-top-content" :style="{ 'text-align': pos }">{{ topTitle || '--' }}</span>
</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('');
watch(
() => (props.title, props.postion),
() => {
topTitle.value = props.title;
pos.value = props.postion;
},
{
deep: true,
immediate: true,
}
);
</script>
<style lang="scss" scoped>
.title-top-warp {
position: relative;
height: 38px;
width: 100%;
.title-top-bg {
width: 100%;
height: 100%;
background-size: 100% 100%;
background-repeat: no-repeat;
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
.title-top-content {
line-height: 38px;
font-size: 14px;
font-weight: bold;
display: inline-block;
transform: skewX(-13deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 4px;
text-shadow: -2px 0 0 1px #add8f1;
width: 100%;
padding: 0 36px;
box-sizing: border-box;
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
}
</style>

View File

@ -1,5 +1,5 @@
import 'virtual:svg-icons-register';
import { createApp } from 'vue';
import { createApp, nextTick } from 'vue';
import App from './App.vue';
import router from './router';
import pinia from './store';
@ -9,8 +9,26 @@ import 'animate.css';
import './utils/permission';
import { registerDirective } from './directives';
import { registerElIcons } from './plugins/icon';
import VueAMap, { initAMapApiLoader } from '@vuemap/vue-amap';
import '@vuemap/vue-amap/dist/style.css';
// 初始化高德地图 API
initAMapApiLoader({
key: 'c843a50db7157faf295c6fa37c48719f',
securityJsCode: 'f09302d3ed65110614bdb26e44717ddf', // 新版key需要配合安全密钥使用
version: '2.0',
Loca: {
version: '2.0',
},
AMapUI: {
plugins: ['misc/PathSimplifier'],
},
plugins: ['AMap.MapType'],
});
const app = createApp(App);
registerElIcons(app);
registerDirective(app);
app.use(pinia).use(router).use(ElementPlus).mount('#app');
nextTick(() => {
registerElIcons(app);
registerDirective(app);
});
app.use(pinia).use(router).use(ElementPlus).use(VueAMap).mount('#app');

View File

@ -29,6 +29,48 @@ export const constantRoutes = [
name: 'home',
meta: { title: '首页', icon: 'House' },
},
{
path: '/land',
component: () => import('@/views/land/index.vue'),
name: 'land',
meta: { title: '土地资源', icon: 'House' },
},
{
path: '/inputs',
name: 'inputs',
component: () => import('@/views/inputs/index.vue'),
hidden: true,
},
{
path: '/entities',
name: 'entities',
component: () => import('@/views/entities/index.vue'),
hidden: true,
},
{
path: '/breed',
name: 'breed',
component: () => import('@/views/breed/index.vue'),
hidden: true,
},
{
path: '/plant',
name: 'plant',
component: () => import('@/views/plant/index.vue'),
hidden: true,
},
{
path: '/trace',
name: 'trace',
component: () => import('@/views/trace/index.vue'),
hidden: true,
},
{
path: '/early',
name: 'early',
component: () => import('@/views/early/index.vue'),
hidden: true,
},
],
},
...demoRouters,

66
src/utils/echarts.js Normal file
View File

@ -0,0 +1,66 @@
import * as echarts from 'echarts/core';
import {
BarChart,
LineChart,
PieChart,
MapChart,
PictorialBarChart,
RadarChart,
GraphChart,
GaugeChart,
ScatterChart,
EffectScatterChart,
} from 'echarts/charts';
import 'echarts-gl';
import 'echarts-liquidfill';
import 'echarts-wordcloud';
import {
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
LegendComponent,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
GraphicComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
BarChart,
LineChart,
PieChart,
MapChart,
RadarChart,
CanvasRenderer,
PictorialBarChart,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
GraphicComponent,
GraphChart,
GaugeChart,
ScatterChart,
EffectScatterChart,
]);
export default echarts;

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,161 @@
<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="datalist">
<div v-for="(item, index) in datalist" :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 == 'num'">
{{ item[b] }}
</span>
<span v-else-if="b == 'duration'" class="duration">
<span class="val">{{ item[b] + 'h' }}</span>
<div class="pro">
<customProgress height="5px" :percent="item.percent" inactive-bg="#081931"></customProgress>
</div>
</span>
<span v-else>
{{ item[b] == 0 ? '待机' : '运行' }}
</span>
</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';
import customProgress from '@/components/customProgress.vue';
const props = defineProps({
// items: {
// type: Array,
// default: () => [],
// },
});
let list = reactive([
{ num: '投喂机', duration: '3.7', status: 1 },
{ num: '喂水机', duration: '10.0', status: 1 },
{ num: '投喂机', duration: '6.4', status: 1 },
{ num: '喂水机', duration: '3.9', status: 1 },
{ num: '投喂机', duration: '3.6', status: 0 },
{ num: '喂水机', duration: '4.5', status: 1 },
{ num: '投喂机', duration: '5.6', status: 1 },
]);
const listKeys = reactive(['num', 'status', 'duration']);
const listKeysHeader = reactive({
num: '设备编号',
status: '设备状态',
duration: '设备今日运行时长',
});
let datalist = computed(() => {
return list.map((m) => {
return {
...m,
percent: Number((Number(parseInt(m.duration) / max.value) * 100).toFixed(0)),
};
});
});
let max = computed(() => {
let valueList = new Set(list.map((item) => item.duration));
let sortValue = [...valueList].sort((a, b) => b - a) || [];
// console.info('valueList', sortValue);
return sortValue.length ? sortValue[0] : 0;
});
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;
}
.duration {
width: 100%;
.val,
.pro {
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
}
.val {
width: 50px;
padding-right: 10px;
}
.pro {
width: calc(100% - 50px);
}
}
}
.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: 100% 100%;
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,116 @@
<template>
<div class="plant-gs-warp">
<div ref="viewwarp" class="plant-gs-content" :style="{ 'background-image': 'url(' + getAssetsFile('images/plant/bg1.png') + ')' }">
<vc-config-provider :cesium-path="vcConfig.cesiumPath" :access-token="vcConfig.accessToken">
<vc-viewer ref="viewerRef" @ready="onViewerReady">
<vc-layer-imagery :minimum-level="12" :maximum-level="18" :contrast="1.8" :saturation="1" :alpha="1">
<vc-imagery-provider-amap :key="'c843a50db7157faf295c6fa37c48719f'"></vc-imagery-provider-amap>
</vc-layer-imagery>
<vc-overlay-html ref="html" :position="pos" :show="show">
<div class="vc-name">耿马镇</div>
</vc-overlay-html>
<vc-entity description="Hello VueCesium">
<vc-graphics-rectangle
ref="rectangle2"
:coordinates="hierarchy4"
:material="[59, 199, 231, 80]"
:rotation="0.9"
:extruded-height="1000"
:height="0"
:outline="true"
:outline-width="0.1"
:clamp-to-ground="false"
:outline-color="[59, 199, 231, 150]"
></vc-graphics-rectangle>
</vc-entity>
</vc-viewer>
</vc-config-provider>
</div>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted, computed, nextTick, getCurrentInstance } from 'vue';
import {
VcViewer,
VcConfigProvider,
VcImageryProviderAmap,
VcLayerImagery,
VcGraphicsPlane,
VcEntity,
VcGraphicsPolygon,
VcGraphicsRectangle,
VcOverlayHtml,
} from 'vue-cesium';
import 'vue-cesium/dist/index.css';
const vcConfig = reactive({
cesiumPath: 'https://cdn.bootcdn.net/ajax/libs/cesium/1.80.0/Cesium.js',
accessToken:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjJjZTUxYi1lOTQ3LTQ3YjctOGI3ZS02ZGUzY2E4YWFkNDkiLCJpZCI6Mjg4MjAxLCJpYXQiOjE3NDMwNTY0MzN9.rjHQiqf7Y8bccaqsapqveULVAUH6M1QkeFp-AKG-frA',
});
const viewerRef = ref(null);
const viewwarp = ref(null);
const rectangle2 = ref(null);
onMounted(() => {
Promise.all([rectangle2.value.creatingPromise]).then((instances) => {
// console.info('aa', instances[0].viewer.entities);
instances[0].viewer.zoomTo(instances[0].viewer.entities);
});
});
const hierarchy4 = reactive([99.302267, 23.438899, 99.312267, 23.448889]);
const pos = reactive([99.302267, 23.438899]);
const onViewerReady = (readyObj) => {
console.info('readyObj', readyObj.Cesium); // Cesium namespace object
console.log(readyObj.viewer); // instanceof Cesium.Viewer
const { Cesium, viewer } = readyObj;
//
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(99.516667, 23.640556, 4000), //
orientation: {
ading: Cesium.Math.toRadians(6), //
pitch: Cesium.Math.toRadians(-25), //
roll: Cesium.Math.toRadians(6), //
},
});
};
</script>
<style lang="scss" scoped>
.plant-gs-warp {
height: 100%;
padding: 10px 0;
width: 100%;
box-sizing: border-box;
.plant-gs-content {
background-size: 100% 100%;
background-repeat: no-repeat;
width: 100%;
height: 100%;
overflow: hidden;
.vc-name {
color: #fff;
font-size: 16px;
font-weight: bold;
}
::v-deep() {
.vc-viewer {
height: calc(100% - 30px) !important;
width: calc(100% - 20px) !important;
margin: 20px 10px 10px 10px !important;
}
canvas {
border-radius: 10px !important;
}
.cesium-viewer-bottom {
display: none !important;
}
}
}
}
</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>

146
src/views/breed/index.vue Normal file
View File

@ -0,0 +1,146 @@
<template>
<div class="data-home-index">
<baseBg :name-val="'breed'" top-title="智慧养殖管理系统">
<template #center>
<el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="存栏数据分析" :top-postion="'left'">
<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" style="height: 100%">
<el-row style="height: 67%">
<el-col :span="24" class="center-top" style="height: 100%">
<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 :span="6" class="right-charts">
<div class="right-charts-item">
<customBack top-title="智慧监控" :top-postion="'right'">
<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>
<deviceCharts></deviceCharts>
</template>
</customBack>
</div>
</el-col>
</el-row>
</template>
</baseBg>
</div>
</template>
<script setup>
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';
import deviceCharts from './components/deviceCharts.vue';
</script>
<style lang="scss" scoped>
.data-home-index {
.left-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.left-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
.right-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.right-charts-item {
width: 100%;
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>

View File

@ -0,0 +1,125 @@
<template>
<div class="demo back-to-roll-list" 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="{ 'zebra-b': (index + 1) % 2 == 0 }" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
<span>
{{ item[b] }}
</span>
</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([
{ title: '农业管理局', time: '2025.01.02', info: '2025农业规划' },
{ title: '农业管理局', time: '2025.01.01', info: '2025养殖规划' },
{ title: '农业管理局', time: '2025.01.02', info: '2025种植规划' },
{ title: '农业管理局', time: '2025.01.01', info: '2025最新政策解读' },
{ title: '农业管理局', time: '2025.01.02', info: '2025最新农业政策' },
{ title: '农业管理局', time: '2025.01.01', info: '2025最新养殖政策' },
{ title: '农业管理局', time: '2025.01.02', info: '2025最新种植政策' },
]);
const listKeys = reactive(['title', 'info', 'time']);
const listKeysHeader = reactive({
title: '发布单位',
info: '热点信息',
time: '发布时间',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.back-to-roll-list {
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: 6px 6px;
&.td-title {
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
.status-no {
color: red;
}
.status-y {
color: #6beff9;
}
}
}
.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,147 @@
<template>
<div class="popular-feelings-warp">
<div
class="popular-feelings-content"
:style="{
'--before-bg': `url(${beforeBg})`,
}"
>
<template v-for="(n, index) in datalist" :key="index">
<div
class="popular-feelings-item"
:style="{
'background-image': 'url(' + getAssetsFile('images/early/back1.png') + ')',
}"
:class="n.name"
>
<div class="content-bg">
<div class="val">{{ n.val || 0 }}</div>
<div class="label">{{ n.title || 0 }}</div>
</div>
</div>
</template>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
let datalist = reactive([
{ name: 'popular', title: '今天舆情量', val: 1872 },
{ title: '今日文章量', name: 'article', val: 1856 },
{ title: '今日浏览量', name: 'view', val: 54681 },
{ title: '文章总数', name: 'total', val: 75671 },
]);
const beforeBg = getAssetsFile('images/early/bg5.png');
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.popular-feelings-warp {
height: 100%;
width: 100%;
padding: 10px;
.popular-feelings-content {
width: 100%;
height: 100%;
position: relative;
.popular-feelings-item {
position: absolute;
background-size: 100% 100%;
background-position: left bottom;
background-repeat: no-repeat;
text-align: center;
&.popular {
left: 0;
top: 50%;
transform: translateY(-50%);
width: 35%;
height: 45%;
.val {
color: #fff;
font-weight: bold;
font-size: 14px;
}
.label {
color: #50e3c2;
margin-top: 8%;
font-size: 10px;
}
}
&.article {
left: 50%;
top: 0;
transform: translateX(-50%);
width: 35%;
height: 45%;
.val {
color: #fff;
font-weight: bold;
font-size: 14px;
}
.label {
color: #50e3c2;
margin-top: 8%;
font-size: 10px;
}
}
&.view {
right: 0;
top: 50%;
transform: translateY(-50%);
width: 35%;
height: 45%;
.val {
color: #fff;
font-weight: bold;
font-size: 14px;
}
.label {
color: #50e3c2;
margin-top: 8%;
font-size: 10px;
}
}
&.total {
left: 50%;
bottom: -10%;
transform: translateX(-50%);
width: 50%;
height: 66%;
.val {
color: #50e3c2;
font-weight: bold;
font-size: 20px;
margin-top: 2%;
}
.label {
color: #fff;
margin-top: 10%;
}
}
.content-bg {
width: 100%;
height: 100%;
}
// .content-bg ::before {
// content: '';
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// background-image: var(--before-bg); /* */
// background-size: 100% 100%;
// background-position: left bottom;
// background-repeat: no-repeat;
// z-index: -1; /* */
// transform: rotate(90deg); /* */
// transform-origin: center; /* */
// }
}
}
}
</style>

View File

@ -0,0 +1,161 @@
<template>
<div class="price-charts">
<custom-echart-scatter-blister :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
//
const colors1 = reactive(['#6beff9', '#e4f116', '#2087e3']);
const dataX = reactive(['周一', '周二', '周三', '周四', '周五', '周六', '周日']);
let legendData = reactive(['最高价', '最低价', '建议价']);
const hexToRGBA = (hex, alpha = 1) => {
let hexCode = hex.replace('#', '');
if (hexCode.length === 3) {
hexCode = hexCode
.split('')
.map((char) => char + char)
.join('');
}
const r = parseInt(hexCode.slice(0, 2), 16);
const g = parseInt(hexCode.slice(2, 4), 16);
const b = parseInt(hexCode.slice(4, 6), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
};
let seriesList = computed(() => {
let list = [];
if (legendData.length > 0) {
list = legendData.map((m, index) => {
let num = [
[100, 30, 60],
[10, 2, 3],
[80, 50, 70],
];
return {
name: m,
data: dataX.map((n) => {
return [
Number((num[index][0] + Math.random() * 100).toFixed(2)),
Number((num[index][1] + Math.random() * 100).toFixed(2)),
Number((num[index][2] + Math.random() * 100).toFixed(2)),
n,
];
}),
type: 'scatter',
symbolSize: (val) => {
// values
let values = [];
values = list[index].data
.map((n) => {
return n;
})
.flat()
.filter((item) => typeof item === 'number');
// console.info('values', values);
//
const max = Math.max(Math.max(...values));
//
const min = Math.min(...values);
//
const maxSize4Pin = 38;
//
const minSize4Pin = 8;
//
var a = (maxSize4Pin - minSize4Pin) / (max - min);
var b = maxSize4Pin - a * max;
return a * val[2] + b;
},
label: {
normal: { show: true, formatter: '', position: 'top' },
emphasis: { show: true, formatter: (param) => param.data[3], position: 'top' },
},
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(25, 100, 150, 0.5)',
shadowOffsetY: 5,
color: {
type: 'linear',
x: 1,
y: 0,
x2: 0,
y2: 0,
colorStops: [
{ offset: 1, color: hexToRGBA(colors1[index], 1) },
{ offset: 0, color: hexToRGBA(colors1[index], 0.8) },
],
globalCoord: false,
},
},
};
});
}
console.info('list', list);
return list;
});
const chartsData = reactive({
option: {
grid: {
left: '3%',
right: '10%',
bottom: '6%',
top: '18%',
containLabel: true,
},
tooltip: {
trigger: 'item',
// formatter: function (params) {
// return params.value[3] + ':' + parseInt(params.value[1] * 1000) / 10 + '%';
// },
},
title: {
text: ' ',
subtext: ' ',
left: 'center',
},
legend: {
right: 10,
top: 5,
data: legendData,
},
xAxis: {
name: ' ',
splitLine: {
show: false,
lineStyle: {
type: 'dashed',
},
},
},
yAxis: {
name: ' ',
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: 'rgba(181,197,221,0.2)',
},
},
scale: true,
},
},
valData: seriesList,
});
onMounted(() => {
if (chartsData.valData && chartsData.valData.length) {
chartsData.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});
}
});
</script>
<style lang="scss" scoped>
.price-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div class="product-type-word-clould">
<custom-echart-word-cloud :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const chartsData = reactive({
option: {
backgroundColor: 'transparent',
tooltip: {
show: true,
textStyle: {
fontSize: '16',
color: '#3c3c3c',
},
backgroundColor: '#fff',
borderColor: '#ddd',
borderWidth: 1,
},
series: [],
},
valData: [
{
type: 'wordCloud',
//
gridSize: 22,
// circle cardioid diamond
// triangle-forward triangle star
shape: 'circle',
//
sizeRange: [10, 30],
//
rotationRange: [0, 0],
//
rotationStep: 90,
//
// maskImage: maskImage,
left: 'center',
top: 'center',
right: null,
bottom: null,
//
width: '90%',
//
height: '90%',
//
drawOutOfBound: false,
textStyle: {
color: function () {
const colors = ['#165DFF', '#6aca37', '#05a4b6', '#f93920', '#f0b114', '#ab1489', '#149cab', '#ab1414', '#27b30f', '#7e11b3', '#11b32e'];
return colors[parseInt(Math.random() * colors.length)];
},
emphasis: {
shadowBlur: 10,
shadowColor: '#2ac',
},
},
data: [
{ value: 11.7392043070835, name: '有机白菜' },
{ value: 9.23723855786, name: '土鸡蛋' },
{ value: 7.75434839431, name: '猪肉' },
{ value: 11.3865516372, name: '牛肉' },
{ value: 7.75434839431, name: '零添加' },
{ value: 5.83541244308, name: '原产地' },
{ value: 15.83541244308, name: '菠萝' },
{ value: 2.83541244308, name: '甘蔗' },
{ value: 5.83541244308, name: '土豆' },
{ value: 10.83541244308, name: '绿色' },
{ value: 5.83541244308, name: '美味' },
{ value: 5.83541244308, name: '特产' },
],
},
],
});
onMounted(() => {
if (chartsData.valData && chartsData.valData.length) {
chartsData.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});
}
});
</script>
<style lang="scss" scoped>
.product-type-word-clould {
height: 100%;
}
</style>

View File

@ -0,0 +1,223 @@
<template>
<div class="real-time-price-wrap">
<div class="tab-warp">
<el-carousel
indicator-position="none"
:autoplay="false"
:arrow="'always'"
:height="'120px'"
:style="{
'--arrow-left-bg': `url(${arrowRightBg})`,
'--arrow-right-bg': `url(${arrowLeftBg})`,
}"
>
<el-carousel-item v-for="item in tablist" :key="item">
<div class="item-warp" :style="{ 'background-image': 'url(' + getAssetsFile('images/early/back2.png') + ')' }">
<div class="img-wrap">
<img :src="getAssetsFile('images/early/icon3.png')" />
</div>
<div class="name">
<span class="name-val">{{ item.name }}</span>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
<div class="price-list">
<div
v-for="(n, index) in pricelist"
:key="index"
:style="{
'background-image': 'url(' + getAssetsFile('images/early/bg5.png') + ')',
height: 'calc((100% - 50px) /' + 3 + ')',
}"
class="data-item"
>
<div class="data-warp">
<div class="small-bg">
<img :src="getAssetsFile('images/early/back3.png')" />
<img :src="getAssetsFile('images/early/' + n.icon)" class="img-icon" />
</div>
<div class="data-pos">
<div class="data-pos-center">
<div class="pos-center">
<div class="label">
<span class="val">{{ n.title }}</span>
<span class="unit">{{ n.unit }}</span>
</div>
<span class="value">{{ n.value }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
let tablist = reactive([
{ name: '西红柿', label: '价格', scr: '' },
{ name: '甘蔗', label: '价格', scr: '' },
{ name: '白菜', label: '价格', scr: '' },
]);
let pricelist = reactive([
{ title: '今日最高价', value: 1.24, unit: '元/kg', icon: 'icon1.png' },
{ title: '今日最低价', value: 0.87, unit: '元/kg', icon: 'icon2.png' },
{ title: '最多价格', value: 1.19, unit: '元/kg', icon: 'icon3.png' },
{ title: '建议售价', value: 1.21, unit: '元/kg', icon: 'icon5.png' },
{ title: '历史最高价', value: 1.36, unit: '元/kg', icon: 'icon6.png' },
{ title: '历史最低价', value: 0.75, unit: '元/kg', icon: 'icon7.png' },
]);
//
const arrowLeftBg = getAssetsFile('images/early/arrowL.png');
const arrowRightBg = getAssetsFile('images/early/arrowR.png');
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.real-time-price-wrap {
height: 100%;
width: 100%;
padding: 10px 0;
text-align: center;
::v-deep() {
.el-carousel__arrow--left {
background-image: var(--arrow-left-bg);
background-size: 100% 100%;
background-position: left center;
width: 68px;
height: 54px;
i {
display: none;
}
}
.el-carousel__arrow--right {
background-image: var(--arrow-right-bg);
background-size: 100% 100%;
background-position: left center;
width: 68px;
height: 54px;
i {
display: none;
}
}
}
.item-warp {
width: calc(100% - 136px);
text-align: center;
margin: 0 auto;
background-size: 80% 80%;
background-position: center 8px;
height: 100%;
background-repeat: no-repeat;
.img-wrap {
width: 80%;
height: 80%;
text-align: center;
margin-left: 10%;
img {
width: 100%;
height: 100%;
margin: auto;
}
}
.name {
width: 100%;
.name-val {
font-size: 16px;
font-weight: bold;
color: #fff;
}
}
}
.price-list {
width: 100%;
height: calc(100% - 120px);
padding-top: 10px;
display: inline-flex;
justify-content: space-between;
flex-wrap: wrap;
.data-item {
width: calc((100% - 10px) / 2);
display: inline-flex;
flex-direction: column;
justify-content: center;
background-size: 100% 100%;
position: relative;
}
.data-warp {
padding: 8px;
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: 54px;
height: 54px;
position: relative;
.img-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 38%;
height: 38%;
}
}
.data-pos {
width: calc(100% - 54px);
padding-left: 8px;
.label,
.value {
display: inline-block;
width: 100%;
}
.label {
color: #fff;
text-align: left;
.val {
font-size: 12px;
}
.unit {
font-size: 10px;
padding-left: 3px;
}
.unit::before {
content: '(';
}
.unit::after {
content: ')';
}
}
.value {
color: #6beff9;
font-size: 16px;
font-weight: bold;
margin-top: 6px;
text-align: left;
}
}
}
}
}
</style>

87
src/views/early/index.vue Normal file
View File

@ -0,0 +1,87 @@
<template>
<div class="data-home-index">
<baseBg :name-val="'early'" top-title="产业预警管理系统">
<template #center>
<el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="舆情数据统计" :top-postion="'left'">
<template #back>
<popularFeelings></popularFeelings>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="追溯主体统计" :top-postion="'left'">
<template #back>
<backToCharts></backToCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="追溯产品分类" :top-postion="'left'">
<template #back>
<productTypeWordClould></productTypeWordClould>
</template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<centerMap></centerMap>
</el-col>
<el-col :span="6" class="right-charts">
<div class="right-charts-item" style="height: 67%">
<customBack top-title="农场品实时价格" :top-postion="'right'">
<template #back>
<realTimePrice></realTimePrice>
</template>
</customBack>
</div>
<div class="right-charts-item" style="height: 33%">
<customBack top-title="农产品价格数据分析" :top-postion="'right'">
<template #back>
<priceCharts></priceCharts>
</template>
</customBack>
</div>
</el-col>
</el-row>
</template>
</baseBg>
</div>
</template>
<script setup>
import centerMap from '@/components/centerMap.vue';
import backToCharts from './components/backToCharts.vue';
import productTypeWordClould from './components/productTypeWordClould.vue';
import priceCharts from './components/priceCharts.vue';
import realTimePrice from './components/realTimePrice.vue';
import popularFeelings from './components/popularFeelings.vue';
</script>
<style lang="scss" scoped>
.data-home-index {
.left-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.left-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
.right-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.right-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
}
</style>

View File

@ -0,0 +1,194 @@
<template>
<div class="benefit-charts">
<custom-echart-bar :chart-data="chartsData.valData" height="100%" :option="chartsData.option" :is-series="true" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const data = [1700, 800, 1700, 600, 800];
const data2 = [2600, 1400, 3350, 1400, 1400];
const colorArr1 = ['rgba(11, 83, 128)', 'rgba(2, 143, 224)', '#2a7fcc'];
const colorArr2 = ['rgb(12, 109, 122)', 'rgba(1, 241, 228)', '#5ce1d6'];
const color1 = {
type: 'linear',
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: colorArr1[0],
},
{
offset: 0.5,
color: colorArr1[0],
},
{
offset: 0.5,
color: colorArr1[1],
},
{
offset: 1,
color: colorArr1[1],
},
],
};
const color2 = {
type: 'linear',
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: colorArr2[0],
},
{
offset: 0.5,
color: colorArr2[0],
},
{
offset: 0.5,
color: colorArr2[1],
},
{
offset: 1,
color: colorArr2[1],
},
],
};
var barWidth = 18;
const chartsData = reactive({
option: {
legend: {
show: false,
},
tooltip: {
trigger: 'axis',
formatter: function (params) {
var str = params[0].name + ':';
params.filter(function (item) {
if (item.componentSubType == 'bar') {
str += '<br/>' + item.seriesName + '' + item.value;
}
});
return str;
},
},
//
grid: {
x: '16%',
x2: '5%',
y: '15%',
y2: '15%',
},
xAxis: {
data: ['肉类', '水果', '蔬菜', '水产', '谷物'],
//
axisLine: {
show: true,
lineStyle: {
width: 1,
color: '#214776',
},
textStyle: {
color: '#fff',
fontSize: '10',
},
},
type: 'category',
axisLabel: {
textStyle: {
color: '#C5DFFB',
fontWeight: 500,
fontSize: '14',
},
},
axisTick: {
textStyle: {
color: '#fff',
fontSize: '16',
},
show: false,
},
},
yAxis: {
name: ' ',
nameTextStyle: {
color: '#DEDEDE',
fontSize: 12,
padding: 10,
},
type: 'value',
splitLine: {
show: true,
lineStyle: {
type: 'dashed', //线 线0
opacity: 0.2, //
},
},
axisTick: {
show: false,
},
axisLine: {
show: false,
},
//
axisLabel: {
show: true,
textStyle: {
color: '#C5DFFB',
},
},
},
series: [
{
z: 1,
name: '绿色',
type: 'bar',
barWidth: barWidth,
barGap: '0%',
data: data,
itemStyle: {
normal: {
color: color2,
},
},
},
{
z: 3,
name: '绿色',
type: 'pictorialBar',
symbolPosition: 'end',
data: data,
symbol: 'diamond',
symbolOffset: ['0%', '-60%'],
symbolSize: [18, 12],
itemStyle: {
normal: {
borderWidth: 2,
color: colorArr2[2],
},
},
},
],
},
valData: [
{ value: 205, name: '肉类' },
{ value: 308, name: '水果' },
{ value: 359, name: '蔬菜' },
{ value: 452, name: '水产' },
{ value: 388, name: '谷物' },
],
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.benefit-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="category-charts">
<custom-echart-pie :chart-data="plantBreed.valData" height="100%" :option="plantBreed.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const plantBreed = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
data: ['肉类', '水果', '蔬菜', '水产', '谷物'],
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
label: {
color: '#333',
},
series: [
{
type: 'pie',
radius: [20, 80],
roseType: 'area',
center: ['40%', '50%'],
label: {
show: false,
},
itemStyle: {
borderRadius: 5,
},
},
],
},
valData: [
{ value: 205, name: '肉类' },
{ value: 308, name: '水果' },
{ value: 359, name: '蔬菜' },
{ value: 452, name: '水产' },
{ value: 388, name: '谷物' },
],
});
onMounted(() => {
if (plantBreed.valData && plantBreed.length) {
plantBreed.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});
}
});
</script>
<style lang="scss" scoped>
.category-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,355 @@
<template>
<div class="entities-category-charts">
<custom-echart-pie-3d :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const dataList = reactive([
{ name: '肉类', val: 1230, itemStyle: { color: 'rgba(56, 136, 235, 1)' } },
{ name: '水果', val: 300, itemStyle: { color: 'rgba(113, 70, 159, 1)' } },
{ name: '蔬菜', val: 800, itemStyle: { color: 'rgba(237, 171, 87, 1)' } },
{ name: '水产', val: 500, itemStyle: { color: 'rgba(231, 89, 77, 1)' } },
{ name: '谷物', val: 600, itemStyle: { color: 'rgba(231, 89, 77, 1)' } },
]);
const heightProportion = ref(0.3); //
// series-surface.parametricEquation
// #region
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, height, radiusScale) => {
//
let midRatio = (startRatio + endRatio) / 3;
let startRadian = startRatio * Math.PI * 2;
let endRadian = endRatio * Math.PI * 2;
let midRadian = midRatio * Math.PI * 2;
//
if (startRatio === 0 && endRatio === 1) {
isSelected = false;
}
// / k 1/3
k = typeof k !== 'undefined' ? k : 1 / 3;
// x y 0
let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
// 1
let hoverRate = isHovered ? 1.1 : 1;
//
return {
u: {
min: -Math.PI,
max: Math.PI * 3,
step: Math.PI / 32,
},
v: {
min: 0,
max: Math.PI * 2,
step: Math.PI / 20,
},
x: function (u, v) {
if (u < startRadian) {
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
if (u > endRadian) {
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
},
y: function (u, v) {
if (u < startRadian) {
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
if (u > endRadian) {
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
},
z: function (u, v) {
if (u < -Math.PI * 0.5) {
return Math.sin(u);
}
if (u > Math.PI * 2.5) {
return Math.sin(u);
}
return Math.sin(v) > 0 ? heightProportion.value * height : -1;
},
};
};
// #endregion
// 3D
const getPie3D = (pieData, internalDiameterRatio, radiusScale = 1.8) => {
let series = [];
let sumValue = 0;
let startValue = 0;
let endValue = 0;
let legendData = [];
let linesSeries = []; // line3Dlabel线
let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
// series-surface
for (let i = 0; i < pieData.length; i++) {
sumValue += pieData[i].value;
let seriesItem = {
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
type: 'surface',
parametric: true,
wireframe: {
show: false,
},
pieData: pieData[i],
pieStatus: {
selected: false,
hovered: false,
k: k,
},
};
if (typeof pieData[i].itemStyle != 'undefined') {
let itemStyle = {};
typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
seriesItem.itemStyle = itemStyle;
}
series.push(seriesItem);
}
// 使 sumValue getParametricEquation
// series-surface series-surface.parametricEquation
for (let i = 0; i < series.length; i++) {
endValue = startValue + series[i].pieData.value;
// console.log(series[i]);
series[i].pieData.startRatio = startValue / sumValue;
series[i].pieData.endRatio = endValue / sumValue;
series[i].parametricEquation = getParametricEquation(
series[i].pieData.startRatio,
series[i].pieData.endRatio,
false,
false,
k,
series[i].pieData.value,
radiusScale
);
startValue = endValue;
// label线
//
const midRadian = (series[i].pieData.startRatio + series[i].pieData.endRatio) * Math.PI;
// v=0
const radius = 1 + k; //
const posX = Math.cos(midRadian) * radius * radiusScale;
const posY = Math.sin(midRadian) * radius * radiusScale;
//
const posZ = heightProportion.value * series[i].pieData.value;
let flag = (midRadian >= 0 && midRadian <= Math.PI / 2) || (midRadian >= (3 * Math.PI) / 2 && midRadian <= Math.PI * 2) ? 1 : -1;
let color = pieData[i].itemStyle.color;
let turningPosArr = [posX * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0), posY * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0), posZ * 1];
let endPosArr = [posX * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0), posY * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0), posZ * 3];
linesSeries.push(
{
type: 'line3D',
lineStyle: {
color: color,
},
data: [[posX, posY, posZ], turningPosArr, endPosArr],
},
// https://www.isqqw.com/assets/layout/images/person.png
{
type: 'scatter3D',
label: {
show: true, //label
distance: 0,
position: 'center',
textStyle: {
color: '#ffffff',
backgroundColor: color,
borderWidth: 2,
fontSize: 8,
padding: 4,
borderRadius: 4,
},
formatter: '{b}',
},
symbolSize: 0,
data: [{ name: series[i].name + '\n' + series[i].pieData.val, value: endPosArr }],
}
);
legendData.push(series[i].name);
}
series = series.concat(linesSeries);
// k
const baseScale = 2; //
const scaleForBase = baseScale * (1 + k); //
//
series.push({
name: 'mouseoutSeries',
type: 'surface',
parametric: true,
wireframe: {
show: false,
},
itemStyle: {
opacity: 0.75,
color: 'rgba(51, 135, 146, 0.75)',
},
parametricEquation: {
u: {
min: 0,
max: Math.PI * 2,
step: Math.PI / 20,
},
v: {
min: 0,
max: Math.PI,
step: Math.PI / 20,
},
x: function (u, v) {
return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * scaleForBase;
},
y: function (u, v) {
return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * scaleForBase;
},
z: function (u, v) {
return Math.cos(v) > 0 ? -0 : -3.5;
},
},
});
series.push({
name: 'bottomRing',
type: 'surface',
parametric: true,
wireframe: {
show: false,
},
itemStyle: {
opacity: 0.6,
color: 'rgba(255, 255, 255, 1)',
},
parametricEquation: {
u: {
min: 0.94, // 92%
max: 0.95, // 100%
step: 0.0001,
},
v: {
min: 0,
max: Math.PI * 2, //
step: Math.PI / 20,
},
x: function (u, v) {
// +
return u * scaleForBase * 1.0 * Math.cos(v);
},
y: function (u, v) {
return u * scaleForBase * 1.0 * Math.sin(v);
},
z: function () {
//
return -3.6;
},
},
});
let maxHeight = Math.max(...pieData.map((item) => item.value)) * heightProportion.value;
series.push({
name: 'topRing',
type: 'surface',
parametric: true,
wireframe: {
show: false,
},
itemStyle: {
opacity: 0.6,
color: 'rgba(255, 255, 255, 1)',
},
parametricEquation: {
u: {
min: 0.94, // 92%
max: 0.95, // 100%
step: 0.0001,
},
v: {
min: 0,
max: Math.PI * 2, //
step: Math.PI / 20,
},
x: function (u, v) {
// +
return u * scaleForBase * 1.0 * Math.cos(v);
},
y: function (u, v) {
return u * scaleForBase * 1.0 * Math.sin(v);
},
z: function () {
//
return maxHeight + 0.1;
},
},
});
return series;
};
let total = 0;
dataList.forEach((item) => {
total += item.val;
});
const chartsData = reactive({
option: {
xAxis3D: {
min: -1.5,
max: 1.5,
},
yAxis3D: {
min: -1.5,
max: 1.5,
},
zAxis3D: {
min: -1,
max: 1,
},
grid3D: {
show: false,
boxHeight: 4,
top: '10%',
viewControl: {
distance: 380,
alpha: 25,
beta: 60,
autoRotate: true, //
},
},
},
valData: [],
});
onMounted(() => {
chartsData.valData = getPie3D(
dataList.map((item) => {
item.value = Number(((item.val / total) * 100).toFixed(2));
return item;
}),
0
);
});
</script>
<style lang="scss" scoped>
.entities-category-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,79 @@
<template>
<div class="entities-statistics">
<custom-echart-mixin :chart-data="chartsData.valData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500', '#e57373'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
show: true,
data: ['个体户', '村集体', '合作社', '经营企业', '趋势'],
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 18,
},
yAxis: [
{
type: 'value',
name: '数量',
axisLabel: {
formatter: '{value}',
},
itemStyle: { fontSize: 10 },
},
{
type: 'value',
name: '占比',
min: 0,
max: 100,
axisLabel: {
formatter: '{value} %',
},
itemStyle: { fontSize: 10 },
},
],
grid: {
x: '10%',
x2: '15%',
y: '20%',
y2: '20%',
},
},
valData: [
{ name: '耿马', value: 40, type: '个体户', seriesType: 'bar', stack: '耿马' },
{ name: '耿马', value: 30, type: '村集体', seriesType: 'bar', stack: '耿马' },
{ name: '耿马', value: 100, type: '合作社', seriesType: 'bar', stack: '耿马' },
{ name: '耿马', value: 60, type: '经营企业', seriesType: 'bar', stack: '耿马', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '耿马', value: 10, type: '趋势', seriesType: 'line' },
{ name: '大香乡', value: 20, type: '个体户', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 20, type: '村集体', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 80, type: '合作社', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 40, type: '经营企业', seriesType: 'bar', stack: '大香乡', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '大香乡', value: 50, type: '趋势', seriesType: 'line' },
],
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.entities-statistics {
height: 100%;
}
</style>

View File

@ -0,0 +1,119 @@
<template>
<div class="demo roll-list-lentities" 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="{ 'zebra-b': (index + 1) % 2 == 0 }" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
{{ item[b] }}
</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([
{ title: '信达农资有限公司', type: '农药', time: '2025.01.02', duration: 8 },
{ title: '方大种源有限公司', type: '种源', time: '2025.01.01', duration: 10 },
{ title: '信誉种源有限公司', type: '种源', time: '2025.01.02', duration: 11 },
{ title: '嘉兴包装有限公司', type: '生产加工', time: '2025.01.01', duration: 15 },
{ title: '达国有限公司', type: '农资', time: '2025.01.02', duration: 14 },
{ title: '华威种植专业合作社', type: '合作社', time: '2025.01.01', duration: 8 },
{ title: '信誉种源合作社', type: '合作社', time: '2025.01.02', duration: 15 },
{ title: '华泰种源有限公司', type: '种源', time: '2025.01.01', duration: 13 },
{ title: '嘉德食品包装包装有限公司', type: '生产加工', time: '2025.01.02', duration: 5 },
]);
const listKeys = reactive(['title', 'type', 'time', 'duration']);
const listKeysHeader = reactive({
title: '主体名称',
type: '主体类别',
time: '注册时间',
duration: '经营时间',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.roll-list-lentities {
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;
}
&.zebra-b {
background: #051225 !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,87 @@
<template>
<div ref="chartsWarp" class="hot-charts">
<custom-echart-bubble :chart-data="seriesData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const chartsData = reactive({
option: {},
valData: [
{ name: '多菌灵', value: 6833.37 },
{ name: '复合肥', value: 6823.93 },
{ name: '水溶肥', value: 3529.8 },
{ name: '大豆种子', value: 9773.04 },
{ name: '农膜', value: 2992.33 },
{ name: '草甘膦', value: 1448.23 },
{ name: '其他', value: 3800.77 },
],
});
let chartsWarp = ref(null);
let seriesData = reactive([]);
const compare = (propertyName) => {
return (object1, object2) => {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value2 < value1) {
return 1;
} else if (value2 > value1) {
return -1;
} else {
return 0;
}
};
};
onMounted(() => {
if (chartsData.valData && chartsData.valData.length) {
let datas = chartsData.valData.sort(compare('value')).reverse();
let maxValue = datas[0].value;
let colors = ['#4983F5', '#3D993D', '#525CCC', '#3344FF', '#39ACE5', '#008099', '#2985CC'];
let total = datas.reduce((acc, current) => {
return acc + current.value;
}, 0);
for (var i = 0; i < datas.length; i++) {
let acct = parseFloat((datas[i].value / total).toFixed(2));
if (acct < 0.2) {
acct = 0.2;
}
if (acct > 0.6) {
acct = 0.6;
}
let reference =
chartsWarp.value.clientHeight < chartsWarp.value.clientWidth
? parseInt(chartsWarp.value.clientHeight)
: parseInt(chartsWarp.value.clientWidth);
var ss = parseInt(acct * reference * 1.3);
var color = colors[i];
var item = {
name: datas[i].name,
value: datas[i].value,
symbolSize: ss,
draggable: true,
itemStyle: {
normal: {
shadowBlur: 10,
shadowColor: color,
color: color,
},
},
};
seriesData.push(item);
}
console.info('seriesData', seriesData);
}
});
</script>
<style lang="scss" scoped>
.hot-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,95 @@
<template>
<div class="data-home-index">
<baseBg :name-val="'entities'" top-title="生产经主体管理系统">
<template #center>
<el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="生产经营主体类别统计" :top-postion="'left'">
<template #back>
<entitiesCategoryCharts></entitiesCategoryCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="各乡镇经营主体统计" :top-postion="'left'">
<template #back>
<entitiesStatistics></entitiesStatistics>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="生产经营主体信息" :top-postion="'left'">
<template #back>
<entitieslist></entitieslist>
</template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<centerMap></centerMap>
</el-col>
<el-col :span="6" class="right-charts">
<div class="right-charts-item">
<customBack top-title="经营类目统计" :top-postion="'right'">
<template #back>
<categoryCharts></categoryCharts>
</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>
<hotCharts></hotCharts>
</template>
</customBack>
</div>
</el-col>
</el-row>
</template>
</baseBg>
</div>
</template>
<script setup>
import centerMap from '@/components/centerMap.vue';
import categoryCharts from './components/categoryCharts.vue';
import entitieslist from './components/entitieslist.vue';
import hotCharts from './components/hotCharts.vue';
import benefitCharts from './components/benefitCharts.vue';
import entitiesStatistics from './components/entitiesStatistics.vue';
import entitiesCategoryCharts from './components/entitiesCategoryCharts.vue';
</script>
<style lang="scss" scoped>
.data-home-index {
.left-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.left-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
.right-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.right-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
}
</style>

12
src/views/error/403.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<el-result icon="error" title="403" sub-title="Sorry, request error">
<template #extra>
<el-button type="primary" @click="push('/')">返回</el-button>
</template>
</el-result>
</template>
<script setup name="error">
import { useRouter } from 'vue-router';
const { push } = useRouter();
</script>

12
src/views/error/404.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<el-result icon="error" title="404" sub-title="Sorry, request error">
<template #extra>
<el-button type="primary" @click="push('/')">返回</el-button>
</template>
</el-result>
</template>
<script setup name="error">
import { useRouter } from 'vue-router';
const { push } = useRouter();
</script>

View File

@ -0,0 +1,167 @@
<template>
<div class="home-comprehensive-warp">
<div class="data-item-row">
<div
v-for="(n, index) in datalist"
:key="index"
:style="{ 'background-image': 'url(' + getAssetsFile('images/vsualized/home/partbg.png') + ')' }"
class="data-item"
>
<div class="data-warp">
<div class="small-bg">
<img :src="getAssetsFile('images/vsualized/home/partbg1.png')" />
<img :src="getAssetsFile('images/vsualized/home/' + n.icon)" class="img-icon" />
</div>
<div class="data-pos">
<div class="data-pos-center">
<div class="pos-center">
<span class="label">{{ n.label }}</span>
<span class="value">{{ n.value }}</span>
</div>
</div>
</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: 27.88, icon: 'farmers.png' },
{ label: '耕地面积(万亩)', value: 103.05, icon: 'area.png' },
{ label: '农业总产值(亿元)', value: 92.81, icon: 'outputVal.png' },
{ label: '人均增收(万元)', value: 1.87, icon: 'Increase.png' },
]);
onMounted(() => {
if (datalist.length) {
datalist.forEach((m, index) => {
let num = 0;
switch (index) {
case 0:
num = 20;
break;
case 1:
num = 100;
break;
case 2:
num = 90;
break;
case 3:
num = 1;
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>
.home-comprehensive-warp {
height: 100%;
.data-item-row {
height: 100%;
display: inline-flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
}
.data-item {
height: calc((100% - 20px) / 2);
width: calc((100% - 20px) / 2);
display: inline-flex;
flex-direction: column;
justify-content: center;
background-size: 100% 100%;
position: relative;
}
.data-warp {
padding: 8px;
text-align: center;
z-index: 1;
display: inline-flex;
justify-content: center;
padding-left: 20px;
.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: 54px;
height: 54px;
position: relative;
.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;
}
}
}
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="home-entities-charts">
<custom-echart-pie :chart-data="plantBreed.valData" height="100%" :option="plantBreed.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const plantBreed = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
data: ['农户', '农企/合作社', '生产加工企业', '农资企业', '种源企业'],
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 12, //
color: '#fff', //
},
},
label: {
color: '#333',
},
series: [
{
type: 'pie',
radius: [20, 80],
roseType: 'area',
center: ['40%', '50%'],
label: {
show: false,
},
itemStyle: {
borderRadius: 5,
},
},
],
},
valData: [
{ value: 100, name: '农户' },
{ value: 105, name: '农企/合作社' },
{ value: 217, name: '生产加工企业' },
{ value: 217, name: '农资企业' },
{ value: 217, name: '种源企业' },
],
});
onMounted(() => {
if (plantBreed.valData && plantBreed.length) {
plantBreed.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});
}
});
</script>
<style lang="scss" scoped>
.home-entities-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<div class="home-inputs-warp">
<div class="data-item-row">
<div
v-for="(n, index) in datalist"
:key="index"
class="data-item"
:style="{
height: 'calc((100% - 20px)' + ' / ' + datalist.length / 2 + ')',
'background-image': 'url(' + getAssetsFile('images/vsualized/home/partbg2.png') + ')',
}"
>
<div class="data-warp">
<div class="data-pos">
<div class="data-pos-center">
<div class="c">
<span class="label">{{ n.label }}</span>
<span class="value">{{ n.value }}</span>
</div>
</div>
</div>
<div class="small-bg">
<img :src="getAssetsFile('images/vsualized/home/partbg3.png')" />
<img :src="getAssetsFile('images/vsualized/home/' + 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: 4800, icon: 'provenance.png' },
{ label: '农药使用(吨)', value: 5000, icon: 'pesticide.png' },
{ label: '化肥使用(吨)', value: 9000, icon: 'fertilizer.png' },
{ label: '饲料(吨)', value: 88943, icon: 'feeduse.png' },
{ label: '兽药(吨)', value: 10, icon: 'animalm.png' },
{ label: '农机使用(台)', value: 8000, icon: 'farmuse.png' },
]);
onMounted(() => {
if (datalist.length) {
datalist.forEach((m, index) => {
let num = 100;
m.value = (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>
.home-inputs-warp {
height: 100%;
.data-item-row {
height: 100%;
display: inline-flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
}
.data-item {
display: inline-flex;
flex-direction: column;
justify-content: center;
background-size: 100% 100%;
position: relative;
width: calc((100% - 20px) / 2);
}
.data-warp {
padding: 8px;
text-align: center;
z-index: 1;
display: inline-flex;
justify-content: center;
padding-left: 20px;
.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: 45px;
height: 45px;
position: relative;
.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;
}
}
}
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<div class="home-plant-breed-charts">
<custom-echart-pie :chart-data="plantBreed.valData" height="100%" :option="plantBreed.option" />
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
const plantBreed = ref({
option: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
data: ['种植面积', '养殖面积', '种植基地', '养殖基地'],
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 12, //
color: '#fff', //
},
},
label: {
color: '#333',
},
series: [
{
type: 'pie',
radius: [20, 80],
roseType: 'area',
center: ['40%', '50%'],
label: {
show: false,
},
itemStyle: {
borderRadius: 2,
},
},
],
},
valData: [
{ value: 100, name: '种植面积' },
{ value: 105, name: '养殖面积' },
{ value: 217, name: '种植基地' },
{ value: 315, name: '养殖基地' },
],
});
</script>
<style lang="scss" scoped>
.home-plant-breed-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,129 @@
<template>
<div ref="refroll" class="demo roll-list" style="height: 100%">
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="datalist">
<div v-for="(item, index) in datalist" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-l">
<div class="item-top">
<span class="label"> {{ item.title || '--' }}</span>
<span class="value"> {{ item.value || '0' }}</span>
</div>
<customProgress height="6px" :percent="item.percent" inactive-bg="#081931"></customProgress>
</div>
<div class="list-item-r">
{{ '0' + (index + 1) }}
</div>
</div>
</div>
</vue3ScrollSeamless>
</div>
<!-- </div> -->
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
import customProgress from '@/components/customProgress.vue';
const props = defineProps({
items: {
type: Array,
default: () => [],
},
});
let list = reactive(props.items);
let refroll = ref(null);
const classOptions = {
singleHeight: 48,
};
let datalist = computed(() => {
let maxwidth = refroll.value && refroll.value.clientWidth;
return list.map((m) => {
//return { ...m, percent: parseInt(Number(parseInt(m.value) / max.value) * maxwidth) };
return {
...m,
percent: Number((Number(parseInt(m.value) / max.value) * 100).toFixed(0)),
};
});
});
let max = computed(() => {
let valueList = new Set(list.map((item) => parseInt(item.value)));
let sortValue = [...valueList].sort((a, b) => b - a) || [];
// console.info('valueList', sortValue);
return sortValue.length ? sortValue[0] : 0;
});
</script>
<style scoped lang="scss">
.roll-list {
.scroll-wrap {
height: 80%;
width: 100%;
margin: 0 auto;
overflow: hidden;
}
.list-item {
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
line-height: 36px;
.list-item-content {
display: inline-flex;
width: 100%;
justify-content: space-around;
position: relative;
.list-item-l,
.list-item-r {
color: #fff;
}
.list-item-l {
width: calc(100% - 0px);
.item-top {
width: 100%;
line-height: 16px;
.label,
.value {
display: inline-block;
width: 100%;
}
.label {
font-size: 12px;
}
.value {
font-size: 10px;
color: #6beff9;
}
}
}
.list-item-r {
text-align: right;
font-size: 20px;
font-weight: bold;
color: #6beff9;
position: absolute;
right: 0;
bottom: 0;
}
}
}
.ui-wrap {
list-style: none;
padding: 0;
margin: 0 auto;
}
.li-item {
display: flex;
align-items: center;
width: 100%;
text-align: center;
}
}
.demo {
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
</style>

View File

@ -0,0 +1,134 @@
<template>
<div class="home-trace-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 = ref({
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,
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#45bfe9' },
{ offset: 1, color: '#01589c' },
],
global: false, // false
},
},
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 - 1 ? 'bar' : 'line',
type: legendList[i],
stack: num + '月',
};
if (val.seriesType == 'line') {
val.smooth = 30;
val.symbol = 'none';
}
if (val.seriesType == 'bar') {
val.symbol = 'none';
}
let lastVal = {
...val,
...itemStyle,
};
list[i] = i < legendList.length - 2 ? 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;
});
</script>
<style lang="scss" scoped>
.home-trace-charts {
height: 100%;
}
</style>

View File

@ -1,3 +1,189 @@
<template>
<div>开发中</div>
<div class="data-home-index">
<baseBg ref="homebase" :name-val="'home'" top-title=" ">
<!-- <template #top> </template> -->
<template #center>
<el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="综合数据统计" :top-postion="'left'">
<template #back>
<comprehensive></comprehensive>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="土地分布数据统计" :top-postion="'left'">
<template #back>
<rolllist :items="rollDataList"></rolllist>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="种养殖数据统计" :top-postion="'left'">
<template #back>
<plantBreedCharts></plantBreedCharts>
</template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<centerMap></centerMap>
</el-col>
<el-col :span="6" class="right-charts">
<div class="right-charts-item">
<customBack top-title="使用投入品数据统计" :top-postion="'left'">
<template #back>
<inputs></inputs>
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="经营主体数据统计" :top-postion="'left'">
<template #back>
<entitiesCharts></entitiesCharts>
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="溯源赋码与扫码数据统计" :top-postion="'left'">
<template #back>
<traceCharts></traceCharts>
</template>
</customBack>
</div>
</el-col>
</el-row>
</template>
</baseBg>
<div class="home-index-top-warp">
<div class="home-index-top" :style="{ 'background-image': 'url(' + getAssetsFile('images/vsualized/home/hometopbg.png') + ')' }">
<div class="home-data-p">
<!-- <h3 class="home-title">耿马县农产品销售情况</h3> -->
<div class="home-data-top">1284.624</div>
<div class="home-data-contrast">
<span class="tips">同比去年</span>
<span class="value">4684.629</span>
<el-icon style="vertical-align: middle" class="contrast-icon" color="#6beff9">
<TopRight />
</el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import centerMap from '@/components/centerMap.vue';
import comprehensive from './components/comprehensive.vue';
import plantBreedCharts from './components/plantBreedCharts.vue';
import entitiesCharts from './components/entitiesCharts.vue';
import inputs from './components/inputs.vue';
import traceCharts from './components/traceCharts.vue';
import rolllist from './components/rolllist.vue';
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted, onUnmounted } from 'vue';
let rollDataList = reactive([
{ title: '勐腊镇', value: 533.1 },
{ title: '孟定镇', value: 1069.2 },
{ title: '孟永镇', value: 411.8 },
{ title: '耿马镇', value: 429.4 },
{ title: '大兴乡', value: 162.7 },
{ title: '勐简乡', value: 2309.9 },
// ...
]);
// const homebase = ref(null);
// onMounted(() => {
// homebase.value && homebase.value.startTime();
// });
// onUnmounted(() => {
// homebase.value && homebase.value.chearTime();
// });
</script>
<style lang="scss" scoped>
.data-home-index {
.home-index-top-warp {
position: fixed;
top: 48px;
left: 0;
width: 100%;
height: 120px;
text-align: center;
padding-top: 24px;
.home-index-top {
margin: auto;
width: calc(100% - 400px);
height: 100%;
background-repeat: no-repeat;
background-position: center bottom;
background-size: contain;
position: relative;
.home-data-p {
position: absolute;
left: 0;
top: 13px;
width: 100%;
}
.home-title {
display: inline-block;
font-size: 18px;
font-weight: bold;
transform: skewX(-10deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 4px;
text-shadow: -2px 0 0 1px #add8f1;
}
.home-data-top {
font-size: 18px;
font-weight: bold;
color: #fff;
letter-spacing: 8px;
margin-bottom: 10px;
}
.home-data-contrast {
.tips {
font-size: 10px;
color: #6beff9;
}
.value {
padding: 0 8px;
font-size: 12px;
font-weight: bold;
color: #fff;
}
}
}
}
.left-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.left-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
background: transparent;
}
.right-charts {
display: flex;
justify-content: space-around;
width: 100%;
height: 100%;
flex-direction: column;
}
.right-charts-item {
width: 100%;
height: calc((100% - 30px) / 3);
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More