Files
urldb/web/pages/admin/system-config.vue
2025-08-02 18:07:11 +08:00

472 lines
22 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>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-100">
<!-- 全局加载状态 -->
<div v-if="pageLoading" class="fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-8 shadow-xl">
<div class="flex flex-col items-center space-y-4">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<div class="text-center">
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">正在加载...</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">请稍候正在加载系统配置</p>
</div>
</div>
</div>
</div>
<div class="">
<div class="max-w-7xl mx-auto">
<!-- 配置表单 -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<form @submit.prevent="saveConfig" class="space-y-6">
<n-tabs type="line" animated>
<n-tab-pane name="站点配置" tab="站点配置">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 网站标题 -->
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
网站标题 *
</label>
<input
v-model="config.siteTitle"
type="text"
required
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="老九网盘资源数据库"
/>
</div>
<!-- 网站描述 -->
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
网站描述
</label>
<input
v-model="config.siteDescription"
type="text"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="专业的老九网盘资源数据库"
/>
</div>
<!-- 关键词 -->
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
关键词 (用逗号分隔)
</label>
<input
v-model="config.keywords"
type="text"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="网盘,资源管理,文件分享"
/>
</div>
<!-- 版权信息 -->
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
版权信息
</label>
<input
v-model="config.copyright"
type="text"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="© 2024 老九网盘资源数据库"
/>
</div>
<!-- <div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
每页显示数量
</label>
<select
v-model.number="config.pageSize"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
>
<option value="20">20 </option>
<option value="50">50 </option>
<option value="100">100 </option>
<option value="200">200 </option>
</select>
</div> -->
<!-- 系统维护模式 -->
<div class="md:col-span-2 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">
<label class="relative inline-flex items-center cursor-pointer">
<input
v-model="config.maintenanceMode"
type="checkbox"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-red-300 dark:peer-focus:ring-red-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-red-600"></div>
</label>
</div>
</div>
</div>
</n-tab-pane>
<n-tab-pane name="功能配置" tab="功能配置">
<div class="space-y-4">
<div class="flex flex-col gap-1">
<!-- 待处理资源自动处理 -->
<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">
<label class="relative inline-flex items-center cursor-pointer">
<input
v-model="config.autoProcessReadyResources"
type="checkbox"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
<div v-if="config.autoProcessReadyResources" class="ml-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
自动处理间隔 (分钟)
</label>
<input
v-model.number="config.autoProcessInterval"
type="number"
min="1"
max="1440"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="30"
/>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
建议设置 5-60 分钟避免过于频繁的处理
</p>
</div>
</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">
<label class="relative inline-flex items-center cursor-pointer">
<input
v-model="config.autoTransferEnabled"
type="checkbox"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
<!-- 自动转存配置仅在开启时显示 -->
<div v-if="config.autoTransferEnabled" class="ml-6 space-y-4">
<!-- 自动转存限制天数 -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
自动转存限制n天内资源
</label>
<input
v-model.number="config.autoTransferLimitDays"
type="number"
min="0"
max="365"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="30"
/>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
只转存指定天数内的资源0表示不限制时间
</p>
</div>
<!-- 最小存储空间 -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
最小存储空间GB
</label>
<input
v-model.number="config.autoTransferMinSpace"
type="number"
min="100"
max="1024"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="500"
/>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
当网盘剩余空间小于此值时停止自动转存100-1024GB
</p>
</div>
</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">
<label class="relative inline-flex items-center cursor-pointer">
<input
v-model="config.autoFetchHotDramaEnabled"
type="checkbox"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
<!-- 自动处理间隔 -->
</div>
</n-tab-pane>
<n-tab-pane name="API配置" tab="API配置">
<div class="space-y-4">
<!-- API Token -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
公开API访问令牌
</label>
<div class="flex gap-2">
<input
v-model="config.apiToken"
type="text"
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="输入API Token用于公开API访问认证"
/>
<button
type="button"
@click="generateApiToken"
class="px-4 py-2 bg-orange-600 text-white rounded-md hover:bg-orange-700 transition-colors"
>
生成
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
用于公开API的访问认证建议使用随机字符串
</p>
</div>
<!-- API使用说明 -->
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200 mb-2">
<i class="fas fa-info-circle mr-1"></i>
API使用说明
</h3>
<div class="text-xs text-blue-700 dark:text-blue-300 space-y-1">
<p> 批量添加资源: POST /api/public/resources/batch-add</p>
<p> 资源搜索: GET /api/public/resources/search</p>
<p> 热门剧: GET /api/public/hot-dramas</p>
<p> 认证方式: 在请求头中添加 X-API-Token 或在查询参数中添加 api_token</p>
</div>
</div>
</div>
</n-tab-pane>
</n-tabs>
<!-- 保存按钮 -->
<div class="flex justify-end space-x-4 pt-6">
<button
type="button"
@click="resetForm"
class="px-6 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
重置
</button>
<button
type="submit"
:disabled="saving"
class="px-6 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
<i v-if="saving" class="fas fa-spinner fa-spin mr-2"></i>
{{ saving ? '保存中...' : '保存配置' }}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup>
// 设置页面布局
definePageMeta({
layout: 'admin',
ssr: false
})
import { ref, onMounted } from 'vue'
import { useSystemConfigApi } from '~/composables/useApi'
import { useSystemConfigStore } from '~/stores/systemConfig'
const systemConfigStore = useSystemConfigStore()
// API
const systemConfigApi = useSystemConfigApi()
// 响应式数据
const loading = ref(false)
const config = ref({
// SEO 配置
siteTitle: '老九网盘资源数据库',
siteDescription: '专业的老九网盘资源数据库',
keywords: '网盘,资源管理,文件分享',
author: '系统管理员',
copyright: '© 2024 老九网盘资源数据库',
// 自动处理配置
autoProcessReadyResources: false,
autoProcessInterval: 30,
autoTransferEnabled: false, // 新增
autoTransferLimitDays: 30, // 新增:自动转存限制天数
autoTransferMinSpace: 500, // 新增最小存储空间GB
autoFetchHotDramaEnabled: false, // 新增
// 其他配置
pageSize: 100,
maintenanceMode: false,
apiToken: '' // 新增
})
// 系统配置状态用于SEO
const systemConfig = ref(null)
// 页面元数据 - 移到变量声明之后
useHead({
title: () => systemConfig.value?.site_title ? `${systemConfig.value.site_title} - 系统配置` : '系统配置 - 老九网盘资源数据库',
meta: [
{
name: 'description',
content: () => systemConfig.value?.site_description || '系统配置管理页面'
},
{
name: 'keywords',
content: () => systemConfig.value?.keywords || '系统配置,管理'
},
{
name: 'author',
content: () => systemConfig.value?.author || '系统管理员'
}
]
})
// 加载配置
const loadConfig = async () => {
try {
loading.value = true
const response = await systemConfigApi.getSystemConfig()
console.log('系统配置响应:', response)
// 使用新的统一响应格式直接使用response
if (response) {
config.value = {
siteTitle: response.site_title || '老九网盘资源数据库',
siteDescription: response.site_description || '专业的老九网盘资源数据库',
keywords: response.keywords || '网盘,资源管理,文件分享',
author: response.author || '系统管理员',
copyright: response.copyright || '© 2024 老九网盘资源数据库',
autoProcessReadyResources: response.auto_process_ready_resources || false,
autoProcessInterval: response.auto_process_interval || 30,
autoTransferEnabled: response.auto_transfer_enabled || false, // 新增
autoTransferLimitDays: response.auto_transfer_limit_days || 30, // 新增:自动转存限制天数
autoTransferMinSpace: response.auto_transfer_min_space || 500, // 新增最小存储空间GB
autoFetchHotDramaEnabled: response.auto_fetch_hot_drama_enabled || false, // 新增
pageSize: response.page_size || 100,
maintenanceMode: response.maintenance_mode || false,
apiToken: response.api_token || '' // 加载API Token
}
systemConfig.value = response // 更新系统配置状态
}
} catch (error) {
console.error('加载配置失败:', error)
// 显示错误提示
} finally {
loading.value = false
}
}
// 保存配置
const saveConfig = async () => {
try {
loading.value = true
const requestData = {
site_title: config.value.siteTitle,
site_description: config.value.siteDescription,
keywords: config.value.keywords,
author: config.value.author,
copyright: config.value.copyright,
auto_process_ready_resources: config.value.autoProcessReadyResources,
auto_process_interval: config.value.autoProcessInterval,
auto_transfer_enabled: config.value.autoTransferEnabled, // 新增
auto_transfer_limit_days: config.value.autoTransferLimitDays, // 新增:自动转存限制天数
auto_transfer_min_space: config.value.autoTransferMinSpace, // 新增最小存储空间GB
auto_fetch_hot_drama_enabled: config.value.autoFetchHotDramaEnabled, // 新增
page_size: config.value.pageSize,
maintenance_mode: config.value.maintenanceMode,
api_token: config.value.apiToken // 保存API Token
}
const response = await systemConfigApi.updateSystemConfig(requestData)
// 使用新的统一响应格式直接检查response是否存在
if (response) {
alert('配置保存成功!')
await loadConfig()
// 自动更新 systemConfig store强制刷新
await systemConfigStore.initConfig(true)
} else {
alert('保存配置失败:未知错误')
}
} catch (error) {
console.error('保存配置失败:', error)
alert('保存配置失败:' + (error.message || '未知错误'))
} finally {
loading.value = false
}
}
// 重置表单
const resetForm = () => {
if (confirm('确定要重置所有配置吗?')) {
loadConfig()
}
}
// 生成API Token
const generateApiToken = () => {
const newToken = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
config.value.apiToken = newToken;
alert('新API Token已生成: ' + newToken);
};
// 页面加载时获取配置
onMounted(() => {
loadConfig()
})
</script>