Lan
2025-09-04 16:14:42 +08:00
parent f83b3d7152
commit 2e52ed61bd
25 changed files with 1678 additions and 245 deletions
+62 -68
View File
@@ -3,8 +3,10 @@ import { inject, ref } from 'vue'
import { ConfigService } from '@/services'
import type { ConfigState } from '@/types'
import { useAlertStore } from '@/stores/alertStore'
import { useI18n } from 'vue-i18n'
const isDarkMode = inject('isDarkMode')
const { t } = useI18n()
const config = ref<ConfigState>({
name: '',
@@ -150,21 +152,21 @@ refreshData()
<template>
<div class="p-6 h-screen overflow-y-auto custom-scrollbar">
<h2 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
系统设置
{{ t('admin.settings.title') }}
</h2>
<div class="space-y-6 rounded-lg shadow-md p-6" :class="[isDarkMode ? 'bg-gray-800 bg-opacity-70' : 'bg-white']">
<!-- 基本设置 -->
<section class="space-y-4">
<h3 class="text-lg font-medium mb-4" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
基本设置
{{ t('admin.settings.basicSettings') }}
</h3>
<!-- 网基本信息 -->
<div class="grid grid-cols-1 gap-6">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
网站名称
{{ t('admin.settings.siteName') }}
</label>
<input type="text" v-model="config.name"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -177,7 +179,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
网站描述
{{ t('admin.settings.websiteDescription') }}
</label>
<input type="text" v-model="config.description"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -190,10 +192,10 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
管理员密码
{{ t('admin.settings.adminPassword') }}
</label>
<div class="relative">
<input type="password" minlength="6" v-model="config.admin_token" placeholder="留空则不修改密码"
<input type="password" minlength="6" v-model="config.admin_token" :placeholder="t('admin.settings.passwordPlaceholder')"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -202,14 +204,14 @@ refreshData()
]" />
<div class="absolute inset-y-0 right-0 flex items-center pr-3 text-sm text-gray-400"
:class="[isDarkMode ? 'text-gray-500' : 'text-gray-400']">
<span class="text-xs">留空则不修改</span>
<span class="text-xs">{{ t('admin.settings.passwordNote') }}</span>
</div>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
关键词
{{ t('admin.settings.keywords') }}
</label>
<input type="text" v-model="config.keywords"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -223,7 +225,7 @@ refreshData()
<!-- 主题选择 -->
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
主题选择
{{ t('manage.settings.themeSelection') }}
</label>
<select v-model="config.themesSelect"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border appearance-none bg-no-repeat bg-right focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none cursor-pointer"
@@ -241,7 +243,7 @@ refreshData()
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
Robots.txt
{{ t('manage.settings.robotsFile') }}
</label>
<textarea v-model="config.robotsText" rows="3"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border resize-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -257,7 +259,7 @@ refreshData()
<div class="grid grid-cols-1 gap-6 mt-8">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
通知标题
{{ t('manage.settings.notificationTitle') }}
</label>
<input type="text" v-model="config.notify_title"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -270,7 +272,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
通知内容
{{ t('manage.settings.notificationContent') }}
</label>
<textarea v-model="config.notify_content" rows="3"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border resize-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -285,14 +287,14 @@ refreshData()
<!-- 存储设置 -->
<div class="space-y-4">
<h3 class="text-lg font-medium mb-4" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
存储设置
{{ t('manage.settings.storageSettings') }}
</h3>
<!-- 通知设置 -->
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
存储路径
{{ t('manage.settings.storagePath') }}
</label>
<input type="text" placeholder="留空则使用默认路径,可不填写" v-model="config.storage_path"
<input type="text" :placeholder="t('manage.settings.storagePathPlaceholder')" v-model="config.storage_path"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -304,7 +306,7 @@ refreshData()
<div class="space-y-4">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
存储方式
{{ t('manage.settings.storageMethod') }}
</label>
<select v-model="config.file_storage"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border appearance-none bg-no-repeat bg-right focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none cursor-pointer"
@@ -315,14 +317,14 @@ refreshData()
]" style="
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M7%208l3%203%203-3%22%20stroke%3D%22%236B7280%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E');
">
<option value="local">本地存储</option>
<option value="s3">S3 存储</option>
<option value="webdav">Webdav 存储</option>
<option value="local">{{ t('manage.settings.localStorage') }}</option>
<option value="s3">{{ t('manage.settings.s3Storage') }}</option>
<option value="webdav">{{ t('manage.settings.webdavStorage') }}</option>
</select>
</div>
<div class="space-y-2" v-if="config.file_storage === 'local'">
<label class="block text-sm font-medium mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
开启切片上传实验性功能还在开发中目前仅本地存储可用可能会出现未知问题
{{ t('manage.settings.chunkUploadNote') }}
</label>
<div class="flex items-center">
<button type="button" @click="config.enableChunk = config.enableChunk === 1 ? 0 : 1"
@@ -337,7 +339,7 @@ refreshData()
]" />
</button>
<span class="ml-3 text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
{{ config.enableChunk === 1 ? '已开启' : '已关闭' }}
{{ config.enableChunk === 1 ? t('common.enabled') : t('common.disabled') }}
</span>
</div>
</div>
@@ -347,7 +349,7 @@ refreshData()
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
Webdav URL
</label>
<input type="text" placeholder="请输入 Webdav URL" v-model="config.webdav_url"
<input type="text" :placeholder="t('manage.settings.webdavUrlPlaceholder')" v-model="config.webdav_url"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -360,7 +362,7 @@ refreshData()
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
Webdav Username
</label>
<input type="text" placeholder="请输入 Webdav Username" v-model="config.webdav_username"
<input type="text" :placeholder="t('manage.settings.webdavUsernamePlaceholder')" v-model="config.webdav_username"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -373,7 +375,7 @@ refreshData()
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
Webdav Password
</label>
<input type="password" placeholder="请输入 Webdav Password" v-model="config.webdav_password"
<input type="password" :placeholder="t('manage.settings.webdavPasswordPlaceholder')" v-model="config.webdav_password"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -388,7 +390,7 @@ refreshData()
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 AccessKeyId
{{ t('manage.settings.s3AccessKeyId') }}
</label>
<input type="text" v-model="config.s3_access_key_id"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -401,7 +403,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 SecretAccessKey
{{ t('manage.settings.s3SecretAccessKey') }}
</label>
<input type="password" v-model="config.s3_secret_access_key"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -414,7 +416,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 BucketName
{{ t('manage.settings.s3BucketName') }}
</label>
<input type="text" v-model="config.s3_bucket_name"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -427,7 +429,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 EndpointUrl
{{ t('manage.settings.s3EndpointUrl') }}
</label>
<input type="text" v-model="config.s3_endpoint_url"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -440,9 +442,9 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 Region Name
{{ t('manage.settings.s3RegionName') }}
</label>
<input type="text" v-model="config.s3_region_name" placeholder="auto"
<input type="text" v-model="config.s3_region_name" :placeholder="t('manage.settings.autoPlaceholder')"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
:class="[
isDarkMode
@@ -453,7 +455,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 Signature Version
{{ t('manage.settings.s3SignatureVersion') }}
</label>
<select v-model="config.s3_signature_version"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -462,14 +464,14 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400'
]">
<option value="s3v2">S3v2</option>
<option value="s3v4">S3v4</option>
<option value="s3v2">{{ t('manage.settings.s3v2') }}</option>
<option value="s3v4">{{ t('manage.settings.s3v4') }}</option>
</select>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
S3 Hostname
{{ t('manage.settings.s3Hostname') }}
</label>
<input type="text" v-model="config.s3_hostname"
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
@@ -483,7 +485,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium mb-2"
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
启用代理
{{ t('manage.settings.enableProxy') }}
</label>
<div class="flex items-center">
<button type="button" @click="config.s3_proxy = config.s3_proxy === 1 ? 0 : 1"
@@ -498,7 +500,7 @@ refreshData()
]" />
</button>
<span class="ml-3 text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
{{ config.s3_proxy === 1 ? '已开启' : '已关闭' }}
{{ config.s3_proxy === 1 ? t('common.enabled') : t('common.disabled') }}
</span>
</div>
</div>
@@ -510,13 +512,13 @@ refreshData()
<!-- 上传限制 -->
<div class="mt-8">
<h3 class="text-lg font-medium mb-4" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
上传限制
{{ t('manage.settings.uploadLimits') }}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
每分钟上传限制
{{ t('manage.settings.uploadPerMinute') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="config.uploadMinute"
@@ -526,13 +528,13 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
]" />
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">分钟</span>
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">{{ t('common.minute') }}</span>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
上传数量限制
{{ t('manage.settings.uploadCountLimit') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="config.uploadCount"
@@ -542,13 +544,13 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
]" />
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">个文件</span>
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">{{ t('common.files') }}</span>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
文件大小限制
{{ t('manage.settings.fileSizeLimit') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="fileSize"
@@ -565,16 +567,16 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400'
]">
<option value="KB">KB</option>
<option value="MB">MB</option>
<option value="GB">GB</option>
<option value="KB">{{ t('manage.settings.fileSizeUnits.kb') }}</option>
<option value="MB">{{ t('manage.settings.fileSizeUnits.mb') }}</option>
<option value="GB">{{ t('manage.settings.fileSizeUnits.gb') }}</option>
</select>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
过期方式
{{ t('manage.settings.expirationType') }}
</label>
<div class="flex flex-wrap gap-3">
<label v-for="style in ['day', 'hour', 'minute', 'forever', 'count']" :key="style"
@@ -589,15 +591,7 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-gray-300 hover:border-indigo-500'
: 'bg-white border-gray-300 text-gray-700 hover:border-indigo-500'
]">
{{
{
day: '按天',
hour: '按小时',
minute: '按分钟',
forever: '永久',
count: '按次数'
}[style]
}}
{{ t(`manage.settings.expiration.${style}`) }}
</div>
</label>
</div>
@@ -605,7 +599,7 @@ refreshData()
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
最长保存时间
{{ t('manage.settings.maxSaveTime') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="saveTime"
@@ -622,17 +616,17 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400'
]">
<option value="秒"></option>
<option value="分"></option>
<option value="时"></option>
<option value="天"></option>
<option value="秒">{{ t('common.second') }}</option>
<option value="分">{{ t('common.minute') }}</option>
<option value="时">{{ t('common.hour') }}</option>
<option value="天">{{ t('common.day') }}</option>
</select>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
游客上传
{{ t('manage.settings.guestUpload') }}
</label>
<div class="flex items-center">
<button type="button" @click="config.openUpload = config.openUpload === 1 ? 0 : 1"
@@ -647,7 +641,7 @@ refreshData()
]" />
</button>
<span class="ml-3 text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
{{ config.openUpload === 1 ? '已开启' : '已关闭' }}
{{ config.openUpload === 1 ? t('common.enabled') : t('common.disabled') }}
</span>
</div>
</div>
@@ -657,13 +651,13 @@ refreshData()
<!-- 错误限制 -->
<div class="mt-8">
<h3 class="text-lg font-medium mb-4" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
错误限制
{{ t('manage.settings.errorLimits') }}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
每分钟错误限制
{{ t('manage.settings.errorPerMinute') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="config.errorMinute"
@@ -673,13 +667,13 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
]" />
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">分钟</span>
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">{{ t('common.minute') }}</span>
</div>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">
错误次数限制
{{ t('manage.settings.errorCountLimit') }}
</label>
<div class="flex items-center space-x-2">
<input type="number" v-model="config.errorCount"
@@ -689,7 +683,7 @@ refreshData()
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
]" />
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"></span>
<span :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">{{ t('common.times') }}</span>
</div>
</div>
</div>
@@ -699,7 +693,7 @@ refreshData()
<div class="flex justify-end mt-8">
<button @click="submitSave"
class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200">
保存设置
{{ t('manage.settings.saveChanges') }}
</button>
</div>
</section>