TenantDrive/templates/admin.html

1096 lines
55 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网盘租户系统 - 管理员面板</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet">
<style>
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100;
padding: 20px;
background-color: #f8f9fa;
}
.main-content {
margin-left: 250px;
padding: 20px;
}
.nav-link {
color: #333;
padding: 10px 15px;
border-radius: 5px;
margin-bottom: 5px;
}
.nav-link:hover {
background-color: #e9ecef;
}
.nav-link.active {
background-color: #0d6efd;
color: white;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 侧边栏 -->
<nav class="col-md-3 col-lg-2 d-md-block sidebar">
<div class="position-sticky">
<h3 class="mb-4">管理员面板</h3>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#dashboard">
<i class="bi bi-speedometer2 me-2"></i>仪表盘
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#accounts">
<i class="bi bi-cloud me-2"></i>网盘账号管理
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#links">
<i class="bi bi-link-45deg me-2"></i>外链管理
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#statistics">
<i class="bi bi-graph-up me-2"></i>统计分析
</a>
</li>
</ul>
</div>
</nav>
<!-- 主要内容区域 -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
<!-- 仪表盘 -->
<div id="dashboard" class="tab-content">
<h2 class="mb-4">系统概览</h2>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">网盘账号总数</h5>
<p class="card-text display-4">0</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">活跃外链数</h5>
<p class="card-text display-4">0</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">今日访问量</h5>
<p class="card-text display-4">0</p>
</div>
</div>
</div>
</div>
</div>
<!-- 网盘账号管理 -->
<div id="accounts" class="tab-content d-none">
<h2 class="mb-4">网盘账号管理</h2>
<!--
添加网盘账号按钮
btn: Bootstrap按钮基础类
btn-primary: 蓝色主题按钮样式
mb-3: margin-bottom为3个单位提供底部间距
data-bs-toggle="modal": 指示此按钮将触发模态框
data-bs-target="#addAccountModal": 指定要打开的模态框ID
-->
<button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#addAccountModal">
<!--
bi bi-plus-circle: Bootstrap图标库中的加号图标
me-2: margin-end为2个单位在图标右侧提供间距
-->
<i class="bi bi-plus-circle me-2"></i>添加网盘账号
</button>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>网盘类型</th>
<th>账号名称</th>
<th>状态</th>
<th>剩余容量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 账号列表将通过JavaScript动态填充 -->
<tr>
{% for user_drive in alluser_drives %}
<td>{{user_drive.provider_name}}</td>
<td>{{user_drive.provider_name}}</td>
<td><span class="badge bg-success">正常</span></td>
<td>1.5TB / 2TB</td>
<td>
<button class="btn btn-sm btn-info editudrivebtn" data-bs-toggle="modal" data-bs-target="#editudriveModal" tid="{{ user_drive.id }}"><i class="bi bi-pencil"></i></button>
<button class="btn btn-sm btn-danger deleteudrivebtn" tid="{{ user_drive.id }}"><i class="bi bi-trash"></i></button>
</td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
</div>
<!-- 外链管理 -->
<div id="links" class="tab-content d-none">
<h2 class="mb-4">外链管理</h2>
<!-- 添加外链按钮 -->
<button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#addExlinkModal">
<i class="bi bi-plus-circle me-2"></i>添加外链
</button>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>外链ID</th>
<th>关联网盘</th>
<th>创建时间</th>
<th>过期时间</th>
<th>剩余次数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 外链列表将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
</div>
<!-- 统计分析 -->
<div id="statistics" class="tab-content d-none">
<h2 class="mb-4">统计分析</h2>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">外链访问趋势</h5>
<canvas id="accessChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">网盘使用分布</h5>
<canvas id="storageChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- 添加网盘账号模态框 -->
<div class="modal fade" id="addAccountModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">添加网盘账号</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addAccountForm">
<div class="mb-3">
<label class="form-label">网盘类型</label>
<select class="form-select" name="type" id="driveType" required>
<option value="">请选择网盘类型</option>
{% set provider_options = providers %}
{% for provider in provider_options %}
<option value="{{ provider.provider_name }}" data-needs-api="{{ provider.provider_name }}">{{ provider.provider_name}}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">配置信息 (JSON)</label>
<div class="position-relative json-editor-area">
<div class="ace-editor border rounded" style="height: 200px; font-family: monospace;"></div>
<textarea class="form-control d-none json-config-textarea" name="config_json"></textarea>
<div class="position-absolute top-0 end-0 p-2">
<button class="btn btn-sm btn-outline-secondary format-json-btn" type="button">
<i class="bi bi-code-square"></i> 格式化
</button>
</div>
</div>
<div class="d-flex justify-content-between mt-1">
<div class="form-text">请输入有效的JSON格式配置信息</div>
<div class="form-text text-end"><span class="json-line-count">0</span> 行 | <span class="json-char-count">0</span> 字符</div>
</div>
<div class="json-error invalid-feedback"></div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveNewAccount">保存</button>
</div>
</div>
</div>
</div>
<!-- 编辑网盘账号模态框 -->
<div class="modal fade" id="editudriveModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑网盘驱动</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="editudriveForm">
<div class="mb-3">
<label class="form-label">配置信息 (JSON)</label>
<div class="position-relative json-editor-area">
<div class="ace-editor border rounded" style="height: 200px; font-family: monospace;"></div>
<textarea class="form-control d-none json-config-textarea" name="config_json"></textarea>
<div class="position-absolute top-0 end-0 p-2">
<button class="btn btn-sm btn-outline-secondary format-json-btn" type="button">
<i class="bi bi-code-square"></i> 格式化
</button>
</div>
</div>
<div class="d-flex justify-content-between mt-1">
<div class="form-text">请输入有效的JSON格式配置信息</div>
<div class="form-text text-end"><span class="json-line-count">0</span> 行 | <span class="json-char-count">0</span> 字符</div>
</div>
<div class="json-error invalid-feedback"></div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveAccount">保存</button>
</div>
</div>
</div>
</div>
<!-- 添加网盘外链模态框 -->
<div class="modal fade" id="addExlinkModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">添加网盘外链</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addExlinkForm">
<div class="mb-3">
<label class="form-label">网盘类型</label>
<select class="form-select" name="disk_type" id="exlinkDriveType" required>
<option value="">请选择网盘类型</option>
{% for provider in providers %}
<option value="{{ provider.provider_name }}">{{ provider.provider_name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">账号</label>
<select class="form-select" name="account_id" id="exlinkAccountId" required>
<option value="">请先选择网盘类型</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">使用次数限制</label>
<input type="number" class="form-control" name="total_quota" required min="1" value="3">
<div class="form-text">设置此外链可以被使用的最大次数</div>
</div>
<div class="mb-3">
<label class="form-label">到期时间</label>
<input type="datetime-local" class="form-control" name="expiry_time" id="exlinkExpiryTime" required>
<div class="form-text">设置此外链的有效期限,超过时间后将无法访问</div>
</div>
<div class="mb-3">
<label class="form-label">备注说明</label>
<input type="text" class="form-control" name="remarks" placeholder="可选">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveExlink">保存</button>
</div>
</div>
</div>
</div>
<!-- 删除确认对话框 -->
<div class="modal fade" id="deleteConfirmModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">确认删除</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>确定要删除此网盘账号吗?此操作不可恢复。</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" id="confirmDelete">确认删除</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js" integrity="sha512-GZ1RIgZaSc8rnco/8CXfRdCpDxRCphenIiZ2ztLy3XQfCbQUSCuk8IudvNHxkRA3oUg6q0qejgN/qqyG1duv5Q==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-language_tools.min.js"></script>
<script>
// 导航切换
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
this.classList.add('active');
const targetId = this.getAttribute('href').substring(1);
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.add('d-none');
});
document.getElementById(targetId).classList.remove('d-none');
});
});
// Bootstrap消息提示函数
function showMessage(message, type = 'success', duration = 3000) {
// 创建消息元素
const messageDiv = document.createElement('div');
// 设置样式类
messageDiv.classList.add('position-fixed', 'top-0', 'start-50', 'translate-middle-x', 'mt-3', 'p-3', 'rounded', 'shadow');
// 根据类型设置不同的样式
switch(type) {
case 'success':
messageDiv.classList.add('bg-success', 'text-white');
break;
case 'warning':
messageDiv.classList.add('bg-warning', 'text-dark');
break;
case 'error':
messageDiv.classList.add('bg-danger', 'text-white');
break;
case 'info':
messageDiv.classList.add('bg-info', 'text-white');
break;
default:
messageDiv.classList.add('bg-success', 'text-white');
}
// 设置消息内容
messageDiv.textContent = message;
// 设置z-index确保显示在最上层
messageDiv.style.zIndex = '9999';
// 添加到body
document.body.appendChild(messageDiv);
// 设置淡入效果
messageDiv.style.opacity = '0';
messageDiv.style.transition = 'opacity 0.3s ease-in-out';
setTimeout(() => {
messageDiv.style.opacity = '1';
}, 10);
// 设置定时移除
setTimeout(() => {
messageDiv.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(messageDiv);
}, 300);
}, duration);
}
// 使用示例:
// showMessage('操作成功', 'success');
// showMessage('警告信息', 'warning');
// showMessage('错误信息', 'error');
// showMessage('提示信息', 'info');
// 初始化图表
const accessCtx = document.getElementById('accessChart').getContext('2d');
new Chart(accessCtx, {
type: 'line',
data: {
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
datasets: [{
label: '访问量',
data: [0, 0, 0, 0, 0, 0, 0],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
});
const storageCtx = document.getElementById('storageChart').getContext('2d');
new Chart(storageCtx, {
type: 'doughnut',
data: {
labels: ['阿里云盘', '百度网盘'],
datasets: [{
data: [0, 0],
backgroundColor: ['#ff6384', '#36a2eb']
}]
}
});
// json编辑器处理
function initAceEditor(editorElement) {
const editor = ace.edit(editorElement);
const editorArea = editorElement.closest('.json-editor-area');
const parentContainer = editorArea.closest('.mb-3'); // Get parent container
const configTextarea = editorArea.querySelector('.json-config-textarea');
const lineCountSpan = parentContainer.querySelector('.json-line-count'); // Search from parent
const charCountSpan = parentContainer.querySelector('.json-char-count'); // Search from parent
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/json");
editor.setOptions({
fontSize: "12pt",
showPrintMargin: false,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
});
// 同步到隐藏的textarea并更新计数
editor.getSession().on('change', function() {
const value = editor.getSession().getValue();
configTextarea.value = value;
lineCountSpan.textContent = editor.getSession().getLength();
charCountSpan.textContent = value.length;
});
// 触发一次change事件以初始化计数
editor.getSession().setValue(configTextarea.value, -1);
return editor;
}
function setupJsonEditors() {
document.querySelectorAll('.ace-editor').forEach(editorElement => {
const editor = initAceEditor(editorElement);
const editorArea = editorElement.closest('.json-editor-area');
const formatButton = editorArea.querySelector('.format-json-btn');
const parentContainer = editorArea.closest('.mb-3'); // Get parent container
const errorDisplay = parentContainer.querySelector('.json-error'); // Search from parent
// 格式化按钮事件
formatButton.addEventListener('click', function() {
try {
const json = JSON.parse(editor.getSession().getValue());
editor.setValue(JSON.stringify(json, null, 2), -1);
errorDisplay.textContent = '';
errorDisplay.style.display = 'none';
editorElement.classList.remove('border-danger');
} catch (e) {
errorDisplay.textContent = '无效的JSON格式: ' + e.message;
errorDisplay.style.display = 'block';
editorElement.classList.add('border-danger');
}
});
// 默认设置一个空的JSON结构
const emptyJson = {
"provider_name": "",
"config": {},
"auth": {}
};
const jsonString = JSON.stringify(emptyJson, null, 2);
if (!editor.getValue()) {
editor.setValue(jsonString, -1);
}
});
}
document.addEventListener('DOMContentLoaded', function() {
setupJsonEditors(); // 初始化所有编辑器
bindEditButtonEvents(); // 初始化编辑按钮事件
bindDeleteButtonEvents(); // 初始化删除按钮事件
bindAddAccountEvent(); // 初始化添加账号事件
bindAddExlinkEvents(); // 初始化添加外链事件
loadExternalLinks(); // 加载外链列表
});
// 设置默认JSON模板 (Add Modal specific)
document.getElementById('driveType').addEventListener('change', function() {
const addModalEditorArea = document.querySelector('#addAccountModal .json-editor-area');
const editorElement = addModalEditorArea.querySelector('.ace-editor');
const editor = ace.edit(editorElement); // Get the Ace editor instance
const selectedType = this.value;
if (selectedType) {
let defaultJson = {};
const providers = JSON.parse('{{ providers | tojson | safe }}'); // Use safe filter
const selectedProvider = providers.find(provider => provider.provider_name === selectedType);
if (selectedProvider && selectedProvider.config_vars) {
defaultJson = selectedProvider.config_vars;
} else {
defaultJson = {
"provider_name": selectedType,
"config": { "api_key": "", "secret_key": "", "redirect_uri": "" },
"auth": { "token": "", "expires_in": 0 }
};
}
editor.setValue(JSON.stringify(defaultJson, null, 2), -1);
}
});
// 绑定编辑按钮事件
function bindEditButtonEvents() {
document.querySelectorAll('.editudrivebtn').forEach(button => {
button.addEventListener('click', function() {
const editModalEditorArea = document.querySelector('#editudriveModal .json-editor-area');
const editorElement = editModalEditorArea.querySelector('.ace-editor');
const editor = ace.edit(editorElement); // Get the Ace editor instance
const selectedtid = this.getAttribute('tid');
if (selectedtid) {
// 获取最新的账号信息
$.ajax({
url: '/admin/user_drive/get',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ id: selectedtid }),
success: function(response) {
if (response.status && response.data) {
let selectedUdrive = null;
// 查找匹配的用户驱动
if (Array.isArray(response.data)) {
selectedUdrive = response.data.find(drive => drive.id == selectedtid);
} else if (response.data.id == selectedtid) {
selectedUdrive = response.data;
}
if (selectedUdrive && selectedUdrive.login_config) {
let configData = selectedUdrive.login_config;
// 如果是字符串格式,解析为对象
if (typeof configData === 'string') {
try {
configData = JSON.parse(configData);
} catch (e) {
console.error('解析配置JSON失败:', e);
}
}
editor.setValue(JSON.stringify(configData, null, 2), -1);
} else {
const defaultJson = {
"provider_name": selectedtid,
"config": { "api_key": "", "secret_key": "", "redirect_uri": "" },
"auth": { "token": "", "expires_in": 0 }
};
editor.setValue(JSON.stringify(defaultJson, null, 2), -1);
}
// 存储选中的ID
document.querySelector('#editudriveModal #saveAccount').setAttribute('data-tid', selectedtid);
}
},
error: function(error) {
console.error('获取账号信息出错:', error);
showMessage('获取账号信息时发生错误', 'error');
// 使用默认值
const defaultJson = {
"provider_name": selectedtid,
"config": { "api_key": "", "secret_key": "", "redirect_uri": "" },
"auth": { "token": "", "expires_in": 0 }
};
editor.setValue(JSON.stringify(defaultJson, null, 2), -1);
document.querySelector('#editudriveModal #saveAccount').setAttribute('data-tid', selectedtid);
}
});
}
});
});
}
// 编辑用户驱动保存事件
$('#editudriveModal #saveAccount').on('click', function() {
const editModalEditorArea = document.querySelector('#editudriveModal .json-editor-area');
const editorElement = editModalEditorArea.querySelector('.ace-editor');
const editor = ace.edit(editorElement);
const configJson = editor.getValue();
const tid = this.getAttribute('data-tid');
try {
// 验证JSON格式
JSON.parse(configJson);
// 发送数据到服务器
$.ajax({
url: '/admin/user_drive/update',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
id: tid,
login_config: configJson
}),
success: function(response) {
console.log(response);
if (response.status) {
showMessage('配置更新成功', 'success');
// 清空并关闭模态框
editor.setValue(configJson, -1);
$('#editudriveModal').modal('hide');
// 刷新网盘账号列表
refreshAccountsList();
} else {
showMessage('配置更新失败: ' + response.message, 'error');
}
},
error: function(error) {
console.error('更新配置出错:', error);
showMessage('更新配置时发生错误,请查看控制台', 'error');
}
});
} catch (e) {
showMessage('无效的JSON格式: ' + e.message, 'error');
const errorDisplay = editModalEditorArea.closest('.mb-3').querySelector('.json-error');
errorDisplay.textContent = '无效的JSON格式: ' + e.message;
errorDisplay.style.display = 'block';
editorElement.classList.add('border-danger');
}
});
// 绑定删除按钮事件
function bindDeleteButtonEvents() {
let selectedDriveId = null;
const deleteConfirmModal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
// 为所有删除按钮添加点击事件
document.querySelectorAll('.deleteudrivebtn').forEach(button => {
button.addEventListener('click', function() {
selectedDriveId = this.getAttribute('tid');
// 重置对话框内容
document.querySelector('#deleteConfirmModal .modal-body p').textContent =
'确定要删除此网盘账号吗?此操作不可恢复。';
deleteConfirmModal.show(); // 显示确认对话框
});
});
// 确认删除按钮点击事件
document.getElementById('confirmDelete').addEventListener('click', function() {
if (selectedDriveId) {
// 发送删除请求
$.ajax({
url: '/admin/user_drive/delete',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ id: selectedDriveId }),
success: function(response) {
if (response.status) {
deleteConfirmModal.hide(); // 只有成功时才隐藏对话框
showMessage('网盘账号删除成功', 'success');
// 刷新网盘账号列表
refreshAccountsList();
} else {
// 显示错误消息在对话框中
document.querySelector('#deleteConfirmModal .modal-body p').textContent =
response.message || '网盘账号删除失败';
showMessage(response.message || '网盘账号删除失败', 'error');
}
},
error: function(error) {
// 显示错误消息在对话框中
document.querySelector('#deleteConfirmModal .modal-body p').textContent =
'删除网盘账号时发生错误,请稍后重试。';
console.error('删除网盘账号出错:', error);
showMessage('删除网盘账号时发生错误,请查看控制台', 'error');
}
});
}
});
}
// 刷新网盘账号列表函数也需要更新,确保新添加的行的删除按钮也有事件绑定
function refreshAccountsList() {
$.ajax({
url: '/admin/user_drive/get',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({}),
success: function(response) {
// 清空现有表格内容
const accountsTableBody = $('#accounts table tbody');
accountsTableBody.empty();
if (response.status) {
// 检查是否有数据,如果没有也显示空表格而不是错误
if (response.data && response.data.length > 0) {
// 添加新的行
response.data.forEach(function(drive) {
const row = $('<tr></tr>');
row.append(`<td>${drive.provider_name}</td>`);
row.append(`<td>${drive.provider_name}</td>`);
row.append(`<td><span class="badge bg-success">正常</span></td>`);
row.append(`<td>1.5TB / 2TB</td>`);
row.append(`
<td>
<button class="btn btn-sm btn-info editudrivebtn" data-bs-toggle="modal" data-bs-target="#editudriveModal" tid="${drive.id}">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-danger deleteudrivebtn" tid="${drive.id}">
<i class="bi bi-trash"></i>
</button>
</td>
`);
accountsTableBody.append(row);
});
// 重新绑定编辑和删除按钮事件
bindEditButtonEvents();
bindDeleteButtonEvents();
} else {
// 如果没有数据,显示一个提示行
const emptyRow = $('<tr></tr>');
emptyRow.append(`<td colspan="5" class="text-center">暂无网盘账号,请添加。</td>`);
accountsTableBody.append(emptyRow);
}
} else {
// 如果API返回失败显示错误消息
showMessage(response.message || '获取网盘账号列表失败', 'error');
const errorRow = $('<tr></tr>');
errorRow.append(`<td colspan="5" class="text-center text-danger">获取数据失败,请刷新页面重试。</td>`);
accountsTableBody.append(errorRow);
}
},
error: function(error) {
console.error('获取网盘账号列表出错:', error);
showMessage('获取网盘账号列表时发生错误', 'error');
// 显示错误消息在表格中
const accountsTableBody = $('#accounts table tbody');
accountsTableBody.empty();
const errorRow = $('<tr></tr>');
errorRow.append(`<td colspan="5" class="text-center text-danger">获取数据失败,请刷新页面重试。</td>`);
accountsTableBody.append(errorRow);
}
});
}
// 添加网盘账号保存事件
function bindAddAccountEvent() {
document.getElementById('saveNewAccount').addEventListener('click', function() {
const addModalEditorArea = document.querySelector('#addAccountModal .json-editor-area');
const editorElement = addModalEditorArea.querySelector('.ace-editor');
const editor = ace.edit(editorElement);
const configJson = editor.getValue();
const driveType = document.getElementById('driveType').value;
if (!driveType) {
showMessage('请选择网盘类型', 'error');
return;
}
try {
// 验证JSON格式
const configData = JSON.parse(configJson);
// 发送数据到服务器
$.ajax({
url: '/admin/user_drive/add',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
provider_name: driveType,
login_config: configData,
remarks: driveType + "的账号"
}),
success: function(response) {
if (response.status) {
showMessage('网盘账号添加成功', 'success');
// 清空并关闭模态框
document.getElementById('driveType').value = '';
editor.setValue('', -1);
$('#addAccountModal').modal('hide');
// 刷新网盘账号列表
refreshAccountsList();
} else {
showMessage('网盘账号添加失败: ' + (response.message || '未知错误'), 'error');
}
},
error: function(error) {
console.error('添加网盘账号出错:', error);
showMessage('添加网盘账号时发生错误,请查看控制台', 'error');
}
});
} catch (e) {
showMessage('无效的JSON格式: ' + e.message, 'error');
const errorDisplay = addModalEditorArea.closest('.mb-3').querySelector('.json-error');
errorDisplay.textContent = '无效的JSON格式: ' + e.message;
errorDisplay.style.display = 'block';
editorElement.classList.add('border-danger');
}
});
}
// 加载外链列表
function loadExternalLinks() {
$.ajax({
url: '/admin/exlink/get',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({}),
success: function(response) {
const linksTableBody = $('#links table tbody');
linksTableBody.empty();
if (response.status) {
if (response.data && response.data.length > 0) {
response.data.forEach(function(link) {
const row = $('<tr></tr>');
row.append(`<td>${link.link_uuid}</td>`);
row.append(`<td>${link.drive_id}</td>`);
// 创建时间和过期时间
row.append(`<td>-</td>`);
if (link.expiry_time) {
const expiryDate = new Date(link.expiry_time);
const formattedDate = expiryDate.toLocaleString();
row.append(`<td>${formattedDate}</td>`);
} else {
row.append(`<td>-</td>`);
}
// 剩余次数
const usedQuota = link.used_quota || 0;
const totalQuota = link.total_quota || 0;
const remainingCount = totalQuota - usedQuota;
row.append(`<td>${remainingCount} / ${totalQuota}</td>`);
// 状态
let statusBadge = '';
if (remainingCount <= 0) {
statusBadge = '<span class="badge bg-danger">已禁用</span>';
} else {
statusBadge = '<span class="badge bg-success">正常</span>';
}
row.append(`<td>${statusBadge}</td>`);
// 操作按钮
row.append(`
<td>
<a href="/exlink/${link.link_uuid}" target="_blank" class="btn btn-sm btn-info">
<i class="bi bi-box-arrow-up-right"></i>
</a>
<button class="btn btn-sm btn-danger deleteExlinkBtn" data-uuid="${link.link_uuid}">
<i class="bi bi-trash"></i>
</button>
</td>
`);
linksTableBody.append(row);
});
// 绑定删除外链按钮事件
bindDeleteExlinkEvents();
} else {
// 如果没有数据,显示一个提示行
const emptyRow = $('<tr></tr>');
emptyRow.append(`<td colspan="7" class="text-center">暂无外链数据,请添加。</td>`);
linksTableBody.append(emptyRow);
}
} else {
showMessage('获取外链列表失败: ' + (response.message || '未知错误'), 'error');
const errorRow = $('<tr></tr>');
errorRow.append(`<td colspan="7" class="text-center text-danger">获取数据失败,请刷新页面重试。</td>`);
linksTableBody.append(errorRow);
}
},
error: function(error) {
console.error('获取外链列表出错:', error);
showMessage('获取外链列表时发生错误', 'error');
// 显示错误消息在表格中
const linksTableBody = $('#links table tbody');
linksTableBody.empty();
const errorRow = $('<tr></tr>');
errorRow.append(`<td colspan="7" class="text-center text-danger">获取数据失败,请刷新页面重试。</td>`);
linksTableBody.append(errorRow);
}
});
}
// 绑定添加外链相关事件
function bindAddExlinkEvents() {
// 当选择网盘类型时,加载对应的账号列表
document.getElementById('exlinkDriveType').addEventListener('change', function() {
const selectedType = this.value;
const accountSelect = document.getElementById('exlinkAccountId');
// 清空当前选项
accountSelect.innerHTML = '<option value="">加载中...</option>';
if (selectedType) {
// 加载对应类型的网盘账号
$.ajax({
url: '/admin/user_drive/get',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ provider_name: selectedType }),
success: function(response) {
accountSelect.innerHTML = '<option value="">请选择账号</option>';
if (response.status && response.data && response.data.length > 0) {
response.data.forEach(function(drive) {
const option = document.createElement('option');
option.value = drive.id;
option.textContent = drive.provider_name + (drive.remarks ? ` (${drive.remarks})` : '');
accountSelect.appendChild(option);
});
} else {
accountSelect.innerHTML = '<option value="">没有可用的账号,请先添加</option>';
}
},
error: function(error) {
console.error('获取网盘账号出错:', error);
accountSelect.innerHTML = '<option value="">加载失败,请重试</option>';
}
});
} else {
accountSelect.innerHTML = '<option value="">请先选择网盘类型</option>';
}
});
// 设置默认到期时间为当前时间后24小时
function setDefaultExpiryTime() {
const expiryTimeInput = document.getElementById('exlinkExpiryTime');
if (expiryTimeInput) {
const now = new Date();
now.setHours(now.getHours() + 24);
// 格式化为datetime-local输入所需的格式YYYY-MM-DDThh:mm
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const formattedDateTime = `${year}-${month}-${day}T${hours}:${minutes}`;
expiryTimeInput.value = formattedDateTime;
}
}
// 初始化表单时设置默认到期时间
setDefaultExpiryTime();
// 每次打开模态框时重置到期时间
$('#addExlinkModal').on('show.bs.modal', function() {
setDefaultExpiryTime();
});
// 绑定保存外链按钮点击事件
document.getElementById('saveExlink').addEventListener('click', function() {
// 获取表单数据
const formData = new FormData(document.getElementById('addExlinkForm'));
const drive_id = formData.get('account_id');
const total_quota = formData.get('total_quota');
const expiry_time = formData.get('expiry_time');
const remarks = formData.get('remarks') || '';
if (!drive_id || !total_quota || !expiry_time) {
showMessage('请填写必要的信息', 'error');
return;
}
// 转换日期时间格式从YYYY-MM-DDThh:mm到YYYY-MM-DD hh:mm:ss
const expiryDate = new Date(expiry_time);
const formattedExpiryTime = expiryDate.toISOString().replace('T', ' ').substring(0, 19);
// 发送创建外链请求
$.ajax({
url: '/admin/exlink/create',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
drive_id: drive_id,
total_quota: total_quota,
expiry_time: formattedExpiryTime,
remarks: remarks
}),
success: function(response) {
if (response.status) {
showMessage('外链创建成功', 'success');
// 清空表单
document.getElementById('exlinkDriveType').value = '';
document.getElementById('exlinkAccountId').innerHTML = '<option value="">请先选择网盘类型</option>';
document.querySelector('[name="total_quota"]').value = '3';
document.querySelector('[name="remarks"]').value = '';
// 关闭模态框
$('#addExlinkModal').modal('hide');
// 重新加载外链列表
loadExternalLinks();
} else {
showMessage('外链创建失败: ' + (response.message || '未知错误'), 'error');
}
},
error: function(error) {
console.error('创建外链出错:', error);
showMessage('创建外链时发生错误,请查看控制台', 'error');
}
});
});
}
// 绑定删除外链按钮事件
function bindDeleteExlinkEvents() {
document.querySelectorAll('.deleteExlinkBtn').forEach(button => {
button.addEventListener('click', function() {
const uuid = this.getAttribute('data-uuid');
if (confirm('确定要删除此外链吗?此操作不可恢复。')) {
$.ajax({
url: '/admin/exlink/delete',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ link_uuid: uuid }),
success: function(response) {
if (response.status) {
showMessage('外链删除成功', 'success');
loadExternalLinks(); // 重新加载外链列表
} else {
showMessage('外链删除失败: ' + (response.message || '未知错误'), 'error');
}
},
error: function(error) {
console.error('删除外链出错:', error);
showMessage('删除外链时发生错误,请查看控制台', 'error');
}
});
}
});
});
}
</script>
</body>
</html>