DCFronted/src/views/index/TerrainList.vue
2025-06-30 19:10:51 +08:00

378 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="map-detail">
<div class="map-header">
<h1>地形图列表</h1>
<div class="filter-controls">
<label for="category-select">分类</label>
<select v-model="selectedCategory" id="category-select" @change="currentPage = 1">
<option v-for="category in categoryList" :key="category" :value="category">
{{ category }}
</option>
</select>
</div>
</div>
<div class="pagination-controls">
<button class="pagination-btn" @click="prevPage" :disabled="currentPage === 1">
&lt; 上一页
</button>
<span class="page-info"> {{ currentPage }} / {{ totalPages }} </span>
<button class="pagination-btn" @click="nextPage" :disabled="currentPage === totalPages">
下一页 &gt;
</button>
</div>
<div v-if="loading" class="loading">加载中...</div>
<div v-if="error" class="error">
加载失败: {{ error }}
<button @click="fetchTerrainList" class="back-btn">重试</button>
</div>
<div class="map-content">
<div v-if="filteredTerrainsByCategory.length > 0" class="terrain-grid">
<div v-for="terrain in paginatedTerrains" :key="terrain.key" class="terrain-item">
<div class="map-image">
<img :src="getImageUrl(terrain.key)" :alt="'地形图 ' + terrain.key" />
<div class="image-overlay">
<span class="image-name">{{ terrain.key }}</span>
<a :href="getImageUrl(terrain.key)" class="download-link" download title="下载">
<svg viewBox="0 0 24 24" width="20" height="20">
<path fill="white" d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>
</a>
</div>
</div>
</div>
</div>
<div v-else-if="!loading" class="no-data">
当前分类下没有可用的地形图数据
</div>
</div>
<div class="pagination-controls bottom">
<button class="pagination-btn" @click="prevPage" :disabled="currentPage === 1">
&lt; 上一页
</button>
<span class="page-info"> {{ currentPage }} / {{ totalPages }} </span>
<button class="pagination-btn" @click="nextPage" :disabled="currentPage === totalPages">
下一页 &gt;
</button>
</div>
</div>
</template>
<script>
export default {
name: 'TerrainList',
data() {
return {
terrains: [],
filteredTerrains: [],
loading: false,
error: null,
currentPage: 1,
itemsPerPage: 100,
apiBaseUrl: 'https://zybdatasupport.online',
categoryList: [],
selectedCategory: '全部',
};
},
computed: {
filteredTerrainsByCategory() {
if (this.selectedCategory === '全部') {
return this.filteredTerrains;
}
return this.filteredTerrains.filter(item =>
item.key.toLowerCase().startsWith(this.selectedCategory + '_')
);
},
totalPages() {
return Math.ceil(this.filteredTerrainsByCategory.length / this.itemsPerPage);
},
paginatedTerrains() {
const start = (this.currentPage - 1) * this.itemsPerPage;
const end = start + this.itemsPerPage;
return this.filteredTerrainsByCategory.slice(start, end);
},
},
created() {
this.fetchTerrainList();
},
methods: {
async fetchTerrainList() {
this.loading = true;
this.error = null;
try {
const response = await fetch(`${this.apiBaseUrl}/terrain`);
if (!response.ok) {
throw new Error('获取地形图列表失败');
}
const data = await response.json();
this.terrains = data;
this.filteredTerrains = data.filter(item => this.isImageFile(item.key));
this.extractCategories();
this.currentPage = 1;
} catch (err) {
this.error = err.message;
console.error('Error fetching terrain list:', err);
} finally {
this.loading = false;
}
},
extractCategories() {
const categories = new Set();
this.filteredTerrains.forEach(item => {
const prefix = item.key.split('_')[0].toLowerCase();
categories.add(prefix);
});
this.categoryList = ['全部', ...Array.from(categories).sort()];
},
getImageUrl(key) {
return `http://dataimg-1307694021.cos.ap-beijing.myqcloud.com/Terrain/jpg/${key}`;
},
isImageFile(key) {
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
const lowerKey = key.toLowerCase();
return imageExtensions.some(ext => lowerKey.endsWith(ext));
},
nextPage() {
if (this.currentPage < this.totalPages) {
this.currentPage++;
}
},
prevPage() {
if (this.currentPage > 1) {
this.currentPage--;
}
}
}
};
</script>
<style scoped>
.map-detail {
padding: 15px;
max-width: 1800px;
margin: 0 auto;
}
.map-header {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.map-header h1 {
margin: 0;
color: #333;
font-size: 1.5rem;
}
.filter-controls {
display: flex;
align-items: center;
gap: 8px;
}
select {
padding: 6px 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.loading, .error, .no-data {
padding: 15px;
text-align: center;
margin: 20px 0;
}
.error {
color: #d32f2f;
background-color: #ffebee;
}
.no-data {
color: #757575;
}
.terrain-grid {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 10px;
}
.terrain-item {
position: relative;
border-radius: 4px;
overflow: hidden;
aspect-ratio: 1;
transition: transform 0.2s;
}
.terrain-item:hover {
transform: scale(1.03);
z-index: 1;
}
.map-image {
position: relative;
width: 100%;
height: 100%;
}
.map-image img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.image-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
padding: 8px;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
opacity: 0;
transition: opacity 0.2s;
}
.terrain-item:hover .image-overlay {
opacity: 1;
}
.image-name {
font-size: 0.7rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 80%;
}
.download-link {
color: white;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
}
.download-link:hover {
color: #4fc3f7;
}
.pagination-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin: 20px 0;
}
.pagination-controls.bottom {
margin-top: 30px;
}
.pagination-btn {
padding: 8px 16px;
background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.pagination-btn:hover:not(:disabled) {
background: #e9ecef;
border-color: #d0d0d0;
}
.pagination-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-info {
font-size: 0.9rem;
color: #666;
}
.back-btn {
display: inline-flex;
align-items: center;
padding: 8px 12px;
background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 6px;
color: #333;
cursor: pointer;
transition: all 0.2s ease;
font-size: 14px;
margin-left: 10px;
}
.back-btn:hover {
background: #e9ecef;
border-color: #d0d0d0;
}
@media (max-width: 1800px) {
.terrain-grid {
grid-template-columns: repeat(8, 1fr);
}
}
@media (max-width: 1400px) {
.terrain-grid {
grid-template-columns: repeat(6, 1fr);
}
}
@media (max-width: 1000px) {
.terrain-grid {
grid-template-columns: repeat(4, 1fr);
}
}
@media (max-width: 700px) {
.terrain-grid {
grid-template-columns: repeat(3, 1fr);
}
.map-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
}
@media (max-width: 500px) {
.terrain-grid {
grid-template-columns: repeat(2, 1fr);
}
.pagination-controls {
flex-direction: column;
gap: 10px;
}
}
</style>