// API Base URL
const API_BASE = '/api';
// State Management
const state = {
currentPage: 'partner-list',
isAuthenticated: false,
currentUser: null,
partners: []
};
// DOM Elements
const loginPage = document.getElementById('loginPage');
const adminDashboard = document.getElementById('adminDashboard');
const loginForm = document.getElementById('loginForm');
const loginError = document.getElementById('loginError');
const logoutBtn = document.getElementById('logoutBtn');
const navLinks = document.querySelectorAll('.nav-link');
const pageTitle = document.getElementById('pageTitle');
const currentUserSpan = document.getElementById('currentUser');
const partnerModal = document.getElementById('partnerModal');
const partnerForm = document.getElementById('partnerForm');
const toast = document.getElementById('toast');
// Initialize App
document.addEventListener('DOMContentLoaded', () => {
checkAuthStatus();
setupEventListeners();
});
// Check Authentication Status
async function checkAuthStatus() {
try {
const response = await fetch(`${API_BASE}/auth/status`, {
credentials: 'include'
});
const data = await response.json();
if (data.authenticated) {
state.isAuthenticated = true;
state.currentUser = data.user;
showDashboard();
} else {
showLogin();
}
} catch (error) {
console.error('Auth check failed:', error);
showLogin();
}
}
// Show Login Page
function showLogin() {
loginPage.style.display = 'flex';
adminDashboard.style.display = 'none';
}
// Show Dashboard
function showDashboard() {
loginPage.style.display = 'none';
adminDashboard.style.display = 'flex';
currentUserSpan.textContent = state.currentUser.username;
navigateToPage('partner-list');
}
// Setup Event Listeners
function setupEventListeners() {
// Login Form
loginForm.addEventListener('submit', handleLogin);
// Logout Button
logoutBtn.addEventListener('click', handleLogout);
// Navigation Links
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const page = link.getAttribute('data-page');
navigateToPage(page);
});
});
// Partner Modal
document.querySelectorAll('.modal-close').forEach(btn => {
btn.addEventListener('click', () => {
partnerModal.classList.remove('show');
});
});
// Click outside modal to close
partnerModal.addEventListener('click', (e) => {
if (e.target === partnerModal) {
partnerModal.classList.remove('show');
}
});
// Partner Form Submit
partnerForm.addEventListener('submit', handlePartnerSubmit);
// Add Partner Button (from nav link)
const addPartnerLink = document.querySelector('[data-page="add-partner"]');
addPartnerLink.addEventListener('click', (e) => {
e.preventDefault();
openPartnerModal();
});
// Storage Configuration Form
const storageConfigForm = document.getElementById('storageConfigForm');
if (storageConfigForm) {
storageConfigForm.addEventListener('submit', handleStorageConfigSubmit);
}
// Create Directory Button
const createDirBtn = document.getElementById('createDirBtn');
if (createDirBtn) {
createDirBtn.addEventListener('click', handleCreateDirectory);
}
// VOD Config Form
const vodConfigForm = document.getElementById('vodConfigForm');
if (vodConfigForm) {
vodConfigForm.addEventListener('submit', handleVodConfigSubmit);
}
// Add Video Form
const addVideoForm = document.getElementById('addVideoForm');
if (addVideoForm) {
addVideoForm.addEventListener('submit', handleVideoUpload);
}
// Video Edit Form
const videoEditForm = document.getElementById('videoEditForm');
if (videoEditForm) {
videoEditForm.addEventListener('submit', handleVideoEditSubmit);
}
// Cover Preview
const videoCover = document.getElementById('videoCover');
if (videoCover) {
videoCover.addEventListener('change', handleCoverPreview);
}
// Change Password Form
const changePasswordForm = document.getElementById('changePasswordForm');
if (changePasswordForm) {
changePasswordForm.addEventListener('submit', handleChangePassword);
}
}
// Handle Login
async function handleLogin(e) {
e.preventDefault();
loginError.style.display = 'none';
const formData = new FormData(loginForm);
const data = {
username: formData.get('username'),
password: formData.get('password')
};
try {
const response = await fetch(`${API_BASE}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
state.isAuthenticated = true;
state.currentUser = result.user;
showDashboard();
} else {
loginError.textContent = result.message;
loginError.style.display = 'block';
}
} catch (error) {
console.error('Login error:', error);
loginError.textContent = 'Login failed. Please try again.';
loginError.style.display = 'block';
}
}
// Handle Logout
async function handleLogout() {
try {
await fetch(`${API_BASE}/auth/logout`, {
method: 'POST',
credentials: 'include'
});
state.isAuthenticated = false;
state.currentUser = null;
showLogin();
showToast('Logged out successfully', 'success');
} catch (error) {
console.error('Logout error:', error);
showToast('Logout failed', 'error');
}
}
// Navigate to Page
function navigateToPage(page) {
state.currentPage = page;
// Update active nav link
navLinks.forEach(link => {
if (link.getAttribute('data-page') === page) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
// Hide all pages
document.querySelectorAll('.page-content').forEach(p => {
p.style.display = 'none';
});
// Show selected page
switch (page) {
case 'storage-config':
pageTitle.textContent = '存储配置';
document.getElementById('storageConfigPage').style.display = 'block';
loadStorageConfig();
break;
case 'add-partner':
openPartnerModal();
break;
case 'partner-list':
pageTitle.textContent = '合作伙伴列表';
document.getElementById('partnerListPage').style.display = 'block';
loadPartners();
break;
case 'video-list':
pageTitle.textContent = '媒体列表';
document.getElementById('videoListPage').style.display = 'block';
loadVideos();
break;
case 'video-config':
pageTitle.textContent = '媒体配置';
document.getElementById('videoConfigPage').style.display = 'block';
loadVodConfig();
break;
case 'add-video':
pageTitle.textContent = '新增媒体';
document.getElementById('addVideoPage').style.display = 'block';
resetVideoUploadForm();
break;
case 'system-settings':
pageTitle.textContent = '系统设置';
document.getElementById('systemSettingsPage').style.display = 'block';
loadSystemSettings();
break;
case 'change-password':
pageTitle.textContent = '修改密码';
document.getElementById('changePasswordPage').style.display = 'block';
document.getElementById('changePasswordForm').reset();
break;
}
}
// Load Storage Configuration
async function loadStorageConfig() {
try {
const response = await fetch(`${API_BASE}/config/storage`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
const { config, dirStatus } = result.data;
document.getElementById('uploadDir').value = config.logo_upload_dir || '';
document.getElementById('maxSize').value = config.logo_max_size || '';
document.getElementById('allowedExt').value = config.allowed_extensions || '';
// Update directory status
const existsIndicator = document.getElementById('dirExists');
const writableIndicator = document.getElementById('dirWritable');
if (dirStatus.exists) {
existsIndicator.classList.add('success');
existsIndicator.classList.remove('error');
} else {
existsIndicator.classList.add('error');
existsIndicator.classList.remove('success');
}
if (dirStatus.writable) {
writableIndicator.classList.add('success');
writableIndicator.classList.remove('error');
} else {
writableIndicator.classList.add('error');
writableIndicator.classList.remove('success');
}
}
} catch (error) {
console.error('Load storage config error:', error);
showToast('Failed to load storage configuration', 'error');
}
}
// Handle Storage Config Submit
async function handleStorageConfigSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = {
logo_upload_dir: formData.get('logo_upload_dir'),
logo_max_size: formData.get('logo_max_size'),
allowed_extensions: formData.get('allowed_extensions')
};
try {
const response = await fetch(`${API_BASE}/config/storage`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showToast('Storage configuration updated successfully', 'success');
loadStorageConfig();
} else {
showToast(result.message || 'Failed to update configuration', 'error');
}
} catch (error) {
console.error('Update storage config error:', error);
showToast('Failed to update configuration', 'error');
}
}
// Handle Create Directory
async function handleCreateDirectory() {
const dirPath = document.getElementById('uploadDir').value;
if (!dirPath) {
showToast('Please enter a directory path', 'warning');
return;
}
try {
const response = await fetch(`${API_BASE}/config/storage/create-dir`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({ dirPath })
});
const result = await response.json();
if (result.success) {
showToast('Directory created successfully', 'success');
loadStorageConfig();
} else {
showToast(result.message || 'Failed to create directory', 'error');
}
} catch (error) {
console.error('Create directory error:', error);
showToast('Failed to create directory', 'error');
}
}
// Load Partners
async function loadPartners() {
try {
const response = await fetch(`${API_BASE}/partners`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
state.partners = result.data;
renderPartners(result.data);
}
} catch (error) {
console.error('Load partners error:', error);
showToast('Failed to load partners', 'error');
}
}
// Render Partners Table
function renderPartners(partners) {
const tbody = document.getElementById('partnersTableBody');
if (partners.length === 0) {
tbody.innerHTML = '
| 暂无合作伙伴 |
';
return;
}
tbody.innerHTML = partners.map(partner => `
| ${partner.id} |
 |
${partner.name} |
${partner.url || '-'} |
${partner.sort_order} |
${partner.status === 1 ? '启用' : '禁用'}
|
|
`).join('');
}
// Open Partner Modal
function openPartnerModal(partnerId = null) {
const modal = document.getElementById('partnerModal');
const modalTitle = document.getElementById('modalTitle');
const form = document.getElementById('partnerForm');
form.reset();
document.getElementById('partnerId').value = '';
document.getElementById('currentLogoPreview').style.display = 'none';
if (partnerId) {
modalTitle.textContent = '编辑合作伙伴';
loadPartnerData(partnerId);
} else {
modalTitle.textContent = '新增合作伙伴';
document.getElementById('partnerLogo').required = true;
}
modal.classList.add('show');
}
// Load Partner Data for Editing
async function loadPartnerData(partnerId) {
try {
const response = await fetch(`${API_BASE}/partners/${partnerId}`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
const partner = result.data;
document.getElementById('partnerId').value = partner.id;
document.getElementById('partnerName').value = partner.name;
document.getElementById('partnerUrl').value = partner.url || '';
document.getElementById('partnerStatus').value = partner.status;
document.getElementById('partnerSortOrder').value = partner.sort_order;
// Show current logo
document.getElementById('currentLogo').src = '/' + partner.logo;
document.getElementById('currentLogoPreview').style.display = 'block';
document.getElementById('partnerLogo').required = false;
}
} catch (error) {
console.error('Load partner error:', error);
showToast('Failed to load partner data', 'error');
}
}
// Handle Partner Form Submit
async function handlePartnerSubmit(e) {
e.preventDefault();
const formData = new FormData(partnerForm);
const partnerId = formData.get('partnerId');
const url = partnerId
? `${API_BASE}/partners/${partnerId}`
: `${API_BASE}/partners`;
const method = partnerId ? 'PUT' : 'POST';
try {
const response = await fetch(url, {
method: method,
credentials: 'include',
body: formData
});
const result = await response.json();
if (result.success) {
showToast(result.message, 'success');
partnerModal.classList.remove('show');
loadPartners();
} else {
showToast(result.message || 'Operation failed', 'error');
}
} catch (error) {
console.error('Partner submit error:', error);
showToast('Operation failed', 'error');
}
}
// Edit Partner (Global function for onclick)
window.editPartner = function(partnerId) {
openPartnerModal(partnerId);
};
// Delete Partner (Global function for onclick)
window.deletePartner = async function(partnerId) {
if (!confirm('Are you sure you want to delete this partner?')) {
return;
}
try {
const response = await fetch(`${API_BASE}/partners/${partnerId}`, {
method: 'DELETE',
credentials: 'include'
});
const result = await response.json();
if (result.success) {
showToast('Partner deleted successfully', 'success');
loadPartners();
} else {
showToast(result.message || 'Failed to delete partner', 'error');
}
} catch (error) {
console.error('Delete partner error:', error);
showToast('Failed to delete partner', 'error');
}
};
// Load System Settings
async function loadSystemSettings() {
try {
const response = await fetch(`${API_BASE}/config/settings`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
renderSystemSettings(result.data);
}
} catch (error) {
console.error('Load system settings error:', error);
showToast('Failed to load system settings', 'error');
}
}
// Render System Settings
function renderSystemSettings(settings) {
const container = document.getElementById('settingsTableContainer');
if (settings.length === 0) {
container.innerHTML = 'No settings found
';
return;
}
const html = `
| Setting Key |
Value |
Description |
${settings.map(setting => `
| ${setting.setting_key} |
${setting.setting_value} |
${setting.description || '-'} |
`).join('')}
`;
container.innerHTML = html;
}
// Show Toast Notification
function showToast(message, type = 'success') {
toast.textContent = message;
toast.className = `toast ${type} show`;
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
// ==================== 视频管理功能 ====================
// 加载视频列表
async function loadVideos() {
try {
const response = await fetch(`${API_BASE}/videos`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
renderVideos(result.data);
}
} catch (error) {
console.error('Load videos error:', error);
showToast('Failed to load videos', 'error');
}
}
// 渲染视频列表
function renderVideos(videos) {
const tbody = document.getElementById('videosTableBody');
if (videos.length === 0) {
tbody.innerHTML = '| 暂无视频 |
';
return;
}
tbody.innerHTML = videos.map(video => {
const duration = video.duration ? formatDuration(video.duration) : '-';
const uploadStatusText = getUploadStatusText(video.upload_status);
const statusBadge = video.status === 1 ? 'badge-success' : video.status === 2 ? 'badge-warning' : 'badge-danger';
const statusText = video.status === 1 ? '启用' : video.status === 2 ? '处理中' : '禁用';
// 判断封面URL是完整URL还是相对路径
const coverSrc = video.cover_url ?
(video.cover_url.startsWith('http') ? video.cover_url : `/${video.cover_url}`) :
'';
return `
| ${video.id} |
${coverSrc ? ` ` : '-'}
|
${video.title_cn} |
${video.title_en} |
${duration} |
${uploadStatusText} |
${statusText} |
|
`;
}).join('');
}
// 格式化时长
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
}
return `${minutes}:${String(secs).padStart(2, '0')}`;
}
// 获取上传状态文本
function getUploadStatusText(status) {
const statusMap = {
'pending': '待上传',
'uploading': '上传中',
'success': '成功',
'failed': '失败'
};
return statusMap[status] || status;
}
// 获取上传状态徽章样式
function getUploadStatusBadge(status) {
const badgeMap = {
'pending': 'badge-secondary',
'uploading': 'badge-warning',
'success': 'badge-success',
'failed': 'badge-danger'
};
return badgeMap[status] || 'badge-secondary';
}
// 编辑视频
window.editVideo = async function(videoId) {
try {
const response = await fetch(`${API_BASE}/videos/${videoId}`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
const video = result.data;
document.getElementById('editVideoId').value = video.id;
document.getElementById('editTitleCn').value = video.title_cn;
document.getElementById('editTitleEn').value = video.title_en;
document.getElementById('editDescCn').value = video.desc_cn || '';
document.getElementById('editDescEn').value = video.desc_en || '';
document.getElementById('editStatus').value = video.status;
document.getElementById('editSortOrder').value = video.sort_order;
document.getElementById('videoModal').classList.add('show');
}
} catch (error) {
console.error('Load video error:', error);
showToast('Failed to load video data', 'error');
}
};
// 处理视频编辑表单提交
async function handleVideoEditSubmit(e) {
e.preventDefault();
const videoId = document.getElementById('editVideoId').value;
const formData = new FormData();
formData.append('title_cn', document.getElementById('editTitleCn').value);
formData.append('title_en', document.getElementById('editTitleEn').value);
formData.append('desc_cn', document.getElementById('editDescCn').value);
formData.append('desc_en', document.getElementById('editDescEn').value);
formData.append('status', document.getElementById('editStatus').value);
formData.append('sort_order', document.getElementById('editSortOrder').value);
try {
const response = await fetch(`${API_BASE}/videos/${videoId}`, {
method: 'PUT',
credentials: 'include',
body: formData
});
const result = await response.json();
if (result.success) {
showToast('Video updated successfully', 'success');
document.getElementById('videoModal').classList.remove('show');
loadVideos();
} else {
showToast(result.message || 'Failed to update video', 'error');
}
} catch (error) {
console.error('Update video error:', error);
showToast('Failed to update video', 'error');
}
}
// 删除视频
window.deleteVideo = async function(videoId) {
if (!confirm('确定要删除这个视频吗?')) {
return;
}
try {
const response = await fetch(`${API_BASE}/videos/${videoId}`, {
method: 'DELETE',
credentials: 'include'
});
const result = await response.json();
if (result.success) {
showToast('Video deleted successfully', 'success');
loadVideos();
} else {
showToast(result.message || 'Failed to delete video', 'error');
}
} catch (error) {
console.error('Delete video error:', error);
showToast('Failed to delete video', 'error');
}
};
// ==================== VOD配置 ====================
// 加载VOD配置
async function loadVodConfig() {
try {
const response = await fetch(`${API_BASE}/videos/vod-config`, {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
const config = result.data;
document.getElementById('secretId').value = config.secret_id || '';
document.getElementById('secretKey').value = config.secret_key || '';
document.getElementById('vodRegion').value = config.region || 'ap-guangzhou';
document.getElementById('subAppId').value = config.sub_app_id || '';
document.getElementById('vodProcedure').value = config.procedure || '';
document.getElementById('storageRegion').value = config.storage_region || '';
// COS配置
document.getElementById('cosBucket').value = config.cos_bucket || '';
document.getElementById('cosRegion').value = config.cos_region || 'ap-guangzhou';
document.getElementById('cosPath').value = config.cos_path || 'video-covers/';
// 防盗链配置
document.getElementById('signKey').value = config.sign_key || '';
}
} catch (error) {
console.error('Load VOD config error:', error);
showToast('加载VOD配置失败', 'error');
}
}
// 处理VOD配置提交
async function handleVodConfigSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = {
secret_id: formData.get('secret_id'),
secret_key: formData.get('secret_key'),
region: formData.get('region'),
sub_app_id: formData.get('sub_app_id'),
procedure: formData.get('procedure'),
storage_region: formData.get('storage_region'),
// COS配置
cos_bucket: formData.get('cos_bucket'),
cos_region: formData.get('cos_region'),
cos_path: formData.get('cos_path'),
// 防盗链配置
sign_key: formData.get('sign_key')
};
try {
const response = await fetch(`${API_BASE}/videos/vod-config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showToast('VOD配置更新成功', 'success');
} else {
showToast(result.message || '配置更新失败', 'error');
}
} catch (error) {
console.error('Update VOD config error:', error);
showToast('配置更新失败', 'error');
}
}
// ==================== 视频上传 ====================
let currentUploadTask = null;
// 重置上传表单
function resetVideoUploadForm() {
document.getElementById('addVideoForm').reset();
document.getElementById('coverPreview').style.display = 'none';
document.getElementById('uploadProgress').style.display = 'none';
document.getElementById('uploadVideoBtn').style.display = 'inline-flex';
document.getElementById('cancelUploadBtn').style.display = 'none';
}
// 封面预览
function handleCoverPreview(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(event) {
document.getElementById('coverPreviewImg').src = event.target.result;
document.getElementById('coverPreview').style.display = 'block';
};
reader.readAsDataURL(file);
}
}
// 处理视频上传
async function handleVideoUpload(e) {
e.preventDefault();
const titleCn = document.getElementById('videoTitleCn').value;
const titleEn = document.getElementById('videoTitleEn').value;
const descCn = document.getElementById('videoDescCn').value;
const descEn = document.getElementById('videoDescEn').value;
const coverFile = document.getElementById('videoCover').files[0];
const videoFile = document.getElementById('videoFile').files[0];
if (!videoFile) {
showToast('请选择视频文件', 'error');
return;
}
try {
// 1. 获取上传签名
showToast('正在获取上传凭证...', 'info');
const sigResponse = await fetch(`${API_BASE}/videos/upload-signature`, {
method: 'POST',
credentials: 'include'
});
const sigResult = await sigResponse.json();
if (!sigResult.success) {
showToast(sigResult.message || '获取上传凭证失败', 'error');
return;
}
// 2. 先创建视频记录(可选上传封面)
const formData = new FormData();
formData.append('title_cn', titleCn);
formData.append('title_en', titleEn);
formData.append('desc_cn', descCn);
formData.append('desc_en', descEn);
if (coverFile) {
formData.append('cover', coverFile);
}
formData.append('status', 2); // Processing
const videoRecordResponse = await fetch(`${API_BASE}/videos`, {
method: 'POST',
credentials: 'include',
body: formData
});
const videoRecordResult = await videoRecordResponse.json();
if (!videoRecordResult.success) {
showToast(videoRecordResult.message || '创建视频记录失败', 'error');
return;
}
const videoId = videoRecordResult.data.id;
// 3. 使用腾讯云VOD SDK上传视频
showUploadProgress(videoFile.name);
const tcVod = new TcVod.default({
getSignature: () => sigResult.data.signature
});
const uploader = tcVod.upload({
mediaFile: videoFile,
});
// 监听上传进度
uploader.on('media_progress', (info) => {
updateUploadProgress(info.percent * 100, info.speed);
});
// 上传完成
uploader.done().then(async (doneResult) => {
console.log('Upload complete:', doneResult);
// 更新视频记录
const updateData = new FormData();
updateData.append('file_id', doneResult.fileId);
updateData.append('video_url', doneResult.video.url);
updateData.append('upload_status', 'success');
updateData.append('status', 1); // Active
await fetch(`${API_BASE}/videos/${videoId}`, {
method: 'PUT',
credentials: 'include',
body: updateData
});
showToast('视频上传成功!', 'success');
setTimeout(() => {
navigateToPage('video-list');
}, 1500);
}).catch(async (error) => {
console.error('Upload failed:', error);
// 更新失败状态
const updateData = new FormData();
updateData.append('upload_status', 'failed');
await fetch(`${API_BASE}/videos/${videoId}`, {
method: 'PUT',
credentials: 'include',
body: updateData
});
showToast('视频上传失败: ' + error.message, 'error');
document.getElementById('uploadVideoBtn').style.display = 'inline-flex';
document.getElementById('cancelUploadBtn').style.display = 'none';
});
currentUploadTask = uploader;
document.getElementById('uploadVideoBtn').style.display = 'none';
document.getElementById('cancelUploadBtn').style.display = 'inline-flex';
// 取消上传按钮
document.getElementById('cancelUploadBtn').onclick = () => {
if (currentUploadTask) {
currentUploadTask.cancel();
showToast('上传已取消', 'warning');
resetVideoUploadForm();
}
};
} catch (error) {
console.error('Video upload error:', error);
showToast('上传失败: ' + error.message, 'error');
}
}
// 显示上传进度
function showUploadProgress(fileName) {
document.getElementById('uploadProgress').style.display = 'block';
document.getElementById('uploadFileName').textContent = fileName;
document.getElementById('uploadPercent').textContent = '0%';
document.getElementById('progressFill').style.width = '0%';
document.getElementById('uploadStatus').textContent = '正在上传...';
document.getElementById('uploadSpeed').textContent = '';
}
// 更新上传进度
function updateUploadProgress(percent, speed) {
const percentInt = Math.floor(percent);
document.getElementById('uploadPercent').textContent = percentInt + '%';
document.getElementById('progressFill').style.width = percentInt + '%';
if (speed) {
const speedText = formatSpeed(speed);
document.getElementById('uploadSpeed').textContent = speedText;
}
}
// 格式化速度
function formatSpeed(bytesPerSecond) {
if (bytesPerSecond < 1024) {
return bytesPerSecond.toFixed(2) + ' B/s';
} else if (bytesPerSecond < 1024 * 1024) {
return (bytesPerSecond / 1024).toFixed(2) + ' KB/s';
} else {
return (bytesPerSecond / (1024 * 1024)).toFixed(2) + ' MB/s';
}
}
// ==================== 修改密码功能 ====================
// 处理修改密码
async function handleChangePassword(e) {
e.preventDefault();
const currentPassword = document.getElementById('currentPassword').value;
const newPassword = document.getElementById('newPassword').value;
const confirmPassword = document.getElementById('confirmPassword').value;
// 验证新密码
if (newPassword.length < 6) {
showToast('新密码长度至少6位', 'error');
return;
}
// 验证两次密码是否一致
if (newPassword !== confirmPassword) {
showToast('两次输入的新密码不一致', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/auth/change-password`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword
})
});
const result = await response.json();
if (result.success) {
showToast('密码修改成功,请重新登录', 'success');
document.getElementById('changePasswordForm').reset();
// 2秒后自动退出登录
setTimeout(() => {
handleLogout();
}, 2000);
} else {
showToast(result.message || '密码修改失败', 'error');
}
} catch (error) {
console.error('Change password error:', error);
showToast('密码修改失败', 'error');
}
}