作品页面添加上下一个切换
This commit is contained in:
BIN
Binary file not shown.
@@ -260,7 +260,14 @@ const getImageUrl = (originalUrl: string) => {
|
|||||||
|
|
||||||
// 点击作品
|
// 点击作品
|
||||||
const handleArtworkClick = (artwork: Artwork) => {
|
const handleArtworkClick = (artwork: Artwork) => {
|
||||||
router.push(`/artwork/${artwork.id}`);
|
// 传递作者ID和作品类型信息,用于导航
|
||||||
|
router.push({
|
||||||
|
path: `/artwork/${artwork.id}`,
|
||||||
|
query: {
|
||||||
|
artistId: artist.value?.id.toString(),
|
||||||
|
artworkType: artworkType.value
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 清除错误
|
// 清除错误
|
||||||
|
|||||||
@@ -74,6 +74,32 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 作品导航 -->
|
||||||
|
<div v-if="showNavigation" class="artwork-navigation">
|
||||||
|
<button
|
||||||
|
@click="navigateToPrevious"
|
||||||
|
class="nav-btn nav-prev"
|
||||||
|
:disabled="!previousArtwork"
|
||||||
|
:title="previousArtwork ? `上一个: ${previousArtwork.title}` : '没有上一个作品'"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
||||||
|
</svg>
|
||||||
|
<span>上一个</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="navigateToNext"
|
||||||
|
class="nav-btn nav-next"
|
||||||
|
:disabled="!nextArtwork"
|
||||||
|
:title="nextArtwork ? `下一个: ${nextArtwork.title}` : '没有下一个作品'"
|
||||||
|
>
|
||||||
|
<span>下一个</span>
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 作品统计 -->
|
<!-- 作品统计 -->
|
||||||
<div class="artwork-stats">
|
<div class="artwork-stats">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
@@ -130,10 +156,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
import artworkService from '@/services/artwork';
|
import artworkService from '@/services/artwork';
|
||||||
|
import artistService from '@/services/artist';
|
||||||
import downloadService from '@/services/download';
|
import downloadService from '@/services/download';
|
||||||
import type { Artwork } from '@/types';
|
import type { Artwork } from '@/types';
|
||||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||||
@@ -152,6 +179,11 @@ const imageLoaded = ref(false);
|
|||||||
const imageError = ref(false);
|
const imageError = ref(false);
|
||||||
const downloading = ref(false);
|
const downloading = ref(false);
|
||||||
|
|
||||||
|
// 导航相关状态
|
||||||
|
const artistArtworks = ref<Artwork[]>([]);
|
||||||
|
const currentArtworkIndex = ref(-1);
|
||||||
|
const navigationLoading = ref(false);
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const currentImageUrl = computed(() => {
|
const currentImageUrl = computed(() => {
|
||||||
if (!artwork.value) return '';
|
if (!artwork.value) return '';
|
||||||
@@ -165,6 +197,25 @@ const currentImageUrl = computed(() => {
|
|||||||
return artwork.value.image_urls.large;
|
return artwork.value.image_urls.large;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 导航相关计算属性
|
||||||
|
const showNavigation = computed(() => {
|
||||||
|
return route.query.artistId && route.query.artworkType;
|
||||||
|
});
|
||||||
|
|
||||||
|
const previousArtwork = computed(() => {
|
||||||
|
if (currentArtworkIndex.value > 0) {
|
||||||
|
return artistArtworks.value[currentArtworkIndex.value - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextArtwork = computed(() => {
|
||||||
|
if (currentArtworkIndex.value >= 0 && currentArtworkIndex.value < artistArtworks.value.length - 1) {
|
||||||
|
return artistArtworks.value[currentArtworkIndex.value + 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
// 获取作品详情
|
// 获取作品详情
|
||||||
const fetchArtworkDetail = async () => {
|
const fetchArtworkDetail = async () => {
|
||||||
const artworkId = parseInt(route.params.id as string);
|
const artworkId = parseInt(route.params.id as string);
|
||||||
@@ -176,6 +227,10 @@ const fetchArtworkDetail = async () => {
|
|||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
// 重置图片加载状态
|
||||||
|
imageLoaded.value = false;
|
||||||
|
imageError.value = false;
|
||||||
|
currentPage.value = 0;
|
||||||
|
|
||||||
const response = await artworkService.getArtworkDetail(artworkId);
|
const response = await artworkService.getArtworkDetail(artworkId);
|
||||||
|
|
||||||
@@ -243,8 +298,96 @@ const clearError = () => {
|
|||||||
error.value = null;
|
error.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取作者作品列表用于导航
|
||||||
|
const fetchArtistArtworks = async () => {
|
||||||
|
const artistId = route.query.artistId;
|
||||||
|
const artworkType = route.query.artworkType;
|
||||||
|
|
||||||
|
if (!artistId || !artworkType) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
navigationLoading.value = true;
|
||||||
|
const response = await artistService.getArtistArtworks(parseInt(artistId as string), {
|
||||||
|
type: artworkType as 'art' | 'manga' | 'novel',
|
||||||
|
limit: 100 // 获取更多作品以便导航
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success && response.data) {
|
||||||
|
artistArtworks.value = response.data.artworks;
|
||||||
|
// 找到当前作品在列表中的位置
|
||||||
|
const currentId = parseInt(route.params.id as string);
|
||||||
|
currentArtworkIndex.value = artistArtworks.value.findIndex(art => art.id === currentId);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取作者作品列表失败:', err);
|
||||||
|
} finally {
|
||||||
|
navigationLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导航到上一个作品
|
||||||
|
const navigateToPrevious = () => {
|
||||||
|
if (previousArtwork.value) {
|
||||||
|
router.push({
|
||||||
|
path: `/artwork/${previousArtwork.value.id}`,
|
||||||
|
query: {
|
||||||
|
artistId: route.query.artistId,
|
||||||
|
artworkType: route.query.artworkType
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导航到下一个作品
|
||||||
|
const navigateToNext = () => {
|
||||||
|
if (nextArtwork.value) {
|
||||||
|
router.push({
|
||||||
|
path: `/artwork/${nextArtwork.value.id}`,
|
||||||
|
query: {
|
||||||
|
artistId: route.query.artistId,
|
||||||
|
artworkType: route.query.artworkType
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听路由变化,重新获取作品详情和导航数据
|
||||||
|
watch(() => route.params.id, () => {
|
||||||
|
// 重新获取作品详情
|
||||||
|
fetchArtworkDetail();
|
||||||
|
|
||||||
|
// 如果是从作者页面来的,重新获取导航数据
|
||||||
|
if (showNavigation.value) {
|
||||||
|
fetchArtistArtworks();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 键盘快捷键支持
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (!showNavigation.value) return;
|
||||||
|
|
||||||
|
if (event.key === 'ArrowLeft' && previousArtwork.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
navigateToPrevious();
|
||||||
|
} else if (event.key === 'ArrowRight' && nextArtwork.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
navigateToNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchArtworkDetail();
|
fetchArtworkDetail();
|
||||||
|
if (showNavigation.value) {
|
||||||
|
fetchArtistArtworks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加键盘事件监听
|
||||||
|
document.addEventListener('keydown', handleKeydown);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时移除事件监听
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -532,6 +675,54 @@ onMounted(() => {
|
|||||||
margin: 0.25rem 0;
|
margin: 0.25rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.artwork-navigation {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: white;
|
||||||
|
color: #374151;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover:not(:disabled) {
|
||||||
|
background: #f3f4f6;
|
||||||
|
border-color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-prev {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-next {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.artwork-content {
|
.artwork-content {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
Reference in New Issue
Block a user