feat(quota): add downloadable traffic quota with local/OSS/share metering
This commit is contained in:
@@ -3118,18 +3118,19 @@
|
||||
<div class="card">
|
||||
<h3 style="margin-bottom: 20px;">用户管理</h3>
|
||||
<div class="admin-users-table-wrap" style="overflow-x: auto;">
|
||||
<table class="admin-users-table" style="width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 760px;">
|
||||
<table class="admin-users-table" style="width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 900px;">
|
||||
<thead>
|
||||
<tr style="background: rgba(255,255,255,0.05);">
|
||||
<th style="padding: 10px; text-align: left; width: 4%;">ID</th>
|
||||
<th style="padding: 10px; text-align: left; width: 10%;">用户名</th>
|
||||
<th style="padding: 10px; text-align: center; width: 10%;">角色</th>
|
||||
<th style="padding: 10px; text-align: left; width: 9%;">用户名</th>
|
||||
<th style="padding: 10px; text-align: center; width: 9%;">角色</th>
|
||||
<th style="padding: 10px; text-align: left; width: 14%;">邮箱</th>
|
||||
<th style="padding: 10px; text-align: center; width: 9%;">存储权限</th>
|
||||
<th style="padding: 10px; text-align: center; width: 9%;">当前存储</th>
|
||||
<th style="padding: 10px; text-align: center; width: 12%;">配额使用</th>
|
||||
<th style="padding: 10px; text-align: center; width: 8%;">状态</th>
|
||||
<th style="padding: 10px; text-align: center; width: 24%;">操作</th>
|
||||
<th style="padding: 10px; text-align: center; width: 8%;">存储权限</th>
|
||||
<th style="padding: 10px; text-align: center; width: 8%;">当前存储</th>
|
||||
<th style="padding: 10px; text-align: center; width: 11%;">存储配额</th>
|
||||
<th style="padding: 10px; text-align: center; width: 11%;">下载流量</th>
|
||||
<th style="padding: 10px; text-align: center; width: 7%;">状态</th>
|
||||
<th style="padding: 10px; text-align: center; width: 19%;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -3176,6 +3177,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 10px; text-align: center; font-size: 12px;">
|
||||
<div v-if="u.download_traffic_quota > 0">
|
||||
<div>{{ formatBytes(u.download_traffic_used || 0) }} / {{ formatBytes(u.download_traffic_quota) }}</div>
|
||||
<div style="font-size: 11px; color: var(--text-muted);">
|
||||
{{ getAdminUserDownloadQuotaPercentage(u) }}%
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>{{ formatBytes(u.download_traffic_used || 0) }} / 不限</div>
|
||||
<div style="font-size: 11px; color: var(--text-muted);">
|
||||
不限
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 10px; text-align: center;">
|
||||
<span v-if="u.is_banned" style="color: #ef4444; font-weight: 600;">已封禁</span>
|
||||
<span v-else-if="!u.is_verified" style="color: #f59e0b; font-weight: 600;">未激活</span>
|
||||
@@ -3517,13 +3532,43 @@
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">下载流量配额</label>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
|
||||
<label style="display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary);">
|
||||
<input type="checkbox" v-model="editStorageForm.download_quota_unlimited">
|
||||
不限流量
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="!editStorageForm.download_quota_unlimited" style="display: flex; gap: 10px;">
|
||||
<input
|
||||
type="number"
|
||||
class="form-input"
|
||||
v-model.number="editStorageForm.download_traffic_quota_value"
|
||||
min="1"
|
||||
max="10240"
|
||||
step="1"
|
||||
style="flex: 1;">
|
||||
<select class="form-input" v-model="editStorageForm.download_quota_unit" style="width: 100px;">
|
||||
<option value="MB">MB</option>
|
||||
<option value="GB">GB</option>
|
||||
<option value="TB">TB</option>
|
||||
</select>
|
||||
</div>
|
||||
<small style="color: var(--text-secondary); font-size: 12px; margin-top: 5px; display: block;">
|
||||
下载流量范围: 不限 或 1MB - 10TB(按实际下载字节扣减)
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px; background: rgba(255,255,255,0.03); border-radius: 6px; margin-bottom: 20px;">
|
||||
<div style="font-size: 13px; color: var(--text-secondary); line-height: 1.6;">
|
||||
<strong style="color: var(--text-primary);">配额说明:</strong><br>
|
||||
• 本地默认配额: 1GB<br>
|
||||
• 当前本地配额: {{ editStorageForm.local_storage_quota_value }} {{ editStorageForm.quota_unit }}
|
||||
({{ editStorageForm.quota_unit === 'GB' ? (editStorageForm.local_storage_quota_value * 1024).toFixed(0) : editStorageForm.local_storage_quota_value }} MB)<br>
|
||||
• 当前 OSS 配额: {{ editStorageForm.oss_storage_quota_value + ' ' + editStorageForm.oss_quota_unit }}
|
||||
• 当前 OSS 配额: {{ editStorageForm.oss_storage_quota_value + ' ' + editStorageForm.oss_quota_unit }}<br>
|
||||
• 已用下载流量: {{ formatBytes(editStorageForm.download_traffic_used || 0) }}<br>
|
||||
• 当前下载流量配额: {{ editStorageForm.download_quota_unlimited ? '不限' : (editStorageForm.download_traffic_quota_value + ' ' + editStorageForm.download_quota_unit) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4884,6 +4929,11 @@
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .file-list-table td.file-list-action-col {
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .file-list-action-col .btn {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -5885,7 +5935,15 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: nowrap;
|
||||
flex-wrap: wrap;
|
||||
margin-left: auto;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-content-head-actions > * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-action-btn {
|
||||
@@ -6242,6 +6300,177 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="app.js?v=20260212007"></script>
|
||||
<style>
|
||||
/* ===== Files Header Action Layout v3 ===== */
|
||||
@media (min-width: 992px) {
|
||||
body.enterprise-netdisk .files-content-head.files-content-head-compact {
|
||||
display: grid;
|
||||
grid-template-columns: max-content minmax(220px, 1fr) minmax(420px, 560px);
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-content-head-meta {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-content-head-actions {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-action-btn,
|
||||
body.enterprise-netdisk .files-head-folder-btn,
|
||||
body.enterprise-netdisk .files-head-view-toggle {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-action-btn,
|
||||
body.enterprise-netdisk .files-head-view-toggle .btn {
|
||||
height: 36px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-view-toggle {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-view-toggle .btn {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) and (min-width: 769px) {
|
||||
body.enterprise-netdisk .files-content-head-actions {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-action-btn,
|
||||
body.enterprise-netdisk .files-head-folder-btn,
|
||||
body.enterprise-netdisk .files-head-view-toggle {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-action-btn,
|
||||
body.enterprise-netdisk .files-head-view-toggle .btn {
|
||||
height: 34px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .files-head-view-toggle {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* ===== Mobile Overflow Guard v1 ===== */
|
||||
@media (max-width: 768px) {
|
||||
body.enterprise-netdisk .card,
|
||||
body.enterprise-netdisk .files-view-card,
|
||||
body.enterprise-netdisk .files-content-shell,
|
||||
body.enterprise-netdisk .files-content-head,
|
||||
body.enterprise-netdisk .files-content-head-meta,
|
||||
body.enterprise-netdisk .files-content-head-actions,
|
||||
body.enterprise-netdisk .settings-section,
|
||||
body.enterprise-netdisk .settings-panel,
|
||||
body.enterprise-netdisk .settings-subpanel,
|
||||
body.enterprise-netdisk .settings-inline-tip,
|
||||
body.enterprise-netdisk .settings-storage-switch,
|
||||
body.enterprise-netdisk .settings-storage-grid,
|
||||
body.enterprise-netdisk .settings-storage-option {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-storage-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-storage-head {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-storage-head > div {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-storage-option [style*="display: flex"][style*="justify-content: space-between"] {
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-storage-option button,
|
||||
body.enterprise-netdisk .settings-oss-panel button,
|
||||
body.enterprise-netdisk .settings-local-panel button {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-page-subtitle,
|
||||
body.enterprise-netdisk .settings-inline-tip,
|
||||
body.enterprise-netdisk .files-content-title,
|
||||
body.enterprise-netdisk .files-head-usage-progress-text {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .file-list {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .file-list-table {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .modal-content {
|
||||
width: calc(100vw - 16px);
|
||||
max-width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body.enterprise-netdisk .settings-panel,
|
||||
body.enterprise-netdisk .settings-subpanel {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
body.enterprise-netdisk .settings-inline-tip {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="app.js?v=20260217003"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user