-
+
@@ -590,6 +590,8 @@ watch(() => searchStore.customApi, (newApi) => {
}
})
+let hasScrolledToResults = false
+
async function handleSearch() {
if (!searchQuery.value.trim()) {return}
@@ -597,6 +599,7 @@ async function handleSearch() {
searchStore.clearResults()
searchStore.isSearching = true
searchStore.errorMessage = ''
+ hasScrolledToResults = false // 重置滚动标志
const searchParams = new URLSearchParams()
searchParams.set('game', searchQuery.value.trim())
@@ -605,6 +608,19 @@ async function handleSearch() {
searchParams.set('api', customApi.value.trim())
}
+ // 在 game 模式下,搜索开始时就并行发起 VNDB 请求
+ const queryForVndb = searchQuery.value.trim()
+ if (searchMode.value === 'game') {
+ fetchVndbData(queryForVndb).then((vndbData) => {
+ // 检查搜索词是否仍匹配(防止快速切换搜索时数据错乱)
+ if (vndbData && searchStore.searchQuery === queryForVndb) {
+ searchStore.vndbInfo = vndbData
+ }
+ }).catch(() => {
+ // VNDB 请求失败不影响主搜索
+ })
+ }
+
try {
await searchGameStream(searchParams, {
onTotal: (total) => {
@@ -614,11 +630,11 @@ async function handleSearch() {
searchStore.searchProgress = { current, total }
},
onPlatformResult: (data) => {
- const isFirstResult = searchStore.platformResults.size === 0
searchStore.setPlatformResult(data.name, data)
- // 第一个结果出现时滚动到结果区域
- if (isFirstResult) {
+ // 等待至少 3 个平台结果后滚动到结果区域(只滚动一次)
+ if (!hasScrolledToResults && searchStore.platformResults.size >= 3) {
+ hasScrolledToResults = true
// 使用 requestAnimationFrame + setTimeout 确保 DOM 已更新
window.requestAnimationFrame(() => {
setTimeout(() => {
@@ -642,6 +658,22 @@ async function handleSearch() {
searchStore.isSearching = false
playCelebration() // 搜索完成音效
+ // 如果结果不足 3 个但有结果,且还没滚动过,则现在滚动
+ if (!hasScrolledToResults && searchStore.platformResults.size > 0) {
+ hasScrolledToResults = true
+ window.requestAnimationFrame(() => {
+ setTimeout(() => {
+ const resultsEl = document.getElementById('results')
+ if (resultsEl) {
+ const headerOffset = 80
+ const elementPosition = resultsEl.getBoundingClientRect().top
+ const offsetPosition = elementPosition + window.pageYOffset - headerOffset
+ window.scrollTo({ top: offsetPosition, behavior: 'smooth' })
+ }
+ }, 50)
+ })
+ }
+
// 保存搜索历史
const resultCount = searchStore.totalResults
saveSearchHistory({
@@ -657,14 +689,6 @@ async function handleSearch() {
playCaution() // 错误音效
},
})
-
- // 获取 VNDB 数据
- if (searchMode.value === 'game') {
- const vndbData = await fetchVndbData(searchQuery.value.trim())
- if (vndbData) {
- searchStore.vndbInfo = vndbData
- }
- }
} catch (error) {
searchStore.errorMessage =
error instanceof Error ? error.message : '搜索失败'
@@ -1012,9 +1036,8 @@ defineExpose({
/* 错误卡片样式 */
.error-card {
- background: linear-gradient(135deg, rgba(254, 242, 242, 0.95), rgba(254, 226, 226, 0.95));
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
+ background: linear-gradient(135deg, rgba(var(--color-error, 254, 242, 242), var(--opacity-panel, 0.85)), rgba(254, 226, 226, var(--opacity-panel, 0.85)));
+ border-radius: var(--radius-lg, 1rem);
border-radius: 1rem;
padding: 1rem;
border: 1px solid rgba(239, 68, 68, 0.2);
@@ -1138,11 +1161,9 @@ defineExpose({
border-radius: 1rem;
background: linear-gradient(
135deg,
- rgba(255, 255, 255, 0.85) 0%,
- rgba(255, 228, 242, 0.75) 100%
+ rgba(255, 255, 255, 0.95) 0%,
+ rgba(255, 245, 250, 0.92) 100%
);
- backdrop-filter: blur(12px) saturate(180%);
- -webkit-backdrop-filter: blur(12px) saturate(180%);
z-index: -1;
}
@@ -1209,25 +1230,17 @@ defineExpose({
}
}
-/* 液态玻璃模式切换器 */
+/* 模式切换器 - 半透明效果 */
.liquid-mode-switch {
- background: rgba(255, 255, 255, 0.25);
- backdrop-filter: blur(12px) saturate(180%);
- -webkit-backdrop-filter: blur(12px) saturate(180%);
- border: 1px solid rgba(255, 255, 255, 0.3);
- box-shadow:
- 0 6px 12px rgba(0, 0, 0, 0.12),
- 0 0 20px rgba(255, 20, 147, 0.06),
- inset 0 1px 1px rgba(255, 255, 255, 0.6);
+ background: rgba(var(--color-bg-light, 255, 255, 255), var(--opacity-button, 0.75));
+ border: var(--border-thin, 1px) solid rgba(var(--color-primary, 255, 20, 147), var(--opacity-border, 0.15));
+ box-shadow: var(--shadow-md, 0 4px 12px rgba(0, 0, 0, 0.1));
}
.dark .liquid-mode-switch {
- background: rgba(30, 30, 40, 0.4);
- border-color: rgba(255, 255, 255, 0.15);
- box-shadow:
- 0 6px 12px rgba(0, 0, 0, 0.2),
- 0 0 20px rgba(255, 105, 180, 0.08),
- inset 0 1px 1px rgba(255, 255, 255, 0.1);
+ background: rgba(var(--color-bg-dark, 30, 41, 59), var(--opacity-button-dark, 0.75));
+ border-color: rgba(var(--color-primary-light, 255, 105, 180), var(--opacity-border-dark, 0.2));
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
/* 模式切换按钮 hover 效果 */
diff --git a/src/components/SearchHistoryModal.vue b/src/components/SearchHistoryModal.vue
index 1322e0e..0e9e374 100644
--- a/src/components/SearchHistoryModal.vue
+++ b/src/components/SearchHistoryModal.vue
@@ -222,35 +222,30 @@ onUnmounted(() => {
.history-modal {
background: linear-gradient(
180deg,
- rgba(255, 255, 255, 0.95) 0%,
- rgba(255, 251, 235, 0.98) 100%
+ rgba(var(--color-bg-light, 255, 255, 255), var(--opacity-panel, 0.85)) 0%,
+ rgba(255, 253, 245, var(--opacity-panel-hover, 0.9)) 100%
);
- backdrop-filter: blur(40px) saturate(1.5);
- -webkit-backdrop-filter: blur(40px) saturate(1.5);
- border: 1px solid rgba(251, 191, 36, 0.2);
+ border: var(--border-thin, 1px) solid rgba(var(--color-warning, 251, 191, 36), var(--opacity-border, 0.15));
+ border-radius: var(--radius-2xl, 1.5rem);
}
/* 历史记录面板 - 右下角弹出 (暗色模式) */
.dark .history-modal {
background: linear-gradient(
180deg,
- rgba(30, 41, 59, 0.95) 0%,
- rgba(30, 27, 17, 0.98) 100%
+ rgba(var(--color-bg-dark, 30, 41, 59), var(--opacity-panel-dark, 0.88)) 0%,
+ rgba(30, 27, 17, var(--opacity-panel-dark-hover, 0.92)) 100%
) !important;
- backdrop-filter: blur(40px) saturate(1.5) !important;
- -webkit-backdrop-filter: blur(40px) saturate(1.5) !important;
- border: 1px solid rgba(251, 191, 36, 0.1) !important;
+ border: var(--border-thin, 1px) solid rgba(var(--color-warning, 251, 191, 36), var(--opacity-border-dark, 0.2)) !important;
}
/* 头部样式 */
.history-header {
- background: rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(10px);
- -webkit-backdrop-filter: blur(10px);
+ background: rgba(var(--color-bg-light, 255, 255, 255), var(--opacity-header, 0.7));
}
.dark .history-header {
- background: rgba(30, 41, 59, 0.8) !important;
+ background: rgba(var(--color-bg-dark, 30, 41, 59), var(--opacity-header-dark, 0.7)) !important;
}
/* 历史记录项 */
diff --git a/src/components/SearchResults.vue b/src/components/SearchResults.vue
index 90c70eb..76147b9 100644
--- a/src/components/SearchResults.vue
+++ b/src/components/SearchResults.vue
@@ -1,15 +1,20 @@
-
-
+
+
@@ -76,56 +81,25 @@
{{ platformData.error }}
-
-
-
+
+
-
-
-
-
-
-
-
- {{ extractPath(result.url) }}
-
-
-
+ :index="index"
+ :source="result"
+ />
+
@@ -154,12 +129,13 @@
import { useSearchStore } from '@/stores/search'
import type { PlatformData } from '@/stores/search'
import { playTap } from '@/composables/useSound'
+import LazyRender from '@/components/LazyRender.vue'
+import ResultItem from '@/components/ResultItem.vue'
import {
ExternalLink,
AlertTriangle,
Crown,
List,
- Link as LinkIcon,
ArrowDown,
CheckCircle,
Star,
@@ -181,18 +157,6 @@ import {
const searchStore = useSearchStore()
-// 从URL中提取路径
-function extractPath(url: string): string {
- try {
- const urlObj = new URL(url)
- // 返回路径部分(去掉域名)
- return urlObj.pathname + urlObj.search + urlObj.hash
- } catch {
- // 如果URL解析失败,返回完整URL
- return url
- }
-}
-
// 获取站点所有结果的唯一标签
function getUniqueTags(platformData: PlatformData) {
const allTags = new Set
()
@@ -215,13 +179,13 @@ function loadMore(platformName: string) {
searchStore.loadMoreResults(platformName, 20)
}
-// 新增:卡片边框颜色
+// 新增:卡片边框颜色(去掉 hover 过渡)
function getBorderClass(color: string) {
const classes: Record = {
- lime: 'border-lime-300 dark:border-lime-700/50 hover:border-lime-400 dark:hover:border-lime-600',
- white: 'border-gray-300 dark:border-slate-600 hover:border-gray-400 dark:hover:border-slate-500',
- gold: 'border-yellow-300 dark:border-yellow-700/50 hover:border-yellow-400 dark:hover:border-yellow-600',
- red: 'border-red-300 dark:border-red-700/50 hover:border-red-400 dark:hover:border-red-600',
+ lime: 'border-lime-300 dark:border-lime-700/50',
+ white: 'border-gray-300 dark:border-slate-600',
+ gold: 'border-yellow-300 dark:border-yellow-700/50',
+ red: 'border-red-300 dark:border-red-700/50',
}
return classes[color] || 'border-gray-300 dark:border-slate-600'
}
@@ -248,12 +212,12 @@ function getHeaderTextColor(color: string) {
return classes[color] || 'text-gray-700 dark:text-gray-300'
}
-// 新增:推荐标签样式(更醒目)
+// 新增:推荐标签样式(使用纯色,避免渐变)
function getRecommendChipClass(color: string) {
const classes: Record = {
- lime: 'bg-gradient-to-r from-lime-400 to-green-500 text-white border-lime-500',
- gold: 'bg-gradient-to-r from-yellow-400 to-orange-500 text-white border-yellow-500',
- red: 'bg-gradient-to-r from-red-400 to-pink-500 text-white border-red-500',
+ lime: 'bg-lime-500 text-white border-lime-600',
+ gold: 'bg-yellow-500 text-white border-yellow-600',
+ red: 'bg-red-500 text-white border-red-600',
}
return classes[color] || ''
}
@@ -346,69 +310,31 @@ function getTagLabel(tag: string) {