feat: 添加SFTP空间使用统计功能

- 新增 /api/user/sftp-usage API,递归统计SFTP服务器空间使用情况
- 返回总使用空间、文件数、文件夹数
- 在设置页面显示SFTP空间统计信息
- 支持手动刷新统计数据
- 适配"仅SFTP"和"用户可选"两种权限模式的UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-27 13:39:51 +08:00
parent 86ed1f4040
commit 4f9b281039
3 changed files with 203 additions and 29 deletions

View File

@@ -1281,6 +1281,29 @@
<div v-else style="font-size: 13px; color: #b45309; background: #fff7ed; border: 1px dashed #fcd34d; padding: 10px; border-radius: 8px; margin-bottom: 10px;">
<i class="fas fa-exclamation-circle"></i> 先填写 SFTP 连接信息再切换
</div>
<!-- SFTP空间使用统计user_choice模式 -->
<div v-if="user?.has_ftp_config" style="margin-bottom: 10px; padding: 10px; background: #f8fafc; border-radius: 8px; border: 1px solid #e2e8f0;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<span style="font-size: 12px; color: #64748b;">空间统计</span>
<button
style="background: none; border: none; color: #4b5fc9; cursor: pointer; font-size: 12px; padding: 2px 6px;"
@click.stop="loadSftpUsage()"
:disabled="sftpUsageLoading">
<i :class="sftpUsageLoading ? 'fas fa-sync-alt fa-spin' : 'fas fa-sync-alt'"></i>
</button>
</div>
<div v-if="sftpUsageLoading && !sftpUsage" style="text-align: center; color: #667eea; font-size: 12px;">
<i class="fas fa-spinner fa-spin"></i> 统计中...
</div>
<div v-else-if="sftpUsageError" style="font-size: 12px; color: #c53030;">
<i class="fas fa-exclamation-triangle"></i> {{ sftpUsageError }}
</div>
<div v-else-if="sftpUsage" style="font-size: 13px; font-weight: 600; color: #4b5fc9;">
{{ sftpUsage.totalSizeFormatted }}
<span style="font-weight: 400; color: #64748b; font-size: 12px;">{{ sftpUsage.fileCount }} 文件)</span>
</div>
<div v-else style="font-size: 12px; color: #94a3b8;">点击刷新查看</div>
</div>
<div style="margin-top: auto;">
<button
class="btn"
@@ -1342,34 +1365,7 @@
</div>
</div>
<!-- SFTP存储信息 - 仅SFTP权限 -->
<div v-if="user && !user.is_admin && storagePermission === 'sftp_only' && user.has_ftp_config" style="margin-bottom: 40px;">
<h3 style="margin-bottom: 20px;">
<i class="fas fa-server"></i> SFTP存储
</h3>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px;">
<div style="margin-bottom: 15px;">
<span style="font-weight: 600; color: #333;">存储方式: </span>
<span style="color: #667eea; font-weight: 600;">SFTP存储</span>
<span style="margin-left: 10px; padding: 4px 12px; background: #17a2b8; color: white; border-radius: 12px; font-size: 12px;">
<i class="fas fa-lock"></i> 仅SFTP
</span>
</div>
<div style="margin-bottom: 15px;">
<span style="font-weight: 600; color: #333;">服务器: </span>
<span>{{ user.ftp_host }}:{{ user.ftp_port }}</span>
</div>
<div style="padding: 10px; background: #d1ecf1; border-left: 4px solid #0c5460; border-radius: 6px; font-size: 13px; color: #0c5460;">
<i class="fas fa-info-circle"></i>
<strong>说明:</strong> 管理员已将您的存储权限设置为"仅SFTP存储"您的文件存储在远程SFTP服务器上。如需使用本地存储请联系管理员修改权限设置。
</div>
</div>
</div>
<!-- SFTP 概览 / 配置入口 -->
<!-- SFTP 概览 / 配置入口 - 仅SFTP权限 -->
<div v-if="user && !user.is_admin && storagePermission === 'sftp_only'" style="margin-bottom: 40px;">
<h3 style="margin-bottom: 20px;">
<i class="fas fa-server"></i> SFTP存储
@@ -1389,6 +1385,68 @@
<i class="fas fa-tools"></i> 配置 / 修改 SFTP
</button>
</div>
<!-- 服务器信息 -->
<div v-if="user.has_ftp_config" style="margin-bottom: 12px; padding: 12px; background: white; border-radius: 10px; border: 1px solid #e2e8f0;">
<div style="font-weight: 600; color: #333; margin-bottom: 8px;">
<i class="fas fa-server" style="color: #667eea;"></i> 服务器信息
</div>
<div style="color: #475569; font-size: 14px;">
{{ user.ftp_host }}:{{ user.ftp_port }}
</div>
</div>
<!-- SFTP空间使用统计 -->
<div v-if="user.has_ftp_config" style="margin-bottom: 12px; padding: 12px; background: white; border-radius: 10px; border: 1px solid #e2e8f0;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<div style="font-weight: 600; color: #333;">
<i class="fas fa-chart-pie" style="color: #667eea;"></i> 空间使用统计
</div>
<button
class="btn btn-secondary"
style="padding: 4px 10px; font-size: 12px; border-radius: 6px;"
@click="loadSftpUsage()"
:disabled="sftpUsageLoading">
<i :class="sftpUsageLoading ? 'fas fa-sync-alt fa-spin' : 'fas fa-sync-alt'"></i>
{{ sftpUsageLoading ? '统计中...' : '刷新' }}
</button>
</div>
<!-- 加载中 -->
<div v-if="sftpUsageLoading && !sftpUsage" style="text-align: center; padding: 20px; color: #667eea;">
<i class="fas fa-spinner fa-spin" style="font-size: 24px;"></i>
<div style="margin-top: 8px; font-size: 13px;">正在统计 SFTP 空间使用情况...</div>
<div style="margin-top: 4px; font-size: 12px; color: #999;">(文件较多时可能需要一些时间)</div>
</div>
<!-- 错误提示 -->
<div v-else-if="sftpUsageError" style="padding: 12px; background: #fff5f5; border-radius: 8px; color: #c53030; font-size: 13px;">
<i class="fas fa-exclamation-triangle"></i> {{ sftpUsageError }}
</div>
<!-- 统计结果 -->
<div v-else-if="sftpUsage" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px;">
<div style="text-align: center; padding: 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;">
<div style="font-size: 20px; font-weight: 700;">{{ sftpUsage.totalSizeFormatted }}</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 4px;">总使用空间</div>
</div>
<div style="text-align: center; padding: 12px; background: #f0f9ff; border-radius: 10px; border: 1px solid #e0f2fe;">
<div style="font-size: 20px; font-weight: 700; color: #0369a1;">{{ sftpUsage.fileCount }}</div>
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">文件数</div>
</div>
<div style="text-align: center; padding: 12px; background: #fefce8; border-radius: 10px; border: 1px solid #fef08a;">
<div style="font-size: 20px; font-weight: 700; color: #a16207;">{{ sftpUsage.dirCount }}</div>
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">文件夹数</div>
</div>
</div>
<!-- 未统计提示 -->
<div v-else style="text-align: center; padding: 16px; color: #64748b; font-size: 13px;">
<i class="fas fa-database" style="font-size: 24px; color: #cbd5e1; margin-bottom: 8px; display: block;"></i>
点击"刷新"按钮统计 SFTP 空间使用情况
</div>
</div>
<div style="padding: 10px; background: #eef2ff; border-radius: 10px; color: #374151; font-size: 13px;">
<i class="fas fa-info-circle" style="color: #4b5fc9;"></i>
数据存储在你的 SFTP 服务器上,如需切换回本地请联系管理员调整权限。

