本项目采用了全新的设计令牌系统和主题覆盖机制,在保持 Vue 2.6 + Element UI 2.x + 若依框架技术栈不变的前提下,实现了现代化的 UI/UX 升级。
src/styles/
├── tokens.scss # 设计令牌定义(颜色、间距、字体等)
├── overrides-element.scss # Element UI 主题覆盖
├── utilities.scss # 基础工具类
└── index.scss # 主样式入口
src/utils/
└── theme.js # 主题管理工具
src/components/
└── XtThemeToggle.vue # 主题切换组件
基于 CSS 变量实现的设计令牌系统,支持明暗主题切换:
:root {
/* 主色系 */
--color-primary: #0EA5E9;
--color-primary-light: #38BDF8;
--color-primary-dark: #0284C7;
/* 语义化颜色 */
--color-success: #22C55E;
--color-warning: #F59E0B;
--color-danger: #EF4444;
/* 圆角系统 */
--radius-lg: 12px;
--radius-xl: 16px;
/* 阴影系统 */
--shadow-card: 0 8px 24px rgba(2, 6, 23, 0.06);
--shadow-card-hover: 0 12px 32px rgba(2, 6, 23, 0.12);
}
/* 暗色主题覆盖 */
html.dark {
--color-bg-primary: #111827;
--color-bg-card: #1F2937;
--color-text-primary: #F9FAFB;
/* ... */
}
完全覆盖 Element UI 的默认样式,使其符合新的设计系统:
提供丰富的 CSS 工具类,支持快速开发:
<!-- 卡片样式 -->
<div class="card">
<div class="card__header">
<h3 class="card__header-title">标题</h3>
<p class="card__header-subtitle">副标题</p>
</div>
<div class="card__content">
内容区域
</div>
</div>
<!-- 间距工具类 -->
<div class="p-4 m-2 rounded-lg shadow-card">
<!-- padding: 16px, margin: 8px -->
</div>
<!-- 文本工具类 -->
<p class="text-primary font-semibold text-lg">主要文本</p>
<p class="text-secondary text-sm">次要文本</p>
<p class="muted">弱化文本</p>
<!-- 布局工具类 -->
<div class="flex items-center justify-between">
<span>左侧内容</span>
<span>右侧内容</span>
</div>
项目已经自动集成了新的设计系统,无需额外配置。所有 Element UI 组件都会自动应用新的主题样式。
使用内置的主题切换组件:
<template>
<div>
<!-- 按钮模式 -->
<XtThemeToggle mode="button" :show-text="true" />
<!-- 选择器模式 -->
<XtThemeToggle mode="select" />
<!-- 分段控制器模式 -->
<XtThemeToggle mode="segmented" :show-text="true" />
<!-- 开关模式 -->
<XtThemeToggle mode="switch" :show-label="true" />
</div>
</template>
<script>
import XtThemeToggle from '@/components/XtThemeToggle'
export default {
components: {
XtThemeToggle
}
}
</script>
// 在组件中使用
export default {
methods: {
toggleTheme() {
this.$toggleTheme() // 切换明暗主题
},
setLightTheme() {
this.$setTheme('light') // 设置亮色主题
},
setDarkTheme() {
this.$setTheme('dark') // 设置暗色主题
},
setAutoTheme() {
this.$setTheme('auto') // 跟随系统主题
},
checkCurrentTheme() {
console.log('当前主题:', this.$getTheme())
console.log('是否暗色主题:', this.$isDark())
console.log('是否亮色主题:', this.$isLight())
}
}
}
import { ThemeMixin } from '@/utils/theme'
export default {
mixins: [ThemeMixin],
methods: {
// 主题变化时的自定义处理
onThemeChange(event) {
console.log('主题已切换:', event)
// event.theme - 设置的主题 ('light', 'dark', 'auto')
// event.effectiveTheme - 实际生效的主题 ('light', 'dark')
// event.isDark - 是否为暗色主题
// event.isLight - 是否为亮色主题
// event.isAuto - 是否为自动主题
// 在这里处理主题变化逻辑
if (event.isDark) {
// 暗色主题特殊处理
} else {
// 亮色主题特殊处理
}
}
}
}
| 令牌名称 | 用途 | 示例值 |
|---|---|---|
--color-primary |
主色调 | #0EA5E9 |
--color-success |
成功状态 | #22C55E |
--color-warning |
警告状态 | #F59E0B |
--color-danger |
危险状态 | #EF4444 |
--color-text-primary |
主要文本 | #0F172A / #F9FAFB |
--color-text-secondary |
次要文本 | #475569 / #E5E7EB |
--color-bg-card |
卡片背景 | #FFFFFF / #1F2937 |
--color-border-primary |
主要边框 | rgba(2,6,23,0.08) / #374151 |
| 令牌名称 | 值 | 用途 |
|---|---|---|
--spacing-1 |
4px |
最小间距 |
--spacing-2 |
8px |
小间距 |
--spacing-3 |
12px |
中小间距 |
--spacing-4 |
16px |
中等间距 |
--spacing-6 |
24px |
大间距 |
--spacing-8 |
32px |
超大间距 |
| 令牌名称 | 值 | 用途 |
|---|---|---|
--radius-sm |
4px |
小圆角 |
--radius-base |
6px |
基础圆角 |
--radius-md |
8px |
中等圆角 |
--radius-lg |
12px |
大圆角(默认) |
--radius-xl |
16px |
超大圆角 |
--radius-full |
9999px |
完全圆角 |
| 令牌名称 | 用途 |
|---|---|
--shadow-sm |
小阴影 |
--shadow-base |
基础阴影 |
--shadow-md |
中等阴影 |
--shadow-lg |
大阴影 |
--shadow-xl |
超大阴影 |
--shadow-card |
卡片阴影 |
--shadow-card-hover |
卡片悬停阴影 |
❌ 不推荐 - 硬编码颜色值:
.my-component {
background: #0EA5E9;
color: #ffffff;
border-radius: 12px;
}
✅ 推荐 - 使用设计令牌:
.my-component {
background: var(--color-primary);
color: var(--color-text-inverse);
border-radius: var(--radius-lg);
}
所有自定义组件使用 Xt 前缀:
<!-- 正确的组件命名 -->
<XtThemeToggle />
<XtGroupedFormCard />
<XtStickyActionBar />
<XtTestConnectionDrawer />
<XtDiffPublishDialog />
<XtRightSummaryPanel />
<template>
<div class="xt-my-component">
<div class="component-header">
<h3 class="header-title">标题</h3>
</div>
<div class="component-content">
内容
</div>
</div>
</template>
<style lang="scss" scoped>
.xt-my-component {
background: var(--color-bg-card);
border-radius: var(--radius-lg);
padding: var(--spacing-6);
box-shadow: var(--shadow-card);
.component-header {
border-bottom: 1px solid var(--color-border-secondary);
padding-bottom: var(--spacing-4);
margin-bottom: var(--spacing-4);
.header-title {
color: var(--color-text-primary);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
margin: 0;
}
}
.component-content {
color: var(--color-text-secondary);
line-height: var(--line-height-relaxed);
}
}
</style>
利用内置的响应式工具类:
<div class="card p-4 lg:p-6">
<h3 class="text-lg lg:text-xl font-semibold">
响应式标题
</h3>
<div class="flex flex-col lg:flex-row gap-4">
<div class="flex-1">左侧内容</div>
<div class="flex-1 lg:hidden">移动端显示</div>
<div class="flex-1 hidden lg:block">桌面端显示</div>
</div>
</div>
在浏览器开发者工具中查看当前生效的设计令牌:
// 控制台中执行
const style = getComputedStyle(document.documentElement)
console.log('主色调:', style.getPropertyValue('--color-primary'))
console.log('卡片背景:', style.getPropertyValue('--color-bg-card'))
console.log('大圆角:', style.getPropertyValue('--radius-lg'))
// 临时修改主色调(用于调试)
document.documentElement.style.setProperty('--color-primary', '#ff6b6b')
// 恢复默认值
document.documentElement.style.removeProperty('--color-primary')
// 测试所有主题
this.$setTheme('light')
setTimeout(() => this.$setTheme('dark'), 1000)
setTimeout(() => this.$setTheme('auto'), 2000)
设计系统内置了完善的可访问性支持:
prefers-reduced-motionprefers-contrast: high访问 /design-system-demo 路由查看完整的设计系统演示,包括:
样式不生效
@/styles/index.scssscoped 样式主题切换不工作
ThemePluginhtml 元素是否有 dark 类Element UI 样式覆盖失效
!important 强制覆盖(不推荐)// 主题状态检查
console.log('当前主题管理器状态:', {
theme: this.$theme.getTheme(),
effectiveTheme: this.$theme.getEffectiveCurrentTheme(),
isDark: this.$theme.isDark(),
isLight: this.$theme.isLight(),
isAuto: this.$theme.isAuto()
})
// CSS 变量检查
const checkTokens = () => {
const tokens = [
'color-primary',
'color-bg-card',
'radius-lg',
'shadow-card'
]
tokens.forEach(token => {
const value = getComputedStyle(document.documentElement)
.getPropertyValue(`--${token}`)
console.log(`--${token}:`, value)
})
}
checkTokens()
在 src/styles/tokens.scss 中添加:
:root {
/* 新的颜色令牌 */
--color-accent: #8B5CF6;
--color-accent-light: #A78BFA;
--color-accent-dark: #7C3AED;
/* 新的间距令牌 */
--spacing-18: 72px;
--spacing-20: 80px;
}
html.dark {
/* 暗色主题下的覆盖 */
--color-accent: #A78BFA;
}
在 src/styles/utilities.scss 中添加:
/* 新的工具类 */
.accent {
color: var(--color-accent) !important;
}
.bg-accent {
background-color: var(--color-accent) !important;
color: var(--color-text-inverse) !important;
}
.border-accent {
border-color: var(--color-accent) !important;
}
// 动态设置品牌色
this.$theme.setThemeColor('primary', '#your-brand-color')
this.$theme.setThemeColor('primary-light', '#your-light-color')
this.$theme.setThemeColor('primary-dark', '#your-dark-color')
如有任何问题或建议,请联系前端架构团队或在项目中创建 Issue。
Happy Coding! 🚀