mirror of
https://github.com/HK560/R1DeltaServerList.git
synced 2026-08-01 00:12:00 -04:00
feat: beautify
This commit is contained in:
parent
825f15d551
commit
2682d8cfce
10 changed files with 155 additions and 70 deletions
|
|
@ -2,9 +2,9 @@
|
|||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/image/icon.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>泰坦陨落服务器列表</title>
|
||||
<title>R1Delta Server List</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/vue": "^2.0.18",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"axios": "^1.4.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "11",
|
||||
|
|
|
|||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
|
|
@ -9,7 +9,7 @@ importers:
|
|||
.:
|
||||
dependencies:
|
||||
'@heroicons/vue':
|
||||
specifier: ^2.0.18
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(vue@3.5.13(typescript@5.8.3))
|
||||
axios:
|
||||
specifier: ^1.4.0
|
||||
|
|
|
|||
BIN
public/image/icon.png
Normal file
BIN
public/image/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
public/image/wallpaper.jpg
Normal file
BIN
public/image/wallpaper.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 442 KiB |
186
src/App.vue
186
src/App.vue
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div class="min-h-screen bg-titanfall-primary text-white flex flex-col">
|
||||
<div class="min-h-screen bg-titanfall-primary text-white flex flex-col bg-cover bg-center bg-no-repeat bg-fixed" >
|
||||
<header class="bg-titanfall-secondary shadow-lg">
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-3xl font-bold text-titanfall-accent">
|
||||
<h1 class="text-3xl font-bold text-titanfall-highlight flex items-center gap-3">
|
||||
<img src="/image/icon.png" alt="Site Icon" class="w-12 h-12" />
|
||||
{{ t('serverList.title') }}
|
||||
</h1>
|
||||
<div class="flex gap-2">
|
||||
|
|
@ -63,61 +64,78 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div
|
||||
v-for="server in filteredServers"
|
||||
:key="`${server.ip}:${server.port}`"
|
||||
class="bg-titanfall-secondary rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-shadow flex flex-col h-[500px]"
|
||||
>
|
||||
<div class="relative h-48 flex-shrink-0">
|
||||
<img
|
||||
:src="getMapImage(server.map_name)"
|
||||
:alt="server.map_name"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-4">
|
||||
<h2 class="text-xl font-bold">{{ server.host_name }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-4 flex-grow flex flex-col">
|
||||
<div class="space-y-2">
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.players') }}:</span>
|
||||
<span>{{ server.total_players }}/{{ server.max_players }}</span>
|
||||
</p>
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.map') }}:</span>
|
||||
<span>{{ t(`serverList.map.${server.map_name}`) }}</span>
|
||||
</p>
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.mode') }}:</span>
|
||||
<span>{{ t(`serverList.filter.${server.game_mode}`) }}</span>
|
||||
</p>
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.version') }}:</span>
|
||||
<span>{{ server.version }}</span>
|
||||
</p>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-gray-400">服务器地址</span>
|
||||
<div class="flex items-center bg-gray-800 rounded-lg p-2 flex-shrink-0">
|
||||
<span class="text-sm font-mono mr-2">{{ server.ip }}:{{ server.port }}</span>
|
||||
<button
|
||||
@click="copyConnectCommand(server)"
|
||||
class="px-3 py-1 bg-titanfall-accent hover:bg-titanfall-accent/80 rounded transition-colors text-sm"
|
||||
>
|
||||
{{ copiedServer === `${server.ip}:${server.port}` ? '已复制' : '复制' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-y-auto flex-grow border-t border-gray-700 py-2">
|
||||
<p class="text-sm text-gray-400">{{ server.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="isLoading" class="flex justify-center items-center min-h-[400px]">
|
||||
<div class="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-titanfall-accent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<TransitionGroup name="fade-list">
|
||||
<div
|
||||
v-for="server in filteredServers"
|
||||
:key="`${server.ip}:${server.port}`"
|
||||
class="bg-titanfall-secondary rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-shadow flex flex-col h-[450px]"
|
||||
>
|
||||
<div class="relative h-48 flex-shrink-0">
|
||||
<img
|
||||
:src="getMapImage(server.map_name)"
|
||||
:alt="server.map_name"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
<div class="absolute top-2 right-2 flex items-center px-3 py-1 rounded-full shadow-lg"
|
||||
:class="{
|
||||
'bg-gray-600': server.total_players === 0,
|
||||
'bg-green-600': server.total_players > 0 && server.total_players < server.max_players,
|
||||
'bg-red-600': server.total_players === server.max_players
|
||||
}">
|
||||
<UserGroupIcon class="w-5 h-5 mr-1" />
|
||||
<span class="font-bold">{{ server.total_players }}/{{ server.max_players }}</span>
|
||||
</div>
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-4">
|
||||
<h2 class="text-xl font-bold">{{ server.host_name }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-4 flex-grow flex flex-col">
|
||||
<div class="space-y-2">
|
||||
<!-- <p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.players') }}:</span>
|
||||
<span>{{ server.total_players }}/{{ server.max_players }}</span>
|
||||
</p> -->
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.map') }}:</span>
|
||||
<span>{{ t(`serverList.map.${server.map_name}`) }}</span>
|
||||
</p>
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.mode') }}:</span>
|
||||
<span>{{ t(`serverList.filter.${server.game_mode}`) }}</span>
|
||||
</p>
|
||||
<p class="flex justify-between">
|
||||
<span class="text-gray-400">{{ t('serverList.status.version') }}:</span>
|
||||
<span>{{ server.version }}</span>
|
||||
</p>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-gray-400">{{ t('serverList.status.address') }}</span>
|
||||
<div class="flex items-center bg-gray-800 rounded-lg p-2 flex-shrink-0">
|
||||
<span class="text-sm font-mono mr-2">{{ server.ip }}:{{ server.port }}</span>
|
||||
<button
|
||||
@click="copyConnectCommand(server)"
|
||||
class="px-3 py-1 bg-titanfall-accent hover:bg-titanfall-accent/80 rounded transition-colors text-sm"
|
||||
>
|
||||
{{ copiedServer === `${server.ip}:${server.port}` ? t('serverList.button.copied') : t('serverList.button.copy') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-y-auto max-h-[80px] flex-grow border-t border-gray-700 py-2">
|
||||
<p class="text-sm text-gray-400">{{ server.description === "" ? t('serverList.message.nodescription') : server.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</Transition>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
|
|
@ -130,6 +148,7 @@ import axios from 'axios'
|
|||
import type { Server, GameMode, GameModeOption } from './types'
|
||||
import { mapImageMapping } from './data/data'
|
||||
import Footer from './components/Footer.vue'
|
||||
import { UserGroupIcon } from '@heroicons/vue/24/solid'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
|
|
@ -137,6 +156,7 @@ const servers = ref<Server[]>([])
|
|||
const searchQuery = ref('')
|
||||
const selectedMode = ref<GameMode>('all')
|
||||
const copiedServer = ref('')
|
||||
const isLoading = ref(true)
|
||||
|
||||
const gameModes: GameModeOption[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
|
|
@ -161,15 +181,18 @@ const filteredServers = computed(() => {
|
|||
|
||||
const fetchServers = async (): Promise<void> => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const response = await axios.get<Server[]>('/r1d-api/servers')
|
||||
servers.value = response.data
|
||||
} catch (error) {
|
||||
console.error('Error fetching servers:', error)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getMapImage = (mapName: string): string => {
|
||||
return mapImageMapping[mapName.toLowerCase()] || '/image/maps/default_map.jpg'
|
||||
return mapImageMapping[mapName.toLowerCase()] || '/image/maps/Menu_Background.jpg'
|
||||
}
|
||||
|
||||
const copyConnectCommand = async (server: Server): Promise<void> => {
|
||||
|
|
@ -187,8 +210,8 @@ const copyConnectCommand = async (server: Server): Promise<void> => {
|
|||
|
||||
onMounted(() => {
|
||||
fetchServers()
|
||||
// 每30秒更新一次服务器列表
|
||||
setInterval(fetchServers, 30000)
|
||||
// 每1分钟更新一次服务器列表
|
||||
setInterval(fetchServers, 60000)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -196,4 +219,49 @@ onMounted(() => {
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
.overflow-y-auto::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-list-enter-active,
|
||||
.fade-list-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-list-enter-from,
|
||||
.fade-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.fade-list-move {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
export default {
|
||||
serverList: {
|
||||
title: 'Titanfall Server List',
|
||||
title: 'R1Delta Server List',
|
||||
search: 'Search servers...',
|
||||
filter: {
|
||||
all: 'All',
|
||||
at: 'Attrition',
|
||||
campaign: 'Campaign',
|
||||
coop: 'Co-op',
|
||||
coop: 'Frontier Defense',
|
||||
ctf: 'Capture the Flag',
|
||||
lts: 'Last Titan Standing',
|
||||
mfdp: 'Marked for Death Pro',
|
||||
|
|
@ -45,7 +45,15 @@ export default {
|
|||
maxPlayers: 'Max Players',
|
||||
map: 'Map',
|
||||
mode: 'Mode',
|
||||
version: 'Version'
|
||||
version: 'Version',
|
||||
address: 'Address'
|
||||
},
|
||||
button: {
|
||||
copy: 'Copy',
|
||||
copied: 'Copied'
|
||||
},
|
||||
message: {
|
||||
nodescription: 'No description'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import en from './en'
|
|||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
locale: 'zh',
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
messages: {
|
||||
zh,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
serverList: {
|
||||
title: 'R1Delta 服务器列表(本页面开发中)',
|
||||
title: 'R1Delta 服务器列表',
|
||||
search: '搜索服务器...',
|
||||
filter: {
|
||||
all: '全部',
|
||||
|
|
@ -45,7 +45,15 @@ export default {
|
|||
maxPlayers: '最大玩家数',
|
||||
map: '地图',
|
||||
mode: '模式',
|
||||
version: '版本'
|
||||
version: '版本',
|
||||
address: '地址'
|
||||
},
|
||||
button: {
|
||||
copy: '复制',
|
||||
copied: '已复制'
|
||||
},
|
||||
message: {
|
||||
nodescription: '此服务器无描述🫡'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ module.exports = {
|
|||
'titanfall': {
|
||||
'primary': '#1a1a1a',
|
||||
'secondary': '#2d2d2d',
|
||||
'accent': '#ff4655'
|
||||
'accent': '#9A333E',
|
||||
'highlight': '#FF4655'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue