Files
SearcjGal-frontend/src/components/SettingsModal.vue
AdingApkgg a66b4b628e feat: 增强主题支持与样式优化
* 在 `index.html` 中定义新的主题颜色变量,支持动态主题切换。
* 更新多个组件的样式以适应新主题,确保在不同模式下的视觉一致性。
* 在 `App.vue` 中初始化主题设置,提升用户体验。
* 增加主题选择功能于 `SettingsModal.vue`,允许用户实时预览和保存主题选择。
* 优化背景层和滚动条样式,提升整体视觉效果。
2025-11-21 03:16:02 +08:00

297 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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/60 dark:bg-slate-800/60 backdrop-blur-2xl backdrop-saturate-150 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/40 dark:border-slate-700/40"
@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-theme-primary/20 dark:border-slate-700">
<i class="fas fa-cog text-theme-primary dark:text-theme-accent text-xl sm:text-2xl" />
<h2 class="text-lg sm:text-xl font-bold text-gray-800 dark:text-slate-100 flex-1">设置</h2>
<button
class="w-10 h-10 flex items-center justify-center rounded-full hover:bg-theme-primary/10 text-gray-500 hover:text-theme-primary dark:text-slate-400 dark:hover:bg-slate-700/50 dark:hover:text-theme-accent transition-all duration-200"
@click="close"
>
<i class="fas fa-times text-xl" />
</button>
</div>
<!-- 内容区域 -->
<div class="flex-1 overflow-y-auto p-4 sm:p-6 custom-scrollbar">
<div class="space-y-6">
<!-- 主题配色 -->
<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-palette" style="color: var(--theme-primary)" />
<span>主题配色</span>
</h3>
<div class="space-y-4">
<p class="text-sm text-gray-600 dark:text-slate-400">
选择您喜欢的主题色温让界面更符合您的审美
</p>
<!-- 主题色卡 -->
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
<button
v-for="(preset, key) in THEME_PRESETS"
:key="key"
class="theme-card group relative p-4 rounded-xl border-2 transition-all duration-300 hover:scale-105"
:class="[
localTheme === key
? 'border-current shadow-lg'
: 'border-gray-200 dark:border-slate-700 hover:border-gray-300 dark:hover:border-slate-600'
]"
:style="{
color: preset.colors.primary
}"
@click="selectTheme(key as ThemePresetKey)"
>
<!-- 选中标记 -->
<div
v-if="localTheme === key"
class="absolute -top-2 -right-2 w-6 h-6 rounded-full flex items-center justify-center text-white text-xs shadow-lg"
:style="{ backgroundColor: preset.colors.primary }"
>
<i class="fas fa-check" />
</div>
<!-- 颜色预览 -->
<div class="flex gap-2 mb-3">
<div
class="w-8 h-8 rounded-lg shadow-md transition-transform group-hover:scale-110"
:style="{ backgroundColor: preset.colors.primary }"
/>
<div
class="w-8 h-8 rounded-lg shadow-md transition-transform group-hover:scale-110"
:style="{ backgroundColor: preset.colors.accent }"
/>
</div>
<!-- 主题名称 -->
<p class="text-sm font-medium text-gray-800 dark:text-slate-100">
{{ preset.name }}
</p>
</button>
</div>
<!-- 预览提示 -->
<div class="bg-gradient-to-r from-theme-primary/5 to-theme-accent/5 dark:from-pink-950/20 dark:to-purple-950/20 border border-theme-primary/30 dark:border-theme-primary/50 rounded-xl p-4">
<div class="flex items-start gap-3">
<i class="fas fa-lightbulb" style="color: var(--theme-primary)" />
<div class="flex-1 text-sm text-gray-700 dark:text-slate-300">
<p class="font-semibold mb-1">实时预览</p>
<p>选择主题后界面会立即更新颜色点击"保存"按钮以保留您的选择</p>
</div>
</div>
</div>
</div>
</div>
<!-- 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" style="color: var(--theme-primary)" />
<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" />
<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 text-gray-900 dark:text-slate-100 placeholder:text-gray-400 dark:placeholder:text-slate-400"
style="border-color: transparent"
@focus="$event.target.style.borderColor = 'var(--theme-primary)'"
@blur="$event.target.style.borderColor = 'transparent'"
/>
</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" />
<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-gray-200 dark:border-slate-700">
<button
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"
@click="reset"
>
<i class="fas fa-undo mr-2" />
重置
</button>
<button
class="px-6 py-2 rounded-xl text-white font-semibold shadow-lg hover:shadow-xl hover:scale-105 transition-all"
:style="{
background: `linear-gradient(to right, var(--theme-primary), var(--theme-primary-dark))`
}"
@click="save"
>
<i class="fas fa-check mr-2" />
保存
</button>
</div>
</div>
</Transition>
</div>
</Transition>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { THEME_PRESETS, DEFAULT_THEME, type ThemePresetKey } from '@/types/theme'
import { getCurrentTheme, applyThemeColors, saveTheme } from '@/utils/themeColors'
const props = defineProps<{
isOpen: boolean
customApi: string
}>()
const emit = defineEmits<{
close: []
save: [customApi: string, theme: ThemePresetKey]
}>()
const localCustomApi = ref(props.customApi)
const localTheme = ref<ThemePresetKey>(getCurrentTheme())
const originalTheme = ref<ThemePresetKey>(getCurrentTheme())
// 监听外部变化
watch(() => props.customApi, (newValue) => {
localCustomApi.value = newValue
})
// 监听打开状态,同步数据
watch(() => props.isOpen, (isOpen) => {
if (isOpen) {
localCustomApi.value = props.customApi
localTheme.value = getCurrentTheme()
originalTheme.value = getCurrentTheme()
}
})
function selectTheme(themeKey: ThemePresetKey) {
localTheme.value = themeKey
// 实时预览
applyThemeColors(themeKey)
}
function close() {
// 如果取消,恢复原来的主题
if (localTheme.value !== originalTheme.value) {
applyThemeColors(originalTheme.value)
}
emit('close')
}
function save() {
// 保存主题
saveTheme(localTheme.value)
emit('save', localCustomApi.value, localTheme.value)
close()
}
function reset() {
localCustomApi.value = ''
localTheme.value = DEFAULT_THEME
applyThemeColors(DEFAULT_THEME)
}
</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>