|
|
import { defineStore } from 'pinia' |
|
|
import { ref } from 'vue' |
|
|
import { apiClient } from '@/config/api' |
|
|
|
|
|
export const useSettingsStore = defineStore('settings', () => { |
|
|
|
|
|
const oemSettings = ref({ |
|
|
siteName: 'Claude Relay Service', |
|
|
siteIcon: '', |
|
|
siteIconData: '', |
|
|
showAdminButton: true, |
|
|
updatedAt: null |
|
|
}) |
|
|
|
|
|
const loading = ref(false) |
|
|
const saving = ref(false) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loadOemSettings = async () => { |
|
|
loading.value = true |
|
|
try { |
|
|
const result = await apiClient.get('/admin/oem-settings') |
|
|
|
|
|
if (result && result.success) { |
|
|
oemSettings.value = { ...oemSettings.value, ...result.data } |
|
|
|
|
|
|
|
|
applyOemSettings() |
|
|
} |
|
|
|
|
|
return result |
|
|
} catch (error) { |
|
|
console.error('Failed to load OEM settings:', error) |
|
|
throw error |
|
|
} finally { |
|
|
loading.value = false |
|
|
} |
|
|
} |
|
|
|
|
|
const saveOemSettings = async (settings) => { |
|
|
saving.value = true |
|
|
try { |
|
|
const result = await apiClient.put('/admin/oem-settings', settings) |
|
|
|
|
|
if (result && result.success) { |
|
|
oemSettings.value = { ...oemSettings.value, ...result.data } |
|
|
|
|
|
|
|
|
applyOemSettings() |
|
|
} |
|
|
|
|
|
return result |
|
|
} catch (error) { |
|
|
console.error('Failed to save OEM settings:', error) |
|
|
throw error |
|
|
} finally { |
|
|
saving.value = false |
|
|
} |
|
|
} |
|
|
|
|
|
const resetOemSettings = async () => { |
|
|
const defaultSettings = { |
|
|
siteName: 'Claude Relay Service', |
|
|
siteIcon: '', |
|
|
siteIconData: '', |
|
|
showAdminButton: true, |
|
|
updatedAt: null |
|
|
} |
|
|
|
|
|
oemSettings.value = { ...defaultSettings } |
|
|
return await saveOemSettings(defaultSettings) |
|
|
} |
|
|
|
|
|
|
|
|
const applyOemSettings = () => { |
|
|
|
|
|
if (oemSettings.value.siteName) { |
|
|
document.title = `${oemSettings.value.siteName} - 管理后台` |
|
|
} |
|
|
|
|
|
|
|
|
if (oemSettings.value.siteIconData || oemSettings.value.siteIcon) { |
|
|
const favicon = document.querySelector('link[rel="icon"]') || document.createElement('link') |
|
|
favicon.rel = 'icon' |
|
|
favicon.href = oemSettings.value.siteIconData || oemSettings.value.siteIcon |
|
|
if (!document.querySelector('link[rel="icon"]')) { |
|
|
document.head.appendChild(favicon) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const formatDateTime = (dateString) => { |
|
|
if (!dateString) return '' |
|
|
return new Date(dateString).toLocaleString('zh-CN', { |
|
|
year: 'numeric', |
|
|
month: '2-digit', |
|
|
day: '2-digit', |
|
|
hour: '2-digit', |
|
|
minute: '2-digit', |
|
|
second: '2-digit' |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
const validateIconFile = (file) => { |
|
|
const errors = [] |
|
|
|
|
|
|
|
|
if (file.size > 350 * 1024) { |
|
|
errors.push('图标文件大小不能超过 350KB') |
|
|
} |
|
|
|
|
|
|
|
|
const allowedTypes = ['image/x-icon', 'image/png', 'image/jpeg', 'image/jpg', 'image/svg+xml'] |
|
|
if (!allowedTypes.includes(file.type)) { |
|
|
errors.push('不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件') |
|
|
} |
|
|
|
|
|
return { |
|
|
isValid: errors.length === 0, |
|
|
errors |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const fileToBase64 = (file) => { |
|
|
return new Promise((resolve, reject) => { |
|
|
const reader = new FileReader() |
|
|
reader.onload = (e) => resolve(e.target.result) |
|
|
reader.onerror = reject |
|
|
reader.readAsDataURL(file) |
|
|
}) |
|
|
} |
|
|
|
|
|
return { |
|
|
|
|
|
oemSettings, |
|
|
loading, |
|
|
saving, |
|
|
|
|
|
|
|
|
loadOemSettings, |
|
|
saveOemSettings, |
|
|
resetOemSettings, |
|
|
applyOemSettings, |
|
|
formatDateTime, |
|
|
validateIconFile, |
|
|
fileToBase64 |
|
|
} |
|
|
}) |
|
|
|