diff --git a/src/components/world-map/world-map-point.vue b/src/components/world-map/world-map-point.vue
index e500d2d..4755eb3 100644
--- a/src/components/world-map/world-map-point.vue
+++ b/src/components/world-map/world-map-point.vue
@@ -2,6 +2,7 @@
diff --git a/src/components/world-map/world-map.vue b/src/components/world-map/world-map.vue
index f8ce7fc..50c8281 100644
--- a/src/components/world-map/world-map.vue
+++ b/src/components/world-map/world-map.vue
@@ -37,10 +37,14 @@
import {
ref,
computed,
+ watch,
} from 'vue';
import validate from '@/utils/validate';
import WorldMapPoint from './world-map-point.vue';
+import {
+ findIntersectingGroups,
+} from '@/utils/world-map';
const props = defineProps({
width: {
@@ -100,16 +104,75 @@ const mapStyle = computed(() => {
return style;
});
-const mapPoints = computed(() => props.locations.map((i) => {
- const item = {
- key: i.key,
- left: (computedSize.value.width / 1280) * i.x,
- top: (computedSize.value.height / 621) * i.y,
- size: i.size || 4,
- label: i.label,
- };
- return item;
-}));
+const mapPoints = ref([]);
+let computeMapPointsTimer = null;
+function computeMapPoints() {
+ if (computeMapPointsTimer) {
+ clearTimeout(computeMapPointsTimer);
+ }
+ if (props.locations.length === 0) {
+ mapPoints.value = [];
+ return;
+ }
+ computeMapPointsTimer = setTimeout(() => {
+ const points = props.locations.map((i) => {
+ const item = {
+ key: i.key,
+ left: (computedSize.value.width / 1280) * i.x,
+ top: (computedSize.value.height / 621) * i.y,
+ size: i.size || 4,
+ label: i.label,
+ servers: i.servers,
+ type: 'single',
+ };
+ const halfSize = (item.size + 8) / 2;
+ item.topLeft = {
+ left: item.left - halfSize,
+ top: item.top - halfSize,
+ };
+ item.bottomRight = {
+ left: item.left + halfSize,
+ top: item.top + halfSize,
+ };
+ return item;
+ });
+ const groups = findIntersectingGroups(points);
+ Object.entries(groups).forEach(([key, group]) => {
+ const item = points.find((i) => i.key === key);
+ if (item.parent) {
+ return;
+ }
+ item.size = 4;
+ item.type = 'group';
+ item.children = group;
+ let label = item.label || '';
+ let servers = [...(item.servers || [])];
+ group.forEach((i) => {
+ if (!i.parent && !i.children) {
+ i.parent = item;
+ label += `\n${i.label}`;
+ servers = servers.concat((i.servers || []));
+ }
+ });
+ item.label = label;
+ item.servers = servers;
+ });
+ mapPoints.value = points.filter((i) => !i.parent);
+ }, 100);
+}
+
+watch(() => props.locations, () => {
+ computeMapPoints();
+}, {
+ immediate: true,
+});
+
+watch(() => computedSize.value, () => {
+ computeMapPoints();
+}, {
+ immediate: true,
+ deep: true,
+});
/**
* 提示框
@@ -125,7 +188,7 @@ const tipsContentStyle = computed(() => {
if (window.innerWidth > 500) {
style.top = `${activeTipsXY.value.y}px`;
style.left = `${activeTipsXY.value.x}px`;
- style.transform = 'translate(-50%, 100%)';
+ style.transform = 'translate(-50%, 20px)';
} else {
style.bottom = '10px';
style.left = '50%';
@@ -133,18 +196,18 @@ const tipsContentStyle = computed(() => {
}
return style;
});
-let timer = null;
+let handlePointTapTimer = null;
function handlePointTap(e) {
tipsContent.value = e.label;
activeTipsXY.value = {
- x: e.left - (e.size / 2),
- y: e.top - e.size,
+ x: e.left,
+ y: e.top - 10,
};
tipsShow.value = true;
- if (timer) {
- clearTimeout(timer);
+ if (handlePointTapTimer) {
+ clearTimeout(handlePointTapTimer);
}
- timer = setTimeout(() => {
+ handlePointTapTimer = setTimeout(() => {
tipsShow.value = false;
}, 5000);
}
@@ -173,6 +236,20 @@ function handlePointTap(e) {
background: rgba(#000, 0.8);
box-shadow: 1px 4px 8px rgba(#303841, 0.4);
z-index: 100;
+ white-space: pre;
+
+ // 向上的尖角
+ &::before {
+ content: '';
+ position: absolute;
+ bottom: 100%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border: 5px solid transparent;
+ border-bottom-color: rgba(#000, 0.8);
+ transform: translateX(-50%);
+ }
}
}
diff --git a/src/utils/world-map-location.js b/src/utils/world-map-location.js
deleted file mode 100644
index e154314..0000000
--- a/src/utils/world-map-location.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import config from '@/config';
-import CODE_MAPS, {
- countryCodeMapping,
- aliasMapping,
-} from '@/data/code-maps';
-
-export const ALIAS_CODE = {
- ...aliasMapping,
- ...countryCodeMapping,
-};
-
-export const alias2code = (code) => ALIAS_CODE[code];
-
-export const locationCode2Info = (code) => {
- const maps = {
- ...CODE_MAPS,
- ...(config.nazhua.customCodeMap || {}),
- };
- let info = maps[code];
- const aliasCode = aliasMapping[code];
- if (!info && aliasCode) {
- info = maps[aliasCode];
- }
- return info;
-};
-
-export const count2size = (count) => {
- if (count < 3) {
- return 4;
- }
- if (count < 5) {
- return 6;
- }
- return 8;
-};
diff --git a/src/utils/world-map.js b/src/utils/world-map.js
new file mode 100644
index 0000000..9dd9deb
--- /dev/null
+++ b/src/utils/world-map.js
@@ -0,0 +1,61 @@
+import config from '@/config';
+import CODE_MAPS, {
+ countryCodeMapping,
+ aliasMapping,
+} from '@/data/code-maps';
+
+export const ALIAS_CODE = {
+ ...aliasMapping,
+ ...countryCodeMapping,
+};
+
+export const alias2code = (code) => ALIAS_CODE[code];
+
+export const locationCode2Info = (code) => {
+ const maps = {
+ ...CODE_MAPS,
+ ...(config.nazhua.customCodeMap || {}),
+ };
+ let info = maps[code];
+ const aliasCode = aliasMapping[code];
+ if (!info && aliasCode) {
+ info = maps[aliasCode];
+ }
+ return info;
+};
+
+export const count2size = (count) => {
+ if (count < 3) {
+ return 4;
+ }
+ if (count < 5) {
+ return 6;
+ }
+ return 8;
+};
+
+export function findIntersectingGroups(coordinates) {
+ const groups = {};
+
+ coordinates.forEach((coordinate, index) => {
+ const intersects = [];
+ const n = 2;
+ coordinates.forEach((otherCoordinate, otherIndex) => {
+ if (index !== otherIndex) {
+ if (
+ coordinate.topLeft.top - otherCoordinate.bottomRight.top < n
+ && coordinate.topLeft.left - otherCoordinate.bottomRight.left < n
+ && coordinate.bottomRight.top - otherCoordinate.topLeft.top > -n
+ && coordinate.bottomRight.left - otherCoordinate.topLeft.left > -n
+ ) {
+ intersects.push(otherCoordinate);
+ }
+ }
+ });
+ if (intersects.length > 0) {
+ groups[coordinate.key] = intersects;
+ }
+ });
+
+ return groups;
+}
diff --git a/src/views/detail.vue b/src/views/detail.vue
index 3b11eb3..6f6c77c 100644
--- a/src/views/detail.vue
+++ b/src/views/detail.vue
@@ -53,7 +53,7 @@ import config from '@/config';
import {
alias2code,
locationCode2Info,
-} from '@/utils/world-map-location';
+} from '@/utils/world-map';
import WorldMap from '@/components/world-map/world-map.vue';
import ServerName from './components/server-detail/server-name.vue';
@@ -99,6 +99,7 @@ const locations = computed(() => {
code,
size: 4,
label: `${name}`,
+ servers: [info.value],
});
}
return arr;
diff --git a/src/views/home.vue b/src/views/home.vue
index b25eaf0..b70f11d 100644
--- a/src/views/home.vue
+++ b/src/views/home.vue
@@ -64,7 +64,7 @@ import {
alias2code,
locationCode2Info,
count2size,
-} from '@/utils/world-map-location';
+} from '@/utils/world-map';
import uuid from '@/utils/uuid';
import WorldMap from '@/components/world-map/world-map.vue';
@@ -149,13 +149,13 @@ const serverLocations = computed(() => {
const code = alias2code(aliasCode) || locationCode;
if (code) {
if (!locationMap[code]) {
- locationMap[code] = 0;
+ locationMap[code] = [];
}
- locationMap[code] += 1;
+ locationMap[code].push(i);
}
});
const locations = [];
- Object.entries(locationMap).forEach(([code, count]) => {
+ Object.entries(locationMap).forEach(([code, servers]) => {
const {
x,
y,
@@ -167,8 +167,9 @@ const serverLocations = computed(() => {
x,
y,
code,
- size: count2size(count),
- label: `${name},${count}台`,
+ size: count2size(servers.length),
+ label: `${name},${servers.length}台`,
+ servers,
});
}
});