智慧种养殖
@ -25,6 +25,7 @@
|
|||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"echarts-liquidfill": "^3.1.0",
|
"echarts-liquidfill": "^3.1.0",
|
||||||
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "^2.7.3",
|
"element-plus": "^2.7.3",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
|
73
main/src/components/custom-echart-pie-gauge/index.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chartRef" :style="{ height, width }"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, watch, watchEffect } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { useEcharts } from '../../hooks/useEcharts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustomEchartPieGauge',
|
||||||
|
props: {
|
||||||
|
chartData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: 'calc(100vh - 78px)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const chartRef = ref(null);
|
||||||
|
const { setOptions, getInstance, resize } = useEcharts(chartRef);
|
||||||
|
const option = reactive({
|
||||||
|
series: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
props.chartData && initCharts();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.size,
|
||||||
|
() => {
|
||||||
|
resize();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function initCharts() {
|
||||||
|
if (props.option) {
|
||||||
|
Object.assign(option, cloneDeep(props.option));
|
||||||
|
}
|
||||||
|
option.series = props.chartData;
|
||||||
|
setOptions(option);
|
||||||
|
resize();
|
||||||
|
getInstance()?.off('click', onClick);
|
||||||
|
getInstance()?.on('click', onClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClick(params) {
|
||||||
|
emit('click', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { chartRef };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
73
main/src/components/custom-echart-word-cloud/index.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chartRef" :style="{ height, width }"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, watch, watchEffect } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { useEcharts } from '../../hooks/useEcharts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustomEchartWordCloud',
|
||||||
|
props: {
|
||||||
|
chartData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: 'calc(100vh - 78px)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const chartRef = ref(null);
|
||||||
|
const { setOptions, getInstance, resize } = useEcharts(chartRef);
|
||||||
|
const option = reactive({
|
||||||
|
series: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
props.chartData && initCharts();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.size,
|
||||||
|
() => {
|
||||||
|
resize();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function initCharts() {
|
||||||
|
if (props.option) {
|
||||||
|
Object.assign(option, cloneDeep(props.option));
|
||||||
|
}
|
||||||
|
option.series = props.chartData;
|
||||||
|
setOptions(option);
|
||||||
|
resize();
|
||||||
|
getInstance()?.off('click', onClick);
|
||||||
|
getInstance()?.on('click', onClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClick(params) {
|
||||||
|
emit('click', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { chartRef };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -15,6 +15,8 @@ import CustomEchartLineLine from './custom-echart-line-line';
|
|||||||
import CustomEchartBubble from './custom-echart-bubble';
|
import CustomEchartBubble from './custom-echart-bubble';
|
||||||
import CustomEchartPie3d from './custom-echart-pie-3d';
|
import CustomEchartPie3d from './custom-echart-pie-3d';
|
||||||
import CustomEchartWaterDroplet from './custom-echart-water-droplet';
|
import CustomEchartWaterDroplet from './custom-echart-water-droplet';
|
||||||
|
import CustomEchartPieGauge from './custom-echart-pie-gauge';
|
||||||
|
import CustomEchartWordCloud from './custom-echart-word-cloud';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
@ -34,4 +36,6 @@ export {
|
|||||||
CustomEchartBubble,
|
CustomEchartBubble,
|
||||||
CustomEchartPie3d,
|
CustomEchartPie3d,
|
||||||
CustomEchartWaterDroplet,
|
CustomEchartWaterDroplet,
|
||||||
|
CustomEchartPieGauge,
|
||||||
|
CustomEchartWordCloud,
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core';
|
||||||
|
|
||||||
import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, GraphChart } from 'echarts/charts';
|
import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, GraphChart, GaugeChart } from 'echarts/charts';
|
||||||
import 'echarts-gl';
|
import 'echarts-gl';
|
||||||
import 'echarts-liquidfill';
|
import 'echarts-liquidfill';
|
||||||
|
import 'echarts-wordcloud';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TitleComponent,
|
TitleComponent,
|
||||||
@ -46,6 +47,7 @@ echarts.use([
|
|||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
GraphicComponent,
|
GraphicComponent,
|
||||||
GraphChart,
|
GraphChart,
|
||||||
|
GaugeChart,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default echarts;
|
export default echarts;
|
||||||
|
@ -2195,6 +2195,11 @@ echarts-liquidfill@^3.1.0:
|
|||||||
resolved "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz#4ec70f3697382d0404c95fff9f3e8dd85c8377da"
|
resolved "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz#4ec70f3697382d0404c95fff9f3e8dd85c8377da"
|
||||||
integrity sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==
|
integrity sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==
|
||||||
|
|
||||||
|
echarts-wordcloud@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-2.1.0.tgz#c3de6fe267044f6c3343e4ff0e05eedb01c05096"
|
||||||
|
integrity sha512-Kt1JmbcROgb+3IMI48KZECK2AP5lG6bSsOEs+AsuwaWJxQom31RTNd6NFYI01E/YaI1PFZeueaupjlmzSQasjQ==
|
||||||
|
|
||||||
echarts@^5.6.0:
|
echarts@^5.6.0:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
|
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
|
||||||
|
@ -13,6 +13,7 @@ declare module 'vue' {
|
|||||||
CurrentTime: typeof import('./src/components/currentTime.vue')['default']
|
CurrentTime: typeof import('./src/components/currentTime.vue')['default']
|
||||||
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
||||||
CustomCard: typeof import('./src/components/CustomCard.vue')['default']
|
CustomCard: typeof import('./src/components/CustomCard.vue')['default']
|
||||||
|
CustomProgress: typeof import('./src/components/customProgress.vue')['default']
|
||||||
CustomSelect: typeof import('./src/components/CustomSelect.vue')['default']
|
CustomSelect: typeof import('./src/components/CustomSelect.vue')['default']
|
||||||
GridSelect: typeof import('./src/components/GridSelect.vue')['default']
|
GridSelect: typeof import('./src/components/GridSelect.vue')['default']
|
||||||
LandClassificationType: typeof import('./src/components/LandClassificationType.vue')['default']
|
LandClassificationType: typeof import('./src/components/LandClassificationType.vue')['default']
|
||||||
|
BIN
sub-government-screen-service/src/assets/images/trace/bg1.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
sub-government-screen-service/src/assets/images/trace/bg2.png
Normal file
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.6 KiB |
@ -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>
|
@ -5,18 +5,24 @@
|
|||||||
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
|
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="list">
|
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="datalist">
|
||||||
<div v-for="(item, index) in list" :key="index" class="list-item">
|
<div v-for="(item, index) in datalist" :key="index" class="list-item">
|
||||||
<div class="list-item-content">
|
<div class="list-item-content">
|
||||||
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
|
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
|
||||||
<template v-for="(b, indexb) in listKeys" :key="indexb">
|
<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 + ')' }">
|
<div class="item-td" :class="item.status == 1 ? 'td-title' : 'td-warn'" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
|
||||||
<span v-if="b != 'status'">
|
<span v-if="b == 'num'">
|
||||||
{{ item[b] }}
|
{{ item[b] }}
|
||||||
</span>
|
</span>
|
||||||
<el-icon v-else>
|
<span v-else-if="b == 'duration'" class="duration">
|
||||||
<Bell></Bell>
|
<span class="val">{{ item[b] + 'h' }}</span>
|
||||||
</el-icon>
|
<div class="pro">
|
||||||
|
<customProgress height="5px" :percent="item.percent" inactive-bg="#081931"></customProgress>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ item[b] == 0 ? '待机' : '运行' }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -30,6 +36,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
||||||
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
||||||
|
import customProgress from '@/components/customProgress.vue';
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// items: {
|
// items: {
|
||||||
// type: Array,
|
// type: Array,
|
||||||
@ -38,13 +45,13 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let list = reactive([
|
let list = reactive([
|
||||||
{ num: '投喂机', duration: '3.7h', status: 1 },
|
{ num: '投喂机', duration: '3.7', status: 1 },
|
||||||
{ num: '喂水机', duration: '10.0h', status: 1 },
|
{ num: '喂水机', duration: '10.0', status: 1 },
|
||||||
{ num: '投喂机', duration: '6.4h', status: 1 },
|
{ num: '投喂机', duration: '6.4', status: 1 },
|
||||||
{ num: '喂水机', duration: '3.9h', status: 1 },
|
{ num: '喂水机', duration: '3.9', status: 1 },
|
||||||
{ num: '投喂机', duration: '3.6h', status: 0 },
|
{ num: '投喂机', duration: '3.6', status: 0 },
|
||||||
{ num: '喂水机', duration: '4.5h', status: 1 },
|
{ num: '喂水机', duration: '4.5', status: 1 },
|
||||||
{ num: '投喂机', duration: '5.6h', status: 1 },
|
{ num: '投喂机', duration: '5.6', status: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const listKeys = reactive(['num', 'status', 'duration']);
|
const listKeys = reactive(['num', 'status', 'duration']);
|
||||||
@ -54,6 +61,22 @@ const listKeysHeader = reactive({
|
|||||||
duration: '设备今日运行时长',
|
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 = {
|
const classOptions = {
|
||||||
singleHeight: 48,
|
singleHeight: 48,
|
||||||
};
|
};
|
||||||
@ -100,6 +123,22 @@ const classOptions = {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #fff;
|
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 {
|
.list-item {
|
||||||
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
|
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
|
||||||
|
@ -12,7 +12,7 @@ import { isEmpty, getAssetsFile } from '@/utils';
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.monitoring-screen-content {
|
.monitoring-screen-content {
|
||||||
background-size: contain;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -13,7 +13,7 @@ import { isEmpty, getAssetsFile } from '@/utils';
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.plant-gs-content {
|
.plant-gs-content {
|
||||||
background-size: contain;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -74,7 +74,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right-charts-item">
|
<div class="right-charts-item">
|
||||||
<customBack top-title="设备数据统计" :top-postion="'right'">
|
<customBack top-title="设备数据统计" :top-postion="'right'">
|
||||||
<template #back> </template>
|
<template #back>
|
||||||
|
<deviceCharts></deviceCharts>
|
||||||
|
</template>
|
||||||
</customBack>
|
</customBack>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -96,6 +98,7 @@ import irrigationCharts from './components/irrigationCharts.vue';
|
|||||||
import healthStatusCharts from './components/healthStatusCharts.vue';
|
import healthStatusCharts from './components/healthStatusCharts.vue';
|
||||||
import monitoringScreen from './components/monitoringScreen.vue';
|
import monitoringScreen from './components/monitoringScreen.vue';
|
||||||
import benefitCharts from './components/benefitCharts.vue';
|
import benefitCharts from './components/benefitCharts.vue';
|
||||||
|
import deviceCharts from './components/deviceCharts.vue';
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.data-home-index {
|
.data-home-index {
|
||||||
|
@ -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>
|
@ -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: 30,
|
||||||
|
// 形状 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: '100%',
|
||||||
|
// 画布高
|
||||||
|
height: '100%',
|
||||||
|
// 是否渲染超出画布的文字
|
||||||
|
drawOutOfBound: false,
|
||||||
|
textStyle: {
|
||||||
|
color: function () {
|
||||||
|
const colors = ['#165DFF', '#6aca37', '#05a4b6', '#f93920', '#f0b114'];
|
||||||
|
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>
|
@ -4,15 +4,35 @@
|
|||||||
<template #center>
|
<template #center>
|
||||||
<el-row style="width: 100%; height: 100%">
|
<el-row style="width: 100%; height: 100%">
|
||||||
<el-col :span="6" class="left-charts">
|
<el-col :span="6" class="left-charts">
|
||||||
<div class="left-charts-item"></div>
|
<div class="left-charts-item">
|
||||||
<div class="left-charts-item"></div>
|
<customBack top-title="舆情数据统计" :top-postion="'left'">
|
||||||
<div class="left-charts-item"></div>
|
<template #back> </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>
|
||||||
<el-col :span="12"></el-col>
|
|
||||||
<el-col :span="6" class="right-charts">
|
<el-col :span="6" class="right-charts">
|
||||||
<div class="right-charts-item"></div>
|
<div class="right-charts-item" style="height: 100%">
|
||||||
<div class="right-charts-item"></div>
|
<customBack top-title="农场品实时价格" :top-postion="'right'">
|
||||||
<div class="right-charts-item"></div>
|
<template #back> </template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
@ -21,6 +41,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import baseBg from '@/components/baseBg.vue';
|
import baseBg from '@/components/baseBg.vue';
|
||||||
|
import customBack from '@/components/customBack.vue';
|
||||||
|
import centerMap from '@/components/centerMap.vue';
|
||||||
|
import backToCharts from './components/backToCharts.vue';
|
||||||
|
import productTypeWordClould from './components/productTypeWordClould.vue';
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.data-home-index {
|
.data-home-index {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="demo roll-list" style="height: 100%" ref="refroll">
|
<div ref="refroll" class="demo roll-list" style="height: 100%">
|
||||||
<vue3ScrollSeamless class="scroll-wrap" :classOptions="classOptions" :dataList="datalist">
|
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="datalist">
|
||||||
<div v-for="(item, index) in datalist" :key="index" class="list-item">
|
<div v-for="(item, index) in datalist" :key="index" class="list-item">
|
||||||
<div class="list-item-content">
|
<div class="list-item-content">
|
||||||
<div class="list-item-l">
|
<div class="list-item-l">
|
||||||
@ -8,11 +8,7 @@
|
|||||||
<span class="label"> {{ item.title || '--' }}</span>
|
<span class="label"> {{ item.title || '--' }}</span>
|
||||||
<span class="value"> {{ item.value || '0' }}</span>
|
<span class="value"> {{ item.value || '0' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-val">
|
<customProgress height="6px" :percent="item.percent" inactive-bg="#081931"></customProgress>
|
||||||
<div class="progress-warp" :style="{ width: item.percent + 'px' }">
|
|
||||||
<div class="progress"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="list-item-r">
|
<div class="list-item-r">
|
||||||
{{ '0' + (index + 1) }}
|
{{ '0' + (index + 1) }}
|
||||||
@ -27,6 +23,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
||||||
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
||||||
|
import customProgress from '@/components/customProgress.vue';
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -44,7 +41,11 @@ const classOptions = {
|
|||||||
let datalist = computed(() => {
|
let datalist = computed(() => {
|
||||||
let maxwidth = refroll.value && refroll.value.clientWidth;
|
let maxwidth = refroll.value && refroll.value.clientWidth;
|
||||||
return list.map((m) => {
|
return list.map((m) => {
|
||||||
return { ...m, percent: parseInt(Number(parseInt(m.value) / max.value) * maxwidth) };
|
//return { ...m, percent: parseInt(Number(parseInt(m.value) / max.value) * maxwidth) };
|
||||||
|
return {
|
||||||
|
...m,
|
||||||
|
percent: Number((Number(parseInt(m.value) / max.value) * 100).toFixed(0)),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,26 +106,6 @@ let max = computed(() => {
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.progress-val {
|
|
||||||
width: calc(100%);
|
|
||||||
.progress-warp {
|
|
||||||
.progress {
|
|
||||||
height: 6px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: linear-gradient(90deg, #45bfe9 0%, #01589c 100%);
|
|
||||||
animation: expandWidth 1s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes expandWidth {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ui-wrap {
|
.ui-wrap {
|
||||||
|
@ -11,11 +11,7 @@
|
|||||||
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
|
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
|
||||||
<div class="item-content">
|
<div class="item-content">
|
||||||
<div class="label">{{ item.title }}</div>
|
<div class="label">{{ item.title }}</div>
|
||||||
<div class="val">
|
<customProgress height="10px" :percent="item.percent"></customProgress>
|
||||||
<div class="progress-warp" :style="{ width: item.percent + 'px' }">
|
|
||||||
<div class="progress"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -28,6 +24,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
|
||||||
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
|
||||||
|
import customProgress from '@/components/customProgress.vue';
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// items: {
|
// items: {
|
||||||
// type: Array,
|
// type: Array,
|
||||||
@ -48,7 +45,11 @@ let refroll = ref(null);
|
|||||||
let datalist = computed(() => {
|
let datalist = computed(() => {
|
||||||
let maxwidth = refroll.value && refroll.value.clientWidth;
|
let maxwidth = refroll.value && refroll.value.clientWidth;
|
||||||
return list.map((m) => {
|
return list.map((m) => {
|
||||||
return { ...m, percent: parseInt(Number(m.value / max.value) * 200) };
|
return {
|
||||||
|
...m,
|
||||||
|
percent: Number((Number(parseInt(m.value) / max.value) * 100).toFixed(0)),
|
||||||
|
pwidth: parseInt(Number(parseInt(m.value) / max.value) * maxwidth),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,26 +131,6 @@ onMounted(() => {});
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.val {
|
|
||||||
width: calc(100% - 50px);
|
|
||||||
.progress-warp {
|
|
||||||
.progress {
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: linear-gradient(90deg, #45bfe9 0%, #01589c 100%);
|
|
||||||
animation: expandWidth 1s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes expandWidth {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.list-item {
|
.list-item {
|
||||||
|
@ -12,7 +12,7 @@ import { isEmpty, getAssetsFile } from '@/utils';
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.monitoring-screen-content {
|
.monitoring-screen-content {
|
||||||
background-size: contain;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -71,16 +71,7 @@ const chartsData = reactive({
|
|||||||
y2: '20%',
|
y2: '20%',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
valData: [
|
valData: [],
|
||||||
// { name: '1月', value: 40, type: '蝗虫', seriesType: 'bar', ...itemStyle, stack: '1月' },
|
|
||||||
// { name: '1月', value: 30, type: '飞蛾', seriesType: 'bar', ...itemStyle, stack: '1月' },
|
|
||||||
// { name: '1月', value: 100, type: '其他', seriesType: 'bar', ...itemStyle, stack: '1月' },
|
|
||||||
// { name: '1月', value: 60, type: '蚜虫', seriesType: 'bar', ...itemStyle, stack: '1月' },
|
|
||||||
// { name: '2月', value: 20, type: '蝗虫', seriesType: 'bar', ...itemStyle, stack: '2月' },
|
|
||||||
// { name: '2月', value: 20, type: '飞蛾', seriesType: 'bar', ...itemStyle, stack: '2月' },
|
|
||||||
// { name: '2月', value: 80, type: '其他', seriesType: 'bar', ...itemStyle, stack: '2月' },
|
|
||||||
// { name: '2月', value: 40, type: '蚜虫', seriesType: 'bar', ...itemStyle, stack: '2月' },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const randomVal = (num) => {
|
const randomVal = (num) => {
|
||||||
|
@ -13,7 +13,7 @@ import { isEmpty, getAssetsFile } from '@/utils';
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.plant-gs-content {
|
.plant-gs-content {
|
||||||
background-size: contain;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -0,0 +1,326 @@
|
|||||||
|
<template>
|
||||||
|
<div class="code-num-charts">
|
||||||
|
<div class="code-num-txt" :style="{ 'background-image': 'url(' + getAssetsFile('images/trace/bg1.png') + ')' }">
|
||||||
|
<div class="num-txt-pos">
|
||||||
|
<template v-for="(n, index) in valData" :key="index">
|
||||||
|
<div class="num-txt">
|
||||||
|
<div class="val">{{ n.value }}</div>
|
||||||
|
<div class="label">{{ n.name }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="code-pie">
|
||||||
|
<custom-echart-pie-gauge :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import { isEmpty, getAssetsFile } from '@/utils';
|
||||||
|
let valData = reactive([
|
||||||
|
{ value: 205.6, name: '生产溯源码' },
|
||||||
|
{ value: 308.7, name: '有效溯源码' },
|
||||||
|
]);
|
||||||
|
const chartsData = reactive({
|
||||||
|
option: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
title: {
|
||||||
|
zlevel: 0,
|
||||||
|
text: 806,
|
||||||
|
subtext: '设备管理',
|
||||||
|
top: '38%',
|
||||||
|
left: '80%',
|
||||||
|
textAlign: 'center',
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
subtextStyle: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
color: [
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(40, 218, 239, 1)' },
|
||||||
|
{ offset: 1, color: 'rgba(130, 249, 255, 0.8)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 1,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(198, 201, 24, 1)' },
|
||||||
|
{ offset: 1, color: 'rgba(198, 201, 24, 0.8)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
x2: 0,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(15, 44, 88, 1)', // 0% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0.7,
|
||||||
|
color: 'rgba(40, 55, 255, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(244, 245, 255, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(75, 238, 114, 0.2)', // 0% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(75, 238, 114, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(255, 19, 0, 0.2)', // 0% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(251, 95, 79, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
valData: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
name: '外层细圆环',
|
||||||
|
radius: ['58%', '60%'],
|
||||||
|
center: ['80%', '50%'],
|
||||||
|
hoverAnimation: true,
|
||||||
|
startAngle: 0,
|
||||||
|
clockWise: false,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(40, 218, 239, 0.8)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [10],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
name: '内层细圆环',
|
||||||
|
radius: ['38%', '40%'],
|
||||||
|
center: ['80%', '50%'],
|
||||||
|
hoverAnimation: true,
|
||||||
|
startAngle: 0,
|
||||||
|
clockWise: false,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(40, 218, 239, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [10],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'pie',
|
||||||
|
startAngle: 0,
|
||||||
|
radius: 70,
|
||||||
|
clockWise: false,
|
||||||
|
hoverAnimation: false,
|
||||||
|
center: ['80%', '50%'],
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'radial',
|
||||||
|
x: 0.5,
|
||||||
|
y: 0.5,
|
||||||
|
r: 0.5,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: 'rgba(129, 197, 200, 0.1)' },
|
||||||
|
{ offset: 0, color: 'rgba(129, 197, 200, 0)' },
|
||||||
|
],
|
||||||
|
globalCoord: false,
|
||||||
|
},
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
//环形
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'pie',
|
||||||
|
clockwise: false,
|
||||||
|
startAngle: -90,
|
||||||
|
radius: ['45%', '55%'],
|
||||||
|
center: ['80%', '50%'],
|
||||||
|
hoverAnimation: false,
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zlevel: 1,
|
||||||
|
labelLine: {
|
||||||
|
normal: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: valData,
|
||||||
|
},
|
||||||
|
//环形分割线
|
||||||
|
{
|
||||||
|
name: '分割线',
|
||||||
|
type: 'gauge',
|
||||||
|
radius: '75%',
|
||||||
|
center: ['80%', '50%'],
|
||||||
|
clockwise: true,
|
||||||
|
startAngle: 90, // 起始角度
|
||||||
|
endAngle: -360, // 结束角度
|
||||||
|
splitNumber: 50, // 分割线数量
|
||||||
|
zlevel: 2,
|
||||||
|
detail: {
|
||||||
|
offsetCenter: [10, 20],
|
||||||
|
formatter: ' ',
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false, // 隐藏轴线
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false, // 隐藏刻度
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
length: 10, // 分割线的长度
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1e4960',
|
||||||
|
width: 1, // 分割线的宽度
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // 隐藏标签
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
||||||
|
div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.code-num-charts {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
.code-num-txt {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 80%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left center;
|
||||||
|
height: 100%;
|
||||||
|
.num-txt-pos {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.num-txt {
|
||||||
|
width: 72%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 16px;
|
||||||
|
line-height: 50px;
|
||||||
|
.val,
|
||||||
|
.label {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
background: linear-gradient(360deg, #34c7d1, #fff); /* 渐变颜色 */
|
||||||
|
-webkit-background-clip: text; /* 裁剪背景为文字形状 */
|
||||||
|
background-clip: text;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
-webkit-text-fill-color: transparent; /* 设置文字颜色为透明 */
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-flex;
|
||||||
|
transform: skewX(-8deg);
|
||||||
|
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: -2px 0 0 1px #add8f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.code-pie {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div class="demo detection-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 v-if="b != 'status'">
|
||||||
|
{{ item[b] }}
|
||||||
|
</span>
|
||||||
|
<span v-else :class="item[b] == 0 ? 'status-no' : 'status-y'">
|
||||||
|
{{ 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';
|
||||||
|
const props = defineProps({
|
||||||
|
// items: {
|
||||||
|
// type: Array,
|
||||||
|
// default: () => [],
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
|
||||||
|
let list = reactive([
|
||||||
|
{ title: '红星农业合作社', agency: '耿马农残检测中心', time: '2025.01.02', status: 1, info: '经销商资质不合格' },
|
||||||
|
{ title: '成大食品加工厂', agency: '耿马农残检测中心', time: '2025.01.01', status: 1, info: '农药成分不合格' },
|
||||||
|
{ title: '大大食品加工厂', agency: '耿马食品检测中心', time: '2025.01.02', status: 0, info: '经销商资质不合格' },
|
||||||
|
{ title: '佳成农业合作社', agency: '耿马农残检测中心', time: '2025.01.01', status: 1, info: '成分不合格' },
|
||||||
|
{ title: '嘉庆食品加工厂', agency: '耿马食品检测中心', time: '2025.01.02', status: 1, info: '经销商资质不合格' },
|
||||||
|
{ title: '汇星农业合作社', agency: '耿马农残检测中心', time: '2025.01.01', status: 1, info: '经销商资质不完全' },
|
||||||
|
{ title: '瑞达农业合作社', agency: '耿马农残检测中心', time: '2025.01.02', status: 1, info: '种源质量不好' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const listKeys = reactive(['title', 'agency', 'status']);
|
||||||
|
const listKeysHeader = reactive({
|
||||||
|
title: '送检单位',
|
||||||
|
agency: '检测单位',
|
||||||
|
status: '检测结果',
|
||||||
|
});
|
||||||
|
|
||||||
|
const classOptions = {
|
||||||
|
singleHeight: 48,
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detection-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>
|
@ -0,0 +1,332 @@
|
|||||||
|
<template>
|
||||||
|
<div class="main-part-charts">
|
||||||
|
<div class="code-num-txt" :style="{ 'background-image': 'url(' + getAssetsFile('images/trace/bg2.png') + ')' }">
|
||||||
|
<div class="num-txt-pos">
|
||||||
|
<template v-for="(n, index) in valData" :key="index">
|
||||||
|
<div class="num-txt">
|
||||||
|
<div class="label">
|
||||||
|
<span>{{ n.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="val">
|
||||||
|
<span class="val-val">{{ n.value }}</span>
|
||||||
|
<span class="unit">{{ n.unit }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="code-pie">
|
||||||
|
<custom-echart-pie-gauge :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import { isEmpty, getAssetsFile } from '@/utils';
|
||||||
|
let valData = reactive([
|
||||||
|
{ value: 356, name: '追溯主体', unit: '家' },
|
||||||
|
{ value: 25, name: '检测机构', unit: '家' },
|
||||||
|
{ value: 199, name: '生成溯源码', unit: '次' },
|
||||||
|
]);
|
||||||
|
const chartsData = reactive({
|
||||||
|
option: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
title: {
|
||||||
|
zlevel: 0,
|
||||||
|
text: 806,
|
||||||
|
subtext: '设备管理',
|
||||||
|
top: '38%',
|
||||||
|
left: '80%',
|
||||||
|
textAlign: 'center',
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
subtextStyle: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
color: [
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(40, 218, 239, 1)' },
|
||||||
|
{ offset: 1, color: 'rgba(130, 249, 255, 0.8)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 1,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(198, 201, 24, 1)' },
|
||||||
|
{ offset: 1, color: 'rgba(198, 201, 24, 0.8)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
x2: 0,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(83, 165, 23, 1)' },
|
||||||
|
{ offset: 1, color: 'rgba(83, 165, 23, 0.8)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(75, 238, 114, 0.2)', // 0% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(75, 238, 114, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(255, 19, 0, 0.2)', // 0% 处的颜色
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(251, 95, 79, 1)', // 100% 处的颜色
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
valData: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
name: '外层细圆环',
|
||||||
|
radius: ['58%', '60%'],
|
||||||
|
center: ['20%', '50%'],
|
||||||
|
hoverAnimation: true,
|
||||||
|
startAngle: 0,
|
||||||
|
clockWise: false,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(40, 218, 239, 0.8)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [10],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
name: '内层细圆环',
|
||||||
|
radius: ['38%', '40%'],
|
||||||
|
center: ['20%', '50%'],
|
||||||
|
hoverAnimation: true,
|
||||||
|
startAngle: 0,
|
||||||
|
clockWise: false,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(40, 218, 239, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [10],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'pie',
|
||||||
|
startAngle: 0,
|
||||||
|
radius: 70,
|
||||||
|
clockWise: false,
|
||||||
|
hoverAnimation: false,
|
||||||
|
center: ['20%', '50%'],
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'radial',
|
||||||
|
x: 0.5,
|
||||||
|
y: 0.5,
|
||||||
|
r: 0.5,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: 'rgba(129, 197, 200, 0.1)' },
|
||||||
|
{ offset: 0, color: 'rgba(129, 197, 200, 0)' },
|
||||||
|
],
|
||||||
|
globalCoord: false,
|
||||||
|
},
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
//环形
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'pie',
|
||||||
|
clockwise: false,
|
||||||
|
startAngle: -90,
|
||||||
|
radius: ['45%', '55%'],
|
||||||
|
center: ['20%', '50%'],
|
||||||
|
hoverAnimation: false,
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zlevel: 1,
|
||||||
|
labelLine: {
|
||||||
|
normal: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: valData,
|
||||||
|
},
|
||||||
|
//环形分割线
|
||||||
|
{
|
||||||
|
name: '分割线',
|
||||||
|
type: 'gauge',
|
||||||
|
radius: '75%',
|
||||||
|
center: ['20%', '50%'],
|
||||||
|
clockwise: true,
|
||||||
|
startAngle: 90, // 起始角度
|
||||||
|
endAngle: -360, // 结束角度
|
||||||
|
splitNumber: 50, // 分割线数量
|
||||||
|
zlevel: 2,
|
||||||
|
detail: {
|
||||||
|
offsetCenter: [10, 20],
|
||||||
|
formatter: ' ',
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false, // 隐藏轴线
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false, // 隐藏刻度
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
length: 10, // 分割线的长度
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1e4960',
|
||||||
|
width: 1, // 分割线的宽度
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // 隐藏标签
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
||||||
|
div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.main-part-charts {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
.code-num-txt {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 80%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 10% 0 10% 18%;
|
||||||
|
.num-txt-pos {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.num-txt {
|
||||||
|
width: 72%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 16px;
|
||||||
|
line-height: 30px;
|
||||||
|
.val,
|
||||||
|
.label {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
.val-val {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-flex;
|
||||||
|
background: linear-gradient(360deg, #34c7d1, #fff); /* 渐变颜色 */
|
||||||
|
-webkit-background-clip: text; /* 裁剪背景为文字形状 */
|
||||||
|
background-clip: text;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
-webkit-text-fill-color: transparent; /* 设置文字颜色为透明 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-flex;
|
||||||
|
transform: skewX(-8deg);
|
||||||
|
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: -2px 0 0 1px #add8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: 8px;
|
||||||
|
color: #34c7d1;
|
||||||
|
padding: 6px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.code-pie {
|
||||||
|
position: absolute;
|
||||||
|
right: left;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="principal-type-charts">
|
||||||
|
<custom-echart-pie :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
|
||||||
|
const chartsData = reactive({
|
||||||
|
option: {
|
||||||
|
color: ['#5cd2db', '#e4f116', '#6af116', '#2bb0ef'],
|
||||||
|
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: ['50%', '65%'],
|
||||||
|
// roseType: 'area',
|
||||||
|
center: ['40%', '50%'],
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 5,
|
||||||
|
},
|
||||||
|
// animationDuration: 15000,
|
||||||
|
// // 可选:设置动画缓动效果
|
||||||
|
// animationEasing: 'elasticOut',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
valData: [
|
||||||
|
{ value: 37, name: '种源企业' },
|
||||||
|
{ value: 135, name: '农企/合作社' },
|
||||||
|
{ value: 41, name: '农资企业' },
|
||||||
|
{ value: 141, 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>
|
||||||
|
.principal-type-charts {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div class="product-type-charts">
|
||||||
|
<custom-echart-pie :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
|
||||||
|
const chartsData = reactive({
|
||||||
|
option: {
|
||||||
|
color: ['#5cd2db', '#e4f116', '#6af116', '#2bb0ef', '#a56aef', '#efb56a'],
|
||||||
|
title: {
|
||||||
|
text: ' ',
|
||||||
|
textStyle: {
|
||||||
|
color: '#333',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['农副产品加工', '水果', '蔬菜', '水产', '禽畜肉蛋', '米面粮油'],
|
||||||
|
right: '0', // 距离左侧10%的位置
|
||||||
|
top: 'middle', // 垂直居中
|
||||||
|
orient: 'vertical', // 图例垂直排列
|
||||||
|
itemWidth: 10, // 图例标记的宽度
|
||||||
|
itemHeight: 8, // 图例标记的高度
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 10, // 图例文字的字体大小
|
||||||
|
color: '#fff', // 图例文字的颜色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: '#333',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['20%', '65%'],
|
||||||
|
roseType: 'area',
|
||||||
|
center: ['40%', '50%'],
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 5,
|
||||||
|
},
|
||||||
|
// animationDuration: 15000,
|
||||||
|
// // 可选:设置动画缓动效果
|
||||||
|
// animationEasing: 'elasticOut',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
valData: [
|
||||||
|
{ value: 37, name: '农副产品加工' },
|
||||||
|
{ value: 135, name: '水果' },
|
||||||
|
{ value: 41, name: '蔬菜' },
|
||||||
|
{ value: 141, name: '水产' },
|
||||||
|
{ value: 41, name: '禽畜肉蛋' },
|
||||||
|
{ value: 141, 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-charts {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pathology-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,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: [8, 8, 0, 0], // 设置柱子的圆角半径
|
||||||
|
},
|
||||||
|
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: 'bar',
|
||||||
|
type: legendList[i],
|
||||||
|
};
|
||||||
|
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>
|
||||||
|
.pathology-charts {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,15 +4,53 @@
|
|||||||
<template #center>
|
<template #center>
|
||||||
<el-row style="width: 100%; height: 100%">
|
<el-row style="width: 100%; height: 100%">
|
||||||
<el-col :span="6" class="left-charts">
|
<el-col :span="6" class="left-charts">
|
||||||
<div class="left-charts-item"></div>
|
<div class="left-charts-item">
|
||||||
<div class="left-charts-item"></div>
|
<customBack top-title="溯源码数据统计" :top-postion="'left'">
|
||||||
<div class="left-charts-item"></div>
|
<template #back>
|
||||||
|
<codeNumCharts></codeNumCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
|
<div class="left-charts-item">
|
||||||
|
<customBack top-title="追溯主体类型统计" :top-postion="'left'">
|
||||||
|
<template #back>
|
||||||
|
<principalTypeCharts></principalTypeCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
|
<div class="left-charts-item">
|
||||||
|
<customBack top-title="追溯产品分类" :top-postion="'left'">
|
||||||
|
<template #back>
|
||||||
|
<productTypeCharts></productTypeCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<centerMap></centerMap>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12"></el-col>
|
|
||||||
<el-col :span="6" class="right-charts">
|
<el-col :span="6" class="right-charts">
|
||||||
<div class="right-charts-item"></div>
|
<div class="right-charts-item">
|
||||||
<div class="right-charts-item"></div>
|
<customBack top-title="溯源主体信息统计" :top-postion="'right'">
|
||||||
<div class="right-charts-item"></div>
|
<template #back>
|
||||||
|
<mainPartCharts></mainPartCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
|
<div class="right-charts-item">
|
||||||
|
<customBack top-title="溯源码数据统计" :top-postion="'right'">
|
||||||
|
<template #back>
|
||||||
|
<traceBarCharts></traceBarCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
|
<div class="right-charts-item">
|
||||||
|
<customBack top-title="最新溯源检测信息" :top-postion="'right'">
|
||||||
|
<template #back>
|
||||||
|
<detectionCharts></detectionCharts>
|
||||||
|
</template>
|
||||||
|
</customBack>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
@ -21,6 +59,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import baseBg from '@/components/baseBg.vue';
|
import baseBg from '@/components/baseBg.vue';
|
||||||
|
import customBack from '@/components/customBack.vue';
|
||||||
|
import centerMap from '@/components/centerMap.vue';
|
||||||
|
import codeNumCharts from './components/codeNumCharts.vue';
|
||||||
|
import mainPartCharts from './components/mainPartCharts.vue';
|
||||||
|
import principalTypeCharts from './components/principalTypeCharts.vue';
|
||||||
|
import productTypeCharts from './components/productTypeCharts.vue';
|
||||||
|
import traceBarCharts from './components/traceBarCharts.vue';
|
||||||
|
import detectionCharts from './components/detectionCharts.vue';
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.data-home-index {
|
.data-home-index {
|
||||||
|