This commit is contained in:
zyb
2025-05-06 19:57:50 +08:00
parent 56d8d7a345
commit 062550d126
22 changed files with 14472 additions and 0 deletions

178
src/views/MapDetail.vue Normal file
View File

@@ -0,0 +1,178 @@
<template>
<div class="map-detail" v-if="map">
<div class="back-button">
<button @click="goBack" class="back-btn">
<span class="back-icon"></span> 返回列表
</button>
</div>
<div class="map-header">
<h1>{{ map.chinese_name }}</h1>
<p class="author">作者: {{ map.user }}</p>
</div>
<div class="map-content">
<div class="map-image">
<img :src="map.img_file" :alt="map.chinese_name">
</div>
<div class="map-info">
<div class="info-item">
<h3>基本信息</h3>
<p>下载次数: {{ map.download_count }}</p>
<p>收藏次数: {{ map.favourite_count }}</p>
<p>玩家数量: {{ map.player_count }}</p>
<p>创建时间: {{ formatDate(map.create_time) }}</p>
</div>
<div class="tags">
<h3>标签</h3>
<div class="tag-list">
<span v-for="tag in map.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
</div>
<div class="actions">
<a :href="map.zip_file" class="download-btn" download>下载地图</a>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getMapDetail } from '../api/maps'
const route = useRoute()
const router = useRouter()
const map = ref(null)
const fetchMapDetail = async () => {
try {
map.value = await getMapDetail(route.params.id)
} catch (error) {
console.error('获取地图详情失败:', error)
}
}
const goBack = () => {
router.back()
}
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('zh-CN')
}
onMounted(() => {
fetchMapDetail()
})
</script>
<style scoped>
.map-detail {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.back-button {
margin-bottom: 20px;
}
.back-btn {
display: inline-flex;
align-items: center;
padding: 8px 16px;
background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 6px;
color: #333;
cursor: pointer;
transition: all 0.2s ease;
font-size: 14px;
}
.back-btn:hover {
background: #e9ecef;
border-color: #d0d0d0;
}
.back-icon {
margin-right: 6px;
font-size: 16px;
}
.map-header {
margin-bottom: 30px;
}
.map-header h1 {
margin: 0;
color: #333;
}
.author {
color: #666;
margin-top: 5px;
}
.map-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.map-image img {
width: 100%;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.map-info {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
.info-item {
margin-bottom: 20px;
}
.info-item h3 {
margin-bottom: 10px;
color: #333;
}
.tags {
margin-bottom: 20px;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.tag {
background: #e9ecef;
padding: 5px 10px;
border-radius: 15px;
font-size: 14px;
color: #495057;
}
.download-btn {
display: inline-block;
padding: 10px 20px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
transition: background 0.2s;
}
.download-btn:hover {
background: #0056b3;
}
</style>

257
src/views/Maps.vue Normal file
View File

@@ -0,0 +1,257 @@
<template>
<div class="maps">
<div class="page-header">
<h1>热门下载地图</h1>
</div>
<div class="filters">
<div class="search-box">
<input
type="text"
placeholder="搜索地图..."
v-model="searchValue"
@input="handleSearchInput"
@blur="handleSearchBlur"
>
</div>
<select v-model="playerCountFilter" class="filter-select" @change="handleFilterChange">
<option value="">玩家数</option>
<option v-for="n in [2,3,4,5,6,7,8]" :key="n" :value="n">{{ n }}</option>
</select>
<select v-model="tagFilter" class="filter-select" @change="handleFilterChange">
<option value="">分类</option>
<option v-for="tag in tagOptions" :key="tag" :value="tag">{{ tag }}</option>
</select>
<select v-model="selectedOrder" class="filter-select" @change="handleFilterChange">
<option v-for="opt in orderOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
</select>
</div>
<div class="table-container">
<table class="maps-table">
<thead>
<tr>
<th>预览图</th>
<th>地图名称</th>
<th>作者</th>
<th>下载次数</th>
<th>收藏次数</th>
<th>玩家数量</th>
<th>创建时间</th>
<th>标签</th>
</tr>
</thead>
<tbody>
<tr v-for="map in maps" :key="map.id" @click="goToMapDetail(map.id)" class="table-row">
<td class="preview-cell">
<img :src="map.thumbnail" :alt="map.chinese_name">
</td>
<td class="map-name">{{ map.chinese_name }}</td>
<td>{{ map.user }}</td>
<td>{{ map.download_count }}</td>
<td>{{ map.favourite_count }}</td>
<td>{{ map.player_count }}</td>
<td>{{ formatDate(map.create_time) }}</td>
<td>
<div class="tags">
<span v-for="tag in map.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页控件 -->
<div class="pagination">
<button
class="page-btn"
:disabled="currentPage === 1"
@click="changePage(currentPage - 1)"
>
上一页
</button>
<div class="page-numbers">
<template v-for="(page, index) in displayedPages" :key="index">
<button
v-if="typeof page === 'number'"
class="page-number"
:class="{ active: page === currentPage }"
@click="changePage(page)"
>
{{ page }}
</button>
<span v-else class="page-ellipsis">{{ page }}</span>
</template>
</div>
<button
class="page-btn"
:disabled="!hasNextPage"
@click="changePage(currentPage + 1)"
>
下一页
</button>
<div class="page-jump">
<input
type="number"
v-model="jumpPage"
:min="1"
:max="totalPages"
class="jump-input"
@keyup.enter="handleJumpPage"
>
<button class="jump-btn" @click="handleJumpPage">跳转</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { getMaps, getAllTags } from '../api/maps'
import '../assets/styles/common.css'
import '../assets/styles/Maps.css'
const router = useRouter()
const route = useRoute()
const maps = ref([])
const currentPage = ref(1)
const totalPages = ref(1)
const hasNextPage = ref(false)
const jumpPage = ref('')
// 排序和筛选相关
const orderOptions = [
{ label: '下载量降序', value: '-download_count' },
{ label: '下载量升序', value: 'download_count' },
{ label: '收藏数降序', value: '-favourite_count' },
{ label: '收藏数升序', value: 'favourite_count' },
{ label: '创建时间降序', value: '-create_time' },
{ label: '创建时间升序', value: 'create_time' }
]
const selectedOrder = ref('-download_count')
const searchValue = ref('')
const playerCountFilter = ref('')
const tagFilter = ref('')
const tagOptions = ref([])
// 计算显示的页码
const displayedPages = computed(() => {
const pages = []
const total = totalPages.value
const current = currentPage.value
if (total <= 5) {
for (let i = 1; i <= total; i++) {
pages.push(i)
}
} else if (current <= 3) {
pages.push(1, 2, 3, 4, '...', total)
} else if (current >= total - 2) {
pages.push(1, '...')
for (let i = total - 2; i <= total; i++) {
if (i > 1) pages.push(i)
}
} else {
pages.push(1, '...', current - 1, current, current + 1, '...', total)
}
return pages
})
// 获取地图数据
const fetchMaps = async (page = 1) => {
try {
const params = {
page,
search: searchValue.value,
player_count: playerCountFilter.value,
tags: tagFilter.value,
ordering: selectedOrder.value
}
console.log('正在获取地图数据,参数:', params)
const data = await getMaps(params)
maps.value = data.results
hasNextPage.value = !!data.next
totalPages.value = Math.ceil(data.count / 20)
currentPage.value = page
console.log('分页状态:', {
currentPage: currentPage.value,
totalPages: totalPages.value,
hasNextPage: hasNextPage.value,
resultsCount: data.results.length
})
} catch (error) {
console.error('获取地图列表失败:', error)
}
}
// 处理搜索输入
const handleSearchInput = () => {
if (!searchValue.value) {
fetchMaps(1)
}
}
// 处理搜索框失去焦点
const handleSearchBlur = () => {
if (searchValue.value) {
fetchMaps(1)
}
}
// 处理筛选条件变化
const handleFilterChange = () => {
console.log('筛选条件变化:', {
playerCount: playerCountFilter.value,
tag: tagFilter.value,
order: selectedOrder.value
})
fetchMaps(1)
}
// 处理页码跳转
const handleJumpPage = () => {
const page = parseInt(jumpPage.value)
if (page && page >= 1 && page <= totalPages.value) {
changePage(page)
}
jumpPage.value = ''
}
// 切换页码
const changePage = (page) => {
console.log('尝试切换页码:', page, '当前页码:', currentPage.value, '总页数:', totalPages.value)
if (page < 1 || page > totalPages.value) {
console.log('页码无效:', page)
return
}
currentPage.value = page
fetchMaps(page)
// 滚动到页面顶部
window.scrollTo({ top: 0, behavior: 'smooth' })
}
// 跳转到地图详情
const goToMapDetail = (id) => {
router.push(`/map/${id}`)
}
// 格式化日期
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('zh-CN')
}
onMounted(() => {
console.log('组件挂载')
fetchMaps(1)
getAllTags().then(tags => {
tagOptions.value = tags
})
})
</script>
<style scoped>
</style>

1170
src/views/WeaponMatch.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
<template>
<div class="weekly-recommend">
<div class="page-header">
<h1>每周地图推荐</h1>
<div class="header-subtitle">
<span class="date-range">{{ currentWeekRange }}</span>
</div>
</div>
<div class="table-container">
<table class="maps-table">
<thead>
<tr>
<th>序号</th>
<th>预览图</th>
<th>地图名称</th>
<th>作者</th>
<th>下载次数</th>
<th>收藏次数</th>
<th>玩家数量</th>
<th>创建时间</th>
<th>标签</th>
</tr>
</thead>
<tbody>
<tr v-for="(map, index) in recommendedMaps" :key="map.id" @click="goToMapDetail(map.id)" class="table-row">
<td class="rank-number">{{ index + 1 }}</td>
<td class="preview-cell">
<img :src="map.thumbnail" :alt="map.chinese_name">
</td>
<td class="map-name">{{ map.chinese_name }}</td>
<td>{{ map.user }}</td>
<td>{{ map.download_count }}</td>
<td>{{ map.favourite_count }}</td>
<td>{{ map.player_count }}</td>
<td>{{ formatDate(map.create_time) }}</td>
<td>
<div class="tags">
<span v-for="tag in map.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { getWeeklyTopMaps } from '../api/maps'
import '../assets/styles/common.css'
import '../assets/styles/WeeklyRecommend.css'
const router = useRouter()
const recommendedMaps = ref([])
// 获取当前周的范围
const currentWeekRange = computed(() => {
const now = new Date()
const start = new Date(now.setDate(now.getDate() - now.getDay()))
const end = new Date(now.setDate(now.getDate() - now.getDay() + 6))
return `${start.toLocaleDateString('zh-CN')} - ${end.toLocaleDateString('zh-CN')}`
})
const goToMapDetail = (id) => {
router.push(`/map/${id}`)
}
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('zh-CN')
}
const fetchRecommendedMaps = async () => {
try {
recommendedMaps.value = await getWeeklyTopMaps()
} catch (error) {
console.error('获取推荐地图失败:', error)
}
}
onMounted(() => {
fetchRecommendedMaps()
})
</script>
<style scoped>
.rank-number {
font-weight: bold;
color: #1a237e;
text-align: center;
width: 50px;
font-size: 1.1em;
}
</style>