迁移
8
components.d.ts
vendored
@ -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']
|
||||
}
|
||||
|
@ -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",
|
||||
|
BIN
src/assets/images/early/arrowL.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
src/assets/images/early/arrowR.png
Normal file
After Width: | Height: | Size: 431 KiB |
BIN
src/assets/images/early/back1.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/assets/images/early/back2.png
Normal file
After Width: | Height: | Size: 702 KiB |
BIN
src/assets/images/early/back3.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/assets/images/early/bg5.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
src/assets/images/early/icon1.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/images/early/icon2.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/images/early/icon3.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/early/icon5.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/early/icon6.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/early/icon7.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/assets/images/inputs/partbg1.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
src/assets/images/inputs/partbg2.png
Normal file
After Width: | Height: | Size: 865 B |
BIN
src/assets/images/inputs/partbg3.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/images/plant/bg1.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
src/assets/images/plant/bg2.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/images/plant/bg3.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
src/assets/images/plant/bg5.png
Normal file
After Width: | Height: | Size: 702 KiB |
BIN
src/assets/images/plant/bg6.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/plant/bg7.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/images/plant/bg8.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
src/assets/images/plant/bg9.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/assets/images/plant/icon1.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
src/assets/images/plant/icon2.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/images/plant/icon3.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
src/assets/images/plant/icon4.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
src/assets/images/plant/icon5.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/images/trace/bg1.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/images/trace/bg2.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/vsualized/bottombj.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/images/vsualized/gmmap.png
Normal file
After Width: | Height: | Size: 170 KiB |
5
src/assets/images/vsualized/gmmap.svg
Normal file
After Width: | Height: | Size: 717 KiB |
BIN
src/assets/images/vsualized/home/Increase.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/images/vsualized/home/animalm.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
src/assets/images/vsualized/home/area.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/images/vsualized/home/farmers.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
src/assets/images/vsualized/home/farmuse.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/images/vsualized/home/feeduse.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
src/assets/images/vsualized/home/fertilizer.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/images/vsualized/home/hometopbg.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
src/assets/images/vsualized/home/nav-on.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/images/vsualized/home/nav.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/images/vsualized/home/outputVal.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
src/assets/images/vsualized/home/partbg.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/images/vsualized/home/partbg1.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/assets/images/vsualized/home/partbg2.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/images/vsualized/home/partbg3.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/vsualized/home/pesticide.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
src/assets/images/vsualized/home/provenance.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/images/vsualized/home/titlebg.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/vsualized/hraderbg.png
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
src/assets/images/vsualized/screenbg.png
Normal file
After Width: | Height: | Size: 285 KiB |
1162
src/components/530926geo.json
Normal file
267
src/components/baseBg.vue
Normal 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>
|
212
src/components/centerMap.vue
Normal 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>
|
54
src/components/code-dialog/index.vue
Normal 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>
|
65
src/components/currentTime.vue
Normal 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'); // JavaScript中月份从0开始,所以需要加1
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
|
||||
// 星期几,注意返回的是0(周日)到6(周六)
|
||||
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>
|
39
src/components/customBack.vue
Normal 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>
|
67
src/components/customProgress.vue
Normal 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
@ -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>
|
26
src/main.js
@ -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');
|
||||
|
@ -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
@ -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;
|
177
src/views/breed/components/InventoryCharts.vue
Normal 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>
|
121
src/views/breed/components/benefitCharts.vue
Normal 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>
|
161
src/views/breed/components/deviceCharts.vue
Normal 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>
|
174
src/views/breed/components/environment.vue
Normal 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>
|
117
src/views/breed/components/growthIndexesCharts.vue
Normal 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>
|
122
src/views/breed/components/healthStatusCharts.vue
Normal 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>
|
179
src/views/breed/components/irrigationCharts.vue
Normal 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>
|
25
src/views/breed/components/monitoringScreen.vue
Normal 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>
|
73
src/views/breed/components/noticeBar.vue
Normal 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>
|
116
src/views/breed/components/plantgs.vue
Normal 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>
|
113
src/views/breed/components/waterIntakeCharts.vue
Normal 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
@ -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>
|
125
src/views/early/components/backToCharts.vue
Normal 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>
|
147
src/views/early/components/popularFeelings.vue
Normal 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>
|
161
src/views/early/components/priceCharts.vue
Normal 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>
|
91
src/views/early/components/productTypeWordClould.vue
Normal 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>
|
223
src/views/early/components/realTimePrice.vue
Normal 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
@ -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>
|
194
src/views/entities/components/benefitCharts.vue
Normal 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>
|
70
src/views/entities/components/categoryCharts.vue
Normal 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>
|
355
src/views/entities/components/entitiesCategoryCharts.vue
Normal 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 = []; // line3D模拟label指示线
|
||||
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>
|
79
src/views/entities/components/entitiesStatistics.vue
Normal 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>
|
119
src/views/entities/components/entitieslist.vue
Normal 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>
|
87
src/views/entities/components/hotCharts.vue
Normal 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>
|
95
src/views/entities/index.vue
Normal 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
@ -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
@ -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>
|
167
src/views/home/components/comprehensive.vue
Normal 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>
|
70
src/views/home/components/entitiesCharts.vue
Normal 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>
|
153
src/views/home/components/inputs.vue
Normal 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>
|
60
src/views/home/components/plantBreedCharts.vue
Normal 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>
|
129
src/views/home/components/rolllist.vue
Normal 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>
|
134
src/views/home/components/traceCharts.vue
Normal 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>
|
@ -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>
|
||||
|