View File

@@ -220,7 +220,12 @@ createApp({
// SFTP配置引导弹窗
showSftpGuideModal: false,
showSftpConfigModal: false
showSftpConfigModal: false,
// SFTP空间使用统计
sftpUsage: null, // { totalSize, totalSizeFormatted, fileCount, dirCount }
sftpUsageLoading: false,
sftpUsageError: null
};
},
@@ -1840,6 +1845,35 @@ handleDragLeave(e) {
console.error('加载用户资料失败:', error);
}
},
// 加载SFTP空间使用统计
async loadSftpUsage() {
// 仅在用户已配置SFTP时才加载
if (!this.user?.has_ftp_config) {
this.sftpUsage = null;
return;
}
this.sftpUsageLoading = true;
this.sftpUsageError = null;
try {
const response = await axios.get(
`${this.apiBase}/api/user/sftp-usage`,
{ headers: { Authorization: `Bearer ${this.token}` } }
);
if (response.data.success) {
this.sftpUsage = response.data.usage;
}
} catch (error) {
console.error('获取SFTP空间使用情况失败:', error);
this.sftpUsageError = error.response?.data?.message || '获取失败';
} finally {
this.sftpUsageLoading = false;
}
},
// 启动定期检查用户配置
startProfileSync() {
// 清除已有的定时器