nazhua/src/views/home.vue
2024-12-03 15:14:56 +00:00

306 lines
6.2 KiB
Vue

<template>
<div class="index-container">
<div class="scroll-container">
<div
class="world-map-box"
>
<world-map
v-if="showWorldMap"
:locations="serverLocations || []"
:width="worldMapWidth"
/>
</div>
<div
v-if="showFilter"
class="fitler-group"
>
<div class="left-box">
<server-option-box
v-if="showTag && tagOptions.length"
v-model="filterFormData.tag"
:options="tagOptions"
/>
</div>
<div class="right-box">
<server-option-box
v-if="onlineOptions.length"
v-model="filterFormData.online"
:options="onlineOptions"
/>
</div>
</div>
<transition-group
name="list"
tag="div"
class="server-list-container"
>
<server-item
v-for="item in filterServerList"
:key="item.ID"
:info="item"
/>
</transition-group>
</div>
</div>
</template>
<script setup>
/**
* 首页
*/
import {
ref,
computed,
onMounted,
onUnmounted,
} from 'vue';
import {
useStore,
} from 'vuex';
import config from '@/config';
import {
alias2code,
locationCode2Info,
count2size,
} from '@/utils/world-map-location';
import uuid from '@/utils/uuid';
import WorldMap from '@/components/world-map/world-map.vue';
import ServerOptionBox from './components/server-list/server-option-box.vue';
import ServerItem from './components/server-list/server-list-item.vue';
const store = useStore();
const worldMapWidth = ref();
const showFilter = config.nazhua.hideFilter !== true;
const filterFormData = ref({
tag: '',
online: '',
});
// 是否显示标签
const showTag = config.nazhua.hideTag !== true;
// 服务器列表
const serverList = computed(() => store.state.serverList);
// 服务器总数
const serverCount = computed(() => store.state.serverCount);
/**
* 解构数据
*/
const serverListData = computed(() => {
const tagMap = {};
serverList.value.forEach((i) => {
if (i.Tag) {
if (!tagMap[i.Tag]) {
tagMap[i.Tag] = 0;
}
tagMap[i.Tag] += 1;
}
});
const tags = [];
Object.entries(tagMap).forEach(([tag, count]) => {
tags.push({
tag,
count,
});
});
return {
tags,
};
});
const tagOptions = computed(() => (serverListData.value?.tags || []).map((i) => ({
key: uuid(),
label: i.tag,
value: i.tag,
})));
const onlineOptions = computed(() => {
if (serverCount.value?.total !== serverCount.value?.online) {
return [{
key: 'online',
label: '在线',
value: '1',
}, {
key: 'offline',
label: '离线',
value: '-1',
}];
}
return [];
});
const filterServerList = computed(() => serverList.value.filter((i) => {
const isFilterArr = [];
if (filterFormData.value.tag) {
isFilterArr.push(i.Tag === filterFormData.value.tag);
}
if (filterFormData.value.online) {
isFilterArr.push(i.online === (filterFormData.value.online * 1));
}
return isFilterArr.length ? isFilterArr.every((o) => o) : true;
}));
/**
* 解构服务器列表的位置数据
*/
const serverLocations = computed(() => {
const locationMap = {};
filterServerList.value.forEach((i) => {
if (i.online === -1) {
return;
}
let aliasCode;
let locationCode;
if (i?.PublicNote?.customData?.location) {
aliasCode = i.PublicNote.customData.location;
locationCode = i.PublicNote.customData.location;
} else if (i?.Host?.CountryCode) {
aliasCode = i.Host.CountryCode.toUpperCase();
}
const code = alias2code(aliasCode) || locationCode;
if (code) {
if (!locationMap[code]) {
locationMap[code] = 0;
}
locationMap[code] += 1;
}
});
const locations = [];
Object.entries(locationMap).forEach(([code, count]) => {
const {
x,
y,
name,
} = locationCode2Info(code) || {};
if (x && y) {
locations.push({
key: code,
x,
y,
code,
size: count2size(count),
label: `${name},${count}`,
});
}
});
return locations;
});
const showWorldMap = computed(() => {
if (config.nazhua?.hideWorldMap) {
return false;
}
if (config.nazhua?.hideHomeWorldMap) {
return false;
}
if (serverList.value.length > 0 && serverLocations.value.length === 0) {
return false;
}
return true;
});
/**
* 处理窗口大小变化
*/
function handleResize() {
worldMapWidth.value = document.querySelector('.server-list-container').clientWidth - 40;
}
onMounted(() => {
handleResize();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style lang="scss" scoped>
.index-container {
width: 100%;
height: 100%;
--list-padding: 20px;
--list-gap-size: 20px;
--list-item-num: 3;
--list-item-width: calc(
(
var(--list-container-width)
- (var(--list-padding) * 2)
- (
var(--list-gap-size)
* (var(--list-item-num) - 1)
)
)
/ var(--list-item-num)
);
.scroll-container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 20px 0;
}
.world-map-box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.server-list-container {
position: relative;
display: flex;
flex-wrap: wrap;
gap: var(--list-gap-size);
padding: 0 var(--list-padding);
width: var(--list-container-width);
margin: auto;
}
// 针对1440px以下的屏幕
@media screen and (max-width: 1440px) {
--list-gap-size: 10px;
}
@media screen and (max-width: 1024px) {
--list-item-num: 2;
}
@media screen and (max-width: 680px) {
--list-item-num: 1;
}
}
.fitler-group {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 10px 20px;
width: var(--list-container-width);
margin: auto;
}
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.3s ease;
}
.list-enter-from {
opacity: 0;
transform: translateY(-30px);
}
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-leave-active {
position: absolute;
}
</style>