diff --git a/ui/src/components/common/WatchlistWidget.vue b/ui/src/components/common/WatchlistWidget.vue index ac75f9d..2815258 100644 --- a/ui/src/components/common/WatchlistWidget.vue +++ b/ui/src/components/common/WatchlistWidget.vue @@ -42,6 +42,32 @@ + +
+ +
+ +
+
+
@@ -57,6 +83,15 @@
+
+ + + + 没有找到匹配的项目 +

尝试调整搜索词或清除搜索条件

+
+
-
+
{{ item.title }}
{{ formatUrl(item.url) }}
@@ -240,6 +276,10 @@ const addMode = ref<'single' | 'batch'>('single'); const batchUrls = ref(''); const autoGenerateTitle = ref(true); +// 搜索和排序 +const searchQuery = ref(''); +const sortOrder = ref<'asc' | 'desc'>('desc'); // 'desc' 表示最新,'asc' 表示最旧 + // Store和Router const watchlistStore = useWatchlistStore(); const route = useRoute(); @@ -251,6 +291,29 @@ const itemCount = computed(() => watchlistStore.itemCount); const loading = computed(() => watchlistStore.loading); const error = computed(() => watchlistStore.error); +// 过滤和排序后的待看项目 +const filteredAndSortedItems = computed(() => { + let filteredItems = [...items.value]; + + // 搜索过滤 + if (searchQuery.value) { + const query = searchQuery.value.toLowerCase(); + filteredItems = filteredItems.filter(item => + item.title.toLowerCase().includes(query) || + item.url.toLowerCase().includes(query) + ); + } + + // 排序 + filteredItems.sort((a, b) => { + const dateA = new Date(a.createdAt).getTime(); + const dateB = new Date(b.createdAt).getTime(); + return sortOrder.value === 'desc' ? dateB - dateA : dateA - dateB; + }); + + return filteredItems; +}); + // 获取当前页面完整URL const getCurrentPageUrl = () => { return window.location.href; @@ -505,6 +568,16 @@ const saveAdd = async () => { } }; +// 切换排序顺序 +const toggleSortOrder = () => { + sortOrder.value = sortOrder.value === 'desc' ? 'asc' : 'desc'; +}; + +// 清除搜索 +const clearSearch = () => { + searchQuery.value = ''; +}; + // 格式化URL显示 const formatUrl = (url: string) => { try { @@ -725,6 +798,99 @@ watch(() => route.fullPath, () => { height: 1rem; } +/* 搜索和排序控制区域样式 */ +.watchlist-controls { + padding: 0.75rem 1rem; + border-bottom: 1px solid #e5e7eb; + display: flex; + align-items: center; + gap: 0.5rem; + background: #f8fafc; +} + +.search-box { + flex: 1; + display: flex; + align-items: center; + background: #f3f4f6; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + padding: 0.375rem 0.75rem; + gap: 0.5rem; +} + +.search-icon { + width: 1.125rem; + height: 1.125rem; + color: #6b7280; +} + +.search-input { + flex: 1; + border: none; + background: none; + font-size: 0.875rem; + color: #374151; + outline: none; +} + +.search-input::placeholder { + color: #9ca3af; +} + +.clear-search-btn { + background: none; + border: none; + cursor: pointer; + color: #6b7280; + padding: 0.25rem; + border-radius: 0.25rem; + transition: all 0.2s; +} + +.clear-search-btn:hover { + background: #f3f4f6; + color: #374151; +} + +.clear-search-btn svg { + width: 0.875rem; + height: 0.875rem; +} + +.sort-controls { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.sort-btn { + display: flex; + align-items: center; + gap: 0.25rem; + background: #f3f4f6; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + padding: 0.375rem 0.75rem; + cursor: pointer; + transition: all 0.2s; +} + +.sort-btn:hover { + background: #e5e7eb; +} + +.sort-icon { + width: 1rem; + height: 1rem; + color: #6b7280; +} + +.sort-text { + font-size: 0.875rem; + color: #374151; +} + /* 内容样式 */ .watchlist-content { max-height: 28rem; diff --git a/ui/src/views/RankingView.vue b/ui/src/views/RankingView.vue index 5f060bf..952898b 100644 --- a/ui/src/views/RankingView.vue +++ b/ui/src/views/RankingView.vue @@ -294,7 +294,7 @@ const goToPage = (page: number) => { const handleArtworkClick = (artwork: Artwork) => { // 保存当前页面的滚动位置 saveScrollPosition(route.fullPath); - + router.push({ path: `/artwork/${artwork.id}`, query: { @@ -387,9 +387,32 @@ watch(() => route.query, () => { } }); -// 组件卸载时清理缓存 +// 键盘事件处理 +const handleKeyDown = (event: KeyboardEvent) => { + // 检查是否在输入框中,如果是则不处理 + const target = event.target as HTMLElement; + if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT')) { + return; + } + + // 只有在有多页且有作品时才处理键盘事件 + if (totalPages.value <= 1 || !artworks.value || artworks.value.length === 0) { + return; + } + + if (event.key === 'ArrowLeft' && currentPage.value > 1) { + event.preventDefault(); + goToPage(currentPage.value - 1); + } else if (event.key === 'ArrowRight' && currentPage.value < totalPages.value) { + event.preventDefault(); + goToPage(currentPage.value + 1); + } +}; + +// 组件卸载时清理缓存和事件监听器 onUnmounted(() => { clearCache(); + window.removeEventListener('keydown', handleKeyDown); }); onMounted(async () => { @@ -416,7 +439,10 @@ onMounted(async () => { } else { await fetchRankingData(1); } - + + // 添加键盘事件监听器 + window.addEventListener('keydown', handleKeyDown); + // 恢复滚动位置 setTimeout(() => { restoreScrollPosition(route.fullPath);