From 1bce12927edde73038590fb1eb90f2954d9ff3a6 Mon Sep 17 00:00:00 2001 From: nojava Date: Tue, 19 May 2026 04:32:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=20UI=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E2=80=94=E2=80=94=E6=B8=85=E6=96=B0=E8=87=AA=E7=84=B6?= =?UTF-8?q?=E9=A3=8E=E8=AE=BE=E8=AE=A1=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一全部页面视觉风格:绿色主色调(#4caf50)、暖象牙白背景(#faf8f5)、 圆角柔和阴影、Element Plus 组件主题覆盖。新增 CSS 设计令牌文件 tokens.css,重写 16 个 Vue 文件的 scoped 样式。 Co-Authored-By: Claude Sonnet 4.6 --- jog-frontend/src/App.vue | 24 ++- jog-frontend/src/assets/styles/tokens.css | 171 +++++++++++++++++++ jog-frontend/src/layouts/AdminLayout.vue | 26 ++- jog-frontend/src/layouts/AppLayout.vue | 13 +- jog-frontend/src/layouts/BlogLayout.vue | 36 ++-- jog-frontend/src/main.js | 1 + jog-frontend/src/views/ArticleDetail.vue | 125 ++++++++++++-- jog-frontend/src/views/Home.vue | 83 +++++++-- jog-frontend/src/views/Login.vue | 54 +++++- jog-frontend/src/views/Register.vue | 40 ++++- jog-frontend/src/views/admin/Articles.vue | 81 ++++++--- jog-frontend/src/views/admin/Categories.vue | 59 ++++--- jog-frontend/src/views/admin/Comments.vue | 87 ++++++---- jog-frontend/src/views/admin/Dashboard.vue | 32 +++- jog-frontend/src/views/admin/Users.vue | 73 +++++--- jog-frontend/src/views/app/ArticleEditor.vue | 47 ++++- jog-frontend/src/views/app/Dashboard.vue | 58 ++++++- jog-frontend/src/views/app/MyArticles.vue | 77 ++++++--- 18 files changed, 874 insertions(+), 213 deletions(-) create mode 100644 jog-frontend/src/assets/styles/tokens.css diff --git a/jog-frontend/src/App.vue b/jog-frontend/src/App.vue index 696f8b7..4738e9b 100644 --- a/jog-frontend/src/App.vue +++ b/jog-frontend/src/App.vue @@ -9,6 +9,28 @@ body { margin: 0; padding: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-family: var(--font-family); + font-size: var(--font-size-base); + color: var(--text-primary); + background: var(--bg-page); + line-height: var(--line-height-base); + -webkit-font-smoothing: antialiased; } + +a { + color: var(--color-primary); + text-decoration: none; + transition: color var(--transition-fast); +} +a:hover { + color: var(--color-primary-700); +} + +::-webkit-scrollbar { width: 8px; height: 8px; } +::-webkit-scrollbar-track { background: var(--bg-soft); } +::-webkit-scrollbar-thumb { + background: var(--border-color-dark); + border-radius: var(--radius-full); +} +::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); } diff --git a/jog-frontend/src/assets/styles/tokens.css b/jog-frontend/src/assets/styles/tokens.css new file mode 100644 index 0000000..83a5141 --- /dev/null +++ b/jog-frontend/src/assets/styles/tokens.css @@ -0,0 +1,171 @@ +:root { + /* ===== Color Palette ===== */ + --color-primary-50: #e8f5e9; + --color-primary-100: #c8e6c9; + --color-primary-200: #a5d6a7; + --color-primary-300: #81c784; + --color-primary-400: #66bb6a; + --color-primary: #4caf50; + --color-primary-600: #43a047; + --color-primary-700: #388e3c; + --color-primary-800: #2e7d32; + --color-primary-900: #1b5e20; + --color-primary-light: rgba(76, 175, 80, 0.1); + + --color-accent: #ff8c42; + --color-accent-light: rgba(255, 140, 66, 0.08); + --color-accent-dark: #e07030; + + --color-success: #67c23a; + --color-warning: #e6a23c; + --color-danger: #f56c6c; + --color-info: #909399; + + --bg-page: #faf8f5; + --bg-card: #ffffff; + --bg-elevated: #ffffff; + --bg-soft: #f5f0eb; + --bg-input: #ffffff; + --bg-sidebar: #2c3e3a; + --bg-sidebar-hover: #344945; + --bg-sidebar-active: #4caf50; + + --text-primary: #2c2c2c; + --text-regular: #5a5a5a; + --text-secondary: #909399; + --text-placeholder: #b0b0b0; + --text-inverse: #ffffff; + + --border-color: #e8e3de; + --border-color-light: #f0ebe6; + --border-color-dark: #d0c8c0; + + /* ===== Typography ===== */ + --font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + sans-serif; + --font-size-xs: 12px; + --font-size-sm: 13px; + --font-size-base: 15px; + --font-size-md: 16px; + --font-size-lg: 18px; + --font-size-xl: 22px; + --font-size-2xl: 28px; + --font-size-3xl: 36px; + + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + --line-height-tight: 1.3; + --line-height-base: 1.6; + --line-height-loose: 1.8; + + /* ===== Spacing ===== */ + --spacing-xs: 4px; + --spacing-sm: 8px; + --spacing-md: 12px; + --spacing-lg: 16px; + --spacing-xl: 24px; + --spacing-2xl: 32px; + --spacing-3xl: 48px; + --spacing-4xl: 64px; + + /* ===== Border Radius ===== */ + --radius-sm: 6px; + --radius-base: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --radius-xl: 20px; + --radius-full: 9999px; + + /* ===== Shadows ===== */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow-base: 0 2px 8px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.12); + --shadow-warm: 0 4px 20px rgba(76, 175, 80, 0.12); + + /* ===== Transitions ===== */ + --transition-fast: 0.15s ease; + --transition-base: 0.25s ease; + --transition-slow: 0.4s ease; +} + +/* ===== Element Plus Theme Overrides ===== */ + +.el-button--primary { + background-color: var(--color-primary) !important; + border-color: var(--color-primary) !important; +} + +.el-button--primary:hover, +.el-button--primary.is-hover { + background-color: var(--color-primary-600) !important; + border-color: var(--color-primary-600) !important; +} + +.el-button--primary:active { + background-color: var(--color-primary-700) !important; + border-color: var(--color-primary-700) !important; +} + +.el-button--primary.is-disabled, +.el-button--primary.is-disabled:hover, +.el-button--primary.is-disabled:focus { + background-color: var(--color-primary-light) !important; + border-color: var(--color-primary-200) !important; + color: var(--color-primary-300) !important; +} + +.el-button--primary.is-text, +.el-button--primary.is-link { + background-color: transparent !important; + border-color: transparent !important; + color: var(--color-primary) !important; +} + +.el-button--primary.is-text:hover, +.el-button--primary.is-link:hover { + color: var(--color-primary-600) !important; + background-color: var(--color-primary-light) !important; +} + +.el-input__wrapper.is-focus { + box-shadow: 0 0 0 1px var(--color-primary) inset !important; +} + +.el-tag--primary { + --el-tag-bg-color: var(--color-primary-light); + --el-tag-border-color: var(--color-primary-200); + --el-tag-text-color: var(--color-primary-700); +} + +.el-card { + border-radius: var(--radius-md); + border-color: var(--border-color-light); + transition: box-shadow var(--transition-base); +} + +.el-dialog { + border-radius: var(--radius-lg); +} + +.el-pagination.is-background .el-pager li:not(.is-disabled).is-active { + background-color: var(--color-primary) !important; +} + +.el-menu--horizontal > .el-menu-item.is-active { + border-bottom-color: var(--color-primary) !important; + color: var(--color-primary) !important; +} + +.el-menu .el-menu-item.is-active { + color: var(--color-primary) !important; + background-color: var(--color-primary-light) !important; +} + +.el-select-dropdown__item.selected { + color: var(--color-primary) !important; +} diff --git a/jog-frontend/src/layouts/AdminLayout.vue b/jog-frontend/src/layouts/AdminLayout.vue index 77b01dd..b438e96 100644 --- a/jog-frontend/src/layouts/AdminLayout.vue +++ b/jog-frontend/src/layouts/AdminLayout.vue @@ -56,27 +56,35 @@ async function handleLogout() { .admin-layout { min-height: 100vh; } -.el-aside { - background: #304156; -} +.el-aside { background: var(--bg-sidebar); } .admin-logo { height: 60px; display: flex; align-items: center; justify-content: center; color: #fff; - font-size: 18px; - font-weight: bold; + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} +.el-aside :deep(.el-menu) { + --el-menu-bg-color: transparent; + --el-menu-hover-bg-color: var(--bg-sidebar-hover); + --el-menu-active-color: var(--color-primary-300); + --el-menu-text-color: #bfcbd9; + border-right: none; } .admin-header { display: flex; align-items: center; justify-content: flex-end; - gap: 10px; - background: #fff; - border-bottom: 1px solid #e4e7ed; + gap: var(--spacing-sm); + background: var(--bg-card); + border-bottom: 1px solid var(--border-color-light); + box-shadow: var(--shadow-sm); } .el-main { - background: #f5f7fa; + background: var(--bg-page); + padding: var(--spacing-xl); } diff --git a/jog-frontend/src/layouts/AppLayout.vue b/jog-frontend/src/layouts/AppLayout.vue index 0cc7d7e..5d184e8 100644 --- a/jog-frontend/src/layouts/AppLayout.vue +++ b/jog-frontend/src/layouts/AppLayout.vue @@ -30,12 +30,19 @@ diff --git a/jog-frontend/src/layouts/BlogLayout.vue b/jog-frontend/src/layouts/BlogLayout.vue index 802cbaf..75211e1 100644 --- a/jog-frontend/src/layouts/BlogLayout.vue +++ b/jog-frontend/src/layouts/BlogLayout.vue @@ -58,8 +58,10 @@ async function handleLogout() { flex-direction: column; } .header { - border-bottom: 1px solid #e4e7ed; - background: #fff; + border-bottom: 1px solid var(--border-color-light); + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(12px); + box-shadow: var(--shadow-sm); } .header-content { max-width: 1200px; @@ -70,37 +72,49 @@ async function handleLogout() { padding: 0 20px; } .logo { - font-size: 20px; - font-weight: bold; - color: #409eff; + font-size: var(--font-size-xl); + font-weight: var(--font-weight-bold); + color: var(--color-primary); text-decoration: none; - margin-right: 40px; + margin-right: var(--spacing-2xl); + transition: color var(--transition-fast); } +.logo:hover { color: var(--color-primary-700); } .nav-menu { flex: 1; border-bottom: none; + --el-menu-bg-color: transparent; + --el-menu-hover-bg-color: var(--color-primary-light); + --el-menu-active-color: var(--color-primary); + --el-menu-text-color: var(--text-regular); } .header-right { display: flex; align-items: center; - gap: 10px; + gap: var(--spacing-sm); } .user-info { display: flex; align-items: center; - gap: 8px; + gap: var(--spacing-sm); cursor: pointer; + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-full); + transition: background var(--transition-fast); } +.user-info:hover { background: var(--color-primary-light); } .main { flex: 1; max-width: 1200px; width: 100%; margin: 0 auto; - padding: 20px; + padding: var(--spacing-xl); } .footer { text-align: center; - color: #909399; - border-top: 1px solid #e4e7ed; + color: var(--text-secondary); + border-top: 1px solid var(--border-color-light); + padding: var(--spacing-lg) 0; + font-size: var(--font-size-sm); } diff --git a/jog-frontend/src/main.js b/jog-frontend/src/main.js index 84b89e4..81f5d97 100644 --- a/jog-frontend/src/main.js +++ b/jog-frontend/src/main.js @@ -2,6 +2,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' +import './assets/styles/tokens.css' import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import App from './App.vue' diff --git a/jog-frontend/src/views/ArticleDetail.vue b/jog-frontend/src/views/ArticleDetail.vue index 44aa978..3917bce 100644 --- a/jog-frontend/src/views/ArticleDetail.vue +++ b/jog-frontend/src/views/ArticleDetail.vue @@ -148,23 +148,112 @@ onMounted(() => { diff --git a/jog-frontend/src/views/Home.vue b/jog-frontend/src/views/Home.vue index da5ab50..4bb0d05 100644 --- a/jog-frontend/src/views/Home.vue +++ b/jog-frontend/src/views/Home.vue @@ -79,36 +79,87 @@ onMounted(() => { diff --git a/jog-frontend/src/views/Login.vue b/jog-frontend/src/views/Login.vue index 6d38ce7..01a59dd 100644 --- a/jog-frontend/src/views/Login.vue +++ b/jog-frontend/src/views/Login.vue @@ -116,19 +116,61 @@ async function handleUserLogin() { justify-content: center; align-items: center; min-height: 100vh; - background: #f5f7fa; + background: linear-gradient(135deg, var(--color-primary-50) 0%, var(--bg-page) 50%, var(--color-accent-light) 100%); + position: relative; + overflow: hidden; +} +.login-page::before { + content: ''; + position: absolute; + width: 400px; + height: 400px; + border-radius: 50%; + background: var(--color-primary-100); + opacity: 0.3; + top: -100px; + right: -100px; +} +.login-page::after { + content: ''; + position: absolute; + width: 300px; + height: 300px; + border-radius: 50%; + background: var(--color-accent-light); + opacity: 0.4; + bottom: -80px; + left: -80px; } .login-card { - width: 420px; - padding: 20px; + width: 440px; + padding: var(--spacing-2xl); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + position: relative; + z-index: 1; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(8px); } .login-card h2 { text-align: center; - margin-bottom: 20px; + margin-bottom: var(--spacing-xl); + color: var(--color-primary-800); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); +} +.login-card :deep(.el-tabs__header) { margin-bottom: var(--spacing-xl); } +.login-card :deep(.el-tabs__active-bar) { background-color: var(--color-primary); } +.login-card :deep(.el-tabs__item.is-active) { color: var(--color-primary); } +.login-card :deep(.el-button--primary) { + height: 44px; + font-size: var(--font-size-md); + border-radius: var(--radius-base); } .footer-link { text-align: center; - margin-top: 16px; - color: #909399; + margin-top: var(--spacing-lg); + color: var(--text-secondary); + font-size: var(--font-size-sm); } diff --git a/jog-frontend/src/views/Register.vue b/jog-frontend/src/views/Register.vue index 6660a1c..a621771 100644 --- a/jog-frontend/src/views/Register.vue +++ b/jog-frontend/src/views/Register.vue @@ -84,19 +84,47 @@ async function handleRegister() { justify-content: center; align-items: center; min-height: 100vh; - background: #f5f7fa; + background: linear-gradient(135deg, var(--bg-page) 0%, var(--color-primary-50) 100%); + position: relative; + overflow: hidden; +} +.register-page::before { + content: ''; + position: absolute; + width: 350px; + height: 350px; + border-radius: 50%; + background: var(--color-primary-100); + opacity: 0.25; + top: -60px; + left: -60px; } .register-card { - width: 420px; - padding: 20px; + width: 440px; + padding: var(--spacing-2xl); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + position: relative; + z-index: 1; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(8px); } .register-card h2 { text-align: center; - margin-bottom: 20px; + margin-bottom: var(--spacing-xl); + color: var(--color-primary-800); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); +} +.register-card :deep(.el-button--primary) { + height: 44px; + font-size: var(--font-size-md); + border-radius: var(--radius-base); } .footer-link { text-align: center; - margin-top: 16px; - color: #909399; + margin-top: var(--spacing-lg); + color: var(--text-secondary); + font-size: var(--font-size-sm); } diff --git a/jog-frontend/src/views/admin/Articles.vue b/jog-frontend/src/views/admin/Articles.vue index f65c7bc..15744a5 100644 --- a/jog-frontend/src/views/admin/Articles.vue +++ b/jog-frontend/src/views/admin/Articles.vue @@ -1,7 +1,7 @@