// WebSocket连接和消息处理逻辑 let socket; // 充值功能 function initRechargeButton() { const rechargeBtn = document.getElementById('rechargeBtn'); if (!rechargeBtn) return; // 处理充值按钮点击 rechargeBtn.addEventListener('click', () => { const modal = new bootstrap.Modal(document.getElementById('rechargeModal')); modal.show(); }); // 处理确认充值按钮点击 document.addEventListener('click', (e) => { if (e.target.id === 'confirmRechargeBtn') { const amount = document.getElementById('rechargeAmount').value; if (!amount || isNaN(amount) || amount <= 0) { showToast('请输入有效的充值金额', 'danger'); return; } // 构建充值URL并打开新窗口 const userKey = localStorage.getItem('userKey'); const url = `https://ifdian.net/order/create?product_type=1&plan_id=7a9ac9d628ee11f088ef5254001e7c00&sku=[{%22sku_id%22:%227aa2922428ee11f0a9945254001e7c00%22,%22count%22:${amount}}]&viokrz_ex=0&remark=${userKey}`; showToast('成功发起充值请求,请在新窗口完成支付', 'info'); setTimeout(() => { window.open(url, '_blank'); }, 1000); // 关闭模态框 const modal = bootstrap.Modal.getInstance(document.getElementById('rechargeModal')); modal.hide(); } }); } function onSocketMessage(event) { try { const data = JSON.parse(event.data); if(data.nickname) document.getElementById('userNickname').textContent = data.nickname; if(data.meowcoin) document.getElementById('userMeowcoin').textContent = data.meowcoin.toFixed(2); if(data.traffic) { const usedTrafficMB = (data.traffic.current_month_traffic / (1024 * 1024)).toFixed(2); const totalTrafficMB = (data.traffic.traffic_limit / (1024 * 1024)).toFixed(2); const trafficPercentage = Math.round((data.traffic.current_month_traffic / data.traffic.traffic_limit) * 100); const trafficCollapse = document.getElementById('trafficDetails'); let isTrafficCollapsed = false; if(trafficCollapse) isTrafficCollapsed = trafficCollapse.classList.contains('show'); else isTrafficCollapsed = false; // 按月流量统计 let monthlyTrafficHTML = ''; if (data.traffic.monthly_traffic) { monthlyTrafficHTML = '
月流量统计
'; // 按年月排序 const sortedMonths = Object.keys(data.traffic.monthly_traffic).sort().reverse(); // 找出最大流量值 const maxTraffic = Math.max(...Object.values(data.traffic.monthly_traffic)); sortedMonths.forEach(month => { const trafficMB = (data.traffic.monthly_traffic[month] / (1024 * 1024)).toFixed(2); monthlyTrafficHTML += `
${month}: ${trafficMB} MB
`; }); monthlyTrafficHTML += '
'; } document.getElementById('trafficUsage').innerHTML = `
${trafficPercentage}%
${monthlyTrafficHTML}
`; } if(data.storage) { const usedMB = (data.storage.used_storage / (1024 * 1024)).toFixed(2); const totalMB = (data.storage.storage_limit / (1024 * 1024)).toFixed(2); const storagePercentage = Math.round((data.storage.used_storage / data.storage.storage_limit) * 100); document.getElementById('storageUsage').innerHTML = `
存储空间 ${usedMB} MB / ${totalMB} MB
${storagePercentage}%
上传图片数: ${data.storage.upload_count}
`; } } catch (e) { console.error('WebSocket消息解析错误:', e); } } // 获取用户主题设置 function fetchUserTheme() { const userKey = localStorage.getItem('userKey'); if (!userKey) return Promise.resolve(null); return fetch(`/auth/get_theme?key=${userKey}`) .then(response => { if (!response.ok) throw new Error('获取主题失败'); return response.json(); }) .then(theme => { // 更新取色器颜色 const colorPicker = document.getElementById('themeColorPicker'); if (colorPicker) { colorPicker.value = theme.theme_color; } // 更新主题模式按钮状态 document.querySelectorAll('[data-theme]').forEach(btn => { btn.classList.remove('active'); if(btn.getAttribute('data-theme') === theme.theme_mode) { btn.classList.add('active'); } }); return theme; }) .catch(error => { console.error('获取主题错误:', error); return null; }); } function initWebSocket() { const userKey = localStorage.getItem('userKey'); socket = new WebSocket(`ws://${window.location.host}/ws/user-info?key=${userKey}`); // 初始化时获取并应用主题 fetchUserTheme().then(theme => { if (theme) { updateThemeColors(theme.theme_color); document.documentElement.setAttribute('data-theme', theme.theme_mode); } }); socket.onopen = function() { console.log('WebSocket连接已建立'); }; socket.onmessage = onSocketMessage; socket.onclose = function() { console.log('WebSocket连接已关闭'); reconnectWebSocket(); }; socket.onerror = function(error) { console.error('WebSocket错误:', error); reconnectWebSocket(); }; } let reconnectAttempts = 0; const maxReconnectAttempts = 5; const baseDelay = 1000; // 1秒基础延迟 function reconnectWebSocket() { if (reconnectAttempts >= maxReconnectAttempts) { console.log('已达到最大重连次数'); return; } const delay = Math.min(baseDelay * Math.pow(2, reconnectAttempts), 30000); // 最大延迟30秒 reconnectAttempts++; console.log(`尝试重连 (${reconnectAttempts}/${maxReconnectAttempts}), 等待 ${delay}ms`); setTimeout(() => { const userKey = localStorage.getItem('userKey'); socket = new WebSocket(`ws://${window.location.host}/ws/user-info?key=${userKey}`); socket.onopen = function() { reconnectAttempts = 0; console.log('WebSocket重新连接成功'); }; socket.onmessage = onSocketMessage; }, delay); } // 更新主题颜色变量 // 监听主题变化 function updateTheme(themeData) { console.log('更新主题:', themeData); const userKey = localStorage.getItem('userKey'); if (!userKey) return; // 更新本地主题 if (themeData.theme_color) { updateThemeColors(themeData.theme_color); } if (themeData.theme_mode) { document.documentElement.setAttribute('data-theme', themeData.theme_mode); } // 保存到后端 fetch('/auth/update_theme', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: userKey, ...themeData }) }).catch(error => { console.error('保存主题错误:', error); }); } function watchThemeChanges() { const userKey = localStorage.getItem('userKey'); if (!userKey) return; // 监听主题选择变化 document.querySelectorAll('[data-theme]').forEach(item => { item.addEventListener('click', function() { const themeMode = this.getAttribute('data-theme'); updateTheme({ theme_mode: themeMode, theme_color: document.getElementById('themeColorPicker').value }); }); }); // 监听颜色选择器变化 document.getElementById('themeColorPicker').addEventListener('change', function() { updateTheme({ theme_color: this.value, theme_mode: document.documentElement.getAttribute('data-theme') }); }); } document.querySelectorAll('[data-theme]').forEach(item => { item.addEventListener('click', e => { e.preventDefault(); const theme = e.target.getAttribute('data-theme'); if (theme === 'auto') { localStorage.removeItem('manualTheme'); const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light'); } else { localStorage.setItem('manualTheme', theme); document.documentElement.setAttribute('data-theme', theme); } document.querySelectorAll('[data-theme]').forEach(btn => btn.classList.remove('active')); e.target.classList.add('active'); }); }); // 保存主题设置 const saveThemeBtn = document.getElementById('saveThemeBtn'); if (saveThemeBtn) { saveThemeBtn.addEventListener('click', function() { const userKey = localStorage.getItem('userKey'); if (!userKey) return; // 获取主题颜色和模式 const themeColor = document.getElementById('themeColorPicker').value; const themeMode = document.querySelector('[data-theme].active')?.getAttribute('data-theme') || 'auto'; // 更新本地主题 updateThemeColors(themeColor); document.documentElement.setAttribute('data-theme', themeMode); // 保存到后端 fetch('/auth/set_theme', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: userKey, theme_color: themeColor, theme_mode: themeMode }) }) .then(response => { if (!response.ok) throw new Error('保存主题失败'); showToast('主题设置已保存', 'success'); const modal = bootstrap.Modal.getInstance(document.getElementById('themeSettingsModal')); modal.hide(); }) .catch(error => { console.error('保存主题错误:', error); showToast('保存主题失败', 'danger'); }); }); } function updateThemeColors(baseColor) { // 将16进制颜色转换为RGB const hexToRgb = (hex) => { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return {r, g, b}; }; // 计算衍生颜色 const rgb = hexToRgb(baseColor); // 计算更亮的颜色 const lighten = (color, percent) => { return { r: Math.min(255, color.r + Math.round(color.r * percent)), g: Math.min(255, color.g + Math.round(color.g * percent)), b: Math.min(255, color.b + Math.round(color.b * percent)) }; }; // 计算更暗的颜色 const darken = (color, percent) => { return { r: Math.max(0, color.r - Math.round(color.r * percent)), g: Math.max(0, color.g - Math.round(color.g * percent)), b: Math.max(0, color.b - Math.round(color.b * percent)) }; }; // RGB转16进制 const rgbToHex = (rgb) => { return `#${((1 << 24) + (rgb.r << 16) + (rgb.g << 8) + rgb.b).toString(16).slice(1)}`; }; // 设置CSS变量 document.documentElement.style.setProperty('--theme-color', baseColor); document.documentElement.style.setProperty('--theme-color-light', rgbToHex(lighten(rgb, 0.3))); document.documentElement.style.setProperty('--theme-color-lighter', rgbToHex(lighten(rgb, 0.6))); document.documentElement.style.setProperty('--theme-color-dark', rgbToHex(darken(rgb, 0.3))); document.documentElement.style.setProperty('--theme-color-darker', rgbToHex(darken(rgb, 0.6))); } // 初始化用户信息 document.addEventListener('DOMContentLoaded', async () => { try { // 显示用户Key const userKey = localStorage.getItem('userKey'); if (userKey) { document.getElementById('userKey').textContent = userKey; document.getElementById('userMeowcoin').textContent = '0.00'; } // 复制Key功能 document.getElementById('copyKeyBtn').addEventListener('click', function() { const keyToCopy = document.getElementById('userKey').textContent; if (keyToCopy) { navigator.clipboard.writeText(keyToCopy) .then(() => { showToast('Key已复制到剪贴板', 'success'); }) .catch(err => { console.error('复制失败:', err); }); } }); // 获取用户信息 const response = await fetch('/auth/userinfo?key=' + userKey); const userInfo = await response.json(); if (response.ok) { // 显示用户信息 document.getElementById('userNickname').textContent = userInfo.nickname; document.getElementById('userMeowcoin').textContent = userInfo.meowcoin ? userInfo.meowcoin.toFixed(2) : '0.00'; const usedMB = (userInfo.used_storage / (1024 * 1024)).toFixed(2); const totalMB = (userInfo.storage_limit / (1024 * 1024)).toFixed(2); const storagePercentage = Math.round((userInfo.used_storage / userInfo.storage_limit) * 100); const usedTrafficMB = (userInfo.current_month_traffic / (1024 * 1024)).toFixed(2); const totalTrafficMB = (userInfo.traffic_limit / (1024 * 1024)).toFixed(2); const trafficPercentage = Math.round((userInfo.current_month_traffic / userInfo.traffic_limit) * 100); document.getElementById('storageUsage').innerHTML = `
存储空间 ${usedMB} MB / ${totalMB} MB
${storagePercentage}%
上传图片数: ${userInfo.upload_count}
`; // 按月流量统计 let monthlyTrafficHTML = ''; if (userInfo.monthly_traffic) { monthlyTrafficHTML = '
月流量统计
'; // 按年月排序 const sortedMonths = Object.keys(userInfo.monthly_traffic).sort().reverse(); // 找出最大流量值 const maxTraffic = Math.max(...Object.values(userInfo.monthly_traffic)); sortedMonths.forEach(month => { const trafficMB = (userInfo.monthly_traffic[month] / (1024 * 1024)).toFixed(2); monthlyTrafficHTML += `
${month}: ${trafficMB} MB
`; }); monthlyTrafficHTML += '
'; } document.getElementById('trafficUsage').innerHTML = `
${trafficPercentage}%
${monthlyTrafficHTML}
`; // 加载用户图片 loadUserImages(); } else { showToast(`获取用户信息失败: ${userInfo.error}`, 'danger'); setTimeout(() => { window.location.href = '/'; }, 3000); } } catch (error) { console.error('获取用户信息出错:', error); if (error instanceof TypeError) { showToast('网络连接错误,请检查网络后重试', 'danger'); } else { showToast('获取用户信息出错: ' + (error.message || '未知错误'), 'danger'); } setTimeout(() => { window.location.href = '/'; }, 3000); } // 初始化WebSocket连接 initWebSocket(); }); // 退出登录功能 document.getElementById('logoutBtn').addEventListener('click', () => { localStorage.removeItem('userKey'); window.location.href = '/'; }); // 显示图片操作菜单 let selectedImages = []; function showImageActionMenu(imgElement, imageData, event = {}) { // 检查是否按下了Ctrl键 const isCtrlPressed = event.ctrlKey || event.metaKey; // 移除已存在的菜单 const existingMenu = document.querySelector('.image-action-menu'); if (existingMenu) existingMenu.remove(); // 处理多选逻辑 if (isCtrlPressed) { // 如果图片已选中,则取消选中 const index = selectedImages.findIndex(img => img._id === imageData._id); if (index > -1) { selectedImages.splice(index, 1); // 移除所有选中图片的高亮效果 imgElement.classList.remove('selected-image'); } else { // 添加新选中的图片 selectedImages.push(imageData); imgElement.classList.add('selected-image'); } } else { if(selectedImages[0] == imageData && selectedImages.length == 1) { document.querySelectorAll('.selected-image').forEach(el => el.classList.remove('selected-image')); selectedImages = []; } else { document.querySelectorAll('.selected-image').forEach(el => el.classList.remove('selected-image')); selectedImages = [imageData]; imgElement.classList.add('selected-image'); } } // 确保所有选中图片都有高亮效果 selectedImages.forEach(img => { const imgEl = document.querySelector(`[data-id="${img._id}"]`); if (imgEl) imgEl.classList.add('selected-image'); }); // 如果没有选中图片,直接返回 if (selectedImages.length === 0) return; // 创建菜单元素 const menu = document.createElement('div'); menu.className = 'image-action-menu bg-white shadow rounded p-2'; menu.style.zIndex = '1060'; menu.style.minWidth = '150px'; // 固定菜单在屏幕底部中央 menu.style.position = 'fixed'; menu.style.bottom = '20px'; menu.style.left = '50%'; menu.style.transform = 'translateX(-50%)'; // 根据选中数量调整菜单项 const previewUrl = selectedImages.length === 1 ? selectedImages[0].url : ''; const copyLinks = selectedImages.map(img => window.location.origin + img.url).join('\n'); const deleteIds = selectedImages.map(img => img._id).join(','); menu.innerHTML = ` ${selectedImages.length > 1 ? '' : ``} `; // 添加菜单到body document.body.appendChild(menu); // 为复制链接按钮添加事件 menu.querySelector('.copy-link-btn').addEventListener('click', () => { const urls = menu.querySelector('.copy-link-btn').getAttribute('data-url'); navigator.clipboard.writeText(urls) .then(() => { showToast(selectedImages.length > 1 ? '多条链接已复制到剪贴板' : '链接已复制到剪贴板', 'success'); menu.remove(); }) .catch(err => { console.error('复制链接失败:', err); showToast('复制链接失败', 'danger'); menu.remove(); }); }); // 为下载按钮添加事件 menu.querySelector('.download-btn').addEventListener('click', () => { if (selectedImages.length > 1) { selectedImages.forEach(img => { const url = window.location.origin + img.url; const filename = url.split('/').pop(); downloadImage(url, filename); }); } else { const url = menu.querySelector('.download-btn').getAttribute('data-url'); const filename = url.split('/').pop(); downloadImage(url, filename); } menu.remove(); }); // 为删除按钮添加事件 menu.querySelector('.delete-btn').addEventListener('click', () => { const imageIds = menu.querySelector('.delete-btn').getAttribute('data-id').split(','); if (imageIds.length > 1) { if (confirm(`确定要删除这${imageIds.length}张图片吗?`)) { imageIds.forEach(id => deleteImage(id)); } } else { deleteImage(imageIds[0]); } menu.remove(); }); // 添加取消选择按钮 const deselectBtn = document.createElement('button'); deselectBtn.className = 'btn btn-sm btn-block text-start text-secondary'; deselectBtn.innerHTML = ' 取消选择'; deselectBtn.addEventListener('click', () => { document.querySelectorAll('.selected-image').forEach(el => el.classList.remove('selected-image')); selectedImages = []; menu.remove(); }); menu.append(deselectBtn); } // 加载用户图片 // 预览图片函数 function previewImage(url) { const previewImage = document.getElementById('previewImage'); if (!previewImage) { console.error('无法获取previewImage元素'); showToast('预览功能初始化失败', 'danger'); return; } previewImage.src = url; const modal = new bootstrap.Modal(document.getElementById('imagePreviewModal')); // 显示加载提示并隐藏图片 document.getElementById('loadingIndicator').classList.remove('d-none'); document.getElementById('previewImage').classList.add('d-none'); modal.show(); // 添加onload事件监听 previewImage.onload = function() { console.log('图片加载完成'); document.getElementById('loadingIndicator').classList.add('d-none'); document.getElementById('previewImage').classList.remove('d-none'); }; // 添加onerror事件监听 previewImage.onerror = function(e) { console.error('图片加载失败'); const loadingIndicator = document.getElementById('loadingIndicator'); loadingIndicator.innerHTML = '

图片加载失败,请检查流量是否充足

'; console.log(e); }; } async function loadUserImages() { try { const response = await fetch('/images/user_images?key=' + localStorage.getItem('userKey')); const data = await response.json(); if (response.ok) { const imageList = document.getElementById('imageList'); imageList.innerHTML = ''; const images = data.images; // 验证返回数据是否为数组 if (!Array.isArray(images)) { console.error('返回数据格式错误: 期望数组但得到', typeof images); return; } // 创建图片预览模态框 const modalHTML = ` `; document.body.insertAdjacentHTML('beforeend', modalHTML); images.forEach(image => { const col = document.createElement('div'); col.className = 'col-3 col-md-3 col-lg-2 p-1'; col.innerHTML = `
用户上传图片
`; // 添加长按事件 const img = col.querySelector('img'); img.style.cursor = 'pointer'; img.addEventListener('click', (e) => { if (e.button === 0) { // 左键点击 showImageActionMenu(img, image, e); } }); imageList.appendChild(col); }); } else { console.error('加载图片失败:', data.error); } } catch (error) { console.error('加载图片出错:', error); } } // 下载图片函数 function downloadImage(url, filename) { fetch(url) .then(response => response.blob()) .then(blob => { const blobUrl = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = blobUrl; a.download = filename || 'image'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(blobUrl); document.body.removeChild(a); showToast('下载开始', 'success'); }) .catch(err => { console.error('下载失败:', err); showToast('下载失败', 'danger'); }); } // 删除图片函数 async function deleteImage(imageId) { try { const response = await fetch(`/images/${imageId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: localStorage.getItem('userKey') }) }); const data = await response.json(); if (response.ok) { showToast('图片删除成功', 'success'); loadUserImages(); // 刷新图片列表 // 获取并更新用户上传计数 const userKey = localStorage.getItem('userKey'); const userInfoResponse = await fetch('/auth/userinfo?key=' + userKey); const userInfo = await userInfoResponse.json(); if (userInfoResponse.ok) { document.getElementById('uploadCount').textContent = userInfo.upload_count; // 更新存储空间显示 const usedMB = (userInfo.used_storage / (1024 * 1024)).toFixed(2); const totalMB = (userInfo.storage_limit / (1024 * 1024)).toFixed(2); const percentage = Math.round((userInfo.used_storage / userInfo.storage_limit) * 100); document.getElementById('storageUsage').innerHTML = `
存储空间 ${usedMB} MB / ${totalMB} MB
${percentage}%
上传图片数: ${userInfo.upload_count}
`; } } else { showToast(`图片删除失败: ${data.error}`, 'danger'); } } catch (error) { console.error('删除图片出错:', error); showToast('删除图片出错', 'danger'); } } // 显示Toast提示 function showToast(message, type) { const toastContainer = document.getElementById('toastContainer'); const toastEl = document.createElement('div'); toastEl.className = `toast show align-items-center text-white bg-${type} border-0`; toastEl.setAttribute('role', 'alert'); toastEl.setAttribute('aria-live', 'assertive'); toastEl.setAttribute('aria-atomic', 'true'); toastEl.innerHTML = `
${message}
`; toastContainer.appendChild(toastEl); // 自动移除Toast setTimeout(() => { toastEl.classList.remove('show'); setTimeout(() => { toastEl.remove(); }, 300); }, 3000); } // 图片上传功能 // 文件选择变化时自动上传 const fileInput = document.getElementById('imageFile'); fileInput.addEventListener('change', async () => { const files = fileInput.files; const userKey = localStorage.getItem('userKey'); if (files.length === 0) { showToast('请选择图片', 'warning'); return; } // 顺序上传处理 let uploadedCount = 0; for (const file of files) { try { await uploadSingleFile(file, userKey, null, () => { uploadedCount++; }); } catch (error) { console.error('文件上传出错:', error); } } showToast(`${uploadedCount}个文件上传成功`, 'success'); }); // 拖拽上传功能 const dropZone = document.getElementById('dropZone'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, preventDefaults, false); }); // 点击拖拽区域触发文件选择 dropZone.addEventListener('click', () => { document.getElementById('imageFile').click(); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, unhighlight, false); }); function highlight() { dropZone.style.borderColor = '#0d6efd'; } function unhighlight() { dropZone.style.borderColor = '#ccc'; } dropZone.addEventListener('drop', handleDrop, false); async function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; const userKey = localStorage.getItem('userKey'); const fileInput = document.getElementById('imageFile'); if (files.length === 0) return; // 顺序上传处理 for (const file of files) { try { await uploadSingleFile(file, userKey, null, () => { fileInput.value = ''; }); } catch (error) { console.error('文件上传出错:', error); } } } // 单文件上传函数 async function uploadSingleFile(file, userKey, submitBtn, onComplete) { if (!file.type.match('image.*')) { showToast('请上传图片文件', 'warning'); return; } // 创建进度条元素 const progressContainer = document.createElement('div'); progressContainer.className = 'progress mt-2'; progressContainer.style.height = '5px'; const progressBar = document.createElement('div'); progressBar.className = 'progress-bar progress-bar-striped progress-bar-animated'; progressBar.style.width = '0%'; progressBar.setAttribute('aria-valuenow', '0'); progressBar.setAttribute('aria-valuemin', '0'); progressBar.setAttribute('aria-valuemax', '100'); progressContainer.appendChild(progressBar); document.getElementById('uploadArea').appendChild(progressContainer); const formData = new FormData(); formData.append('key', userKey); formData.append('file', file); return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { // 只显示90%的进度,留10%给服务器处理 const percentComplete = Math.round((e.loaded / e.total) * 90); progressBar.style.width = percentComplete + '%'; progressBar.setAttribute('aria-valuenow', percentComplete); } }); xhr.onload = async () => { try { const data = JSON.parse(xhr.responseText); if (xhr.status >= 200 && xhr.status < 300) { // 完成最后10%的进度 progressBar.style.width = '100%'; progressBar.setAttribute('aria-valuenow', 100); // 显示上传结果 const previewUrl = data.thumb_url || data.url; document.getElementById('uploadPreviewImage').src = previewUrl; document.getElementById('uploadResult').classList.remove('d-none'); // 创建复制按钮 const copyBtn = document.createElement('button'); copyBtn.className = 'btn btn-sm btn-outline-primary'; copyBtn.innerHTML = ' 复制链接'; copyBtn.onclick = (e) => { e.preventDefault(); const fullUrl = window.location.origin + data.url; navigator.clipboard.writeText(fullUrl) .then(() => showToast('链接已复制', 'success')) .catch(err => console.error('复制失败:', err)); }; // 替换原有链接元素 const urlContainer = document.getElementById('imageUrl'); urlContainer.innerHTML = ''; urlContainer.appendChild(copyBtn); showToast('图片上传成功', 'success'); if (onComplete) { onComplete(); } // 确保所有异步操作完成后再resolve try { // 重新加载用户图片和用户信息 await loadUserImages(); // 移除进度条 progressContainer.remove(); resolve(data); } catch (innerError) { console.error('异步操作出错:', innerError); reject(innerError); } } else { showToast(`上传失败: ${data.error}`, 'danger'); reject(new Error(data.error || '上传失败')); } } catch (error) { console.error('上传请求出错:', error); showToast('上传请求出错', 'danger'); reject(error); } finally { progressContainer.remove(); } }; xhr.onerror = () => { console.error('上传请求出错'); showToast('上传请求出错', 'danger'); submitBtn.disabled = false; submitBtn.textContent = '上传'; progressContainer.remove(); reject(new Error('上传请求出错')); }; xhr.open('POST', '/images/upload'); xhr.send(formData); }); }; // 初始化查询按钮 function initQueryBillButton() { const queryBillBtn = document.getElementById('queryBillBtn'); if (!queryBillBtn) return; queryBillBtn.addEventListener('click', () => { const modal = new bootstrap.Modal(document.getElementById('billQueryModal')); modal.show(); }); // 设置默认时间范围为一个月前到今天 const today = new Date(); const oneMonthAgo = new Date(); oneMonthAgo.setMonth(today.getMonth() - 1); document.getElementById('startDate').valueAsDate = oneMonthAgo; document.getElementById('endDate').valueAsDate = today; function queryBills() { const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; const userKey = localStorage.getItem('userKey'); if (!startDate || !endDate) { showToast('请选择开始和结束日期', 'danger'); return; } fetch(`/payment/orders?start=${startDate}&end=${endDate}&key=${userKey}`) .then(response => response.json()) .then(data => { const tableBody = document.getElementById('billTableBody'); tableBody.innerHTML = ''; if (data.error) { showToast(data.error, 'danger'); return; } if (data.length === 0) { tableBody.innerHTML = '没有找到账单记录'; return; } data.forEach(bill => { const row = document.createElement('tr'); row.innerHTML = ` ${bill.order_id} ${bill.created_at} ${bill.amount} ${bill.status == 'completed' ? '已完成' : '未完成'} `; tableBody.appendChild(row); }); }) .catch(error => { showToast('查询失败: ' + error.message, 'danger'); }); } // 处理账单查询提交 document.getElementById('queryBillSubmit').addEventListener('click', () => { queryBills(); }); queryBills(); // 初始化时查询账单 } // 在页面加载时初始化 window.addEventListener('DOMContentLoaded', () => { initRechargeButton(); initQueryBillButton(); watchThemeChanges(); document.getElementById('saveNicknameBtn').addEventListener('click', async () => { const newNickname = document.getElementById('newNickname').value.trim(); if (!newNickname) { showToast('请输入新昵称', 'warning'); return; } try { const response = await fetch('/auth/update_nickname', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: localStorage.getItem('userKey'), new_nickname: newNickname }) }); const data = await response.json(); if (response.ok) { document.getElementById('userNickname').textContent = newNickname; document.getElementById('newNickname').value = ''; showToast('昵称修改成功', 'success'); const editNicknameModal = bootstrap.Modal.getInstance(document.getElementById('editNicknameModal')); editNicknameModal.hide(); } else { showToast(`昵称修改失败: ${data.error}`, 'danger'); } } catch (error) { console.error('修改昵称出错:', error); showToast('修改昵称出错', 'danger'); } }); document.getElementById('themeSettingsBtn').addEventListener('click', function() { var themeSettingsModal = new bootstrap.Modal(document.getElementById('themeSettingsModal')); themeSettingsModal.show(); }); });