样式架构
介绍
RuoYi-Plus-UniApp 移动端采用 SCSS 预处理器构建样式系统,结合 BEM 命名规范和 CSS 变量实现主题定制。样式架构分为全局样式、组件样式和工具样式三个层次,支持多端适配和暗黑模式。
核心特性:
- SCSS 预处理 - 使用 SCSS 实现变量、混合宏、函数等高级特性
- BEM 命名规范 - 采用 Block-Element-Modifier 命名约定
- CSS 变量 - 支持运行时主题切换和动态样式
- 多端适配 - 统一的 rpx 单位系统适配不同屏幕
- 暗黑模式 - 内置完整的暗黑主题变量
目录结构
src/
├── uni.scss # 全局 SCSS 变量(自动注入)
├── static/style/
│ └── index.scss # 全局样式入口
└── wd/components/common/abstracts/
├── _config.scss # BEM 配置
├── _function.scss # SCSS 函数
├── _mixin.scss # SCSS 混合宏
└── variable.scss # 组件变量定义BEM 命名规范
配置定义
scss
// _config.scss
$namespace: 'wd'; // 命名空间前缀
$elementSeparator: '__'; // 元素分隔符
$modifierSeparator: '--'; // 修饰符分隔符
$state-prefix: 'is-'; // 状态前缀命名示例
scss
// Block: wd-button
// Element: wd-button__text
// Modifier: wd-button--primary
// State: wd-button.is-disabled
.wd-button {
// Block 样式
&__text {
// Element 样式
}
&--primary {
// Modifier 样式
}
&.is-disabled {
// State 样式
}
}BEM 混合宏
scss
// 定义 Block
@mixin b($block) {
$B: $namespace + "-" + $block !global;
.#{$B} {
@content;
}
}
// 定义 Element
@mixin e($element...) {
$selector: &;
$selectors: "";
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
// 定义 Modifier
@mixin m($modifier...) {
$selectors: "";
@each $item in $modifier {
$selectors: #{$selectors + & + $modifierSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
// 定义状态
@mixin when($states...) {
@at-root {
@each $state in $states {
&.#{$state-prefix + $state} {
@content;
}
}
}
}使用示例
scss
@include b(button) {
display: inline-flex;
align-items: center;
@include e(text) {
font-size: 28rpx;
}
@include e(icon) {
margin-right: 8rpx;
}
@include m(primary) {
background: $-color-theme;
color: #fff;
}
@include m(large) {
height: 88rpx;
padding: 0 32rpx;
}
@include when(disabled) {
opacity: 0.6;
pointer-events: none;
}
@include when(loading) {
pointer-events: none;
}
}全局变量
uni.scss 变量
UniApp 内置的全局样式变量,无需导入即可使用:
scss
// 行为颜色
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
// 文字颜色
$uni-text-color: #333;
$uni-text-color-inverse: #fff;
$uni-text-color-grey: #999;
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
// 背景颜色
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1;
$uni-bg-color-mask: rgb(0 0 0 / 40%);
// 边框颜色
$uni-border-color: #c8c7cc;
// 文字尺寸
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16px;
// 圆角
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
// 间距
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
// 透明度
$uni-opacity-disabled: 0.3;主题色变量
WD UI 组件库的主题色变量:
scss
// 主题颜色
$-color-theme: var(--wot-color-theme, #4d80f0);
$-color-white: var(--wot-color-white, rgb(255, 255, 255));
$-color-black: var(--wot-color-black, rgb(0, 0, 0));
// 辅助色
$-color-success: var(--wot-color-success, #34d19d);
$-color-warning: var(--wot-color-warning, #f0883a);
$-color-danger: var(--wot-color-danger, #fa4350);
$-color-purple: var(--wot-color-purple, #8268de);
$-color-yellow: var(--wot-color-yellow, #f0cd1d);
$-color-blue: var(--wot-color-blue, #2bb3ed);
$-color-info: var(--wot-color-info, #909399);
// 灰度色阶
$-color-gray-1: var(--wot-color-gray-1, #f9f9f9);
$-color-gray-2: var(--wot-color-gray-2, #f2f3f5);
$-color-gray-3: var(--wot-color-gray-3, #ebedf0);
$-color-gray-4: var(--wot-color-gray-4, #dcdee0);
$-color-gray-5: var(--wot-color-gray-5, #c8c9cc);
$-color-gray-6: var(--wot-color-gray-6, #969799);
$-color-gray-7: var(--wot-color-gray-7, #646566);
$-color-gray-8: var(--wot-color-gray-8, #323233);文字颜色变量
scss
// 浅色背景下的文字颜色
$-color-title: var(--wot-color-title, $-color-black); // 标题
$-color-content: var(--wot-color-content, #262626); // 正文
$-color-secondary: var(--wot-color-secondary, #595959); // 次要信息
$-color-aid: var(--wot-color-aid, #8c8c8c); // 辅助文字
$-color-tip: var(--wot-color-tip, #bfbfbf); // 提示文字
$-color-border: var(--wot-color-border, #d9d9d9); // 边框
$-color-border-light: var(--wot-color-border-light, #e8e8e8); // 分割线
$-color-bg: var(--wot-color-bg, #f5f5f5); // 背景色字号变量
scss
$-fs-big: var(--wot-fs-big, 48rpx); // 大型标题
$-fs-important: var(--wot-fs-important, 38rpx); // 重要数据
$-fs-title: var(--wot-fs-title, 32rpx); // 标题字号
$-fs-content: var(--wot-fs-content, 28rpx); // 正文字号
$-fs-secondary: var(--wot-fs-secondary, 24rpx); // 次要信息
$-fs-aid: var(--wot-fs-aid, 20rpx); // 辅助文字暗黑模式
暗黑模式变量
scss
$-dark-background: var(--wot-dark-background, #131313);
$-dark-background2: var(--wot-dark-background2, #1b1b1b);
$-dark-background3: var(--wot-dark-background3, #141414);
$-dark-background4: var(--wot-dark-background4, #323233);
$-dark-background5: var(--wot-dark-background5, #646566);
$-dark-background6: var(--wot-dark-background6, #380e08);
$-dark-background7: var(--wot-dark-background7, #707070);
$-dark-color: var(--wot-dark-color, $-color-white);
$-dark-color2: var(--wot-dark-color2, #f2270c);
$-dark-color3: var(--wot-dark-color3, rgba(232, 230, 227, 0.8));
$-dark-color-gray: var(--wot-dark-color-gray, $-color-secondary);
$-dark-border-color: var(--wot-dark-border-color, #3a3a3c);暗黑模式切换
typescript
// useTheme.ts
import { ref } from 'vue'
const isDark = ref(false)
export function useTheme() {
const toggleTheme = () => {
isDark.value = !isDark.value
document.documentElement.classList.toggle('dark', isDark.value)
}
const setTheme = (dark: boolean) => {
isDark.value = dark
document.documentElement.classList.toggle('dark', dark)
}
return {
isDark,
toggleTheme,
setTheme
}
}常用混合宏
文本省略
scss
// 单行省略
@mixin lineEllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 多行省略
@mixin multiEllipsis($lineNumber: 3) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lineNumber;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
}0.5px 边框
scss
// 单方向 0.5px 边框
@mixin halfPixelBorder($direction: "bottom", $left: 0, $color: $-color-border-light) {
position: relative;
&::after {
position: absolute;
display: block;
content: "";
width: if($left == 0, 100%, calc(100% - #{$left}));
height: 1px;
left: $left;
@if ($direction == "bottom") {
bottom: 0;
} @else {
top: 0;
}
transform: scaleY(0.5);
background: $color;
}
}
// 环绕 0.5px 边框
@mixin halfPixelBorderSurround($color: $-color-border-light) {
position: relative;
&::after {
position: absolute;
display: block;
content: ' ';
pointer-events: none;
width: 200%;
height: 200%;
left: 0;
top: 0;
border: 1px solid $color;
transform: scale(0.5);
box-sizing: border-box;
transform-origin: left top;
}
}清除按钮样式
scss
@mixin buttonClear {
outline: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
background: transparent;
}清除浮动
scss
@mixin clearFloat {
&::after {
display: block;
content: "";
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
}组件样式覆盖
使用 CSS 变量
vue
<template>
<wd-button custom-class="my-button">按钮</wd-button>
</template>
<style lang="scss">
.my-button {
--wot-button-primary-bg-color: #ff6600;
--wot-button-primary-color: #fff;
}
</style>使用 custom-class
vue
<template>
<wd-cell custom-class="my-cell" title="标题" />
</template>
<style lang="scss">
.my-cell {
background: #f5f5f5;
:deep(.wd-cell__title) {
color: #333;
font-weight: bold;
}
}
</style>使用 custom-style
vue
<template>
<wd-button
custom-style="background: linear-gradient(90deg, #ff6600, #ff9900);"
>
渐变按钮
</wd-button>
</template>最佳实践
1. 变量优先
优先使用已定义的变量,保持样式一致性:
scss
// 推荐
.my-component {
color: $-color-content;
font-size: $-fs-content;
padding: $-size-side-padding;
}
// 不推荐
.my-component {
color: #262626;
font-size: 28rpx;
padding: 30rpx;
}2. 使用 BEM 混合宏
scss
// 推荐
@include b(my-component) {
@include e(header) {
// ...
}
@include m(large) {
// ...
}
}
// 不推荐
.wd-my-component {
.wd-my-component__header {
// ...
}
}3. 响应式设计
使用 rpx 单位实现自动适配:
scss
.my-component {
width: 100%;
padding: 32rpx;
font-size: 28rpx;
border-radius: 16rpx;
}4. 主题兼容
同时考虑亮色和暗黑模式:
scss
.my-component {
background: $-color-white;
color: $-color-content;
:global(.dark) & {
background: $-dark-background;
color: $-dark-color;
}
}常见问题
1. 样式不生效?
检查以下几点:
- 组件是否支持
custom-class属性 - 是否正确使用
:deep()穿透选择器 - 样式优先级是否足够
2. 暗黑模式下样式异常?
确保使用了 CSS 变量而非固定颜色值,CSS 变量会在主题切换时自动更新。
3. rpx 单位在 H5 端不生效?
UniApp 会自动将 rpx 转换为对应的 px 值,确保在 uni.scss 中正确配置了设计稿宽度。
