feat: Phase 2 - 核心状态管理、搜索组件

- apps/web/src/stores/posts.ts: 文章状态管理 (列表、详情、分类、标签、搜索)
- apps/web/src/stores/settings.ts: 站点设置状态管理 (音乐、SEO、功能开关)
- apps/web/src/stores/music.ts: 全局音乐播放器状态
- apps/web/src/components/SearchModal.vue: 搜索弹窗组件 (热门关键词、历史记录、实时建议)
This commit is contained in:
2026-07-24 14:59:11 +08:00
parent 5030808c55
commit 2091fa648e
6 changed files with 1244 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { pb } from '@/composables/usePocketBase'
export const useSettingsStore = defineStore('settings', () => {
const settings = ref(null)
const loading = ref(false)
const error = ref(null)
const siteName = computed(() => settings.value?.site_name || '云升数码')
const siteSubtitle = computed(() => settings.value?.site_subtitle || '专业数码评测与技术分享')
const siteDescription = computed(() => settings.value?.site_description || '')
const siteUrl = computed(() => settings.value?.site_url || '')
const logo = computed(() => settings.value?.logo)
const logoDark = computed(() => settings.value?.logo_dark)
const favicon = computed(() => settings.value?.favicon)
const footerText = computed(() => settings.value?.footer_text || '')
const footerCopyright = computed(() => settings.value?.footer_copyright || '')
const icpNumber = computed(() => settings.value?.icp_number || '')
const socialLinks = computed(() => {
try {
return JSON.parse(settings.value?.social_links || '{}')
} catch {
return {}
}
})
const backgroundMusic = computed(() => ({
enabled: settings.value?.background_music_enabled ?? true,
type: settings.value?.background_music_type || 'local',
file: settings.value?.background_music_file,
url: settings.value?.background_music_url,
title: settings.value?.background_music_title || '背景音乐',
artist: settings.value?.background_music_artist || '云升数码',
cover: settings.value?.background_music_cover,
volume: settings.value?.background_music_volume ?? 0.5,
autoplay: settings.value?.background_music_autoplay ?? false,
loop: settings.value?.background_music_loop ?? true,
}))
const commentConfig = computed(() => ({
enabled: settings.value?.comment_enabled ?? false,
system: settings.value?.comment_system || 'giscus',
config: settings.value?.comment_config ? JSON.parse(settings.value?.comment_config) : {},
}))
const analyticsConfig = computed(() => ({
googleAnalyticsId: settings.value?.google_analytics_id || '',
baiduAnalyticsId: settings.value?.baidu_analytics_id || '',
customHeadCode: settings.value?.custom_head_code || '',
customBodyCode: settings.value?.custom_body_code || '',
}))
const featureFlags = computed(() => ({
maintenanceMode: settings.value?.maintenance_mode ?? false,
maintenanceMessage: settings.value?.maintenance_message || '',
registrationEnabled: settings.value?.registration_enabled ?? false,
rssEnabled: settings.value?.rss_enabled ?? true,
searchEnabled: settings.value?.search_enabled ?? true,
cacheTtl: settings.value?.cache_ttl ?? 3600,
imageOptimization: settings.value?.image_optimization ?? true,
cdnUrl: settings.value?.cdn_url || '',
}))
async function fetchSettings() {
loading.value = true
error.value = null
try {
const result = await pb.collection('site_settings').getFirstListItem('', {
fields: '*',
})
settings.value = result
return result
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
async function updateSettings(data) {
if (!settings.value) return
try {
const result = await pb.collection('site_settings').update(settings.value.id, data)
settings.value = result
return result
} catch (err) {
error.value = err.message
throw err
}
}
return {
settings,
loading,
error,
siteName,
siteSubtitle,
siteDescription,
siteUrl,
logo,
logoDark,
favicon,
footerText,
footerCopyright,
icpNumber,
socialLinks,
backgroundMusic,
commentConfig,
analyticsConfig,
featureFlags,
fetchSettings,
updateSettings,
}
})