mirror of
https://github.com/Moe-Sakura/frontend.git
synced 2026-04-08 09:30:33 +08:00
feat: 增加设置功能与统计信息展示
* 在 `App.vue` 中引入 `SettingsModal` 和 `StatsCorner` 组件,增强用户设置和统计信息的可视化。 * 更新 `TopToolbar` 组件,添加设置按钮以便用户快速访问设置。 * 在 `SearchHeader.vue` 中优化布局,提升视觉效果和用户体验。
This commit is contained in:
30
src/App.vue
30
src/App.vue
@@ -8,12 +8,19 @@
|
||||
/>
|
||||
|
||||
<main class="flex-1 flex flex-col min-h-screen">
|
||||
<TopToolbar :current-background-url="randomImageUrl" />
|
||||
<SearchHeader />
|
||||
<StatsCorner />
|
||||
<TopToolbar :current-background-url="randomImageUrl" @open-settings="openSettings" />
|
||||
<SearchHeader ref="searchHeaderRef" />
|
||||
<SearchResults />
|
||||
<FloatingButtons />
|
||||
<CommentsModal />
|
||||
<VndbPanel />
|
||||
<SettingsModal
|
||||
:is-open="isSettingsOpen"
|
||||
:custom-api="searchStore.customApi"
|
||||
@close="closeSettings"
|
||||
@save="saveSettings"
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,12 +29,14 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { imageDB } from "@/utils/imageDB";
|
||||
import { useSearchStore } from "@/stores/search";
|
||||
import StatsCorner from "@/components/StatsCorner.vue";
|
||||
import TopToolbar from "@/components/TopToolbar.vue";
|
||||
import SearchHeader from "@/components/SearchHeader.vue";
|
||||
import SearchResults from "@/components/SearchResults.vue";
|
||||
import FloatingButtons from "@/components/FloatingButtons.vue";
|
||||
import CommentsModal from "@/components/CommentsModal.vue";
|
||||
import VndbPanel from "@/components/VndbPanel.vue";
|
||||
import SettingsModal from "@/components/SettingsModal.vue";
|
||||
|
||||
const searchStore = useSearchStore();
|
||||
const randomImageUrl = ref("");
|
||||
@@ -38,6 +47,9 @@ const shuffledQueue = ref<string[]>([]);
|
||||
let fetchInterval: number | null = null;
|
||||
let displayInterval: number | null = null;
|
||||
|
||||
// 设置模态框
|
||||
const isSettingsOpen = ref(false);
|
||||
|
||||
const MAX_CACHE_SIZE = 10000; // 最大缓存 10000 张图片
|
||||
const CLEANUP_BATCH_SIZE = 2000; // 每次清理 2000 张
|
||||
const FETCH_INTERVAL = 5000; // 5秒获取一次
|
||||
@@ -368,6 +380,20 @@ onUnmounted(() => {
|
||||
// 关闭数据库连接
|
||||
imageDB.close();
|
||||
});
|
||||
|
||||
// 设置相关函数
|
||||
function openSettings() {
|
||||
isSettingsOpen.value = true;
|
||||
}
|
||||
|
||||
function closeSettings() {
|
||||
isSettingsOpen.value = false;
|
||||
}
|
||||
|
||||
function saveSettings(customApi: string) {
|
||||
// 保存自定义 API 到 store
|
||||
searchStore.setCustomApi(customApi);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="container mx-auto w-full px-4 sm:px-6 lg:px-8 py-4 sm:py-6">
|
||||
<div class="flex flex-col items-center gap-4 sm:gap-6">
|
||||
<!-- 主内容区域 - 垂直居中布局 -->
|
||||
<div class="flex flex-col items-center justify-center min-h-[60vh]">
|
||||
<!-- Title with gamepad icon and status -->
|
||||
<div
|
||||
class="header-title flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 my-12 sm:my-12 animate-fade-in-down"
|
||||
class="header-title flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 mb-8 sm:mb-12 animate-fade-in-down"
|
||||
>
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl lg:text-5xl font-bold text-center text-white drop-shadow-[0_4px_8px_rgba(0,0,0,0.3)] flex items-center gap-2 sm:gap-3"
|
||||
@@ -26,6 +27,7 @@
|
||||
<SearchHistory
|
||||
ref="searchHistoryRef"
|
||||
@select="handleHistorySelect"
|
||||
class="w-full max-w-2xl px-2 sm:px-0 mb-4"
|
||||
/>
|
||||
|
||||
<!-- Search Form -->
|
||||
@@ -48,22 +50,6 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Custom API Input -->
|
||||
<div class="relative">
|
||||
<i
|
||||
class="fas fa-link absolute left-3 sm:left-4 top-3 sm:top-4 text-gray-400 text-lg sm:text-xl pointer-events-none z-10"
|
||||
></i>
|
||||
<input
|
||||
v-model="customApi"
|
||||
type="url"
|
||||
placeholder="自定义 API 地址 (可选)"
|
||||
class="w-full pl-10 sm:pl-12 pr-4 py-3 sm:py-4 text-sm sm:text-base rounded-2xl bg-white/98 dark:bg-slate-800/95 text-gray-900 dark:text-slate-100 placeholder:text-gray-400 dark:placeholder:text-slate-400 backdrop-blur-md shadow-lg focus:shadow-2xl focus:scale-[1.01] transition-all outline-none border-2 border-transparent focus:border-pink-500 dark:focus:border-purple-500"
|
||||
/>
|
||||
<p class="text-xs text-white/90 dark:text-slate-300 drop-shadow-md mt-2 font-medium">
|
||||
例如: https://api.searchgal.homes 或 http://127.0.0.1:8898
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Search Button and Mode Selector -->
|
||||
<div class="flex flex-col gap-3">
|
||||
<button
|
||||
@@ -79,67 +65,42 @@
|
||||
>
|
||||
</button>
|
||||
|
||||
<!-- Search Mode Selector -->
|
||||
<div class="flex justify-center gap-2 sm:gap-3">
|
||||
<button
|
||||
type="button"
|
||||
@click="searchMode = 'game'"
|
||||
:class="[
|
||||
'mode-chip px-6 py-2 rounded-full font-medium transition-all flex items-center gap-2',
|
||||
searchMode === 'game'
|
||||
? 'bg-pink-500 dark:bg-purple-600 text-white shadow-lg scale-105'
|
||||
: 'bg-white/90 dark:bg-slate-700/90 text-gray-700 dark:text-slate-200 hover:bg-white dark:hover:bg-slate-600',
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-gamepad"></i>
|
||||
<span>游戏</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="searchMode = 'patch'"
|
||||
:class="[
|
||||
'mode-chip px-6 py-2 rounded-full font-medium transition-all flex items-center gap-2',
|
||||
searchMode === 'patch'
|
||||
? 'bg-pink-500 dark:bg-purple-600 text-white shadow-lg scale-105'
|
||||
: 'bg-white/90 dark:bg-slate-700/90 text-gray-700 dark:text-slate-200 hover:bg-white dark:hover:bg-slate-600',
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-tools"></i>
|
||||
<span>补丁</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Busuanzi Statistics -->
|
||||
<div
|
||||
class="flex justify-center items-center gap-6 text-sm text-white/90 drop-shadow-md font-medium mt-2"
|
||||
>
|
||||
<span class="flex items-center gap-1">
|
||||
<i class="fas fa-eye"></i>
|
||||
<span id="busuanzi_value_page_pv" class="font-semibold">-</span>
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<i class="fas fa-user"></i>
|
||||
<span id="busuanzi_value_site_uv" class="font-semibold">-</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Version and GitHub Info -->
|
||||
<div class="flex flex-wrap items-center justify-center gap-2 sm:gap-3 mt-4">
|
||||
<div
|
||||
class="px-3 py-1.5 rounded-full bg-white/90 dark:bg-slate-700/90 backdrop-blur-md text-gray-600 dark:text-slate-300 font-medium shadow-md flex items-center gap-2 text-sm"
|
||||
>
|
||||
<span>251007</span>
|
||||
<!-- Search Mode Selector - 胶囊开关 -->
|
||||
<div class="flex justify-center">
|
||||
<div class="mode-switch-container relative bg-white/90 dark:bg-slate-700/90 backdrop-blur-md rounded-full p-1 shadow-lg">
|
||||
<!-- 滑动背景 -->
|
||||
<div
|
||||
class="mode-slider absolute top-1 bottom-1 rounded-full bg-gradient-to-r from-pink-500 to-pink-600 dark:from-purple-600 dark:to-purple-700 shadow-md transition-all duration-300 ease-out"
|
||||
:style="{
|
||||
left: searchMode === 'game' ? '4px' : 'calc(50%)',
|
||||
width: 'calc(50% - 4px)'
|
||||
}"
|
||||
/>
|
||||
|
||||
<!-- 游戏按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
@click="searchMode = 'game'"
|
||||
class="mode-option relative z-10 px-6 py-2 rounded-full font-medium transition-all duration-300 flex items-center gap-2"
|
||||
:class="searchMode === 'game' ? 'text-white' : 'text-gray-700 dark:text-slate-300'"
|
||||
>
|
||||
<i class="fas fa-gamepad"></i>
|
||||
<span>游戏</span>
|
||||
</button>
|
||||
|
||||
<!-- 补丁按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
@click="searchMode = 'patch'"
|
||||
class="mode-option relative z-10 px-6 py-2 rounded-full font-medium transition-all duration-300 flex items-center gap-2"
|
||||
:class="searchMode === 'patch' ? 'text-white' : 'text-gray-700 dark:text-slate-300'"
|
||||
>
|
||||
<i class="fas fa-tools"></i>
|
||||
<span>补丁</span>
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
href="https://github.com/Moe-Sakura/SearchGal"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="px-3 py-1.5 rounded-full bg-white/90 dark:bg-slate-700/90 backdrop-blur-md text-gray-700 dark:text-slate-200 hover:text-pink-500 dark:hover:text-purple-400 font-medium hover:scale-105 transition-all shadow-md flex items-center gap-2 text-sm"
|
||||
>
|
||||
<i class="fab fa-github"></i>
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -153,7 +114,7 @@
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div v-if="searchStore.errorMessage" class="w-full max-w-2xl">
|
||||
<div v-if="searchStore.errorMessage" class="w-full max-w-2xl px-2 sm:px-0 mt-4">
|
||||
<div
|
||||
class="bg-red-50 dark:bg-red-900/30 border-2 border-red-200 dark:border-red-800/50 rounded-2xl p-4 shadow-lg"
|
||||
>
|
||||
@@ -167,12 +128,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Usage Notice -->
|
||||
<div class="w-full max-w-4xl mt-6 sm:mt-8 px-2 sm:px-0 animate-fade-in animation-delay-1000">
|
||||
<div
|
||||
class="usage-notice bg-white/75 dark:bg-slate-800/90 backdrop-blur-md rounded-2xl sm:rounded-3xl shadow-xl p-4 sm:p-6 lg:p-8"
|
||||
>
|
||||
<!-- Usage Notice - 独立于居中区域 -->
|
||||
<div class="w-full max-w-4xl mx-auto mt-8 sm:mt-12 px-2 sm:px-0 animate-fade-in animation-delay-1000">
|
||||
<div
|
||||
class="usage-notice bg-white/75 dark:bg-slate-800/90 backdrop-blur-md rounded-2xl sm:rounded-3xl shadow-xl p-4 sm:p-6 lg:p-8"
|
||||
>
|
||||
<h2
|
||||
class="text-xl sm:text-2xl font-bold text-gray-800 dark:text-slate-100 mb-4 sm:mb-6 flex items-center gap-2"
|
||||
>
|
||||
@@ -264,7 +226,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -474,4 +435,28 @@ async function handleSearch() {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 胶囊开关样式 */
|
||||
.mode-switch-container {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mode-slider {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mode-option {
|
||||
min-width: 100px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 640px) {
|
||||
.mode-option {
|
||||
min-width: 80px;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
203
src/components/SettingsModal.vue
Normal file
203
src/components/SettingsModal.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<!-- 遮罩层 -->
|
||||
<Transition
|
||||
enter-active-class="transition-opacity duration-200 ease-out"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="transition-opacity duration-200 ease-in"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
|
||||
@click.self="close"
|
||||
>
|
||||
<!-- 对话框 -->
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-200 ease-out"
|
||||
enter-from-class="opacity-0 scale-95"
|
||||
enter-to-class="opacity-100 scale-100"
|
||||
leave-active-class="transition-all duration-200 ease-in"
|
||||
leave-from-class="opacity-100 scale-100"
|
||||
leave-to-class="opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="bg-white/95 dark:bg-slate-800/95 backdrop-blur-xl rounded-2xl sm:rounded-3xl shadow-2xl w-full max-w-2xl max-h-[90vh] mx-4 flex flex-col overflow-hidden border border-white/30 dark:border-slate-700/50"
|
||||
@click.stop
|
||||
>
|
||||
<!-- 标题栏 -->
|
||||
<div class="flex items-center gap-2 sm:gap-3 px-4 sm:px-6 py-4 sm:py-5 border-b border-pink-100 dark:border-slate-700">
|
||||
<i class="fas fa-cog text-pink-500 dark:text-purple-400 text-xl sm:text-2xl"></i>
|
||||
<h2 class="text-lg sm:text-xl font-bold text-gray-800 dark:text-slate-100 flex-1">设置</h2>
|
||||
<button
|
||||
@click="close"
|
||||
class="w-10 h-10 flex items-center justify-center rounded-full hover:bg-pink-50 text-gray-500 hover:text-pink-500 dark:text-slate-400 dark:hover:bg-slate-700/50 dark:hover:text-purple-400 transition-all duration-200"
|
||||
>
|
||||
<i class="fas fa-times text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="flex-1 overflow-y-auto p-4 sm:p-6 custom-scrollbar">
|
||||
<div class="space-y-6">
|
||||
<!-- API 设置 -->
|
||||
<div class="setting-section">
|
||||
<h3 class="text-base sm:text-lg font-semibold text-gray-800 dark:text-slate-100 mb-3 flex items-center gap-2">
|
||||
<i class="fas fa-server text-pink-500 dark:text-purple-400"></i>
|
||||
<span>API 设置</span>
|
||||
</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- 自定义 API 地址 -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-slate-300 mb-2">
|
||||
自定义 API 地址
|
||||
</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-link absolute left-3 sm:left-4 top-3 sm:top-4 text-gray-400 text-lg sm:text-xl pointer-events-none z-10"></i>
|
||||
<input
|
||||
v-model="localCustomApi"
|
||||
type="url"
|
||||
placeholder="https://cfapi.searchgal.homes"
|
||||
class="w-full pl-10 sm:pl-12 pr-4 py-3 sm:py-4 text-sm sm:text-base rounded-xl bg-white dark:bg-slate-700/50 backdrop-blur-md shadow-md focus:shadow-lg focus:scale-[1.01] transition-all outline-none border-2 border-transparent focus:border-pink-500 dark:focus:border-purple-500 text-gray-900 dark:text-slate-100 placeholder:text-gray-400 dark:placeholder:text-slate-400"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-slate-400 mt-2">
|
||||
留空使用默认 API 地址。例如: https://cfapi.searchgal.homes 或 http://127.0.0.1:8787
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- API 状态 -->
|
||||
<div class="bg-blue-50 dark:bg-blue-950/30 border border-blue-200 dark:border-blue-800/50 rounded-xl p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<i class="fas fa-info-circle text-blue-500 dark:text-blue-400 text-lg mt-0.5"></i>
|
||||
<div class="flex-1 text-sm text-blue-700 dark:text-blue-300">
|
||||
<p class="font-semibold mb-1">关于自定义 API</p>
|
||||
<p>您可以使用自己部署的后端 API 进行搜索。API 需要兼容 SearchGal 的接口规范。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 更多设置可以在这里添加 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="flex items-center justify-end gap-3 px-4 sm:px-6 py-4 border-t border-pink-100 dark:border-slate-700">
|
||||
<button
|
||||
@click="reset"
|
||||
class="px-4 py-2 rounded-xl text-gray-700 dark:text-slate-300 hover:bg-gray-100 dark:hover:bg-slate-700 transition-all font-medium"
|
||||
>
|
||||
<i class="fas fa-undo mr-2"></i>
|
||||
重置
|
||||
</button>
|
||||
<button
|
||||
@click="save"
|
||||
class="px-6 py-2 rounded-xl bg-gradient-to-r from-pink-500 to-pink-600 dark:from-purple-600 dark:to-purple-700 text-white font-semibold shadow-lg hover:shadow-xl hover:scale-105 transition-all"
|
||||
>
|
||||
<i class="fas fa-check mr-2"></i>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
isOpen: boolean
|
||||
customApi: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
save: [customApi: string]
|
||||
}>()
|
||||
|
||||
const localCustomApi = ref(props.customApi)
|
||||
|
||||
// 监听外部变化
|
||||
watch(() => props.customApi, (newValue) => {
|
||||
localCustomApi.value = newValue
|
||||
})
|
||||
|
||||
// 监听打开状态,同步数据
|
||||
watch(() => props.isOpen, (isOpen) => {
|
||||
if (isOpen) {
|
||||
localCustomApi.value = props.customApi
|
||||
}
|
||||
})
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function save() {
|
||||
emit('save', localCustomApi.value)
|
||||
close()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
localCustomApi.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义滚动条 - 粉色渐变 */
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, rgb(236, 72, 153), rgb(139, 92, 246));
|
||||
border-radius: 10px;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(180deg, rgb(219, 39, 119), rgb(124, 58, 237));
|
||||
}
|
||||
|
||||
/* 暗色模式滚动条 */
|
||||
.dark .custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: rgba(30, 41, 59, 0.3);
|
||||
}
|
||||
|
||||
.dark .custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, rgb(139, 92, 246), rgb(99, 102, 241));
|
||||
}
|
||||
|
||||
.dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(180deg, rgb(124, 58, 237), rgb(79, 70, 229));
|
||||
}
|
||||
|
||||
/* 设置区块 */
|
||||
.setting-section {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
50
src/components/StatsCorner.vue
Normal file
50
src/components/StatsCorner.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div
|
||||
class="fixed top-4 left-4 z-40 flex flex-col gap-2 animate-fade-in"
|
||||
>
|
||||
<!-- 访问统计 -->
|
||||
<div
|
||||
class="stats-card bg-white/90 dark:bg-slate-800/90 backdrop-blur-md rounded-2xl shadow-lg px-4 py-3 flex items-center gap-3 border border-white/30 dark:border-slate-700/50"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-eye text-pink-500 dark:text-purple-400"></i>
|
||||
<span id="busuanzi_value_page_pv" class="font-semibold text-gray-800 dark:text-slate-100">-</span>
|
||||
</div>
|
||||
<div class="w-px h-4 bg-gray-300 dark:bg-slate-600"></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-user text-pink-500 dark:text-purple-400"></i>
|
||||
<span id="busuanzi_value_site_uv" class="font-semibold text-gray-800 dark:text-slate-100">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 左上角访客统计组件
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 640px) {
|
||||
.stats-card {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,6 +22,26 @@
|
||||
<i :class="showCopiedTip ? 'fas fa-check' : 'fas fa-share-alt'" class="text-lg sm:text-xl"></i>
|
||||
</button>
|
||||
|
||||
<!-- GitHub 按钮 -->
|
||||
<a
|
||||
href="https://github.com/Moe-Sakura/SearchGal"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="访问 GitHub 仓库"
|
||||
class="toolbar-button github-button"
|
||||
>
|
||||
<i class="fab fa-github text-lg sm:text-xl"></i>
|
||||
</a>
|
||||
|
||||
<!-- 设置按钮 -->
|
||||
<button
|
||||
@click="openSettings"
|
||||
aria-label="设置"
|
||||
class="toolbar-button settings-button"
|
||||
>
|
||||
<i class="fas fa-cog text-lg sm:text-xl"></i>
|
||||
</button>
|
||||
|
||||
<!-- 主题切换按钮 -->
|
||||
<button
|
||||
@click="cycleTheme"
|
||||
@@ -96,6 +116,11 @@ const props = defineProps<{
|
||||
currentBackgroundUrl?: string
|
||||
}>()
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
openSettings: []
|
||||
}>()
|
||||
|
||||
// 状态
|
||||
const showSaveTip = ref(false)
|
||||
const showCopiedTip = ref(false)
|
||||
@@ -178,6 +203,11 @@ async function shareSearch() {
|
||||
}
|
||||
}
|
||||
|
||||
// 打开设置
|
||||
function openSettings() {
|
||||
emit('openSettings')
|
||||
}
|
||||
|
||||
// 保存背景图(使用源格式和文件名)
|
||||
async function saveBackgroundImage() {
|
||||
if (!props.currentBackgroundUrl) return
|
||||
@@ -331,6 +361,24 @@ watch(themeMode, () => {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* GitHub 按钮特殊样式 */
|
||||
.github-button {
|
||||
color: rgb(31, 41, 55);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dark .github-button {
|
||||
color: rgb(226, 232, 240);
|
||||
}
|
||||
|
||||
.github-button:hover {
|
||||
border-color: rgba(31, 41, 55, 0.5);
|
||||
}
|
||||
|
||||
.dark .github-button:hover {
|
||||
border-color: rgba(226, 232, 240, 0.5);
|
||||
}
|
||||
|
||||
/* 主题按钮特殊样式 */
|
||||
.theme-button:hover {
|
||||
border-color: rgba(245, 158, 11, 0.5);
|
||||
|
||||
Reference in New Issue
Block a user