This commit is contained in:
李想 2025-04-24 17:34:28 +08:00
commit 91175aa8c1
12 changed files with 182 additions and 170 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 717 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -2,22 +2,25 @@
<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%"
width="100%"
:option="chartsData.option"
:geo="geoData"
:name="mapName"
@click="mapClick"
/>
<custom-echart-maps height="100%" width="100%" :option="chartsData.option" :geo="geoData" :name="mapName" @click="mapClick" />
</div>
<el-dialog v-model="isShow" :title="currentMap.name + dialogTitle" width="360" :before-close="handleClose" custom-class="map-info-dialog">
</el-dialog>
</div>
</template>
<script setup>
import { isEmpty, getAssetsFile } from '@/utils';
import { ref, reactive, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import geoJsonData from '../components/530926geo.json'; //
const route = useRoute();
const props = defineProps({
dialogTitle: {
type: String,
default: '首页',
},
});
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;
@ -25,8 +28,7 @@ var iconUrl = getAssetsFile('images/vsualized/gmmap2.png').href;
const isShow = ref(false);
let geoData = geoJsonData;
let mapName = ref('ZJ');
const info = ref({});
let mapName = ref('ZJ' + route.name);
const chartsData = reactive({
option: {
title: {
@ -56,11 +58,12 @@ const chartsData = reactive({
geo3D: {
map: mapName.value,
roam: true,
zoom: 1.2,
zoom: 1,
left: '30px',
show: true,
zlevel: -1, //
viewControl: {
distance: 110,
distance: 115,
alpha: 60,
beta: 0,
minBeta: -360,
@ -103,138 +106,68 @@ const chartsData = reactive({
},
},
series: [
//
{
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; //
type: 'map3D',
map: mapName.value,
zoom: 1,
left: '30px',
viewControl: {
distance: 115,
alpha: 60,
beta: 0,
minBeta: -360,
maxBeta: 720,
// 使
// rotateSensitivity: 0,
// zoomSensitivity: 0,
// panSensitivity: 0,
},
itemStyle: {
//
color: 'rgba(75,255,180,0.2)', //
opacity: 1, // [ default: 1 ]
borderWidth: 1.5, // (线) [ default: 0 ]
borderColor: '#4bffb4', // [ default: #333 ]
},
label: {
formatter: '{b}',
position: 'right',
show: false,
show: true,
distance: 0,
color: '#000',
padding: [6, 4, 2, 4],
borderRadius: 4,
backgroundColor: 'rgba(255,255,255,0.2)',
textStyle: {
fontSize: 12,
color: '#E87813', //
borderWidth: 0,
borderColor: '#000',
},
},
rippleEffect: {
period: 4, //
scale: 3, //
brushType: 'stroke', // 'stroke' 'fill'
emphasis: {
//
label: {
show: true,
color: '#fff',
},
itemStyle: {
color: 'rgba(75,255,180,0.3)', //
},
},
hoverAnimation: false,
},
],
},
valData: [
{
map: mapName.value,
roam: true,
zoom: 0.9,
show: true,
itemStyle: {
//
color: 'rgba(75,255,180,0.2)', //
opacity: 1, // [ default: 1 ]
borderWidth: 1.5, // (线) [ default: 0 ]
borderColor: 'rgba(92, 184, 238, 1)', // [ default: #333 ]
},
label: {
show: true,
distance: 0,
color: '#000',
padding: [6, 4, 2, 4],
borderRadius: 4,
backgroundColor: 'rgba(255,255,255,.66)',
textStyle: {
fontSize: 12,
color: '#E87813', //
borderWidth: 0,
borderColor: '#000',
},
},
// emphasis: {
// //
// label: {
// show: true,
// color: '#fff',
// },
// itemStyle: {
// // show: false,
// areaColor: {
// type: 'linear',
// x: 0,
// y: 0,
// x2: 1,
// y2: 0,
// colorStops: [
// { offset: 0, color: '#4bffb4' },
// { offset: 1, color: '#4bffb4' },
// ],
// },
// },
// },
},
//
{
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,
},
],
});
let currentMap = reactive({});
const mapClick = (data) => {
isShow.value = true;
currentMap = data;
console.info('mapClick', data);
};
const handleClose = () => {
isShow.value = false;
};
onMounted(() => {
console.info('iconUrl', iconUrl);
});
@ -243,6 +176,28 @@ onMounted(() => {
div {
box-sizing: border-box;
}
::v-deep() {
.el-dialog {
background: url('@/assets/images/vsualized/mapopup.png') no-repeat left top;
min-height: 200px;
max-height: 500px;
overflow-y: auto;
background-size: 100% 100%;
padding: 16px;
}
.el-dialog__header {
margin-top: 10px;
text-align: left;
padding-left: 48px;
.el-dialog__title,
i {
color: #fff !important;
}
.el-dialog__headerbtn {
top: 8px !important;
}
}
}
.map-center-warp {
width: 100%;
text-align: center;

View File

@ -37,7 +37,7 @@ export default {
emits: ['click'],
setup(props, { emit }) {
const chartRef = ref(null);
const { setOptions, getInstance } = useEcharts(chartRef);
const { setOptions, getInstance, startAutoPlay } = useEcharts(chartRef);
const option = reactive({
tooltip: {
trigger: 'axis',
@ -97,6 +97,11 @@ export default {
option.series = props.isSeries && option.series.length > 0 ? option.series : seriesData;
option.xAxis.data = xAxisData;
setOptions(option);
startAutoPlay({
interval: 2000,
seriesIndex: 0,
showTooltip: true,
});
getInstance()?.off('click', onClick);
getInstance()?.on('click', onClick);
}

View File

@ -1,5 +1,5 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
<div ref="chartMap" :style="{ height, width }"></div>
</template>
<script>
import { ref, reactive, watch, watchEffect } from 'vue';
@ -42,13 +42,13 @@ export default {
},
emits: ['click'],
setup(props, { emit }) {
const chartRef = ref(null);
const { setOptions, getInstance, resize, registerMap, startAutoPlay } = useEcharts(chartRef);
const option = reactive({
// series: [],
});
const chartMap = ref(null);
const { setOptions, getInstance, resize, regMap, startAutoPlay, onMapClick } = useEcharts(chartMap);
const option = reactive({});
watchEffect(() => {
regMap(props.name, props.geo);
console.info('watchEffect');
props.chartData && initCharts();
});
@ -66,25 +66,22 @@ export default {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
// option.series = props.chartData;
setOptions(option);
onMapClick(({ name, data }) => {
console.info('onMapClick点击区域:', name);
console.info('onMapClick关联数据:', data);
emit('click', { name, data });
});
startAutoPlay({
interval: 2000,
seriesIndex: 0,
showTooltip: true,
});
registerMap(props.name, props.geo);
resize();
getInstance()?.off('click', onClick);
getInstance()?.on('click', onClick);
}
function onClick(params) {
console.info('地图onClick', params);
emit('click', params);
}
return { chartRef };
return { chartMap };
},
};
</script>

View File

@ -187,7 +187,7 @@ onUnmounted(() => {
.custom-rank-list {
overflow: hidden;
width: 100%;
height: 100%;
height: v-bind('h+"px"');
.row-item {
display: flex;
justify-content: center;

View File

@ -1,4 +1,4 @@
import { unref, nextTick, watch, computed, ref } from 'vue';
import { unref, nextTick, watch, computed, ref, markRaw } from 'vue';
import { useDebounceFn, tryOnUnmounted } from '@vueuse/core';
import { useTimeoutFn } from './useTimeout';
import { useEventListener } from './useEventListener';
@ -11,6 +11,8 @@ export const useEcharts = (elRef, theme = 'default') => {
const currentIndex = ref(-1);
const dataLength = ref(0);
let mapClickHandler = null;
// 新增方法 - 启动轮播
const startAutoPlay = (options = {}) => {
const {
@ -60,6 +62,7 @@ export const useEcharts = (elRef, theme = 'default') => {
const getDarkMode = computed(() => {
return theme === 'default' ? 'dark' : theme;
});
let chartInstance = null;
let resizeFn = resize;
const cacheOptions = ref({});
@ -89,7 +92,8 @@ export const useEcharts = (elRef, theme = 'default') => {
return;
}
chartInstance = echarts.init(el, t);
chartInstance = markRaw(echarts.init(el, t));
const { removeEvent } = useEventListener({
el: window,
name: 'resize',
@ -105,6 +109,35 @@ export const useEcharts = (elRef, theme = 'default') => {
});
}
function handleMapClick(params) {
console.info('handleMapClick', params);
// 过滤非地图区域的点击事件
if (params.seriesType === 'map3D' || params.seriesType === 'map') {
// 获取点击区域信息
const mapName = params.name;
const regionData = params.data || {};
console.info('seriesType', params.seriesType);
// 执行注册的回调函数
if (typeof mapClickHandler === 'function') {
mapClickHandler({
name: mapName,
data: regionData,
coordinates: params.event?.event?.point,
// originalParams: params
});
}
}
}
function onMapClick(handler) {
mapClickHandler = handler;
// 返回解绑方法
return () => {
mapClickHandler = null;
};
}
function setOptions(options = {}, clear = true) {
const mergedOptions = {
animation: true,
@ -130,8 +163,11 @@ export const useEcharts = (elRef, theme = 'default') => {
if (!chartInstance) return;
}
clear && chartInstance?.clear();
chartInstance?.setOption(unref(getOptions));
// 立即绑定事件
chartInstance.off('click');
chartInstance.on('click', handleMapClick);
}, 30);
});
}
@ -145,12 +181,16 @@ export const useEcharts = (elRef, theme = 'default') => {
* @param {string} mapName - 地图名称
* @param {object} geoJSON - GeoJSON 数据
*/
function registerMap(mapName, geoJSON) {
function regMap(mapName, geoJSON) {
if (!mapName || !geoJSON) {
console.warn('地图名称或 GeoJSON 数据无效');
return;
}
echarts.registerMap(mapName, geoJSON);
console.info('getMap', echarts.getMap(mapName));
if (!echarts.getMap(mapName)) {
echarts.registerMap(mapName, geoJSON, { override: true });
}
}
watch(
@ -167,6 +207,9 @@ export const useEcharts = (elRef, theme = 'default') => {
tryOnUnmounted(() => {
stopAutoPlay(); // 清理定时器
if (!chartInstance) return;
if (chartInstance) {
chartInstance.off('click', handleMapClick);
}
removeResizeFn();
chartInstance.dispose();
chartInstance = null;
@ -184,8 +227,9 @@ export const useEcharts = (elRef, theme = 'default') => {
resize,
echarts,
getInstance: () => chartInstance,
registerMap,
regMap,
startAutoPlay, // 暴露轮播方法
stopAutoPlay,
onMapClick,
};
};

View File

@ -31,6 +31,7 @@ import {
TimelineComponent,
CalendarComponent,
GraphicComponent,
GeoComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
@ -61,6 +62,7 @@ echarts.use([
GaugeChart,
ScatterChart,
EffectScatterChart,
GeoComponent,
]);
export default echarts;

View File

@ -18,6 +18,14 @@ const state = reactive({
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: 15,

View File

@ -7,7 +7,7 @@
<script setup>
import { ref } from 'vue';
const options = ref({
attr: { w: '100%', h: 200 },
attr: { w: '100%', h: 240 },
option: {
//
dataset: [
@ -25,20 +25,20 @@ const options = ref({
{ name: '大同', value: 51.11 },
],
type: 'column',
rowNum: 5,
rowNum: 6,
isAnimation: true,
waitTime: 2,
waitTime: 5,
unit: '万元',
sort: true,
height: 16,
height: 12,
color: 'linear-gradient(90deg,rgba(53,208,192,0.00), #35d0c0)',
textColor: '#fff',
borderRadius: '12px',
carousel: 'single',
indexPrefix: 'TOP',
indexFontSize: 12,
leftFontSize: 12,
rightFontSize: 12,
leftFontSize: 14,
rightFontSize: 14,
valueFormatter: (item) => {
return item.value;
},
@ -48,8 +48,6 @@ const options = ref({
<style scoped lang="scss">
.rank {
padding: 10px 20px;
height: 200px;
overflow: hidden;
&:deep(.row-item) {
.ranking-info {
@ -65,15 +63,20 @@ const options = ref({
.ranking-value {
color: #fe7f03 !important;
}
.inside-column {
background: linear-gradient(90deg, rgba(254, 127, 3, 0), #fe7f03) !important;
}
}
&:nth-child(2) {
.ranking-info {
color: #fef906 !important;
}
.ranking-value {
color: #fef906 !important;
}
.inside-column {
background: linear-gradient(90deg, rgba(254, 249, 6, 0), #fef906) !important;
}
}
&:nth-child(3) {
.ranking-info {
@ -82,6 +85,9 @@ const options = ref({
.ranking-value {
color: #02fd94 !important;
}
.inside-column {
background: linear-gradient(90deg, rgba(2, 253, 148, 0), #02fd94) !important;
}
}
}
}

View File

@ -24,7 +24,7 @@
</div>
</el-col>
<el-col :span="12">
<centerMap></centerMap>
<centerMap :dialog-title="'土地资源'"></centerMap>
</el-col>
<el-col :span="6" class="right-charts">
<div class="right-charts-item">