优化页面效果

This commit is contained in:
root 2024-12-08 22:39:14 +08:00
parent 04afcb743e
commit 9e4ec1d8df
8 changed files with 634 additions and 20 deletions

View file

@ -1 +1 @@
VITE_API_BASE_URL=http://admin.gitdl.cn:50050 VITE_API_BASE_URL=http://api.ifileproxy.gitdl.cn

View file

@ -4,7 +4,35 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理系统</title> <!-- SEO Meta Tags -->
<title>iFileProxy - 文件代理下载系统</title>
<meta name="description" content="iFileProxy是一个开源的文件代理下载系统支持Github加速下载、离线下载等功能完全免费使用。" />
<meta name="keywords" content="iFileProxy,文件代理,Github加速,离线下载,代理下载,开源项目" />
<meta name="author" content="iFileProxy Team" />
<!-- Open Graph Meta Tags -->
<meta property="og:title" content="iFileProxy - 文件代理下载系统" />
<meta property="og:description" content="开源免费的文件代理下载系统支持Github加速下载、离线下载等功能。" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://gitdl.cn" />
<meta property="og:image" content="/og-image.png" />
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="iFileProxy - 文件代理下载系统" />
<meta name="twitter:description" content="开源免费的文件代理下载系统支持Github加速下载、离线下载等功能。" />
<meta name="twitter:image" content="/og-image.png" />
<!-- Mobile Meta Tags -->
<meta name="theme-color" content="#1890ff" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- Robots Meta Tag -->
<meta name="robots" content="index, follow" />
<!-- Canonical URL -->
<link rel="canonical" href="https://gitdl.cn" />
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View file

@ -131,5 +131,12 @@ export const TaskAPI = {
*/ */
searchTasks(params) { searchTasks(params) {
return request.get('/Management/SearchTasks', { params }) return request.get('/Management/SearchTasks', { params })
},
/**
* 获取IP访问限制数据
*/
getIPAccessLimitData() {
return request.get('/Management/GetIPAccessLimitData')
} }
} }

View file

