diff --git a/adminui/css/style.css b/adminui/css/style.css
new file mode 100644
index 0000000..d65284b
--- /dev/null
+++ b/adminui/css/style.css
@@ -0,0 +1,299 @@
+
+/* http_server_management_console/frontend/css/style.css */
+:root {
+ --primary: #409EFF;
+ --secondary: #67C23A;
+ --danger: #F56C6C;
+ --warning: #E6A23C;
+ --info: #909399;
+ --dark: #1a1a2e;
+ --light: #f8f9fa;
+ --bg-gradient: linear-gradient(135deg, var(--dark) 0%, #16213e 100%);
+ --glass-bg: rgba(255, 255, 255, 0.05);
+ --glass-border: rgba(255, 255, 255, 0.1);
+ --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
+}
+
+body {
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
+ background: var(--bg-gradient);
+ color: var(--light);
+ min-height: 100vh;
+ margin: 0;
+ padding: 0;
+}
+
+/* 路由列表展开收起动画 */
+.route-list {
+ transition: all 0.3s ease;
+ overflow: hidden;
+ max-height: 0;
+ opacity: 0;
+}
+
+.route-list.expanded {
+ max-height: 1000px;
+ opacity: 1;
+}
+
+.route-list.collapsed {
+ max-height: 0;
+ opacity: 0;
+}
+
+.toggle-icon {
+ transition: transform 0.3s ease;
+}
+
+.toggle-icon.collapsed {
+ transform: rotate(-90deg);
+}
+
+/* Element UI 风格按钮 */
+.el-button {
+ border-radius: 4px;
+ padding: 10px 20px;
+ font-size: 14px;
+ font-weight: 500;
+ transition: all 0.3s;
+ border: 1px solid transparent;
+}
+
+.el-button--primary {
+ background-color: var(--primary);
+ color: white;
+}
+
+.el-button--primary:hover {
+ background-color: #66b1ff;
+ border-color: #66b1ff;
+}
+
+.el-button--success {
+ background-color: var(--secondary);
+ color: white;
+}
+
+.el-button--success:hover {
+ background-color: #85ce61;
+ border-color: #85ce61;
+}
+
+.el-button--danger {
+ background-color: var(--danger);
+ color: white;
+}
+
+.el-button--danger:hover {
+ background-color: #f78989;
+ border-color: #f78989;
+}
+
+.el-button--warning {
+ background-color: var(--warning);
+ color: white;
+}
+
+.el-button--warning:hover {
+ background-color: #ebb563;
+ border-color: #ebb563;
+}
+
+/* 卡片样式 */
+.el-card {
+ border-radius: 4px;
+ border: 1px solid var(--glass-border);
+ background-color: var(--glass-bg);
+ color: var(--light);
+ overflow: hidden;
+ transition: all 0.3s;
+}
+
+.el-card__header {
+ padding: 18px 20px;
+ border-bottom: 1px solid var(--glass-border);
+ box-sizing: border-box;
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.el-card__body {
+ padding: 20px;
+}
+
+/* 表单元素 */
+.el-form-item {
+ margin-bottom: 22px;
+}
+
+.el-form-item__label {
+ color: var(--light);
+ font-size: 14px;
+ line-height: 40px;
+ padding: 0 12px 0 0;
+ box-sizing: border-box;
+}
+
+.el-input__inner {
+ background-color: rgba(0, 0, 0, 0.2);
+ border: 1px solid var(--glass-border);
+ color: var(--light);
+ border-radius: 4px;
+ padding: 0 15px;
+ height: 40px;
+ line-height: 40px;
+ transition: all 0.3s;
+}
+
+.el-input__inner:focus {
+ border-color: var(--primary);
+ outline: 0;
+ box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+}
+
+/* 表格样式 */
+.el-table {
+ background-color: transparent;
+ color: var(--light);
+}
+
+.el-table th {
+ background-color: rgba(0, 0, 0, 0.2);
+ color: var(--light);
+}
+
+.el-table tr {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.el-table--enable-row-hover .el-table__body tr:hover>td {
+ background-color: rgba(0, 0, 0, 0.3);
+}
+
+/* 对话框样式 */
+.el-dialog {
+ background: rgba(26, 26, 46, 0.9);
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
+ border: 1px solid var(--glass-border);
+}
+
+.el-dialog__header {
+ padding: 20px;
+ border-bottom: 1px solid var(--glass-border);
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.el-dialog__title {
+ color: var(--light);
+ font-size: 18px;
+ line-height: 24px;
+}
+
+.el-dialog__body {
+ padding: 20px;
+ color: var(--light);
+}
+
+.el-dialog__footer {
+ padding: 10px 20px;
+ border-top: 1px solid var(--glass-border);
+ text-align: right;
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+/* 科技感主题 */
+.theme-tech {
+ --primary: #00f7ff;
+ --secondary: #00c8ff;
+ --danger: #ff3e3e;
+ --warning: #ffcc00;
+ --dark: #0f0f23;
+ --light: #e0e0ff;
+ --bg-gradient: linear-gradient(135deg, #0f0f23 0%, #1e1e3e 100%);
+ --glass-bg: rgba(0, 199, 255, 0.05);
+ --glass-border: rgba(0, 199, 255, 0.15);
+}
+
+/* 暗色主题 */
+.theme-dark {
+ --primary: #409EFF;
+ --secondary: #67C23A;
+ --danger: #F56C6C;
+ --warning: #E6A23C;
+ --dark: #121212;
+ --light: #e0e0e0;
+ --bg-gradient: linear-gradient(135deg, #121212 0%, #1e1e1e 100%);
+}
+
+/* 亮色主题 */
+.theme-light {
+ --primary: #0066ff;
+ --secondary: #6200ee;
+ --danger: #ff1744;
+ --warning: #ff9100;
+ --dark: #f5f5f5;
+ --light: #333333;
+ --bg-gradient: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
+ --glass-bg: rgba(0, 0, 0, 0.05);
+ --glass-border: rgba(0, 0, 0, 0.1);
+}
+
+/* 响应式布局 */
+@media (max-width: 768px) {
+ .el-card {
+ margin-bottom: 20px;
+ }
+
+ .el-form-item {
+ margin-bottom: 15px;
+ }
+
+ .el-button {
+ padding: 8px 15px;
+ font-size: 13px;
+ }
+}
+
+/* 滚动条样式 */
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.1);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--primary);
+ border-radius: 3px;
+}
+
+/* 动画效果 */
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.fade-enter-active, .fade-leave-active {
+ transition: opacity 0.3s;
+}
+
+.fade-enter, .fade-leave-to {
+ opacity: 0;
+}
+
+/* 玻璃卡片效果 */
+.glass-card {
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ background: var(--glass-bg);
+ border: 1px solid var(--glass-border);
+ box-shadow: var(--glass-shadow);
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+}
+
+.glass-card:hover {
+ box-shadow: 0 10px 40px 0 rgba(31, 38, 135, 0.37);
+ transform: translateY(-3px);
+}
diff --git a/adminui/index.html b/adminui/index.html
index 30bd6d0..a067850 100644
--- a/adminui/index.html
+++ b/adminui/index.html
@@ -1,5 +1,353 @@
-
-
- GoHttpd Running...
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+ HTTP服务器管理控制台
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HTTP服务器管理控制台
+
+
+
+
+ 导出配置
+
+
+ 导入配置
+
+
+
+
+ 暗色主题
+ 亮色主题
+ 科技主题
+
+
+
+
+
+
+
+
+
+
+
+
+ 服务器配置
+
+
+ 添加网站
+
+
+
+
+
+
+
{{ site.name }}
+
{{ site.domain }}:{{ site.port }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ URL路由配置
+
+
+
+ 添加路由
+
+
+
+
+
+
+
+
+ {{ route.path }}
+ → {{ route.target }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 操作日志
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [{{ log.timestamp }}]
+
+ {{ log.message }}
+
+
+
+ 无匹配日志记录
+
+
+
+
+
+
+
+
+
+ 服务器状态
+
+
+
+
+
+ {{ requestCount }}
+ 访问量
+
+
+
+
+ {{ goroutineCount }}
+ Goroutines
+
+
+
+
+
+
访问量趋势
+
+
+
+
+
Goroutine变化
+
+
+
+
+
+
+ 快速操作
+
+
+
+
+ 启动
+
+
+
+
+ 停止
+
+
+
+
+ 重启
+
+
+
+
+ 日志
+
+
+
+
+
+
+
+ 性能监控
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 静态资源
+ 反向代理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminui/js/app.js b/adminui/js/app.js
new file mode 100644
index 0000000..0925781
--- /dev/null
+++ b/adminui/js/app.js
@@ -0,0 +1,524 @@
+
+// http_server_management_console/frontend/js/app.js
+new Vue({
+ el: '#app',
+ data() {
+ return {
+ serverConfig: JSON.parse(localStorage.getItem('serverConfig')) || {
+ sites: [
+ {
+ id: Date.now(),
+ name: '示例网站',
+ domain: 'example.com',
+ port: 8080,
+ ssl: false,
+ collapsed: false,
+ routes: [
+ {
+ 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: '已取消删除'
+ });
+ });
+ },
+
+ // 路由管理
+ 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();
+ }
+ }
+ }
+});
diff --git a/adminui/js/chart.js b/adminui/js/chart.js
new file mode 100644
index 0000000..7490701
--- /dev/null
+++ b/adminui/js/chart.js
@@ -0,0 +1,139 @@
+
+// http_server_management_console/frontend/js/chart.js
+export default {
+ methods: {
+ 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)';
+ }
+
+ this.requestChart.data.datasets[0].borderColor = requestColor;
+ this.requestChart.data.datasets[0].backgroundColor = requestColor.replace('0.8', '0.1');
+ this.requestChart.update();
+
+ this.goroutineChart.data.datasets[0].borderColor = goroutineColor;
+ this.goroutineChart.data.datasets[0].backgroundColor = goroutineColor.replace('0.8', '0.1');
+ this.goroutineChart.update();
+ }
+ },
+ mounted() {
+ this.initCharts();
+ },
+ beforeDestroy() {
+ if (this.chartInterval) {
+ clearInterval(this.chartInterval);
+ }
+ }
+}
diff --git a/adminui/js/logger.js b/adminui/js/logger.js
new file mode 100644
index 0000000..a1a4590
--- /dev/null
+++ b/adminui/js/logger.js
@@ -0,0 +1,99 @@
+
+// http_server_management_console/frontend/js/logger.js
+export default {
+ data() {
+ return {
+ logs: [],
+ filterType: 'all',
+ searchQuery: '',
+ logLevels: [
+ { value: 'all', label: '全部' },
+ { value: 'info', label: '信息' },
+ { value: 'success', label: '成功' },
+ { value: 'warning', label: '警告' },
+ { value: 'danger', label: '错误' }
+ ]
+ }
+ },
+ created() {
+ this.loadLogs();
+ },
+ 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;
+ }
+ },
+ methods: {
+ loadLogs() {
+ const savedLogs = localStorage.getItem('serverLogs');
+ this.logs = savedLogs ? JSON.parse(savedLogs) : [
+ { timestamp: '2025-06-12 14:40:22', message: '添加了路由配置 /api → http://backend:3000', type: 'success' },
+ { timestamp: '2025-06-12 14:38:15', message: '修改了网站端口 8080 → 8443', type: 'warning' },
+ { timestamp: '2025-06-12 14:35:07', message: '创建了网站配置 example.com', type: 'info' }
+ ];
+ },
+ addLog(message, type = 'info') {
+ const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
+ this.logs.unshift({ timestamp, message, type });
+
+ if (this.logs.length > 1000) {
+ this.logs = this.logs.slice(0, 1000);
+ }
+
+ this.saveLogs();
+ },
+ clearLogs() {
+ this.$confirm('确定要清空所有操作日志吗?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ this.logs = [];
+ this.saveLogs();
+ this.addLog('清空了所有操作日志', '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.addLog('导出了操作日志', 'info');
+ },
+ saveLogs() {
+ localStorage.setItem('serverLogs', JSON.stringify(this.logs));
+ },
+ 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';
+ }
+ }
+}