378 lines
8.8 KiB
Vue
378 lines
8.8 KiB
Vue
<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">
|
||
< 上一页
|
||
</button>
|
||
<span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
|
||
<button class="pagination-btn" @click="nextPage" :disabled="currentPage === totalPages">
|
||
下一页 >
|
||
</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">
|
||
< 上一页
|
||
</button>
|
||
<span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
|
||
<button class="pagination-btn" @click="nextPage" :disabled="currentPage === totalPages">
|
||
下一页 >
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'TerrainList',
|
||
data() {
|
||
return {
|
||
terrains: [],
|
||
filteredTerrains: [],
|
||
loading: false,
|
||
error: null,
|
||
currentPage: 1,
|
||
itemsPerPage: 100,
|
||
apiBaseUrl: 'http://zybdatasupport.online:8000',
|
||
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>
|
||
|