@ -1,5 +1,5 @@
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://admin.gitdl.cn:50050' export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://admin.gitdl.cn:50050'
export const DOWNLOAD_BASE_URL = import.meta.env.VITE_DOWNLOAD_BASE_URL || 'https://gitdl.cn'
export const API_ENDPOINTS = { export const API_ENDPOINTS = {
TASK: { TASK: {
LIST: '/Management/GetTaskList', LIST: '/Management/GetTaskList',

View file

@ -9,7 +9,7 @@
</template> </template>
<script setup> <script setup>
import { ref, provide, watch } from 'vue' import { ref, provide, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const loading = ref(false) const loading = ref(false)
@ -27,6 +27,33 @@ watch(
}, },
{ immediate: true } { immediate: true }
) )
onMounted(() => {
//
const schema = {
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "iFileProxy",
"applicationCategory": "DownloadManager",
"operatingSystem": "Web",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"description": "开源免费的文件代理下载系统支持Github加速下载等功能。",
"url": "https://gitdl.cn",
"author": {
"@type": "Organization",
"name": "iFileProxy Team"
}
}
const script = document.createElement('script')
script.type = 'application/ld+json'
script.text = JSON.stringify(schema)
document.head.appendChild(script)
})
</script> </script>
<style scoped> <style scoped>

View file

@ -88,8 +88,8 @@ const routes = [
name: 'VisitorDownload', name: 'VisitorDownload',
component: () => import('../views/visitor/DownloadPage.vue'), component: () => import('../views/visitor/DownloadPage.vue'),
meta: { meta: {
public: true, title: 'iFileProxy - 文件代理下载',
title: 'iFileProxy - 文件代理下载' description: '免费开源的文件代理下载系统支持Github加速下载等功能。'
} }
}, },
{ {
@ -97,8 +97,8 @@ const routes = [
name: 'VisitorTasks', name: 'VisitorTasks',
component: () => import('../views/visitor/TaskList.vue'), component: () => import('../views/visitor/TaskList.vue'),
meta: { meta: {
public: true, title: 'iFileProxy - 任务列表',
title: 'iFileProxy - 任务列表' description: '查看和管理您的下载任务进度。'
} }
}, },
{ {
@ -170,10 +170,14 @@ router.beforeEach(async (to, from, next) => {
// 添加全局路由守卫来更新标题 // 添加全局路由守卫来更新标题
router.afterEach((to) => { router.afterEach((to) => {
// 设置默认标题 // 更新标题
const defaultTitle = '后台管理系统' document.title = to.meta.title || 'iFileProxy - 文件代理下载系统'
// 如果路由有title元信息则使用它否则使用默认标题
document.title = to.meta.title || defaultTitle // 更新描述
const metaDescription = document.querySelector('meta[name="description"]')
if (metaDescription) {
metaDescription.setAttribute('content', to.meta.description || 'iFileProxy是一个开源的文件代理下载系统支持Github加速下载、离线下载等功能完全免费使用。')
}
}) })
export default router export default router

View file

@ -223,13 +223,51 @@
</div> </div>
</div> </div>
</el-tab-pane> </el-tab-pane>
<!-- 添加 IP 访问统计标签页 -->
<el-tab-pane label="IP访问统计" name="ipStats">
<div class="ip-stats-content">
<div class="stats-header">
<h3>IP访问统计</h3>
<el-button type="primary" @click="refreshIPStats">
<el-icon><Refresh /></el-icon>刷新
</el-button>
</div>
<el-table
v-loading="ipStatsLoading"
:data="ipStatsList"
style="width: 100%"
border>
<el-table-column prop="date" label="日期" width="120" />
<el-table-column prop="ip" label="IP地址" min-width="160">
<template #default="{ row }">
<el-link
type="primary"
:href="`https://ip138.com/iplookup.php?ip=${row.ip}`"
target="_blank"
:underline="false">
{{ row.ip }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="count" label="访问次数" width="100">
<template #default="{ row }">
<el-tag :type="getAccessCountType(row.count)">
{{ row.count }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { import {
Monitor, Refresh, Search, RefreshLeft, Monitor, Refresh, Search, RefreshLeft,
@ -428,6 +466,9 @@ const refreshDebugInfo = async () => {
onMounted(() => { onMounted(() => {
fetchLogs() fetchLogs()
refreshDebugInfo() refreshDebugInfo()
if (activeTab.value === 'ipStats') {
fetchIPStats()
}
}) })
const exceptionDialogVisible = ref(false) const exceptionDialogVisible = ref(false)
@ -485,6 +526,61 @@ const isLongMessage = (message) => {
const toggleMessage = (logId) => { const toggleMessage = (logId) => {
expandedMessages.value[logId] = !expandedMessages.value[logId] expandedMessages.value[logId] = !expandedMessages.value[logId]
} }
// IP
const ipStatsLoading = ref(false)
const ipStatsList = ref([])
// IP 访
const fetchIPStats = async () => {
ipStatsLoading.value = true
try {
const response = await TaskAPI.getIPAccessLimitData()
if (response.retcode === 0) {
//
const list = []
Object.entries(response.data).forEach(([date, ips]) => {
Object.entries(ips).forEach(([ip, count]) => {
list.push({
date,
ip,
count
})
})
})
ipStatsList.value = list.sort((a, b) => {
// 访
const dateCompare = b.date.localeCompare(a.date)
if (dateCompare !== 0) return dateCompare
return b.count - a.count
})
}
} catch (error) {
console.error('获取IP访问统计失败:', error)
ElMessage.error('获取IP访问统计失败')
} finally {
ipStatsLoading.value = false
}
}
// IP
const refreshIPStats = () => {
fetchIPStats()
}
// 访
const getAccessCountType = (count) => {
if (count <= 10) return 'success'
if (count <= 30) return 'warning'
return 'danger'
}
//
watch(activeTab, (newTab) => {
if (newTab === 'ipStats') {
fetchIPStats()
}
})
</script> </script>
<style scoped> <style scoped>
@ -723,4 +819,31 @@ const toggleMessage = (logId) => {
color: #909399; color: #909399;
font-size: 12px; font-size: 12px;
} }
.ip-stats-content {
margin-top: 20px;
}
.stats-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.stats-header {
flex-direction: column;
gap: 12px;
h3 {
margin: 0;
}
.el-button {
width: 100%;
}
}
}
</style> </style>

View file

@ -3,31 +3,64 @@
<el-card class="download-card"> <el-card class="download-card">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<h2>iFileProxy 离线下载</h2> <h1>iFileProxy 文件代理下载</h1>
<div class="domain-highlight">
<div class="domain">
git<span class="dl-text">dl</span>.cn
</div>
<div class="domain-meaning">
<span class="meaning-item">
<el-icon><Download /></el-icon>
<span><span class="dl-text">D</span>own<span class="dl-text">l</span>oad(下载)</span>
</span>
<span class="divider">|</span>
<span class="meaning-item">
<el-icon><Connection /></el-icon>
<span><span class="dl-text">D</span>ai<span class="dl-text">l</span>i(代理)</span>
</span>
</div>
<div class="domain-desc">
简单易记的文件加速下载域名
</div>
</div>
<div class="subtitle"> <div class="subtitle">
本程序可实现文件代理下载如加速Github和一些墙内无法访问的文件等程序开源免费 开源免费的文件代理下载系统支持多种下载方式解决网络访问受限问题
</div> </div>
</div> </div>
</template> </template>
<el-form @submit.prevent="handleSubmit"> <el-form @submit.prevent="handleSubmit">
<el-form-item label="下载方式">
<el-radio-group v-model="downloadType">
<el-radio label="offline">离线下载</el-radio>
<el-radio label="stream">直接下载</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="目标文件URL"> <el-form-item label="目标文件URL">
<el-input <el-input
v-model="downloadUrl" v-model="downloadUrl"
placeholder="请输入要下载的文件链接" placeholder="请输入要下载的文件链接"
:disabled="loading" /> :disabled="loading">
<template #append v-if="downloadType === 'stream'">
<el-button @click="handleStreamDownload">直接下载</el-button>
</template>
</el-input>
</el-form-item> </el-form-item>
<el-button <el-button
v-if="downloadType === 'offline'"
type="primary" type="primary"
@click="handleSubmit" @click="handleSubmit"
:loading="loading" :loading="loading"
class="submit-btn"> class="submit-btn">
提交 提交离线任务
</el-button> </el-button>
</el-form> </el-form>
<br />
<div class="status-info">
<div class="server-status">
<el-descriptions :column="2" border> <el-descriptions :column="2" border>
<el-descriptions-item label="运行中任务"> <el-descriptions-item label="运行中任务">
{{ serverLoad.running }} {{ serverLoad.running }}
@ -40,11 +73,11 @@
<div class="action-links"> <div class="action-links">
<el-link type="primary" @click="$router.push('/visitor/tasks')"> <el-link type="primary" @click="$router.push('/visitor/tasks')">
查询文件下载任务状态 查询任务状态
</el-link> </el-link>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-link type="danger" @click="$router.push('/visitor/delete')"> <el-link type="danger" @click="$router.push('/visitor/delete')">
删除我的访客信息 删除访客信息
</el-link> </el-link>
</div> </div>
@ -54,6 +87,157 @@
type="info" type="info"
:closable="false" /> :closable="false" />
</div> </div>
<div class="features">
<div class="features-title">主要功能</div>
<el-row :gutter="20">
<el-col :span="8" :xs="24" :sm="12" :md="8" class="feature-item">
<el-card shadow="hover">
<template #header>
<div class="feature-header">
<el-icon><Download /></el-icon>
<span>多种下载方式</span>
</div>
</template>
<ul>
<li>离线下载任务</li>
<li>直接流式下载</li>
<li>支持内容缓存,节省存储</li>
<li>断点续传支持</li>
</ul>
</el-card>
</el-col>
<el-col :span="8" :xs="24" :sm="12" :md="8" class="feature-item">
<el-card shadow="hover">
<template #header>
<div class="feature-header">
<el-icon><Connection /></el-icon>
<span>代理加速</span>
</div>
</template>
<ul>
<li>GitHub 文件加速</li>
<li>超大文件代理支持</li>
<li>通用 HTTP(S) 代理</li>
<li>Git 克隆支持</li>
</ul>
</el-card>
</el-col>
<el-col :span="8" :xs="24" :sm="12" :md="8" class="feature-item">
<el-card shadow="hover">
<template #header>
<div class="feature-header">
<el-icon><Lock /></el-icon>
<span>安全可靠</span>
</div>
</template>
<ul>
<li>文件哈希校验</li>
<li>智能访问控制</li>
<li>HTTPS 加密传输</li>
<li>设备指纹验证</li>
</ul>
</el-card>
</el-col>
</el-row>
</div>
<div class="usage">
<div class="usage-title">快速上手</div>
<el-row :gutter="20">
<el-col :span="24" class="usage-item">
<el-card shadow="hover">
<template #header>
<div class="usage-header">
<el-tag type="success" effect="dark">方式一直接下载</el-tag>
<span class="usage-desc">适用于小文件快速下载无需等待直接在浏览器打开</span>
</div>
</template>
<div class="usage-content">
<div class="example-title">
<el-icon><Link /></el-icon>
<span>在任意链接前加上 <code>{{ DOWNLOAD_BASE_URL }}</code></span>
</div>
<div class="example-box">
<div class="example-label">示例链接</div>
<el-input
readonly
:value="`${DOWNLOAD_BASE_URL}/https://github.com/user/repo/raw/master/example.zip`">
<template #append>
<el-button @click="copyText(`${DOWNLOAD_BASE_URL}/https://github.com/user/repo/raw/master/example.zip`)">
复制
</el-button>
</template>
</el-input>
</div>
</div>
</el-card>
</el-col>
<el-col :span="24" class="usage-item">
<el-card shadow="hover">
<template #header>
<div class="usage-header">
<el-tag type="primary" effect="dark">方式二Git克隆加速</el-tag>
<span class="usage-desc">加速 GitHub 仓库克隆解决连接超时问题</span>
</div>
</template>
<div class="usage-content">
<div class="example-title">
<el-icon><Document /></el-icon>
<span>替换 git clone 命令中的域名为 <code>gitdl.cn</code></span>
</div>
<div class="example-box">
<div class="example-label">原始命令</div>
<el-input
readonly
value="git clone https://github.com/user/example.git"
class="original-command" />
<div class="example-label">加速命令</div>
<el-input
readonly
:value="`git clone ${DOWNLOAD_BASE_URL}/https://github.com/user/example.git`">
<template #append>
<el-button @click="copyText(`git clone ${DOWNLOAD_BASE_URL}/https://github.com/user/example.git`)">
复制
</el-button>
</template>
</el-input>
<div class="example-label">或者</div>
<el-input
readonly
:value="`git clone ${DOWNLOAD_BASE_URL}/github.com/user/example.git`">
<template #append>
<el-button @click="copyText(`git clone ${DOWNLOAD_BASE_URL}/github.com/user/example.git`)">
复制
</el-button>
</template>
</el-input>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<div class="project-info">
<div class="info-title">项目信息</div>
<p>
本服务完全免费服务器配置 1Gbps 带宽项目已开源欢迎贡献代码
</p>
<div class="repo-links">
<el-link type="primary" href="https://git.linxi.info/sunxianglin_admin/iFileProxyAdmin" target="_blank">
前端仓库
</el-link>
<el-divider direction="vertical" />
<el-link type="primary" href="https://git.linxi.info/sunxianglin_admin/iFileProxy" target="_blank">
后端仓库
</el-link>
</div>
</div>
</el-card> </el-card>
</div> </div>
</template> </template>
@ -63,6 +247,8 @@ import { ref, onMounted, inject } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getRandomTip } from '@/utils/visitorTips' import { getRandomTip } from '@/utils/visitorTips'
import { VisitorAPI } from '@/api/visitor' import { VisitorAPI } from '@/api/visitor'
import { Download, Connection, Lock, Link, Document } from '@element-plus/icons-vue'
import { DOWNLOAD_BASE_URL } from '@/config/api.config'
const loading = inject('visitorLoading') const loading = inject('visitorLoading')
const downloadUrl = ref('') const downloadUrl = ref('')
@ -72,6 +258,7 @@ const serverLoad = ref({
}) })
const currentTip = ref(getRandomTip()) const currentTip = ref(getRandomTip())
const downloadType = ref('offline')
// //
const fetchServerLoad = async () => { const fetchServerLoad = async () => {
@ -108,6 +295,19 @@ const handleSubmit = async () => {
} }
} }
const handleStreamDownload = () => {
if (!downloadUrl.value) {
ElMessage.warning('请输入下载链接')
return
}
window.open(`${DOWNLOAD_BASE_URL}/${downloadUrl.value}`, '_blank')
}
const copyText = (text) => {
navigator.clipboard.writeText(text)
ElMessage.success('复制成功')
}
onMounted(() => { onMounted(() => {
fetchServerLoad() fetchServerLoad()
}) })
@ -163,4 +363,229 @@ onMounted(() => {
padding: 0 10px; padding: 0 10px;
} }
} }
.features {
margin: 30px 0;
}
.features-title,
.usage-title,
.info-title {
font-size: 18px;
font-weight: 500;
margin-bottom: 20px;
color: #303133;
}
.feature-item {
margin-bottom: 20px;
}
.feature-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: 500;
}
.feature-item ul {
padding-left: 20px;
margin: 0;
}
.feature-item li {
margin: 8px 0;
color: #606266;
}
.usage-content {
padding: 16px;
}
.usage-content p {
margin-bottom: 12px;
color: #606266;
}
.project-info {
margin: 30px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 4px;
}
.repo-links {
margin-top: 12px;
display: flex;
align-items: center;
gap: 12px;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.feature-item {
margin-bottom: 16px;
}
.usage-content {
padding: 12px;
}
.features-title,
.usage-title,
.info-title {
font-size: 16px;
margin-bottom: 16px;
}
}
.domain-highlight {
margin: 16px 0;
padding: 16px;
background: linear-gradient(135deg, #e6f7ff, #f0f5ff);
border-radius: 8px;
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.domain {
font-size: 36px;
font-weight: bold;
font-family: 'Monaco', 'Courier New', monospace;
letter-spacing: 1px;
color: #333;
}
.dl-text {
color: #f56c6c;
position: relative;
display: inline-block;
transition: all 0.3s ease;
font-weight: bold;
}
.dl-text:hover {
transform: scale(1.1);
text-shadow: 1px 1px 2px rgba(245,108,108,0.2);
}
.domain-meaning {
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
color: #666;
}
.meaning-item {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.8);
border-radius: 4px;
font-family: 'Monaco', 'Courier New', monospace;
letter-spacing: 0.5px;
}
.divider {
color: #999;
font-weight: 300;
}
.domain-desc {
font-size: 14px;
color: #666;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.domain {
font-size: 32px;
}
.domain-meaning {
font-size: 13px;
flex-direction: column;
gap: 8px;
}
.divider {
display: none;
}
.domain-highlight {
margin: 12px 0;
padding: 12px;
}
}
.usage-item {
margin-bottom: 20px;
}
.usage-header {
display: flex;
align-items: center;
gap: 12px;
}
.usage-desc {
color: #666;
font-size: 14px;
}
.example-title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
color: #303133;
font-weight: 500;
}
.example-title code {
background: #f5f7fa;
padding: 2px 6px;
border-radius: 4px;
color: #f56c6c;
font-family: 'Monaco', 'Courier New', monospace;
}
.example-box {
background: #f8f9fa;
padding: 16px;
border-radius: 4px;
}
.example-label {
color: #909399;
margin-bottom: 8px;
font-size: 13px;
}
.original-command {
margin-bottom: 16px;
opacity: 0.6;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.usage-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.example-box {
padding: 12px;
}
.usage-desc {
font-size: 13px;
}
}
</style> </style>