diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 99f6c5d..2a2b033 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,16 @@ "permissions": { "allow": [ "Bash(dir:*)", - "Bash(tree:*)" + "Bash(tree:*)", + "Bash(git checkout:*)", + "Bash(/home/congsh/.claude/plugins/cache/claude-plugins-official/ralph-loop/f1be96f0fb58/scripts/setup-ralph-loop.sh:*)", + "Bash(python3:*)", + "Bash(pip3 install:*)", + "Bash(playwright install:*)", + "mcp__plugin_playwright_playwright__browser_navigate", + "mcp__plugin_playwright_playwright__browser_install", + "Bash(./:*)", + "Bash(npx playwright install:*)" ] } } diff --git a/docker-compose.yml b/docker-compose.yml index cd6a142..82c10b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,19 +45,19 @@ services: redis: condition: service_healthy - # ==================== Web TUI 服务 ==================== - webtui: + # ==================== Web UI 服务 ==================== + webui: build: context: . dockerfile: Dockerfile - container_name: minenasai-webtui + container_name: minenasai-webui restart: unless-stopped ports: - - "${WEBTUI_PORT:-8080}:8080" + - "${WEBUI_PORT:-8080}:8080" environment: - MINENASAI_ENV=production - - MINENASAI_WEBTUI_HOST=0.0.0.0 - - MINENASAI_WEBTUI_PORT=8080 + - MINENASAI_WEBUI_HOST=0.0.0.0 + - MINENASAI_WEBUI_PORT=8080 volumes: - /volume2/docker/mineNasAi/data:/app/data - /volume2/docker/mineNasAi/logs:/app/logs diff --git a/src/minenasai/webtui/static/webui/css/webui.css b/src/minenasai/webtui/static/webui/css/webui.css index 61643fc..b6aafd7 100644 --- a/src/minenasai/webtui/static/webui/css/webui.css +++ b/src/minenasai/webtui/static/webui/css/webui.css @@ -1,100 +1,353 @@ -/* MineNASAI WebUI 样式 */ +/* ============================================================ + MineNASAI - Cyberpunk Professional Dashboard + 赛博朋克专业版控制台 -/* 暗色主题(默认) */ + 设计原则: + 1. 使用 CSS 变量实现真正的全局主题切换 + 2. Dark 模式:高对比度,文字清晰可读 + 3. Light 模式:干净的白色背景,深色文字 + ============================================================ */ + +/* ==================== CSS 变量系统 ==================== */ + +/* 暗色主题(默认) - 深空黑 + 霓虹光 */ :root { - --bg-primary: #1a1b26; - --bg-secondary: #24283b; - --bg-tertiary: #1f2335; - --text-primary: #c0caf5; - --text-secondary: #565f89; - --accent-primary: #7aa2f7; - --accent-success: #9ece6a; - --accent-warning: #e0af68; - --accent-error: #f7768e; - --border-color: #3b4261; + /* 背景色系 */ + --bg-primary: #0d0d12; + --bg-secondary: #14141a; + --bg-tertiary: #1a1a24; + --bg-elevated: #22222e; + --bg-hover: #2a2a38; + + /* 文字色系 - 高对比度确保可读性 */ + --text-primary: #ffffff; + --text-secondary: #b4b4c8; + --text-tertiary: #8a8a9a; + --text-muted: #6a6a7a; + --text-invert: #0d0d12; + + /* 强调色 - 霓虹系列 */ + --accent-primary: #00f0ff; + --accent-secondary: #a855f7; + --accent-success: #00ff9d; + --accent-warning: #ffb800; + --accent-error: #ff3366; + --accent-info: #00d9ff; + + /* 边框 */ + --border-color: rgba(255, 255, 255, 0.08); + --border-hover: rgba(0, 240, 255, 0.3); + --border-focus: var(--accent-primary); + + /* 阴影 - 多层光晕效果 */ + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.6); + --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.7); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.8); + --shadow-glow-sm: 0 0 10px rgba(0, 240, 255, 0.2); + --shadow-glow-md: 0 0 20px rgba(0, 240, 255, 0.3); + --shadow-glow-lg: 0 0 40px rgba(0, 240, 255, 0.4); + + /* 渐变 */ + --gradient-primary: linear-gradient(135deg, #00f0ff 0%, #a855f7 100%); + --gradient-success: linear-gradient(135deg, #00ff9d 0%, #00d9ff 100%); + --gradient-bg: linear-gradient(180deg, rgba(255,255,255,0.02) 0%, rgba(255,255,255,0) 100%); + + /* 字体 */ + --font-display: 'Orbitron', 'Noto Sans SC', sans-serif; + --font-body: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace; + + /* 间距 */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 32px; + --space-2xl: 48px; + + /* 圆角 */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + + /* 过渡 */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1); + + /* 光晕效果 */ + --glow-primary: 0 0 20px rgba(0, 240, 255, 0.5); + --glow-success: 0 0 20px rgba(0, 255, 157, 0.5); + --glow-error: 0 0 20px rgba(255, 51, 102, 0.5); + + /* 覆盖层 */ + --overlay-dark: rgba(0, 0, 0, 0.6); + --overlay-light: rgba(255, 255, 255, 0.1); + + /* Element Plus 变量覆盖 - Dark 模式 */ + --el-bg-color: var(--bg-primary); + --el-bg-color-page: var(--bg-secondary); + --el-bg-color-overlay: var(--bg-elevated); + --el-text-color-primary: var(--text-primary); + --el-text-color-regular: var(--text-secondary); + --el-text-color-secondary: var(--text-tertiary); + --el-text-color-placeholder: var(--text-muted); + --el-border-color: var(--border-color); + --el-border-color-light: var(--border-color); + --el-border-color-lighter: rgba(255, 255, 255, 0.05); + --el-border-color-extra-light: rgba(255, 255, 255, 0.03); + --el-border-color-dark: rgba(255, 255, 255, 0.15); + --el-border-color-darker: rgba(255, 255, 255, 0.2); + --el-fill-color-light: var(--bg-tertiary); + --el-fill-color-lighter: var(--bg-hover); + --el-fill-color-extra-light: var(--bg-primary); + --el-fill-color-dark: var(--bg-secondary); + --el-fill-color-darker: var(--bg-tertiary); + --el-fill-color-blank: var(--bg-primary); + --el-box-shadow-light: var(--shadow-sm); + --el-box-shadow: var(--shadow-md); + --el-box-shadow-dark: var(--shadow-lg); + --el-disabled-bg-color: var(--bg-tertiary); + --el-disabled-text-color: var(--text-muted); + --el-disabled-border-color: var(--border-color); + --el-overlay-color-light: var(--overlay-light); + --el-overlay-color-lighter: rgba(255, 255, 255, 0.4); + --el-mask-color: var(--overlay-dark); + --el-mask-color-extra-light: rgba(0, 0, 0, 0.3); } -/* 亮色主题 */ +/* 亮色主题 - 纯净白 + 深色高对比度文字 */ .theme-light { + /* 背景色系 - 纯净白色 */ --bg-primary: #ffffff; - --bg-secondary: #f5f7fa; - --bg-tertiary: #fafafa; - --text-primary: #303133; - --text-secondary: #909399; - --accent-primary: #409eff; - --accent-success: #67c23a; - --accent-warning: #e6a23c; - --accent-error: #f56c6c; - --border-color: #dcdfe6; + --bg-secondary: #f8f9fa; + --bg-tertiary: #f1f3f5; + --bg-elevated: #ffffff; + --bg-hover: #e9ecef; + + /* 文字色系 - 深色高对比度 */ + --text-primary: #1a1a1a; + --text-secondary: #4a4a4a; + --text-tertiary: #6a6a6a; + --text-muted: #9a9a9a; + --text-invert: #ffffff; + + /* 强调色 - 蓝色系 */ + --accent-primary: #0066cc; + --accent-secondary: #6b21a8; + --accent-success: #059669; + --accent-warning: #d97706; + --accent-error: #dc2626; + --accent-info: #0284c7; + + /* 边框 */ + --border-color: rgba(0, 0, 0, 0.08); + --border-hover: rgba(0, 102, 204, 0.2); + --border-focus: var(--accent-primary); + + /* 阴影 - 柔和阴影 */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.10); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); + --shadow-glow-sm: 0 0 10px rgba(0, 102, 204, 0.1); + --shadow-glow-md: 0 0 20px rgba(0, 102, 204, 0.15); + --shadow-glow-lg: 0 0 40px rgba(0, 102, 204, 0.2); + + /* 渐变 */ + --gradient-primary: linear-gradient(135deg, #0066cc 0%, #6b21a8 100%); + --gradient-success: linear-gradient(135deg, #059669 0%, #0284c7 100%); + --gradient-bg: linear-gradient(180deg, rgba(0,0,0,0.01) 0%, rgba(0,0,0,0) 100%); + + /* 光晕效果 - 弱化 */ + --glow-primary: 0 0 15px rgba(0, 102, 204, 0.3); + --glow-success: 0 0 15px rgba(5, 150, 105, 0.3); + --glow-error: 0 0 15px rgba(220, 38, 38, 0.3); + + /* 覆盖层 */ + --overlay-dark: rgba(0, 0, 0, 0.4); + --overlay-light: rgba(255, 255, 255, 0.6); } +/* ==================== Element Plus 变量覆盖 ==================== */ +/* 这是关键!确保所有 Element Plus 组件都使用我们的变量 */ + +.theme-light { + --el-bg-color: var(--bg-primary); + --el-bg-color-page: var(--bg-secondary); + --el-bg-color-overlay: var(--bg-elevated); + --el-text-color-primary: var(--text-primary); + --el-text-color-regular: var(--text-secondary); + --el-text-color-secondary: var(--text-tertiary); + --el-text-color-placeholder: var(--text-muted); + --el-border-color: var(--border-color); + --el-border-color-light: var(--border-color); + --el-border-color-lighter: rgba(0, 0, 0, 0.05); + --el-border-color-extra-light: rgba(0, 0, 0, 0.03); + --el-border-color-dark: rgba(0, 0, 0, 0.15); + --el-border-color-darker: rgba(0, 0, 0, 0.2); + --el-fill-color-light: var(--bg-tertiary); + --el-fill-color-lighter: var(--bg-hover); + --el-fill-color-extra-light: var(--bg-primary); + --el-fill-color-dark: var(--bg-secondary); + --el-fill-color-darker: var(--bg-tertiary); + --el-fill-color-blank: var(--bg-primary); + --el-box-shadow-light: var(--shadow-sm); + --el-box-shadow: var(--shadow-md); + --el-box-shadow-dark: var(--shadow-lg); + --el-disabled-bg-color: var(--bg-tertiary); + --el-disabled-text-color: var(--text-muted); + --el-disabled-border-color: var(--border-color); + --el-overlay-color-light: var(--overlay-light); + --el-overlay-color-lighter: rgba(255, 255, 255, 0.4); + --el-mask-color: var(--overlay-dark); + --el-mask-color-extra-light: rgba(0, 0, 0, 0.3); +} + +/* ==================== 全局样式 ==================== */ + * { margin: 0; padding: 0; box-sizing: border-box; } +html { + font-size: 14px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + body { - font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; + font-family: var(--font-body); background-color: var(--bg-primary); color: var(--text-primary); - height: 100vh; - overflow: hidden; + line-height: 1.6; + min-height: 100vh; + overflow-x: hidden; + transition: background-color var(--transition-base), color var(--transition-base); } +#app { + min-height: 100vh; + width: 100%; +} + +/* 强制所有元素使用 CSS 变量 */ +#app * { + border-color: var(--border-color); +} + +/* ==================== 布局容器 ==================== */ + .app-container { - height: 100vh; -} - -/* 侧边栏 */ -.sidebar { + min-height: 100vh; + display: flex; background-color: var(--bg-primary); - border-right: 1px solid var(--border-color); - transition: width 0.3s; - overflow: hidden; } +/* ==================== 侧边栏 ==================== */ + +.sidebar { + width: 260px; + background-color: var(--bg-secondary); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + transition: all var(--transition-base); + position: relative; + z-index: 100; +} + +/* Logo 区域 */ .logo { - height: 60px; + height: 64px; display: flex; align-items: center; justify-content: center; - gap: 10px; + gap: 12px; border-bottom: 1px solid var(--border-color); cursor: pointer; + transition: all var(--transition-fast); + background: var(--gradient-bg); +} + +.logo:hover { + background: var(--bg-tertiary); } .logo-icon { - font-size: 24px; + font-size: 28px; + filter: drop-shadow(0 0 8px var(--accent-primary)); } .logo-text { + font-family: var(--font-display); font-size: 18px; - font-weight: 600; - color: var(--accent-primary); + font-weight: 700; + color: var(--text-primary); + letter-spacing: 1px; + background: var(--gradient-primary); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } +/* ==================== Element Plus 组件覆盖 ==================== */ + +/* 布局容器 - 关键修复 */ +.el-container, +.el-aside, +.el-header, +.el-main { + background-color: transparent !important; + color: var(--text-primary) !important; +} + +/* 菜单 */ .el-menu { + background-color: transparent !important; border-right: none !important; + padding: var(--space-sm); +} + +.el-menu-item, +.el-sub-menu__title { + color: var(--text-secondary) !important; + border-radius: var(--radius-md); + margin-bottom: var(--space-xs); + transition: all var(--transition-fast); +} + +.el-menu-item:hover, +.el-sub-menu__title:hover { + background-color: var(--bg-hover) !important; + color: var(--text-primary) !important; } .el-menu-item.is-active { - background-color: var(--bg-secondary) !important; + background: var(--gradient-primary) !important; + color: #ffffff !important; + box-shadow: var(--shadow-glow-sm); } -.el-menu-item:hover, .el-sub-menu__title:hover { +/* 子菜单 */ +.el-sub-menu .el-menu { background-color: var(--bg-tertiary) !important; } -/* 头部 */ +/* ==================== 头部 ==================== */ + .header { background-color: var(--bg-secondary); border-bottom: 1px solid var(--border-color); + height: 64px; display: flex; align-items: center; justify-content: space-between; - padding: 0 20px; - height: 60px; + padding: 0 var(--space-lg); + transition: all var(--transition-base); } .header-left { @@ -105,153 +358,416 @@ body { .header-right { display: flex; align-items: center; - gap: 16px; + gap: var(--space-md); } -/* 主内容区 */ -.main-content { - background-color: var(--bg-tertiary); - padding: 20px; - overflow-y: auto; +/* 面包屑 */ +.el-breadcrumb__inner { + color: var(--text-primary) !important; + font-weight: 500; } -/* 卡片样式 */ -.config-card { - background-color: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 8px; - margin-bottom: 20px; +.el-breadcrumb__separator { + color: var(--text-tertiary) !important; } -.config-card .el-card__header { - background-color: var(--bg-tertiary); - border-bottom: 1px solid var(--border-color); - padding: 15px 20px; -} - -.config-card .el-card__body { - padding: 20px; -} - -/* 统计卡片 */ -.stat-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 16px; - margin-bottom: 20px; -} - -.stat-card { - background-color: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 8px; - padding: 20px; -} - -.stat-card-title { - font-size: 14px; - color: var(--text-secondary); - margin-bottom: 8px; -} - -.stat-card-value { - font-size: 28px; - font-weight: 600; - color: var(--accent-primary); -} - -.stat-card-sub { - font-size: 12px; - color: var(--text-secondary); - margin-top: 4px; -} - -/* 表单样式 */ -.el-form-item__label { - color: var(--text-secondary) !important; -} - -.el-input__inner, .el-textarea__inner, .el-select .el-input__inner { +/* 标签 */ +.el-tag { + border: 1px solid var(--border-color) !important; background-color: var(--bg-tertiary) !important; - border-color: var(--border-color) !important; color: var(--text-primary) !important; } -.el-input__inner:focus, .el-textarea__inner:focus { - border-color: var(--accent-primary) !important; +/* ==================== 主内容区 ==================== */ + +.main-content { + background-color: var(--bg-primary); + padding: var(--space-lg); + min-height: calc(100vh - 64px); + overflow-y: auto; } +/* 滚动条美化 */ +.main-content::-webkit-scrollbar { + width: 8px; +} + +.main-content::-webkit-scrollbar-track { + background: transparent; +} + +.main-content::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: var(--radius-full); +} + +.main-content::-webkit-scrollbar-thumb:hover { + background: var(--border-hover); +} + +/* ==================== 卡片组件 ==================== */ + +.el-card { + background-color: var(--bg-secondary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-lg) !important; + box-shadow: var(--shadow-sm) !important; + transition: all var(--transition-base) !important; + overflow: hidden; +} + +.el-card:hover { + box-shadow: var(--shadow-md) !important; + border-color: var(--border-hover) !important; +} + +.el-card__header { + background-color: var(--bg-tertiary) !important; + border-bottom: 1px solid var(--border-color) !important; + padding: var(--space-md) var(--space-lg) !important; + color: var(--text-primary) !important; + font-weight: 600; + font-size: 15px; +} + +.el-card__body { + padding: var(--space-lg) !important; + color: var(--text-primary) !important; + background-color: var(--bg-secondary) !important; +} + +/* 配置卡片 */ +.config-card { + background-color: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + margin-bottom: var(--space-lg); + transition: all var(--transition-base); +} + +.config-card:hover { + border-color: var(--accent-primary); + box-shadow: var(--shadow-glow-sm); +} + +/* ==================== 表单组件 ==================== */ + +.el-form-item__label { + color: var(--text-secondary) !important; + font-weight: 500; + font-size: 13px; +} + +/* 输入框 */ +.el-input__wrapper { + background-color: var(--bg-tertiary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-md) !important; + box-shadow: none !important; + transition: all var(--transition-fast) !important; +} + +.el-input__wrapper:hover { + border-color: var(--border-hover) !important; +} + +.el-input__wrapper.is-focus { + border-color: var(--accent-primary) !important; + box-shadow: var(--shadow-glow-sm) !important; +} + +.el-input__inner { + color: var(--text-primary) !important; + background: transparent !important; +} + +.el-input__inner::placeholder { + color: var(--text-muted) !important; +} + +/* 文本域 */ +.el-textarea__inner { + background-color: var(--bg-tertiary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-md) !important; + color: var(--text-primary) !important; + transition: all var(--transition-fast) !important; +} + +.el-textarea__inner:focus { + border-color: var(--accent-primary) !important; + box-shadow: var(--shadow-glow-sm) !important; +} + +.el-textarea__inner::placeholder { + color: var(--text-muted) !important; +} + +/* 下拉选择 */ .el-select-dropdown { background-color: var(--bg-secondary) !important; - border-color: var(--border-color) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-md) !important; + box-shadow: var(--shadow-lg) !important; } .el-select-dropdown__item { color: var(--text-primary) !important; + transition: all var(--transition-fast); } -.el-select-dropdown__item.hover, .el-select-dropdown__item:hover { +.el-select-dropdown__item.hover, +.el-select-dropdown__item:hover { + background-color: var(--bg-hover) !important; + color: var(--accent-primary) !important; +} + +.el-select-dropdown__item.is-selected { + color: var(--accent-primary) !important; + background: rgba(0, 240, 255, 0.1) !important; +} + +.theme-light .el-select-dropdown__item.is-selected { + background: rgba(0, 102, 204, 0.1) !important; +} + +/* ==================== 按钮组件 ==================== */ + +.el-button { + border-radius: var(--radius-md) !important; + font-weight: 500 !important; + transition: all var(--transition-fast) !important; + border: 1px solid var(--border-color) !important; +} + +.el-button--default { background-color: var(--bg-tertiary) !important; + border-color: var(--border-color) !important; + color: var(--text-primary) !important; } -/* 表格样式 */ +.el-button--default:hover { + background-color: var(--bg-hover) !important; + border-color: var(--border-hover) !important; + color: var(--accent-primary) !important; +} + +.el-button--primary { + background: var(--gradient-primary) !important; + border: none !important; + color: #ffffff !important; +} + +.el-button--primary:hover { + transform: translateY(-1px); + box-shadow: var(--shadow-glow-md) !important; +} + +/* ==================== 表格组件 ==================== */ + .el-table { --el-table-bg-color: var(--bg-secondary); --el-table-header-bg-color: var(--bg-tertiary); --el-table-tr-bg-color: var(--bg-secondary); - --el-table-row-hover-bg-color: var(--bg-tertiary); + --el-table-row-hover-bg-color: var(--bg-hover); --el-table-text-color: var(--text-primary); --el-table-header-text-color: var(--text-secondary); --el-table-border-color: var(--border-color); + --el-table-current-row-bg-color: rgba(0, 240, 255, 0.05); } -/* 终端容器 */ +/* ==================== 开关组件 ==================== */ + +.el-switch__core { + background-color: var(--bg-tertiary) !important; + border: 1px solid var(--border-color) !important; +} + +/* ==================== 对话框 ==================== */ + +.el-dialog { + background-color: var(--bg-secondary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-xl) !important; + box-shadow: var(--shadow-lg) !important; +} + +.el-dialog__header { + border-bottom: 1px solid var(--border-color) !important; + padding: var(--space-lg) !important; +} + +.el-dialog__title { + color: var(--text-primary) !important; + font-weight: 600; + font-size: 16px; +} + +.el-dialog__body { + padding: var(--space-lg) !important; + color: var(--text-primary) !important; +} + +/* ==================== 标签页 ==================== */ + +.el-tabs__item { + color: var(--text-secondary) !important; + font-weight: 500; + transition: all var(--transition-fast); +} + +.el-tabs__item:hover { + color: var(--accent-primary) !important; +} + +.el-tabs__item.is-active { + color: var(--accent-primary) !important; +} + +.el-tabs__active-bar { + background-color: var(--accent-primary) !important; +} + +/* ==================== 分割线 ==================== */ + +.el-divider { + border-color: var(--border-color) !important; +} + +/* ==================== 消息提示 ==================== */ + +.el-message { + background-color: var(--bg-secondary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-md) !important; + box-shadow: var(--shadow-lg) !important; + color: var(--text-primary) !important; +} + +/* ==================== 下拉菜单 ==================== */ + +.el-dropdown-menu { + background-color: var(--bg-secondary) !important; + border: 1px solid var(--border-color) !important; + border-radius: var(--radius-md) !important; + box-shadow: var(--shadow-lg) !important; +} + +.el-dropdown-menu__item { + color: var(--text-primary) !important; + transition: all var(--transition-fast); +} + +.el-dropdown-menu__item:hover { + background-color: var(--bg-hover) !important; + color: var(--accent-primary) !important; +} + +/* ==================== 特殊组件 ==================== */ + +/* 统计卡片 */ +.stat-card { + background-color: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: var(--space-lg); + transition: all var(--transition-base); + position: relative; + overflow: hidden; +} + +.stat-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--gradient-primary); + opacity: 0; + transition: opacity var(--transition-base); +} + +.stat-card:hover { + border-color: var(--accent-primary); + box-shadow: var(--shadow-glow-sm); + transform: translateY(-2px); +} + +.stat-card:hover::before { + opacity: 1; +} + +.stat-card-title { + font-size: 13px; + color: var(--text-secondary); + font-weight: 500; + margin-bottom: var(--space-sm); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.stat-card-value { + font-size: 32px; + font-weight: 700; + font-family: var(--font-display); + color: var(--text-primary); + line-height: 1; + margin-bottom: var(--space-xs); +} + +.stat-card-sub { + font-size: 12px; + color: var(--text-tertiary); +} + +/* Provider 卡片 */ +.provider-card { + background-color: var(--bg-tertiary); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: var(--space-md); + margin-bottom: var(--space-md); + transition: all var(--transition-base); + cursor: pointer; +} + +.provider-card:hover { + border-color: var(--accent-primary); + background-color: var(--bg-hover); + box-shadow: var(--shadow-glow-sm); +} + +/* ==================== 终端容器 ==================== */ + .terminal-container { background-color: var(--bg-primary); border: 1px solid var(--border-color); - border-radius: 8px; + border-radius: var(--radius-lg); overflow: hidden; - height: calc(100vh - 340px); - min-height: 300px; + box-shadow: var(--shadow-md); } .terminal-header { display: flex; align-items: center; justify-content: space-between; - padding: 10px 15px; + padding: var(--space-sm) var(--space-md); background-color: var(--bg-secondary); border-bottom: 1px solid var(--border-color); } -.terminal-title { - display: flex; - align-items: center; - gap: 8px; -} - -.terminal-status { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: var(--accent-error); -} - -.terminal-status.connected { - background-color: var(--accent-success); -} - .terminal-body { - height: calc(100% - 45px); - padding: 8px; + padding: var(--space-md); + background-color: var(--bg-primary); } -/* 日志容器 */ +/* ==================== 日志容器 ==================== */ + .logs-container { background-color: var(--bg-primary); border: 1px solid var(--border-color); - border-radius: 8px; - height: calc(100vh - 180px); + border-radius: var(--radius-lg); overflow: hidden; } @@ -259,476 +775,118 @@ body { display: flex; align-items: center; justify-content: space-between; - padding: 10px 15px; + padding: var(--space-sm) var(--space-md); background-color: var(--bg-secondary); border-bottom: 1px solid var(--border-color); } .logs-body { - height: calc(100% - 45px); + padding: var(--space-md); + max-height: 500px; overflow-y: auto; - padding: 10px 15px; - font-family: 'Cascadia Code', 'Fira Code', monospace; + font-family: var(--font-mono); font-size: 12px; + line-height: 1.6; } .log-entry { - padding: 4px 0; + padding: var(--space-xs) 0; border-bottom: 1px solid var(--border-color); display: flex; - gap: 10px; + gap: var(--space-md); } .log-time { - color: var(--text-secondary); + color: var(--text-tertiary); flex-shrink: 0; + font-size: 11px; } .log-level { flex-shrink: 0; - padding: 0 6px; - border-radius: 3px; + padding: 2px 8px; + border-radius: var(--radius-sm); font-size: 10px; + font-weight: 600; text-transform: uppercase; } .log-level.info { - background-color: rgba(122, 162, 247, 0.2); + background-color: rgba(0, 240, 255, 0.2); color: var(--accent-primary); } .log-level.warn { - background-color: rgba(224, 175, 104, 0.2); + background-color: rgba(255, 184, 0, 0.2); color: var(--accent-warning); } .log-level.error { - background-color: rgba(247, 118, 142, 0.2); + background-color: rgba(255, 51, 102, 0.2); color: var(--accent-error); } .log-message { flex: 1; word-break: break-all; + color: var(--text-primary); } -/* 按钮样式 */ -.el-button--primary { - --el-button-bg-color: var(--accent-primary); - --el-button-border-color: var(--accent-primary); - --el-button-hover-bg-color: #89b4fa; - --el-button-hover-border-color: #89b4fa; -} +/* ==================== 响应式设计 ==================== */ -/* 对话框样式 */ -.el-dialog { - --el-dialog-bg-color: var(--bg-secondary); - --el-dialog-border-radius: 8px; -} - -.el-dialog__header { - border-bottom: 1px solid var(--border-color); -} - -.el-dialog__title { - color: var(--text-primary) !important; -} - -/* 标签页 */ -.el-tabs__item { - color: var(--text-secondary) !important; -} - -.el-tabs__item.is-active { - color: var(--accent-primary) !important; -} - -/* Provider 卡片 */ -.provider-card { - background-color: var(--bg-tertiary); - border: 1px solid var(--border-color); - border-radius: 8px; - padding: 16px; - margin-bottom: 12px; - transition: border-color 0.3s; -} - -.provider-card:hover { - border-color: var(--accent-primary); -} - -.provider-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 12px; -} - -.provider-name { - display: flex; - align-items: center; - gap: 8px; - font-weight: 500; -} - -.provider-badge { - font-size: 11px; - padding: 2px 8px; - border-radius: 10px; - background-color: rgba(122, 162, 247, 0.2); - color: var(--accent-primary); -} - -.provider-badge.domestic { - background-color: rgba(158, 206, 106, 0.2); - color: var(--accent-success); -} - -/* LLM 配置状态卡片 */ -.status-chip { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - border: 1px solid var(--border-color); - border-radius: 8px; - cursor: pointer; - transition: all 0.2s; - background-color: var(--bg-tertiary); -} - -.status-chip:hover { - border-color: var(--accent-primary); - background-color: rgba(122, 162, 247, 0.1); -} - -.status-chip.selected { - border-color: var(--accent-primary); - background-color: rgba(122, 162, 247, 0.15); -} - -.status-chip.active { - border-color: var(--accent-success); -} - -.status-chip.active .status-icon { - opacity: 1; -} - -.status-icon { - font-size: 16px; - opacity: 0.8; -} - -.status-name { - font-size: 13px; - font-weight: 500; -} - -/* 测试结果样式 */ -.test-result { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 12px; - border-radius: 4px; - font-size: 14px; -} - -.test-result.success { - color: var(--accent-success); - background-color: rgba(158, 206, 106, 0.15); -} - -.test-result.error { - color: var(--accent-error); - background-color: rgba(247, 118, 142, 0.15); -} - -/* 响应式 */ @media (max-width: 768px) { .sidebar { position: fixed; - z-index: 100; - left: -220px; + left: -260px; + height: 100vh; + z-index: 1000; + box-shadow: var(--shadow-lg); } - + .sidebar.open { left: 0; } + + .main-content { + padding: var(--space-md); + } + + .header { + padding: 0 var(--space-md); + } } -/* ========== 亮色主题全面覆盖 ========== */ +/* ==================== 动画 ==================== */ -/* 全局背景 & 文字 */ -.theme-light, -.theme-light body { - background-color: #f0f2f5 !important; - color: #303133 !important; +@keyframes glow { + 0%, 100% { + box-shadow: var(--shadow-glow-sm); + } + 50% { + box-shadow: var(--shadow-glow-md); + } } -.theme-light .app-container { - background-color: #f0f2f5; +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } } -/* 侧边栏 */ -.theme-light .sidebar { - background-color: #fff !important; - border-right-color: #e4e7ed !important; +/* ==================== 工具类 ==================== */ + +.text-gradient { + background: var(--gradient-primary); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -.theme-light .logo { - border-bottom-color: #e4e7ed !important; -} - -.theme-light .el-menu { - background-color: #fff !important; -} - -.theme-light .el-menu-item, -.theme-light .el-sub-menu__title { - color: #303133 !important; -} - -.theme-light .el-menu-item.is-active { - background-color: #ecf5ff !important; - color: #409eff !important; -} - -.theme-light .el-menu-item:hover, -.theme-light .el-sub-menu__title:hover { - background-color: #f5f7fa !important; -} - -/* 头部 */ -.theme-light .header { - background-color: #fff !important; - border-bottom-color: #e4e7ed !important; - color: #303133 !important; -} - -.theme-light .header h3, -.theme-light .header span { - color: #303133 !important; -} - -/* 主内容区 */ -.theme-light .main-content { - background-color: #f0f2f5 !important; -} - -/* 卡片 */ -.theme-light .el-card { - background-color: #fff !important; - border-color: #e4e7ed !important; - color: #303133 !important; - --el-card-bg-color: #fff; -} - -.theme-light .el-card__header { - background-color: #fafbfc !important; - border-bottom-color: #e4e7ed !important; - color: #303133 !important; -} - -.theme-light .el-card__body { - color: #303133 !important; -} - -.theme-light .config-card { - background-color: #fff !important; -} - -.theme-light .config-card .el-card__header { - background-color: #fafbfc !important; - border-bottom-color: #e4e7ed !important; -} - -.theme-light .provider-card { - background-color: #fafbfc !important; - border-color: #e4e7ed !important; -} - -.theme-light .stat-card { - background-color: #fff !important; - border-color: #e4e7ed !important; -} - -/* 表单 */ -.theme-light .el-form-item__label { - color: #606266 !important; -} - -.theme-light .el-input__inner, -.theme-light .el-textarea__inner { - background-color: #fff !important; - border-color: #dcdfe6 !important; - color: #303133 !important; -} - -.theme-light .el-input__wrapper { - background-color: #fff !important; - box-shadow: 0 0 0 1px #dcdfe6 inset !important; -} - -.theme-light .el-input__wrapper:hover { - box-shadow: 0 0 0 1px #c0c4cc inset !important; -} - -.theme-light .el-input__wrapper.is-focus { - box-shadow: 0 0 0 1px #409eff inset !important; -} - -.theme-light .el-select-dropdown { - background-color: #fff !important; -} - -.theme-light .el-select-dropdown__item { - color: #303133 !important; -} - -/* 按钮 */ -.theme-light .el-button--default { - --el-button-bg-color: #fff; - --el-button-border-color: #dcdfe6; - --el-button-text-color: #606266; - --el-button-hover-bg-color: #ecf5ff; - --el-button-hover-border-color: #c6e2ff; - --el-button-hover-text-color: #409eff; -} - -.theme-light .el-button--primary { - --el-button-bg-color: #409eff; - --el-button-border-color: #409eff; - --el-button-hover-bg-color: #66b1ff; - --el-button-hover-border-color: #66b1ff; -} - -/* 分割线 */ -.theme-light .el-divider { - border-color: #e4e7ed !important; -} - -/* 标签 */ -.theme-light .el-tag { - --el-tag-bg-color: #ecf5ff; - --el-tag-border-color: #d9ecff; - --el-tag-text-color: #409eff; -} - -/* Alert */ -.theme-light .el-alert--info { - background-color: #f4f4f5 !important; - color: #909399 !important; -} - -.theme-light .el-alert--info .el-alert__title { - color: #606266 !important; -} - -/* 表格 */ -.theme-light .el-table { - --el-table-bg-color: #fff; - --el-table-header-bg-color: #fafbfc; - --el-table-tr-bg-color: #fff; - --el-table-row-hover-bg-color: #f5f7fa; - --el-table-text-color: #303133; - --el-table-header-text-color: #909399; - --el-table-border-color: #ebeef5; -} - -/* Tabs */ -.theme-light .el-tabs__item { - color: #909399 !important; -} - -.theme-light .el-tabs__item.is-active { - color: #409eff !important; -} - -.theme-light .el-tabs--border-card { - background-color: #fff !important; - border-color: #dcdfe6 !important; -} - -.theme-light .el-tabs__header { - background-color: #f5f7fa !important; - border-bottom-color: #e4e7ed !important; -} - -/* Switch */ -.theme-light .el-switch__label { - color: #606266 !important; -} - -/* 对话框 */ -.theme-light .el-dialog { - --el-dialog-bg-color: #fff; -} - -.theme-light .el-dialog__title { - color: #303133 !important; -} - -.theme-light .el-breadcrumb__inner { - color: #303133 !important; -} - -/* 终端 & 日志 */ -.theme-light .terminal-container, -.theme-light .logs-container { - background-color: #fff !important; - border-color: #e4e7ed !important; -} - -.theme-light .terminal-header, -.theme-light .logs-header { - background-color: #fafbfc !important; - border-bottom-color: #e4e7ed !important; -} - -.theme-light .log-entry { - border-bottom-color: #ebeef5 !important; -} - -/* LLM 配置状态卡片 */ -.theme-light .status-chip { - background-color: #fff !important; - border-color: #e4e7ed !important; -} - -.theme-light .status-chip:hover, -.theme-light .status-chip.selected { - background-color: #ecf5ff !important; - border-color: #409eff !important; -} - -.theme-light .test-result.success { - color: #67c23a; - background-color: rgba(103, 194, 58, 0.1); -} - -.theme-light .test-result.error { - color: #f56c6c; - background-color: rgba(245, 108, 108, 0.1); -} - -/* 文字颜色确保 */ -.theme-light span, -.theme-light p, -.theme-light div, -.theme-light label { - color: inherit; -} - -.theme-light .provider-name, -.theme-light .status-name, -.theme-light .stat-card-title, -.theme-light .log-message { - color: #303133; -} - -.theme-light .stat-card-sub, -.theme-light .log-time { - color: #909399; +.glow-effect { + animation: glow 2s ease-in-out infinite; } diff --git a/src/minenasai/webtui/static/webui/index.html b/src/minenasai/webtui/static/webui/index.html index c830e53..e03a151 100644 --- a/src/minenasai/webtui/static/webui/index.html +++ b/src/minenasai/webtui/static/webui/index.html @@ -4,10 +4,18 @@ MineNASAI - 控制台 + + + + + + + + @@ -25,9 +33,6 @@ :default-active="activeMenu" :collapse="isCollapsed" :collapse-transition="false" - :background-color="theme === 'dark' ? '#1a1b26' : '#ffffff'" - :text-color="theme === 'dark' ? '#c0caf5' : '#303133'" - active-text-color="#7aa2f7" @select="handleMenuSelect" > diff --git a/src/minenasai/webtui/static/webui/js/app.js b/src/minenasai/webtui/static/webui/js/app.js index 7d93aea..2cbcaa6 100644 --- a/src/minenasai/webtui/static/webui/js/app.js +++ b/src/minenasai/webtui/static/webui/js/app.js @@ -54,8 +54,8 @@ const api = { // 创建 Vue 应用 app = createApp({ setup() { - // 主题状态 - const theme = ref(localStorage.getItem('minenasai-theme') || 'light'); + // 主题状态 - 默认暗色模式 + const theme = ref(localStorage.getItem('minenasai-theme') || 'dark'); // 组件状态 const isCollapsed = ref(false); @@ -218,14 +218,31 @@ app = createApp({ } } + // 应用主题到全局(html/body),确保 Element Plus 弹层与透明容器生效 + function applyThemeClass(value) { + const root = document.documentElement; + const body = document.body; + if (value === 'light') { + root.classList.add('theme-light'); + body.classList.add('theme-light'); + } else { + root.classList.remove('theme-light'); + body.classList.remove('theme-light'); + } + } + // 切换主题 function toggleTheme() { theme.value = theme.value === 'dark' ? 'light' : 'dark'; localStorage.setItem('minenasai-theme', theme.value); + applyThemeClass(theme.value); } // 初始化 onMounted(() => { + // 根据存储应用主题到全局 + applyThemeClass(theme.value); + loadConfig(); loadStats(); loadAgents();