update: 完善新后台

This commit is contained in:
ctwj
2025-08-08 01:52:57 +08:00
parent 667338368a
commit be66667890
5 changed files with 696 additions and 405 deletions

View File

@@ -376,10 +376,22 @@ const dataManagementItems = ref([
// 系统配置菜单项
const systemConfigItems = ref([
{
to: '/admin/system-config',
label: '系统配置',
icon: 'fas fa-cog',
active: (route: any) => route.path.startsWith('/admin/system-config')
to: '/admin/site-config',
label: '站点配置',
icon: 'fas fa-globe',
active: (route: any) => route.path.startsWith('/admin/site-config')
},
{
to: '/admin/feature-config',
label: '功能配置',
icon: 'fas fa-sliders-h',
active: (route: any) => route.path.startsWith('/admin/feature-config')
},
{
to: '/admin/dev-config',
label: '开发配置',
icon: 'fas fa-code',
active: (route: any) => route.path.startsWith('/admin/dev-config')
},
{
to: '/admin/users',
@@ -416,7 +428,7 @@ const autoExpandCurrentGroup = () => {
// 检查当前页面属于哪个分组并展开
if (currentPath.startsWith('/admin/resources') || currentPath.startsWith('/admin/ready-resources') || currentPath.startsWith('/admin/tags') || currentPath.startsWith('/admin/categories') || currentPath.startsWith('/admin/accounts')) {
expandedGroups.value.dataManagement = true
} else if (currentPath.startsWith('/admin/system-config') || currentPath.startsWith('/admin/users')) {
} else if (currentPath.startsWith('/admin/site-config') || currentPath.startsWith('/admin/feature-config') || currentPath.startsWith('/admin/dev-config') || currentPath.startsWith('/admin/users')) {
expandedGroups.value.systemConfig = true
} else if (currentPath.startsWith('/admin/hot-dramas')) {
expandedGroups.value.operation = true
@@ -438,7 +450,7 @@ watch(() => useRoute().path, (newPath) => {
// 根据新路径展开对应分组
if (newPath.startsWith('/admin/resources') || newPath.startsWith('/admin/ready-resources') || newPath.startsWith('/admin/tags') || newPath.startsWith('/admin/categories') || newPath.startsWith('/admin/accounts')) {
expandedGroups.value.dataManagement = true
} else if (newPath.startsWith('/admin/system-config') || newPath.startsWith('/admin/users')) {
} else if (newPath.startsWith('/admin/site-config') || newPath.startsWith('/admin/feature-config') || newPath.startsWith('/admin/dev-config') || newPath.startsWith('/admin/users')) {
expandedGroups.value.systemConfig = true
} else if (newPath.startsWith('/admin/hot-dramas')) {
expandedGroups.value.operation = true

View File

@@ -0,0 +1,286 @@
<template>
<div class="space-y-6">
<!-- 页面标题 -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">开发配置</h1>
<p class="text-gray-600 dark:text-gray-400">管理API和开发相关配置</p>
</div>
<n-button type="primary" @click="saveConfig" :loading="saving">
<template #icon>
<i class="fas fa-save"></i>
</template>
保存配置
</n-button>
</div>
<!-- 配置表单 -->
<n-card>
<div class="space-y-6">
<!-- API Token -->
<div>
<n-form-item label="公开API访问令牌" path="api_token">
<div class="flex gap-2">
<n-input
v-model:value="configForm.api_token"
type="password"
placeholder="输入API Token用于公开API访问认证"
show-password-on="click"
/>
<n-button
v-if="!configForm.api_token"
type="primary"
@click="generateApiToken"
>
生成
</n-button>
<template v-else>
<n-button
type="primary"
@click="copyApiToken"
>
复制
</n-button>
<n-button
type="warning"
@click="regenerateApiToken"
>
重新生成
</n-button>
</template>
</div>
<template #help>
API Token用于公开API的访问认证请妥善保管
</template>
</n-form-item>
</div>
<!-- API文档链接 -->
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<h3 class="text-lg font-medium text-blue-900 dark:text-blue-100 mb-2">
API文档
</h3>
<p class="text-sm text-blue-700 dark:text-blue-300 mb-3">
查看完整的API文档和使用说明
</p>
<n-button type="primary" @click="openApiDocs">
<template #icon>
<i class="fas fa-book"></i>
</template>
查看API文档
</n-button>
</div>
</div>
</n-card>
</div>
</template>
<script setup lang="ts">
// 设置页面布局
definePageMeta({
layout: 'admin',
ssr: false
})
const notification = useNotification()
const saving = ref(false)
// 配置表单数据
const configForm = ref({
api_token: ''
})
// 获取系统配置
const fetchConfig = async () => {
try {
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
const response = await systemConfigApi.getSystemConfig()
if (response) {
configForm.value = {
api_token: response.api_token || ''
}
}
} catch (error) {
console.error('获取系统配置失败:', error)
notification.error({
content: '获取系统配置失败',
duration: 3000
})
}
}
// 保存配置
const saveConfig = async () => {
try {
saving.value = true
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
await systemConfigApi.updateSystemConfig({
api_token: configForm.value.api_token
})
notification.success({
content: '开发配置保存成功',
duration: 3000
})
} catch (error) {
console.error('保存开发配置失败:', error)
notification.error({
content: '保存开发配置失败',
duration: 3000
})
} finally {
saving.value = false
}
}
// 生成API Token
const generateApiToken = async () => {
try {
const token = Math.random().toString(36).substring(2) + Date.now().toString(36)
configForm.value.api_token = token
notification.success({
content: 'API Token生成成功',
duration: 3000
})
} catch (error) {
console.error('生成API Token失败:', error)
notification.error({
content: '生成API Token失败',
duration: 3000
})
}
}
// 复制API Token
const copyApiToken = async () => {
try {
await navigator.clipboard.writeText(configForm.value.api_token)
notification.success({
content: 'API Token已复制到剪贴板',
duration: 3000
})
} catch (error) {
console.error('复制API Token失败:', error)
notification.error({
content: '复制API Token失败',
duration: 3000
})
}
}
// 重新生成API Token
const regenerateApiToken = async () => {
try {
const token = Math.random().toString(36).substring(2) + Date.now().toString(36)
configForm.value.api_token = token
notification.success({
content: 'API Token重新生成成功',
duration: 3000
})
} catch (error) {
console.error('重新生成API Token失败:', error)
notification.error({
content: '重新生成API Token失败',
duration: 3000
})
}
}
// 打开API文档
const openApiDocs = () => {
window.open('/api-docs', '_blank')
}
// 打开API测试工具
const openApiTest = () => {
window.open('/api-test', '_blank')
}
// 导出配置
const exportConfig = async () => {
try {
const configData = {
api_token: configForm.value.api_token,
export_time: new Date().toISOString()
}
const blob = new Blob([JSON.stringify(configData, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `dev-config-${new Date().toISOString().split('T')[0]}.json`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
notification.success({
content: '配置导出成功',
duration: 3000
})
} catch (error) {
console.error('导出配置失败:', error)
notification.error({
content: '导出配置失败',
duration: 3000
})
}
}
// 导入配置
const importConfig = () => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.json'
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (file) {
try {
const text = await file.text()
const configData = JSON.parse(text)
if (configData.api_token) {
configForm.value.api_token = configData.api_token
notification.success({
content: '配置导入成功',
duration: 3000
})
} else {
notification.error({
content: '配置文件格式错误',
duration: 3000
})
}
} catch (error) {
console.error('导入配置失败:', error)
notification.error({
content: '导入配置失败',
duration: 3000
})
}
}
}
input.click()
}
// 页面加载时获取配置
onMounted(() => {
fetchConfig()
})
// 设置页面标题
useHead({
title: '开发配置 - 老九网盘资源数据库'
})
</script>
<style scoped>
/* 自定义样式 */
</style>

View File

@@ -0,0 +1,199 @@
<template>
<div class="space-y-6">
<!-- 页面标题 -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">功能配置</h1>
<p class="text-gray-600 dark:text-gray-400">管理系统功能开关和参数设置</p>
</div>
<n-button type="primary" @click="saveConfig" :loading="saving">
<template #icon>
<i class="fas fa-save"></i>
</template>
保存配置
</n-button>
</div>
<!-- 配置表单 -->
<n-card>
<div class="space-y-6">
<!-- 自动处理 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
待处理资源自动处理
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动处理待处理的资源无需手动操作
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.auto_process_enabled" />
</div>
</div>
<!-- 自动处理间隔 -->
<div v-if="configForm.auto_process_enabled" class="ml-6">
<n-form-item label="自动处理间隔 (分钟)" path="auto_process_interval">
<n-input
v-model:value="configForm.auto_process_interval"
type="text"
placeholder="30"
/>
<template #help>
建议设置 5-60 分钟避免过于频繁的处理
</template>
</n-form-item>
</div>
<!-- 自动转存 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
自动转存
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动转存资源到其他网盘平台
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.auto_transfer_enabled" />
</div>
</div>
<!-- 自动转存配置 -->
<div v-if="configForm.auto_transfer_enabled" class="ml-6 space-y-4">
<n-form-item label="自动转存限制n天内资源" path="auto_transfer_limit_days">
<n-input
v-model:value="configForm.auto_transfer_limit_days"
type="text"
placeholder="30"
/>
<template #help>
只转存指定天数内的资源0表示不限制时间
</template>
</n-form-item>
<n-form-item label="最小存储空间GB" path="auto_transfer_min_space">
<n-input
v-model:value="configForm.auto_transfer_min_space"
type="text"
placeholder="500"
/>
<template #help>
当网盘剩余空间小于此值时停止自动转存100-1024GB
</template>
</n-form-item>
</div>
<!-- 热播剧自动获取 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
自动拉取热播剧
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动从豆瓣获取热播剧信息
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.hot_drama_auto_fetch" />
</div>
</div>
</div>
</n-card>
</div>
</template>
<script setup lang="ts">
// 设置页面布局
definePageMeta({
layout: 'admin',
ssr: false
})
const notification = useNotification()
const saving = ref(false)
// 配置表单数据
const configForm = ref({
auto_process_enabled: false,
auto_process_interval: 30,
auto_transfer_enabled: false,
auto_transfer_limit_days: 30,
auto_transfer_min_space: 500,
hot_drama_auto_fetch: false
})
// 获取系统配置
const fetchConfig = async () => {
try {
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
const response = await systemConfigApi.getSystemConfig()
if (response) {
configForm.value = {
auto_process_enabled: response.auto_process_ready_resources || false,
auto_process_interval: response.auto_process_interval || 30,
auto_transfer_enabled: response.auto_transfer_enabled || false,
auto_transfer_limit_days: response.auto_transfer_limit_days || 30,
auto_transfer_min_space: response.auto_transfer_min_space || 500,
hot_drama_auto_fetch: response.auto_fetch_hot_drama_enabled || false
}
}
} catch (error) {
console.error('获取系统配置失败:', error)
notification.error({
content: '获取系统配置失败',
duration: 3000
})
}
}
// 保存配置
const saveConfig = async () => {
try {
saving.value = true
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
await systemConfigApi.updateSystemConfig({
auto_process_ready_resources: configForm.value.auto_process_enabled,
auto_process_interval: configForm.value.auto_process_interval,
auto_transfer_enabled: configForm.value.auto_transfer_enabled,
auto_transfer_limit_days: configForm.value.auto_transfer_limit_days,
auto_transfer_min_space: configForm.value.auto_transfer_min_space,
auto_fetch_hot_drama_enabled: configForm.value.hot_drama_auto_fetch
})
notification.success({
content: '功能配置保存成功',
duration: 3000
})
} catch (error) {
console.error('保存功能配置失败:', error)
notification.error({
content: '保存功能配置失败',
duration: 3000
})
} finally {
saving.value = false
}
}
// 页面加载时获取配置
onMounted(() => {
fetchConfig()
})
// 设置页面标题
useHead({
title: '功能配置 - 老九网盘资源数据库'
})
</script>
<style scoped>
/* 自定义样式 */
</style>

View File

@@ -0,0 +1,193 @@
<template>
<div class="space-y-6">
<!-- 页面标题 -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">站点配置</h1>
<p class="text-gray-600 dark:text-gray-400">管理网站基本信息和设置</p>
</div>
<n-button type="primary" @click="saveConfig" :loading="saving">
<template #icon>
<i class="fas fa-save"></i>
</template>
保存配置
</n-button>
</div>
<!-- 配置表单 -->
<n-card>
<n-form
ref="formRef"
:model="configForm"
:rules="rules"
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging"
>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 网站标题 -->
<n-form-item label="网站标题" path="site_title">
<n-input
v-model:value="configForm.site_title"
placeholder="请输入网站标题"
/>
</n-form-item>
<!-- 网站描述 -->
<n-form-item label="网站描述" path="site_description">
<n-input
v-model:value="configForm.site_description"
placeholder="请输入网站描述"
/>
</n-form-item>
<!-- 关键词 -->
<n-form-item label="关键词" path="keywords">
<n-input
v-model:value="configForm.keywords"
placeholder="请输入关键词,用逗号分隔"
/>
</n-form-item>
<!-- 版权信息 -->
<n-form-item label="版权信息" path="copyright">
<n-input
v-model:value="configForm.copyright"
placeholder="请输入版权信息"
/>
</n-form-item>
<!-- 维护模式 -->
<n-form-item label="维护模式" path="maintenance_mode">
<n-switch v-model:value="configForm.maintenance_mode" />
<template #help>
开启后网站将显示维护页面
</template>
</n-form-item>
<!-- 违禁词 -->
<n-form-item label="违禁词" path="forbidden_words" class="md:col-span-2">
<n-input
v-model:value="configForm.forbidden_words"
placeholder="请输入违禁词,用逗号分隔"
type="textarea"
:rows="4"
/>
<template #help>
包含这些词汇的资源将被过滤
</template>
</n-form-item>
</div>
</n-form>
</n-card>
</div>
</template>
<script setup lang="ts">
// 设置页面布局
definePageMeta({
layout: 'admin',
ssr: false
})
const notification = useNotification()
const formRef = ref()
const saving = ref(false)
// 配置表单数据
const configForm = ref({
site_title: '',
site_description: '',
keywords: '',
copyright: '',
maintenance_mode: false,
forbidden_words: ''
})
// 表单验证规则
const rules = {
site_title: {
required: true,
message: '请输入网站标题',
trigger: 'blur'
},
site_description: {
required: true,
message: '请输入网站描述',
trigger: 'blur'
}
}
// 获取系统配置
const fetchConfig = async () => {
try {
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
const response = await systemConfigApi.getSystemConfig()
if (response) {
configForm.value = {
site_title: response.site_title || '',
site_description: response.site_description || '',
keywords: response.keywords || '',
copyright: response.copyright || '',
maintenance_mode: response.maintenance_mode || false,
forbidden_words: response.forbidden_words || ''
}
}
} catch (error) {
console.error('获取系统配置失败:', error)
notification.error({
content: '获取系统配置失败',
duration: 3000
})
}
}
// 保存配置
const saveConfig = async () => {
try {
await formRef.value?.validate()
saving.value = true
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
await systemConfigApi.updateSystemConfig({
site_title: configForm.value.site_title,
site_description: configForm.value.site_description,
keywords: configForm.value.keywords,
copyright: configForm.value.copyright,
maintenance_mode: configForm.value.maintenance_mode,
forbidden_words: configForm.value.forbidden_words
})
notification.success({
content: '站点配置保存成功',
duration: 3000
})
} catch (error) {
console.error('保存站点配置失败:', error)
notification.error({
content: '保存站点配置失败',
duration: 3000
})
} finally {
saving.value = false
}
}
// 页面加载时获取配置
onMounted(() => {
fetchConfig()
})
// 设置页面标题
useHead({
title: '站点配置 - 老九网盘资源数据库'
})
</script>
<style scoped>
/* 自定义样式 */
</style>

View File

@@ -1,399 +0,0 @@
<template>
<div class="space-y-6">
<!-- 页面标题 -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">系统配置</h1>
<p class="text-gray-600 dark:text-gray-400">管理系统配置和设置</p>
</div>
<n-button type="primary" @click="saveConfig" :loading="saving">
<template #icon>
<i class="fas fa-save"></i>
</template>
保存配置
</n-button>
</div>
<!-- 配置表单 -->
<n-card>
<n-tabs type="line" animated>
<!-- 站点配置 -->
<n-tab-pane name="站点配置" tab="站点配置">
<n-form
ref="formRef"
:model="configForm"
:rules="rules"
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging"
>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 网站标题 -->
<n-form-item label="网站标题" path="site_title">
<n-input
v-model:value="configForm.site_title"
placeholder="请输入网站标题"
/>
</n-form-item>
<!-- 网站描述 -->
<n-form-item label="网站描述" path="site_description">
<n-input
v-model:value="configForm.site_description"
placeholder="请输入网站描述"
/>
</n-form-item>
<!-- 关键词 -->
<n-form-item label="关键词" path="keywords">
<n-input
v-model:value="configForm.keywords"
placeholder="请输入关键词,用逗号分隔"
/>
</n-form-item>
<!-- 版权信息 -->
<n-form-item label="版权信息" path="copyright">
<n-input
v-model:value="configForm.copyright"
placeholder="请输入版权信息"
/>
</n-form-item>
<!-- 维护模式 -->
<n-form-item label="维护模式" path="maintenance_mode">
<n-switch v-model:value="configForm.maintenance_mode" />
<template #help>
开启后网站将显示维护页面
</template>
</n-form-item>
<!-- 违禁词 -->
<n-form-item label="违禁词" path="forbidden_words" class="md:col-span-2">
<n-input
v-model:value="configForm.forbidden_words"
placeholder="请输入违禁词,用逗号分隔"
type="textarea"
:rows="4"
/>
<template #help>
包含这些词汇的资源将被过滤
</template>
</n-form-item>
</div>
</n-form>
</n-tab-pane>
<!-- 功能配置 -->
<n-tab-pane name="功能配置" tab="功能配置">
<div class="space-y-6">
<!-- 自动处理 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
待处理资源自动处理
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动处理待处理的资源无需手动操作
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.auto_process_enabled" />
</div>
</div>
<!-- 自动处理间隔 -->
<div v-if="configForm.auto_process_enabled" class="ml-6">
<n-form-item label="自动处理间隔 (分钟)" path="auto_process_interval">
<n-input
v-model:value="configForm.auto_process_interval"
type="text"
placeholder="30"
/>
<template #help>
建议设置 5-60 分钟避免过于频繁的处理
</template>
</n-form-item>
</div>
<!-- 自动转存 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
自动转存
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动转存资源到其他网盘平台
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.auto_transfer_enabled" />
</div>
</div>
<!-- 自动转存配置 -->
<div v-if="configForm.auto_transfer_enabled" class="ml-6 space-y-4">
<n-form-item label="自动转存限制n天内资源" path="auto_transfer_limit_days">
<n-input
v-model:value="configForm.auto_transfer_limit_days"
type="text"
placeholder="30"
/>
<template #help>
只转存指定天数内的资源0表示不限制时间
</template>
</n-form-item>
<n-form-item label="最小存储空间GB" path="auto_transfer_min_space">
<n-input
v-model:value="configForm.auto_transfer_min_space"
type="text"
placeholder="500"
/>
<template #help>
当网盘剩余空间小于此值时停止自动转存100-1024GB
</template>
</n-form-item>
</div>
<!-- 热播剧自动获取 -->
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div class="flex-1">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
自动拉取热播剧
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
开启后系统将自动从豆瓣获取热播剧信息
</p>
</div>
<div class="ml-4">
<n-switch v-model:value="configForm.hot_drama_auto_fetch" />
</div>
</div>
</div>
</n-tab-pane>
<!-- API配置 -->
<n-tab-pane name="API配置" tab="API配置">
<div class="space-y-6">
<!-- API Token -->
<div>
<n-form-item label="公开API访问令牌" path="api_token">
<div class="flex gap-2">
<n-input
v-model:value="configForm.api_token"
type="password"
placeholder="输入API Token用于公开API访问认证"
show-password-on="click"
/>
<n-button
v-if="!configForm.api_token"
type="primary"
@click="generateApiToken"
>
生成
</n-button>
<template v-else>
<n-button
type="primary"
@click="copyApiToken"
>
复制
</n-button>
<n-button
type="default"
@click="generateApiToken"
>
重新生成
</n-button>
</template>
</div>
<template #help>
用于公开API的访问认证建议使用随机字符串
</template>
</n-form-item>
</div>
<!-- API使用说明 -->
<n-card>
<template #header>
<span class="text-lg font-semibold">API使用说明</span>
</template>
<div class="space-y-2 text-sm">
<p><strong>批量添加资源:</strong> POST /api/public/resources/batch-add</p>
<p><strong>资源搜索:</strong> GET /api/public/resources/search</p>
<p><strong>热门剧:</strong> GET /api/public/hot-dramas</p>
</div>
</n-card>
</div>
</n-tab-pane>
</n-tabs>
</n-card>
<!-- 系统状态 -->
<n-card>
<template #header>
<span class="text-lg font-semibold">系统状态</span>
</template>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<div class="flex items-center">
<i class="fas fa-server text-green-600 text-xl mr-3"></i>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">系统状态</p>
<p class="text-lg font-semibold text-green-600">正常运行</p>
</div>
</div>
</div>
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<div class="flex items-center">
<i class="fas fa-database text-blue-600 text-xl mr-3"></i>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">数据库</p>
<p class="text-lg font-semibold text-blue-600">连接正常</p>
</div>
</div>
</div>
<div class="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<div class="flex items-center">
<i class="fas fa-clock text-yellow-600 text-xl mr-3"></i>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">自动处理</p>
<p class="text-lg font-semibold text-yellow-600">
{{ configForm.auto_process_enabled ? '已启用' : '已禁用' }}
</p>
</div>
</div>
</div>
<div class="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<div class="flex items-center">
<i class="fas fa-exchange-alt text-purple-600 text-xl mr-3"></i>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">自动转存</p>
<p class="text-lg font-semibold text-purple-600">
{{ configForm.auto_transfer_enabled ? '已启用' : '已禁用' }}
</p>
</div>
</div>
</div>
</div>
</n-card>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'admin' as any
})
// 获取运行时配置
const config = useRuntimeConfig()
// 使用API
const { useSystemConfigApi } = await import('~/composables/useApi')
const systemConfigApi = useSystemConfigApi()
// 响应式数据
const saving = ref(false)
const formRef = ref()
// 配置表单
const configForm = ref({
site_title: '',
site_description: '',
keywords: '',
copyright: '',
maintenance_mode: false,
api_token: '',
auto_process_enabled: false,
auto_process_interval: '30',
auto_transfer_enabled: false,
auto_transfer_limit_days: '30',
auto_transfer_min_space: '500',
forbidden_words: '',
hot_drama_auto_fetch: false
})
// 表单验证规则
const rules = {
site_title: {
required: true,
message: '请输入网站标题',
trigger: 'blur'
}
}
// 获取配置
const { data: configData, refresh: refreshConfig } = await useAsyncData(
'systemConfig',
() => systemConfigApi.getSystemConfig()
)
// 监听配置数据变化
watch(configData, (newData) => {
if (newData && (newData as any)?.data) {
configForm.value = {
...configForm.value,
...(newData as any).data
}
}
}, { immediate: true })
// 保存配置
const saveConfig = async () => {
try {
saving.value = true
await systemConfigApi.updateSystemConfig(configForm.value)
useNotification().success({
content: '配置保存成功',
duration: 3000
})
await refreshConfig()
} catch (error: any) {
useNotification().error({
content: error.message || '保存配置失败',
duration: 5000
})
} finally {
saving.value = false
}
}
// 生成API Token
const generateApiToken = () => {
const token = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
configForm.value.api_token = token
useNotification().success({
content: 'API Token已生成',
duration: 3000
})
}
// 复制API Token
const copyApiToken = async () => {
try {
await navigator.clipboard.writeText(configForm.value.api_token)
useNotification().success({
content: 'API Token已复制到剪贴板',
duration: 3000
})
} catch (error) {
useNotification().error({
content: '复制失败',
duration: 3000
})
}
}
</script>
<style scoped>
/* 确保Font Awesome图标正确显示 */
.fas {
font-family: 'Font Awesome 6 Free';
font-weight: 900;
}
</style>