visitor-map.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <div class="visitor-trend">
  3. <div ref="chartRef" style="width: 100%; min-height: 300px"></div>
  4. <div style="position: absolute; top: 20px; right: 0">
  5. <el-select v-model="selectedCountry" style="width: 160px" @change="initChart">
  6. <el-option v-for="item in countryOptions" :key="item.value" :label="item.label" :value="item.value" />
  7. </el-select>
  8. </div>
  9. </div>
  10. </template>
  11. <script setup lang="ts">
  12. import { ref, onMounted, onUnmounted } from 'vue';
  13. import * as echarts from 'echarts/core';
  14. import { MapChart } from 'echarts/charts';
  15. import { TooltipComponent, GeoComponent } from 'echarts/components';
  16. import { CanvasRenderer } from 'echarts/renderers';
  17. echarts.use([MapChart, TooltipComponent, GeoComponent, CanvasRenderer]);
  18. const chartRef = ref<HTMLElement | null>(null);
  19. let chartInstance: echarts.ECharts | null = null;
  20. const countryOptions = [
  21. { value: 'world', label: '世界' },
  22. { value: 'usa', label: '美国' },
  23. { value: 'china', label: '中国' },
  24. { value: 'vietnam', label: '越南' },
  25. { value: 'thailand', label: '泰国' },
  26. { value: 'malaysia', label: '马来西亚' },
  27. { value: 'singapore', label: '新加坡' },
  28. { value: 'indonesia', label: '印度尼西亚' },
  29. { value: 'philippines', label: '菲律宾' },
  30. { value: 'cambodia', label: '柬埔寨' },
  31. { value: 'laos', label: '老挝' },
  32. { value: 'myanmar', label: '缅甸' },
  33. { value: 'brunei', label: '文莱' },
  34. ];
  35. const selectedCountry = ref('world');
  36. // 动态 import 映射
  37. const countryImportMap: Record<string, () => Promise<any>> = {
  38. world: () => import('echarts-countries-js/echarts-countries-js/world.js'),
  39. usa: () => import('echarts-countries-js/echarts-countries-js/USA.js'),
  40. china: () => import('echarts-countries-js/echarts-countries-js/China.js'),
  41. vietnam: () => import('echarts-countries-js/echarts-countries-js/Vietnam.js'),
  42. thailand: () => import('echarts-countries-js/echarts-countries-js/Thailand.js'),
  43. malaysia: () => import('echarts-countries-js/echarts-countries-js/Malaysia.js'),
  44. singapore: () => import('echarts-countries-js/echarts-countries-js/Singapore.js'),
  45. indonesia: () => import('echarts-countries-js/echarts-countries-js/Indonesia.js'),
  46. philippines: () => import('echarts-countries-js/echarts-countries-js/Philippines.js'),
  47. cambodia: () => import('echarts-countries-js/echarts-countries-js/Cambodia.js'),
  48. laos: () => import('echarts-countries-js/echarts-countries-js/Laos.js'),
  49. myanmar: () => import('echarts-countries-js/echarts-countries-js/Myanmar.js'),
  50. brunei: () => import('echarts-countries-js/echarts-countries-js/Brunei.js'),
  51. };
  52. async function initChart() {
  53. if (!chartRef.value) return;
  54. if (chartInstance) {
  55. chartInstance.dispose();
  56. }
  57. chartInstance = echarts.init(chartRef.value);
  58. const countryKey = selectedCountry.value;
  59. const importFn = countryImportMap[countryKey];
  60. console.log(importFn);
  61. if (!importFn) {
  62. chartInstance.setOption({
  63. title: { text: '地图数据未找到', left: 'center', top: 'center' },
  64. });
  65. return;
  66. }
  67. let geoJson;
  68. try {
  69. const mod = await importFn();
  70. geoJson = mod.default || mod;
  71. } catch (e) {
  72. chartInstance.setOption({
  73. title: { text: '地图数据加载失败', left: 'center', top: 'center' },
  74. });
  75. return;
  76. }
  77. echarts.registerMap(countryKey, geoJson);
  78. const option = {
  79. tooltip: {
  80. trigger: 'item',
  81. formatter: '{b}',
  82. },
  83. geo: {
  84. map: countryKey,
  85. roam: true,
  86. emphasis: {
  87. itemStyle: {
  88. areaColor: '#a3d5ff',
  89. },
  90. },
  91. },
  92. series: [
  93. {
  94. type: 'map',
  95. map: countryKey,
  96. roam: true,
  97. label: {
  98. show: true,
  99. },
  100. itemStyle: {
  101. areaColor: '#e0f7fa',
  102. borderColor: '#333',
  103. borderWidth: 1
  104. },
  105. emphasis: {
  106. label: { show: true },
  107. itemStyle: { areaColor: '#a3d5ff' }
  108. },
  109. data: [],
  110. },
  111. ],
  112. };
  113. chartInstance.setOption(option);
  114. window.addEventListener('resize', handleResize);
  115. }
  116. const handleResize = () => {
  117. if (chartInstance) {
  118. chartInstance.resize();
  119. }
  120. };
  121. onMounted(() => {
  122. initChart();
  123. });
  124. onUnmounted(() => {
  125. if (chartInstance) {
  126. window.removeEventListener('resize', handleResize);
  127. chartInstance.dispose();
  128. chartInstance = null;
  129. }
  130. });
  131. </script>
  132. <style scoped lang="scss">
  133. .visitor-trend {
  134. position: relative;
  135. width: 100%;
  136. min-height: 300px;
  137. }
  138. </style>