// http_server_management_console/frontend/js/app.js new Vue({ el: '#app', data() { return { serverConfig: JSON.parse(localStorage.getItem('serverConfig')) || { sites: [ { name: '示例网站', server: 'example.com', port: 8080, enable_ssl: false, collapsed: false, paths: [ { path: '/static', type: 'static', target: '静态资源' }, { path: '/api', type: 'proxy', target: 'http://backend:3000' } ] } ] }, logs: JSON.parse(localStorage.getItem('serverLogs')) || [ { timestamp: '2025-05-06 23:13:22', message: '添加了路由配置 /api → http://backend:3000', type: 'success' }, { timestamp: '2025-05-06 23:12:15', message: '修改了网站端口 8080 → 8443', type: 'warning' }, { timestamp: '2025-05-06 23:10:07', message: '创建了网站配置 example.com', type: 'info' } ], requestCount: 0, goroutineCount: 10, filterType: 'all', searchQuery: '', logLevels: [ { value: 'all', label: '全部' }, { value: 'info', label: '信息' }, { value: 'success', label: '成功' }, { value: 'warning', label: '警告' }, { value: 'danger', label: '错误' } ], siteDialog: { visible: false, title: '添加网站', form: { id: null, name: '', domain: '', port: 80, ssl: false, collapsed: false }, editIndex: null }, routeDialog: { visible: false, title: '添加路由', form: { path: '', type: 'static', target: '' }, siteIndex: null, editIndex: null }, requestChart: null, goroutineChart: null, chartInterval: null } }, computed: { filteredLogs() { let result = this.logs; if (this.filterType !== 'all') { result = result.filter(log => log.type === this.filterType); } if (this.searchQuery) { const query = this.searchQuery.toLowerCase(); result = result.filter(log => log.message.toLowerCase().includes(query) || log.timestamp.includes(query) ); } return result; } }, mounted() { this.requestCount = Math.floor(Math.random() * 1000) + 500; // 模拟数据更新 setInterval(() => { this.requestCount += Math.floor(Math.random() * 10); this.goroutineCount = Math.max(5, this.goroutineCount + Math.floor(Math.random() * 5) - 2); }, 3000); // 应用已保存的主题 const savedTheme = localStorage.getItem('theme'); if (savedTheme) { document.body.className = savedTheme; } // 初始化图表 this.initCharts(); }, beforeDestroy() { if (this.chartInterval) { clearInterval(this.chartInterval); } }, methods: { // 站点配置管理 toggleCollapse(index) { this.serverConfig.sites[index].collapsed = !this.serverConfig.sites[index].collapsed; this.saveConfig(); }, saveConfig() { localStorage.setItem('serverConfig', JSON.stringify(this.serverConfig)); this.logAction('保存了服务器配置', 'success'); }, addSite() { this.siteDialog = { visible: true, title: '添加网站', form: { id: Date.now(), name: '新网站', domain: 'new-site.com', port: 80, ssl: false, collapsed: false }, editIndex: null }; }, editSite(index) { const site = this.serverConfig.sites[index]; this.siteDialog = { visible: true, title: '编辑网站', form: JSON.parse(JSON.stringify(site)), editIndex: index }; }, saveSite() { if (this.siteDialog.editIndex !== null) { this.serverConfig.sites.splice(this.siteDialog.editIndex, 1, this.siteDialog.form); this.logAction(`更新了网站 ${this.siteDialog.form.name} 的配置`, 'success'); } else { this.serverConfig.sites.push({ ...this.siteDialog.form, routes: [] }); this.logAction(`添加了网站 ${this.siteDialog.form.name}`, 'success'); } this.saveConfig(); this.siteDialog.visible = false; }, deleteSite(index) { this.$confirm('确定要删除这个网站吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { const site = this.serverConfig.sites[index]; this.serverConfig.sites.splice(index, 1); this.saveConfig(); this.logAction(`删除了网站 ${site.name}`, 'warning'); this.$message({ type: 'success', message: '删除成功!' }); }).catch(() => { this.$message({ type: 'info', message: '已取消删除' }); }); }, //get site getSites() { //visit REST API to get sites, url is /api/config //return this.serverConfig.sites; this.$axios.get('/api/config').then(response => { this.sites = response.data; this.siteIndex = siteIndex; this.getRoutes(siteIndex); }); }, // 路由管理 addRoute(siteIndex) { this.routeDialog = { visible: true, title: '添加路由', form: { path: '', type: 'static', target: '' }, siteIndex, editIndex: null }; }, editRoute(siteIndex, routeIndex) { const route = this.serverConfig.sites[siteIndex].routes[routeIndex]; this.routeDialog = { visible: true, title: '编辑路由', form: JSON.parse(JSON.stringify(route)), siteIndex, editIndex: routeIndex }; }, saveRoute() { const site = this.serverConfig.sites[this.routeDialog.siteIndex]; if (this.routeDialog.editIndex !== null) { site.routes.splice(this.routeDialog.editIndex, 1, this.routeDialog.form); this.logAction(`更新了路由 ${this.routeDialog.form.path}`, 'success'); } else { site.routes.push(this.routeDialog.form); this.logAction(`添加了路由 ${this.routeDialog.form.path}`, 'success'); } this.saveConfig(); this.routeDialog.visible = false; }, deleteRoute(siteIndex, routeIndex) { this.$confirm('确定要删除这个路由吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { const route = this.serverConfig.sites[siteIndex].routes[routeIndex]; this.serverConfig.sites[siteIndex].routes.splice(routeIndex, 1); this.saveConfig(); this.logAction(`删除了路由 ${route.path}`, 'warning'); this.$message({ type: 'success', message: '删除成功!' }); }).catch(() => { this.$message({ type: 'info', message: '已取消删除' }); }); }, // 配置导入导出 exportConfig() { const dataStr = JSON.stringify(this.serverConfig, null, 2); const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); const exportFileDefaultName = `server-config-${new Date().toISOString().slice(0,10)}.json`; const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', exportFileDefaultName); linkElement.click(); this.logAction('导出了服务器配置', 'success'); }, importConfig() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = e => { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = event => { try { const importedConfig = JSON.parse(event.target.result); this.$confirm('确定要导入此配置吗?当前配置将被覆盖。', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.serverConfig = importedConfig; this.saveConfig(); this.logAction('导入了服务器配置', 'success'); this.$message({ type: 'success', message: '导入成功!' }); }).catch(() => { this.$message({ type: 'info', message: '已取消导入' }); }); } catch (error) { this.$message.error('配置文件格式错误: ' + error.message); } }; reader.readAsText(file); }; input.click(); }, // 日志管理 logAction(message, type = 'info') { const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19); this.logs.unshift({ timestamp, message, type }); if (this.logs.length > 100) { this.logs = this.logs.slice(0, 100); } localStorage.setItem('serverLogs', JSON.stringify(this.logs)); }, clearLogs() { this.$confirm('确定要清空所有操作日志吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.logs = []; localStorage.setItem('serverLogs', JSON.stringify(this.logs)); this.logAction('清空了所有操作日志', 'warning'); this.$message.success('日志已清空'); }).catch(() => { this.$message.info('已取消清空操作'); }); }, exportLogs() { const dataStr = JSON.stringify(this.logs, null, 2); const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); const exportFileDefaultName = `server-logs-${new Date().toISOString().slice(0,10)}.json`; const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', exportFileDefaultName); linkElement.click(); this.logAction('导出了操作日志', 'info'); }, getTypeColor(type) { const colors = { info: 'text-blue-400', success: 'text-green-400', warning: 'text-yellow-400', danger: 'text-red-400' }; return colors[type] || 'text-gray-400'; }, // 主题管理 changeTheme(theme) { document.body.className = theme; localStorage.setItem('theme', theme); // 更新图表颜色以匹配主题 this.updateChartColors(theme); let themeName = '未知'; switch (theme) { case 'theme-dark': themeName = '暗色'; break; case 'theme-light': themeName = '亮色'; break; case 'theme-tech': themeName = '科技'; break; } this.logAction(`切换为${themeName}主题`, 'info'); }, // 服务器控制 startServer() { this.logAction('启动了HTTP服务器', 'success'); this.$message.success('服务器已启动'); }, stopServer() { this.logAction('停止了HTTP服务器', 'warning'); this.$message.warning('服务器已停止'); }, restartServer() { this.logAction('重启了HTTP服务器', 'info'); this.$message.info('服务器正在重启'); }, showLogs() { this.$message.info('正在显示日志'); }, // 图表功能 initCharts() { // 初始化请求图表 const requestCtx = document.getElementById('request-chart').getContext('2d'); this.requestChart = new Chart(requestCtx, { type: 'line', data: { labels: Array.from({length: 12}, (_, i) => `${i*5}秒`), datasets: [{ label: '访问量', data: Array.from({length: 12}, () => Math.floor(Math.random() * 100) + 50), borderColor: 'rgba(58, 134, 255, 0.8)', backgroundColor: 'rgba(58, 134, 255, 0.1)', borderWidth: 2, tension: 0.4, fill: true, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: false, grid: { display: false } }, y: { display: false, grid: { display: false } } } } }); // 初始化Goroutine图表 const goroutineCtx = document.getElementById('goroutine-chart').getContext('2d'); this.goroutineChart = new Chart(goroutineCtx, { type: 'line', data: { labels: Array.from({length: 12}, (_, i) => `${i*5}秒`), datasets: [{ label: 'Goroutines', data: Array.from({length: 12}, () => Math.floor(Math.random() * 20) + 10), borderColor: 'rgba(131, 56, 236, 0.8)', backgroundColor: 'rgba(131, 56, 236, 0.1)', borderWidth: 2, tension: 0.4, fill: true, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: false, grid: { display: false } }, y: { display: false, grid: { display: false } } } } }); // 模拟实时数据更新 this.chartInterval = setInterval(() => { // 更新请求图表数据 const requestData = this.requestChart.data.datasets[0].data; requestData.shift(); requestData.push(Math.floor(Math.random() * 30) + requestData[requestData.length - 1] - 15); this.requestChart.update(); // 更新Goroutine图表数据 const goroutineData = this.goroutineChart.data.datasets[0].data; goroutineData.shift(); goroutineData.push(Math.floor(Math.random() * 5) + goroutineData[goroutineData.length - 1] - 2); this.goroutineChart.update(); }, 5000); }, updateChartColors(theme) { let requestColor, goroutineColor; switch(theme) { case 'theme-tech': requestColor = 'rgba(0, 247, 255, 0.8)'; goroutineColor = 'rgba(0, 200, 255, 0.8)'; break; case 'theme-light': requestColor = 'rgba(0, 102, 255, 0.8)'; goroutineColor = 'rgba(98, 0, 238, 0.8)'; break; default: // dark theme requestColor = 'rgba(58, 134, 255, 0.8)'; goroutineColor = 'rgba(131, 56, 236, 0.8)'; } if (this.requestChart) { this.requestChart.data.datasets[0].borderColor = requestColor; this.requestChart.data.datasets[0].backgroundColor = requestColor.replace('0.8', '0.1'); this.requestChart.update(); } if (this.goroutineChart) { this.goroutineChart.data.datasets[0].borderColor = goroutineColor; this.goroutineChart.data.datasets[0].backgroundColor = goroutineColor.replace('0.8', '0.1'); this.goroutineChart.update(); } } } });