Compare commits

...

5 Commits

Author SHA1 Message Date
lx
5d1e1a15a3 feat 2025-04-25 11:38:46 +08:00
lx
3677c1e953 feat 2025-04-25 11:37:59 +08:00
lx
64a09455c4 Merge branch 'main' of http://47.109.205.240:3000/Web/digital-agriculture-screen 2025-04-25 09:08:49 +08:00
lx
1bedb1ac49 Merge branch 'main' of http://47.109.205.240:3000/Web/digital-agriculture-screen 2025-04-25 09:07:54 +08:00
lx
422807e150 feat 2025-04-25 09:07:46 +08:00
7 changed files with 433 additions and 112 deletions

2
components.d.ts vendored
View File

@ -16,6 +16,8 @@ declare module 'vue' {
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']
CustomEchartHyaline: typeof import('./src/components/custom-echart-hyaline/index.vue')['default']
CustomEchartHyalineCake: typeof import('./src/components/custom-echart-hyaline-cake/index.vue')['default']
CustomEchartLine: typeof import('./src/components/custom-echart-line/index.vue')['default']
CustomEchartLineLine: typeof import('./src/components/custom-echart-line-line/index.vue')['default']
CustomEchartMaps: typeof import('./src/components/custom-echart-maps/index.vue')['default']

View File

@ -0,0 +1,282 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script>
import { ref, reactive, watchEffect } from 'vue';
import { cloneDeep } from 'lodash';
import { useEcharts } from '@/hooks/useEcharts';
export default {
name: 'CustomEchartHyalineCake',
props: {
chartData: {
type: Array,
default: () => [
{
name: '项目一',
value: 60,
},
{
name: '项目二',
value: 44,
},
{
name: '项目三',
value: 32,
},
],
},
option: {
type: Object,
default: () => ({}),
},
type: {
type: String,
default: 'bar',
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: 'calc(100vh - 78px)',
},
isSeries: {
type: Boolean,
default: false,
},
},
emits: ['click'],
setup(props, { emit }) {
const chartRef = ref(null);
const { setOptions, getInstance, startAutoPlay } = useEcharts(chartRef);
const option = ref({});
const itemHeight = ref(120);
// let colors = ['#07daf2', '#64de8a', '#ecc23c', '#ffdb5c', '#ff9f7f', '#9fe6b8', '#67e0e3', '#32c5e9', '#fb7293', '#9A60B4', '#ea7ccc'];
watchEffect(() => {
props.chartData && initCharts();
});
function initCharts() {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
option.value = getPie3D(props.chartData, 0.5);
setOptions(option.value);
}
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
const midRatio = (startRatio + endRatio) / 2;
const startRadian = startRatio * Math.PI * 2;
const endRadian = endRatio * Math.PI * 2;
const midRadian = midRatio * Math.PI * 2;
if (startRatio === 0 && endRatio === 1) {
isSelected = false;
}
k = typeof k !== 'undefined' ? k : 1 / 3;
const offsetX = Math.cos(midRadian) * 0.2;
const offsetY = Math.sin(midRadian) * 0.2;
const hoverRate = 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(u, v) {
if (u < startRadian) {
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
if (u > endRadian) {
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
},
y(u, v) {
if (u < startRadian) {
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
if (u > endRadian) {
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
},
z(u, v) {
if (u < -Math.PI * 0.5) {
return Math.sin(u);
}
if (u > Math.PI * 2.5) {
return Math.sin(u) * h * 0.1;
}
return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
},
};
}
// 3D
function getPie3D(pieData) {
const series = [];
//
let sumValue = 0;
let startValue = 0;
let endValue = 0;
const legendData = [];
const k = 1;
for (let i = 0; i < pieData.length; i += 1) {
sumValue += pieData[i].value;
const seriesItem = {
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
type: 'surface',
parametric: true,
wireframe: {
show: false,
},
pieData: pieData[i],
itemStyle: {
// color: colors[i], //
opacity: '0.6',
borderRadius: 300,
borderColor: '#fff',
borderWidth: 0,
},
pieStatus: {
selected: false,
hovered: false,
k,
},
};
if (typeof pieData[i].itemStyle !== 'undefined') {
const { itemStyle } = pieData[i];
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);
}
for (let i = 0; i < series.length; i += 1) {
endValue = startValue + series[i].pieData.value;
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,
itemHeight.value
);
startValue = endValue;
legendData.push(series[i].name);
}
const option = {
tooltip: {
position: function (point, params, dom, rect, size) {
var x = point[0];
var y = point[1];
var viewWidth = size.viewSize[0];
var viewHeight = size.viewSize[1];
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// tooltip 使
if (x + boxWidth > viewWidth) {
x = x - boxWidth;
}
if (y + boxHeight > viewHeight) {
y = y - boxHeight;
}
// tooltip
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
return [x, y];
},
formatter: (params) => {
if (params.seriesName !== 'mouseoutSeries') {
return `
${params.seriesName}<br/>
<span style="display:inline-block;
margin-right:5px;
border-radius:10px;
width:10px;
height:10px;
background-color:${params.color};"></span>
${option.series[params.seriesIndex].pieData.value}
`;
}
return '';
},
},
xAxis3D: {
min: -1,
max: 1.5,
},
yAxis3D: {
min: -1,
max: 1.5,
},
zAxis3D: {
min: -1,
max: 1.5,
},
itemGap: 20,
grid3D: {
itemGap: 20,
show: false,
boxHeight: 5,
top: '0',
left: '-20%',
viewControl: {
//3d
alpha: 60, //( )
distance: 260, //zoom()
rotateSensitivity: 10, //0
zoomSensitivity: 10, //0
panSensitivity: 10, //0
autoRotate: true, //
autoRotateAfterStill: 2, //, autoRotate
},
},
legend: {
show: true,
right: '5%',
top: '25%',
orient: 'vertical',
icon: 'circle',
itemHeight: 12,
itemWidth: 12,
itemGap: 10,
textStyle: {
color: '#fff',
fontSize: 14,
fontWeight: '400',
},
formatter: (name) => {
if (props.chartData.length) {
const item = props.chartData.filter((item) => item.name === name)[0];
return ` ${name} ${item.value}万亩`;
}
},
},
series,
};
return option;
}
function onClick(params) {
emit('click', params);
}
return { chartRef };
},
};
</script>

View File

@ -20,6 +20,7 @@ import CustomEchartWordCloud from './custom-echart-word-cloud';
import customEchartScatterBlister from './custom-echart-scatter-blister';
import customEchartMaps from './custom-echart-maps';
import customScrollTitle from './custom-scroll-title';
import customEchartHyalineCake from './custom-echart-hyaline-cake';
export {
SvgIcon,
@ -44,4 +45,5 @@ export {
customEchartScatterBlister,
customEchartMaps,
customScrollTitle,
customEchartHyalineCake,
};

View File

@ -60,7 +60,6 @@ watch(
};
});
list.value.sort((a, b) => b.value - a.value);
console.log('list', list.value);
},
{
immediate: true,
@ -83,7 +82,6 @@ function handleAc(val) {
}
const _circleNum = computed(() => {
let s = ((currNum.value / allNum.value) * 100).toFixed(1);
console.log('s----------', currNum.value, allNum.value, s);
return s + '%';
});
</script>

View File

@ -0,0 +1,44 @@
<template>
<customEchartHyalineCake :chart-data="data" height="100%" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
/* --------------- data --------------- */
// #region
const data = ref([
{
name: '小麦',
value: 60.8,
},
{
name: '荞麦',
value: 44.4,
},
{
name: '贡菜',
value: 24.3,
},
{
name: '油菜',
value: 32.7,
},
{
name: '马铃薯',
value: 32.9,
},
{
name: '玉米',
value: 32.1,
},
]);
// #endregion
/* --------------- methods --------------- */
// #region
// #endregion
</script>
<style lang="scss" scoped></style>

View File

@ -1,114 +1,106 @@
<template>
<div class="demo roll-list-land-plan" 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> -->
<custom-echart-bar :chart-data="state.data" height="100%" :option="state.option" />
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
const props = defineProps({
// items: {
// type: Array,
// default: () => [],
// },
import { reactive } from 'vue';
const state = reactive({
data: [
{ value: 20, name: '耿马镇' },
{ value: 15, name: '勐撒镇' },
{ value: 12, name: '勐永镇' },
{ value: 16, name: '孟定镇' },
{ value: 8, name: '勐简乡' },
{ value: 12, name: '贺派乡' },
{ value: 10, name: '四排山乡' },
{ value: 9, name: '芒洪乡' },
{ value: 8, name: '大兴乡' },
],
option: {
grid: {
left: '5%',
right: '5%',
bottom: '5%',
top: '10%',
containLabel: true,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips">
<span>${params.name}</span><br/>
<span>${params.marker} ${params.data} 万亩</span>
</div>`;
return str;
},
},
barStyle: {
barWidth: 14,
itemStyle: {
borderWidth: 14,
borderRadius: [8, 8, 8, 8], //
},
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#35D0C0' },
{ offset: 1, color: '#35D0C0' },
],
global: false,
},
},
xAxis: {
type: 'value',
splitLine: {
show: false,
lineStyle: {
type: 'dashed',
color: '#E5E5E5',
},
},
axisLabel: {
show: false,
textStyle: {
color: '#424242',
},
},
show: false,
axisLine: {
show: true,
},
axisTick: {
show: false,
},
},
yAxis: {
type: 'category',
data: ['耿马镇', '勐撒镇', '勐永镇', '孟定镇', '勐简乡', '贺派乡', '四排山乡', '芒洪乡', '大兴乡'],
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLine: {
show: false,
},
},
series: [
{
type: 'bar',
// barWidth: '40%', //
// barMinHeight: 2, //
// barGap: '20%', //
},
],
},
});
let list = reactive([
{ title: '耿马镇', area: '540', chg: '0.5%' },
{ title: '勐撒镇', area: '1210', chg: '0.7%' },
{ title: '孟定镇', area: '188.3', chg: '-0.8%' },
{ title: '孟简镇', area: '98.7', chg: '1.2%' },
{ title: '孟永镇', area: '165.5', chg: '0.9%' },
]);
const listKeys = reactive(['title', 'area', 'chg']);
const listKeysHeader = reactive({
title: '乡/镇',
area: '面积',
chg: '涨跌幅',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.roll-list-land-plan {
margin-top: 8px;
.scroll-wrap {
height: 80%;
width: 100%;
margin: 4px auto;
overflow: hidden;
}
.list-item-header {
background: #144482;
font-size: 10px;
width: 100%;
.item-td {
padding: 8px 6px;
}
}
.list-item-boday {
background: transparent;
width: 100%;
.item-td {
padding: 4px 6px;
&.td-title {
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
}
}
.list-item {
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
line-height: 18px;
.list-item-content {
display: inline-flex;
width: 100%;
justify-content: space-around;
position: relative;
}
}
}
.demo {
// display: flex;
// align-items: center;
// justify-content: center;
// margin-top: 10px;
}
</style>

View File

@ -64,7 +64,7 @@
<div class="right-charts-item">
<customBack top-title="各地农用地利用面积" :top-postion="'right'">
<template #back>
<landareaCharts></landareaCharts>
<cake />
</template>
</customBack>
</div>
@ -80,6 +80,7 @@ import landCirculation from './components/landCirculation.vue';
import landPlan from './components/landPlan.vue';
import landPatrol from './components/landPatrol.vue';
import LandAera from './components/LandAera.vue';
import cake from './components/cake.vue';
import { nextTick, ref } from 'vue';
const landPatrolRef = ref(null);