393 lines
No EOL
9.5 KiB
Vue
393 lines
No EOL
9.5 KiB
Vue
<template>
|
|
<div class="config-view">
|
|
<el-card class="config-card">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span class="title">系统配置</span>
|
|
<el-button type="primary" @click="handleRefresh" class="refresh-btn">
|
|
<el-icon><Refresh /></el-icon>刷新配置
|
|
</el-button>
|
|
</div>
|
|
</template>
|
|
|
|
<el-tabs type="border-card">
|
|
<el-tab-pane label="下载配置">
|
|
<el-descriptions :column="isMobile ? 1 : 2" border>
|
|
<el-descriptions-item label="临时文件位置">
|
|
{{ config.Download.savePath }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="下载线程数">
|
|
{{ config.Download.threadNum }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="最大文件大小">
|
|
{{ formatFileSize(config.Download.maxAllowedFileSize) }}
|
|
<el-tooltip
|
|
content="仅针对新添加的任务 若是已经添加任务的文件则不受限制"
|
|
placement="top">
|
|
<el-icon class="ml-2"><InfoFilled /></el-icon>
|
|
</el-tooltip>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="最大并行任务">
|
|
{{ config.Download.maxParallelTasks }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="最大队列长度">
|
|
{{ config.Download.maxQueueLength }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="缓存生命周期">
|
|
{{ formatDuration(config.Download.cacheLifetime) }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="Aria2c路径">
|
|
{{ config.Download.aria2cPath }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
</el-tab-pane>
|
|
|
|
<el-tab-pane label="安全配置">
|
|
<el-descriptions :column="1" border>
|
|
<el-descriptions-item label="禁止代理的主机">
|
|
<el-tag
|
|
v-for="host in config.Security.blockedHost"
|
|
:key="host"
|
|
class="mx-1"
|
|
type="danger">
|
|
{{ host }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="禁止代理的文件">
|
|
<el-tag
|
|
v-for="file in config.Security.blockedFileName"
|
|
:key="file"
|
|
class="mx-1"
|
|
type="danger">
|
|
{{ file }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="IP黑名单">
|
|
<el-tag
|
|
v-for="ip in config.Security.blockedClientIP"
|
|
:key="ip"
|
|
class="mx-1"
|
|
type="danger">
|
|
{{ ip }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="IP每日请求限制">
|
|
{{ config.Security.dailyRequestLimitPerIP }} 次/天
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="统计请求的路径">
|
|
<el-tag
|
|
v-for="route in config.Security.routesToTrack"
|
|
:key="route"
|
|
class="mx-1"
|
|
type="info">
|
|
{{ route }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="允许不同IP下载">
|
|
<el-tag :type="config.Security.allowDifferentIPsForDownload ? 'success' : 'danger'">
|
|
{{ config.Security.allowDifferentIPsForDownload ? '是' : '否' }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
</el-tab-pane>
|
|
|
|
<el-tab-pane label="数据库配置">
|
|
<el-descriptions :column="isMobile ? 1 : 2" border>
|
|
<el-descriptions-item label="数据库主机">
|
|
{{ config.Database.Common.Host }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="端口">
|
|
{{ config.Database.Common.Port }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="用户名">
|
|
{{ config.Database.Common.User }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="密码">
|
|
********
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="连接池最大连接数">
|
|
{{ config.Database.MaxConnectionPoolSize }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="数据库" :span="isMobile ? 1 : 2">
|
|
<div class="database-tags">
|
|
<el-tag
|
|
v-for="db in config.Database.Databases"
|
|
:key="db.DatabaseName"
|
|
class="database-tag">
|
|
{{ db.DatabaseName }}
|
|
<el-tooltip
|
|
v-if="db.Description"
|
|
:content="db.Description"
|
|
placement="top">
|
|
<el-icon class="ml-2"><InfoFilled /></el-icon>
|
|
</el-tooltip>
|
|
</el-tag>
|
|
</div>
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { Refresh, InfoFilled } from '@element-plus/icons-vue'
|
|
import { TaskAPI } from '../api/task'
|
|
|
|
const config = ref({
|
|
Download: {
|
|
savePath: '',
|
|
threadNum: 0,
|
|
maxAllowedFileSize: 0,
|
|
maxParallelTasks: 0,
|
|
maxQueueLength: 0,
|
|
aria2cPath: '',
|
|
cacheLifetime: 0
|
|
},
|
|
Security: {
|
|
blockedHost: [],
|
|
blockedFileName: [],
|
|
blockedClientIP: [],
|
|
routesToTrack: [],
|
|
dailyRequestLimitPerIP: 0,
|
|
allowDifferentIPsForDownload: false
|
|
},
|
|
Database: {
|
|
Common: {
|
|
Host: '',
|
|
Port: 0,
|
|
User: '',
|
|
Password: '********'
|
|
},
|
|
Databases: []
|
|
}
|
|
})
|
|
|
|
const formatFileSize = (bytes) => {
|
|
if (!bytes) return '0 B'
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
let i = 0
|
|
while (bytes >= 1024 && i < units.length - 1) {
|
|
bytes /= 1024
|
|
i++
|
|
}
|
|
return `${bytes.toFixed(2)} ${units[i]}`
|
|
}
|
|
|
|
const formatDuration = (seconds) => {
|
|
if (seconds < 60) return `${seconds}秒`
|
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}分钟`
|
|
return `${Math.floor(seconds / 3600)}小时${Math.floor((seconds % 3600) / 60)}分钟`
|
|
}
|
|
|
|
const fetchConfig = async () => {
|
|
try {
|
|
const response = await TaskAPI.getSystemConfig()
|
|
if (response.retcode === 0) {
|
|
config.value = response.data
|
|
}
|
|
} catch (error) {
|
|
console.error('获取系统配置失败:', error)
|
|
}
|
|
}
|
|
|
|
const handleRefresh = async () => {
|
|
try {
|
|
await fetchConfig()
|
|
ElMessage.success('配置已刷新')
|
|
} catch (error) {
|
|
ElMessage.error('刷新配置失败')
|
|
}
|
|
}
|
|
|
|
// 添加移动端检测
|
|
const isMobile = computed(() => {
|
|
return window.innerWidth <= 768
|
|
})
|
|
|
|
// 监听窗口大小变化
|
|
onMounted(() => {
|
|
window.addEventListener('resize', () => {
|
|
isMobile.value = window.innerWidth <= 768
|
|
})
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', () => {
|
|
isMobile.value = window.innerWidth <= 768
|
|
})
|
|
})
|
|
|
|
onMounted(() => {
|
|
fetchConfig()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.config-view {
|
|
padding: 20px;
|
|
}
|
|
|
|
.config-card {
|
|
background: #fff;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.title {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 140px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.mx-1 {
|
|
margin: 0 4px;
|
|
}
|
|
|
|
.ml-2 {
|
|
margin-left: 4px;
|
|
}
|
|
|
|
:deep(.el-tabs__content) {
|
|
padding: 20px;
|
|
}
|
|
|
|
:deep(.el-tag) {
|
|
margin: 4px;
|
|
}
|
|
|
|
.database-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.database-tag {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
/* 移动端样式 */
|
|
@media screen and (max-width: 768px) {
|
|
.config-view {
|
|
padding: 10px;
|
|
}
|
|
|
|
.card-header {
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.title {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.refresh-btn {
|
|
width: 100%;
|
|
}
|
|
|
|
:deep(.el-descriptions) {
|
|
margin-top: 12px;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 100px;
|
|
min-width: 100px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
:deep(.el-descriptions__content) {
|
|
font-size: 12px;
|
|
word-break: break-all;
|
|
}
|
|
|
|
:deep(.el-descriptions__cell) {
|
|
padding: 12px !important;
|
|
}
|
|
|
|
:deep(.el-tag) {
|
|
font-size: 11px;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.database-tags {
|
|
gap: 6px;
|
|
}
|
|
|
|
.database-tag {
|
|
font-size: 11px;
|
|
padding: 0 6px;
|
|
}
|
|
|
|
:deep(.el-tabs__item) {
|
|
font-size: 13px;
|
|
padding: 0 12px;
|
|
}
|
|
|
|
:deep(.el-tabs__content) {
|
|
padding: 12px;
|
|
}
|
|
|
|
:deep(.el-descriptions__cell) {
|
|
padding: 12px !important;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 90px;
|
|
min-width: 90px;
|
|
font-size: 12px;
|
|
padding-right: 12px;
|
|
}
|
|
|
|
:deep(.el-descriptions__content) {
|
|
font-size: 12px;
|
|
word-break: break-all;
|
|
}
|
|
}
|
|
|
|
/* 平板端样式 */
|
|
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
|
.config-view {
|
|
padding: 15px;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 120px;
|
|
}
|
|
|
|
.database-tags {
|
|
gap: 8px;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 110px;
|
|
}
|
|
}
|
|
|
|
/* 优化标签显示 */
|
|
:deep(.el-tag) {
|
|
white-space: nowrap;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* 优化按钮图标对齐 */
|
|
:deep(.el-button .el-icon) {
|
|
margin-right: 4px;
|
|
vertical-align: middle;
|
|
}
|
|
</style> |