chore: 完成多批次代码优化与重构

- 重构工作流模块目录结构,迁移代码文件
- 修复类型断言空值安全问题,添加 ! 操作符
- 优化样式类名,替换 flex-cc 为标准 flex 工具类
- 更新路由标签简化文案,移除冗余注释
- 调整 ruff 配置,放宽行长度限制
- 更新 README 与多语言文案,优化项目描述
- 修复表单、图表组件的类型与样式问题
- 简化搜索表单、数据卡片的布局代码
This commit is contained in:
zhangtao
2026-06-20 05:31:46 +08:00
parent 82b742620b
commit d34d4a4c50
378 changed files with 4149 additions and 6375 deletions
+60
View File
@@ -0,0 +1,60 @@
# src/styles/ 目录结构
全局样式按**职责**分目录组织:
```
styles/
├── index.scss 入口(薄)
│
├── core/ 全局基础
│ ├── _fa-tokens.scss 业务色 hex(partial,不单独编译)
│ ├── reset.scss 浏览器重置
│ ├── app.scss 项目级全局 app 样式
│ └── mixin.scss SCSS mixin(vite additionalData 注入)
│
├── element-plus/ Element Plus 相关
│ ├── _theme.scss 主题色(@use with 注入 EP common/var)
│ ├── _overrides.scss 组件样式覆写(按钮 / 弹窗 / 表格 / 表单 ...)
│ └── _dark.scss 暗黑主题
│
└── animations/ 全局动画
├── router-transition.scss
├── theme-change.scss
└── theme-animation.scss
```
## 命名约定
- `_xxx.scss` Sass partial(下划线开头,不单独编译)
- `xxx.scss` 入口文件(被 `index.scss` `@use` 引入)
## 添加新样式的流程
1. 找准归属目录
- 修改 EP 样式 → `element-plus/_overrides.scss`
- 新增动画 → `animations/`
- 业务页专属 → **就近放在该组件目录下**(不要放到 styles/)
2. 跑构建(`pnpm build`)确保 SCSS 编译通过
3. 全局变量(颜色、圆角、高度)放到 `core/_fa-tokens.scss` 或 `:root` 块
## 业务页样式就近原则(重要)
业务页 / 业务组件的样式 **不放到 styles/**,而是放到该组件同目录下:
- `src/components/layouts/_fa-layouts.scss` ← layouts/index.vue 用
- `src/components/views/fa-login/_fa-login.scss` ← fa-login 组件用
引用方式用相对路径(`@use "./fa-layouts"` 或 `@use "../fa-login"`),
避免 `@styles/...` 这种"全局别名"误导读代码的人以为它是全局样式。
## 历史
- 原 `element-plus-theme.scss`(位于 `styles/` 根)已迁移到 `element-plus/_theme.scss`
- 原 `core/el-ui.scss` → `element-plus/_overrides.scss`
- 原 `core/dark.scss` → `element-plus/_dark.scss`
- 原 `core/router-transition.scss` / `theme-change.scss` / `theme-animation.scss` → `animations/`
- 原 `core/highlight.scss` / `md.scss` → `vendors/`(已按需化到具体组件)
- 原 `core/variables.scss` 已废弃
- 原 `custom/fa-layouts.scss` → `components/layouts/_fa-layouts.scss`
- 原 `custom/fa-login.scss` → `components/views/fa-login/_fa-login.scss`
- 原 `core/tailwind.css` → 移到 `src/styles/tailwind.css`
@@ -0,0 +1,11 @@
// 主题色 token 单一来源(partial,不单独编译)
// 任何 SCSS 模块需要使用功能性色 hex 值,都从这里 @use 引入:
// @use "@styles/core/fa-tokens" as *;
// 保证整个项目只有这一处定义 success/warning/danger/error/info 的 hex 值。
$fa-primary: #4080ff;
$fa-success: #13deb9;
$fa-warning: #ffae1f;
$fa-danger: #ff4d4f;
$fa-error: #fa896b;
$fa-info: #909399;
+1 -5
View File
@@ -89,16 +89,12 @@
--tw-backdrop-sepia: ;
}
// 色弱模式
// 色弱模式(用户偏好开关,通过 useSettingsHandlers.ts 动态加到 <html> 上)
.color-weak {
-webkit-filter: invert(80%);
filter: invert(80%);
}
#noop {
display: none;
}
// 语言切换选中样式
.langDropDownStyle {
// 选中项背景颜色
@@ -1,2 +0,0 @@
// 导入暗黑主题
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
File diff suppressed because it is too large Load Diff
+14
View File
@@ -31,6 +31,15 @@
height: 0;
}
/* Element Plus 自定义滚动条(ElScrollbar 组件)主题色
* 使用 !important 是因为 Element Plus 的 CSS 在 main.ts 中晚于本文件加载,
* 同特异性下后加载胜出,会把无 !important 的变量覆盖回默认值。
* 亮色/暗色两套都用 !important 保持一致。 */
.el-scrollbar {
--el-scrollbar-bg-color: var(--theme-color) !important;
--el-scrollbar-hover-bg-color: color-mix(in srgb, var(--theme-color) 80%, black) !important;
}
.dark {
::-webkit-scrollbar-track {
background-color: var(--default-bg-color);
@@ -39,4 +48,9 @@
::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--theme-color) 60%, transparent) !important;
}
.el-scrollbar {
--el-scrollbar-bg-color: color-mix(in srgb, var(--theme-color) 60%, transparent) !important;
--el-scrollbar-hover-bg-color: color-mix(in srgb, var(--theme-color) 80%, black) !important;
}
}
@@ -1,49 +0,0 @@
/**
* Element Plus 功能性颜色覆写 CSS 变量。
*
* 默认主色请与 src/config/setting.ts 中 themeColorPresets 首项保持一致。
*
* 注意:primary 颜色由运行时 setElementThemeColor() 动态写入,此处只覆写
* success / warning / danger / error / info 等编译期确定的功能性颜色。
* 这些变量无法通过 SCSS `@forward ... with()` 传递到 Element Plus 组件编译链,
* 因为 Dart Sass 模块隔离机制下 Element Plus 组件内部 `@use 'common/var'` 获得的是独立模块实例。
*/
@use "sass:map";
@use "element-plus/theme-chalk/src/common/var.scss" as ep with (
$colors: (
"white": #ffffff,
"black": #000000,
"primary": (
"base": #4080ff,
),
"success": (
"base": #13deb9,
),
"warning": (
"base": #ffae1f,
),
"danger": (
"base": #ff4d4f,
),
"error": (
"base": #fa896b,
),
"info": (
"base": #909399,
),
),
$button: (
"hover-bg-color": var(--el-color-primary-light-9),
"hover-border-color": var(--el-color-primary),
"border-color": var(--el-color-primary),
"text-color": var(--el-color-primary),
),
$messagebox: (
"border-radius": "12px",
),
$popover: (
"padding": "14px",
"border-radius": "10px",
)
);
@@ -1,101 +0,0 @@
.app-layout {
display: flex;
width: 100%;
min-height: 100vh;
background: var(--default-bg-color);
#app-sidebar {
flex-shrink: 0;
}
#app-main {
display: flex;
flex: 1;
flex-direction: column;
min-width: 0;
height: 100vh;
overflow: auto;
#app-header {
position: sticky;
top: 0;
z-index: 50;
flex-shrink: 0;
width: 100%;
}
#app-content {
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
padding: 20px;
overflow: auto;
:deep(.layout-content) {
box-sizing: border-box;
width: 100%;
min-width: 0;
margin: auto;
// 子页面默认 style(勿用 overflow:hidden:在 flex 主区内会裁切长页导致无法滚动)
.page-content {
position: relative;
box-sizing: border-box;
min-height: 0;
padding: 20px;
overflow: hidden auto;
background: var(--default-box-color);
border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
}
}
}
}
}
@media only screen and (width <= 1180px) {
.app-layout {
#app-main {
height: 100dvh;
}
}
}
@media only screen and (width <= 800px) {
.app-layout {
position: relative;
#app-sidebar {
position: fixed;
top: 0;
left: 0;
z-index: 300;
height: 100vh;
}
#app-main {
width: 100%;
height: auto;
overflow: visible;
#app-content {
:deep(.layout-content) {
width: calc(100% - 40px);
}
}
}
}
}
@media only screen and (width <= 640px) {
.app-layout {
#app-main {
#app-content {
:deep(.layout-content) {
width: calc(100% - 30px);
}
}
}
}
}
@@ -1,442 +0,0 @@
/**
* 登录根布局
* - 顶栏 fixed 透明叠在页面上(不占一条实体顶区);仅插画列 / 表单区随布局切换
* - left / right:插画列与表单列横向排列(约 65% / 35%)
* - center:全宽 + 视口级背景;登录卡居中、阴影高亮
*/
.login-auth-split {
display: flex;
min-width: 0;
min-height: 0;
}
.login-auth-split__col--illustration {
box-sizing: border-box;
min-width: 0;
min-height: 0;
}
.login-auth-split__col--form {
box-sizing: border-box;
min-width: 0;
min-height: 0;
}
/* 横向:插画 ~65%,表单列占剩余 */
.login-auth-split--left,
.login-auth-split--right {
flex-direction: row;
}
.login-auth-split--left .login-auth-split__col--illustration,
.login-auth-split--right .login-auth-split__col--illustration {
flex: 0 0 65%;
}
.login-auth-split--left .login-auth-split__col--form,
.login-auth-split--right .login-auth-split__col--form {
flex: 1 1 0;
min-width: 0;
}
.login-auth-split--left {
flex-direction: row-reverse;
}
@media only screen and (width <= 1600px) {
.login-auth-split--left .login-auth-split__col--illustration,
.login-auth-split--right .login-auth-split__col--illustration {
flex: 0 0 60%;
}
}
/* 居中模式:仅一列表单壳占满屏宽 */
.login-auth-split--center {
flex-direction: column;
}
.login-auth-split--center .login-auth-split__col--form {
flex: 1 1 auto;
width: 100%;
min-height: 0;
background-color: transparent;
}
/* 顶栏与页脚之间的区域:纵向居中承载登录卡 */
.login-auth-split--center .login-page-panel__scroll.login-page-panel__scroll--centered {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 0;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
/* 盖过 Tailwind overflow-y-auto:仅卡片内滚动,外层不出滚动条 */
overflow: hidden;
}
.login-page-panel__scroll--centered .login-panel-align-row {
flex: 0 1 auto;
justify-content: center;
width: 100%;
min-height: 0;
max-height: 100%;
}
/* 悬浮卡片:玻璃拟态,圆角透明 */
.login-auth-split--center .auth-right-wrap {
width: min(480px, calc(100vw - 2rem)) !important;
max-width: min(480px, calc(100vw - 2rem));
height: auto;
max-height: calc(100dvh - 8rem);
padding: 1.25rem 1.75rem;
overflow: hidden auto;
background: color-mix(in srgb, var(--el-bg-color) 72%, transparent);
border: 1px solid color-mix(in srgb, var(--el-border-color-lighter) 60%, transparent);
border-radius: 20px;
box-shadow:
0 1px 8px rgb(15 23 42 / 5%),
0 0 16px color-mix(in srgb, var(--el-color-primary) 6%, transparent);
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
}
.login-auth-split--center .auth-right-wrap .form {
height: auto;
min-height: 0;
padding-top: 0;
padding-bottom: 0;
}
html.dark .login-auth-split--center .auth-right-wrap {
background: color-mix(in srgb, var(--el-bg-color) 55%, transparent);
border-color: rgb(255 255 255 / 8%);
box-shadow:
0 1px 8px rgb(0 0 0 / 18%),
0 0 16px color-mix(in srgb, var(--el-color-primary) 10%, transparent);
}
/* 居中模式下页脚弱化,减少「分区后台」感 */
.login-page-footer--floating-layout {
border-top-color: color-mix(in srgb, var(--el-border-color-lighter) 55%, transparent);
}
html.dark .login-page-footer--floating-layout {
border-top-color: rgb(255 255 255 / 8%);
}
/* 居中模式 footer 单行展示 */
.login-auth-split--center .login-page-footer--floating-layout {
.login-footer-text {
white-space: nowrap;
}
.login-footer-row {
display: inline;
}
.login-footer-row + .login-footer-row {
margin-top: 0;
}
.login-footer-sep-center {
display: inline;
}
}
@media only screen and (width <= 1180px) {
.login-auth-split {
flex-direction: column !important;
}
.login-auth-split__col--illustration {
flex: 0 0 auto !important;
width: 100%;
}
.login-auth-split__col--form {
flex: 1 1 auto !important;
width: 100%;
min-height: 0;
}
/* 窄屏横排切竖排:恢复插画;居中模式本身无插画,不受影响 */
.login-auth-split--center .auth-right-wrap {
width: min(420px, calc(100vw - 1.5rem)) !important;
max-width: min(420px, calc(100vw - 1.5rem));
max-height: calc(100dvh - 6rem);
}
}
/* 授权页表单区域(左右分栏时) */
.auth-right-wrap {
position: relative;
flex-shrink: 0;
width: 440px;
height: 670px;
padding-top: 5px;
padding-bottom: 5px;
overflow: hidden;
animation: slideInRight 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
@media (width <= 640px) {
width: 100%;
height: auto;
max-height: calc(100vh - 10rem);
padding-right: 1.75rem;
padding-left: 1.75rem;
}
@media (width <= 768px) {
animation: none;
}
.form {
height: 100%;
padding-top: 10px;
padding-bottom: 40px;
}
.title {
font-size: 2.25rem;
font-weight: 600;
line-height: 2.5rem;
color: var(--fa-gray-900);
@media (width <= 768px) {
font-size: 1.875rem;
line-height: 2.25rem;
}
@media (width <= 640px) {
padding-top: 2.5rem;
}
}
.sub-title {
margin-top: 10px;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--fa-gray-600);
}
.custom-height {
height: 40px !important;
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.login-page-panel {
.btn {
border-radius: 8px;
}
}
html.dark .login-page-panel {
.text-g-800 {
color: rgb(255 255 255 / 78%) !important;
}
}
.login-page-form {
margin-top: 1rem;
}
.login-page-form .el-form-item {
margin-bottom: 1.1rem;
}
.el-select__wrapper {
min-height: 42px !important;
}
.login-secondary-actions .login-secondary-btn {
height: 42px;
border-radius: 8px;
}
html.dark {
.login-page-form .el-input__wrapper {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
.login-page-form .el-input__inner {
color: rgb(255 255 255 / 92%);
}
.login-page-form .el-input__inner::placeholder {
color: rgb(255 255 255 / 35%);
}
.login-page-form .el-select .el-select__wrapper {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
.login-page-form .el-select__placeholder {
color: rgb(255 255 255 / 40%);
}
.login-page-form .el-select__selected-item {
color: rgb(255 255 255 / 92%);
}
.login-remember .el-checkbox__label {
color: rgb(255 255 255 / 65%);
}
.login-secondary-btn {
color: rgb(255 255 255 / 85%) !important;
background: transparent !important;
border-color: rgb(255 255 255 / 22%) !important;
border-radius: 8px;
&:hover {
color: var(--el-color-primary) !important;
background: var(--el-color-primary-light-9) !important;
border-color: var(--el-color-primary) !important;
}
}
.login-secondary-actions .login-secondary-btn {
height: 42px;
}
}
.login-page-footer {
margin-top: 0;
text-align: center;
border-top: 1px solid var(--el-border-color-lighter);
.login-footer-text {
line-height: 1.65;
color: var(--el-text-color-secondary) !important;
}
.login-footer-row {
white-space: nowrap;
}
.login-footer-row + .login-footer-row {
margin-top: 0.25rem;
}
.login-footer-sep-center {
display: none;
}
.login-page-footer__link {
color: var(--el-text-color-regular);
text-decoration: none;
transition: color 0.2s ease;
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
margin: 0 0.35rem;
color: var(--el-border-color);
}
.login-page-footer__record {
display: inline-block;
color: var(--el-text-color-placeholder);
}
}
html.dark .login-page-footer {
border-top-color: rgb(255 255 255 / 8%);
.login-footer-text {
color: rgb(255 255 255 / 42%) !important;
}
.login-page-footer__link {
color: rgb(255 255 255 / 48%);
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
color: rgb(255 255 255 / 25%);
}
.login-page-footer__record {
color: rgb(255 255 255 / 35%);
}
}
.login-mobile-otp-cell {
box-sizing: border-box;
flex: 1;
min-width: 0;
max-width: 48px;
height: 40px;
padding: 0;
font-size: 1rem;
font-weight: 500;
color: var(--el-text-color-primary);
text-align: center;
appearance: none;
outline: none;
background: var(--el-fill-color-blank);
border: 1px solid var(--el-border-color);
border-radius: 8px;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease;
&:focus {
border-color: var(--el-color-primary);
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
}
}
.login-mobile-sms-btn {
border-radius: 8px;
}
.login-mobile-actions {
.el-button + .el-button {
margin-left: 0 !important;
}
.el-button {
box-sizing: border-box;
width: 100%;
min-width: 0;
max-width: 100%;
}
}
.login-qr-card {
box-sizing: border-box;
}
html.dark {
.login-mobile-otp-cell {
background: rgb(255 255 255 / 6%);
border-color: rgb(255 255 255 / 14%);
&:focus {
border-color: var(--el-color-primary);
}
}
}
@@ -1,97 +0,0 @@
.hljs {
display: block;
padding: 0.5em;
overflow-x: auto;
color: #a6accd;
}
.hljs-string,
.hljs-section,
.hljs-selector-class,
.hljs-template-variable,
.hljs-deletion {
color: #aed07e !important;
}
.hljs-comment,
.hljs-quote {
color: #6f747d;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c792ea;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #c86068;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #abb2bf;
}
.hljs-attribute {
color: #c792ea;
}
.hljs-function {
color: #c792ea;
}
.hljs-type {
color: #f07178;
}
.hljs-title {
color: #82aaff !important;
}
.hljs-built_in,
.hljs-class {
color: #82aaff;
}
// 括号
.hljs-params {
color: #a6accd;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #de7e61;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id {
color: #61aeee;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
@@ -1,16 +1,37 @@
/*
* 深色主题
* 单页面移除深色主题 document.getElementsByTagName("html")[0].removeAttribute('class')
*
* 本文件合并了原 dark.scss + el-dark.scss:
* - 顶部 @use 引入 Element Plus 暗色 CSS 变量(取代独立 el-dark.scss)
* - 下方覆盖项目级 html.dark CSS 变量与 wangEditor 在暗色模式下的样式
*/
// 引入 Element Plus 暗黑主题 CSS 变量
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
// 引入主题色 token 单一来源(与 element-plus-theme.scss 共用)
@use "../core/fa-tokens" as *;
$font-color: rgba(#fff, 0.85);
/* 覆盖element-plus默认深色背景色 */
html.dark {
// element-plus
// element-plus 通用背景/文本
--el-bg-color: var(--default-box-color);
--el-text-color-regular: #{$font-color};
// element-plus 功能性色(与 light 模式一致 —— 用户未要求 dark 模式独立色板)
// 用 SCSS 插值强制解析变量(避免被字面量输出)。
// 注意:实际上 dark mode 下不需要这些覆写 —— element-plus 的 dark/css-vars.scss
// 经 SCSS 注入后会用我们的 colors(含 dark 适配的 light-x/dark-2),
// 见 html.dark#1 块。这里仅作为冗余防御,万一 EP dark/css-vars.scss 未走注入路径也能兜底。
--el-color-success: #{$fa-success};
--el-color-warning: #{$fa-warning};
--el-color-danger: #{$fa-danger};
--el-color-error: #{$fa-error};
--el-color-info: #{$fa-info};
// 富文本编辑器
// 工具栏背景颜色
--w-e-toolbar-bg-color: #18191c;
@@ -1,12 +1,28 @@
// 优化 Element Plus 组件库默认样式
// 颜色 hex 全部从 _fa-tokens.scss 引入,保证整个项目只有一处定义业务色。
// 运行时 setElementThemeColor() 仍会覆盖 --el-color-primary(用户可切换主题色)。
@use "../core/fa-tokens" as *;
:root {
// 系统主色 fallback(setElementThemeColor 运行时动态覆盖)
--el-color-primary: #{$fa-primary};
// 固定业务色板(EP 组件内部 var(--el-color-*) 消费)
--el-color-success: #{$fa-success};
--el-color-warning: #{$fa-warning};
--el-color-danger: #{$fa-danger};
--el-color-error: #{$fa-error};
--el-color-info: #{$fa-info};
// 系统主色
--main-color: var(--el-color-primary);
--el-color-white: white !important;
--el-color-black: white !important;
// 输入框边框颜色
// --el-border-color: #E4E4E7 !important; // DCDFE6
// 按钮样式偏好
--el-button-hover-bg-color: #e8f2ff !important;
--el-button-hover-border-color: var(--el-color-primary) !important;
--el-button-border-color: var(--el-color-primary) !important;
--el-button-text-color: var(--el-color-primary) !important;
// 按钮粗度
--el-font-weight-primary: 400 !important;
--el-component-custom-height: 36px !important;
@@ -0,0 +1,76 @@
/**
* Element Plus 主题 SCSS 入口(vite additionalData 注入)。
*
* 原理
* ----
* vite.config.ts 的 css.preprocessorOptions.scss.additionalData 把本文件
* 注入到每个 SCSS 文件开头(含 EP 内部组件)。
*
* 本文件用 @use "element-plus/theme-chalk/src/common/var.scss" with(...)
* **直接修改 common/var 模块本身**(带我们的 colors / $button / $messagebox /
* $popover)—— Sass 模块缓存机制下:
* - 此 @use 是 common/var 模块在 Sass cache 中的首次加载
* - with 参数覆盖模块内部的 !default 默认值
* - 后续所有 @use "common/var" 拿到的都是带我们配置的实例
* - 包含 EP 组件内 @use "common/var"(如 button.scss 的 button-variant mixin)
* 与 var.scss 内 @use "common/var"
*
* 这就是 Element Plus 官方推荐的"从源头解决"方式:
* https://element-plus.org/zh-CN/guide/theming
*
* 关键陷阱
* --------
* @forward with(...) 与 @use with(...) 不同:
* - @forward with(...) 创建新的转发实例(不影响原 common/var 模块)
* - @use with(...) 替换原 common/var 模块的默认参数(影响所有消费者)
*
* 与运行时 setElementThemeColor() 的协作
* ---------------------------------------
* - 本文件注入 success / warning / danger / error / info(业务固定色板)
* 以及 $button / $messagebox / $popover 等组件样式偏好。
* - primary 由运行时 setElementThemeColor() 动态写入 :root
* (因为用户在设置面板里可切换 themeColorPresets)
* - SCSS 注入的 primary 退化为默认值 #4080ff,与 themeColorPresets 首项保持一致。
*/
@use "../core/fa-tokens.scss" as *;
// 关键:@use with 直接修改 common/var 模块本身(影响所有 @use common/var 的消费者)
@use "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
"primary": (
"base": $fa-primary,
),
"success": (
"base": $fa-success,
),
"warning": (
"base": $fa-warning,
),
"danger": (
"base": $fa-danger,
),
"error": (
"base": $fa-error,
),
"info": (
"base": $fa-info,
),
),
// 按钮样式偏好:primary 链路按钮 hover 用浅色背景(更柔和的视觉反馈)
$button: (
"hover-bg-color": var(--el-color-primary-light-9),
"hover-border-color": var(--el-color-primary),
"border-color": var(--el-color-primary),
"text-color": var(--el-color-primary),
),
// 圆角偏好
$messagebox: (
"border-radius": "12px",
),
$popover: (
"border-radius": "12px",
)
);
+21 -22
View File
@@ -1,26 +1,25 @@
// 重置默认样式
@use "./core/reset";
// ============================================================
// 全局样式入口(src/styles/index.scss)
// ============================================================
// 目录结构:
// core/ 全局基础(重置、mixin、Tailwind、tokens)
// element-plus/ EP 主题与样式覆写(theme / overrides / dark)
// animations/ 全局动画(路由切换、主题切换)
// vendors/ 第三方组件样式覆写(highlight.js、markdown)
// custom/ 业务页专属样式
// ============================================================
// 应用全局样式
// ---------- core/ ----------
@use "./core/reset";
@use "./core/app";
// Element Plus 样式优化
@use "./core/el-ui";
// ---------- element-plus/ ----------
// Element Plus 组件样式覆写(按钮、弹窗、表格等)
@use "./element-plus/overrides";
// 暗黑主题
@use "./element-plus/dark";
// Element Plus 功能性颜色覆写 CSS 变量
@use "./core/variables";
// Element Plus 暗黑主题
@use "./core/el-dark";
// 暗黑主题样式优化
@use "./core/dark";
// 路由切换动画
@use "./core/router-transition";
// 主题切换过渡优化
@use "./core/theme-change";
// 主题切换圆形扩散动画
@use "./core/theme-animation";
// ---------- animations/ ----------
@use "./animations/router-transition";
@use "./animations/theme-change";
@use "./animations/theme-animation";
@@ -2,6 +2,19 @@
@custom-variant dark (&:where(.dark, .dark *));
/* Tailwind v4 的 @source 指令用于指定扫描哪些文件以提取 class 名用于 purge。
* 在 vite 插件模式下,tailwindcss vite 插件已会自动扫描所有 CSS 文件。
* 注释内不能出现连续的星号斜杠(避免 CSS 解析器提前关闭注释)。
*/
@source "../../**/*.vue";
@source "../../**/*.ts";
@source "../../**/*.tsx";
@source "../../**/*.js";
@source "../../**/*.jsx";
@source "../../**/*.html";
@source "../../**/*.scss";
@source "../../**/*.css";
/* ==================== Tailwind Theme Configuration ==================== */
@theme {
/* Box Color (Light: white / Dark: black) */
@@ -49,6 +62,46 @@
border-radius: calc(var(--custom-radius) / 2 + 2px);
}
/* ==================== Flex Shortcut Utilities ====================
* 解决项目里大量重复的 flex 居中 / 两端对齐 / 列布局组合(详见
* scripts/find-tailwind-candidates.py 统计结果)。
* 在模板里直接 class="flex-cc gap-2" 比 class="flex items-center justify-center gap-2" 短 60%。
*/
@utility flex-cc {
display: flex;
align-items: center;
justify-content: center;
}
@utility flex-cb {
display: flex;
align-items: center;
justify-content: space-between;
}
@utility flex-cs {
display: flex;
align-items: center;
justify-content: flex-start;
}
@utility flex-ce {
display: flex;
align-items: center;
justify-content: flex-end;
}
@utility flex-sc {
display: flex;
flex-direction: column;
}
@utility flex-scb {
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* ==================== Light Mode Variables ==================== */
:root {
/* Base Colors */
@@ -129,70 +182,9 @@
--fa-el-active-color: #2e2e38;
}
/* ==================== Custom Utility Classes ==================== */
@layer utilities {
/* Flexbox Layout Utilities */
.flex-c {
display: flex;
align-items: center;
}
.flex-b {
display: flex;
justify-content: space-between;
}
.flex-cc {
display: flex;
align-items: center;
justify-content: center;
}
.flex-cb {
display: flex;
align-items: center;
justify-content: space-between;
}
/* Transition Utilities */
.tad-200 {
transition: all 200ms;
}
.tad-300 {
transition: all 300ms;
}
/* Border Utilities */
.border-full-d {
border: 1px solid var(--default-border);
}
.border-b-d {
border-bottom: 1px solid var(--default-border);
}
.border-t-d {
border-top: 1px solid var(--default-border);
}
.border-l-d {
border-left: 1px solid var(--default-border);
}
.border-r-d {
border-right: 1px solid var(--default-border);
}
/* Cursor Utilities */
.c-p {
cursor: pointer;
}
}
/* ==================== Custom Component Classes ==================== */
@layer components {
/* Fa Card Header Component */
/* ================================== Fa Layout ================================== */
.fa-card-header {
display: flex;
justify-content: space-between;
@@ -220,4 +212,53 @@
}
}
}
/* ================================== Chart Components ================================== */
/* 图表容器:占满父容器剩余空间(10 处使用) */
.chart-box {
flex: 1;
width: 100%;
min-height: 0;
}
/* 迷你图表区:占满高度(5 处使用) */
.mc-chart {
flex: 1;
min-height: 0;
}
/* 迷你图表面板(6 处使用) */
.mini-chart-panel {
position: relative;
display: flex;
flex-direction: column;
padding: 10px 12px;
overflow: hidden;
background: linear-gradient(180deg, rgb(0 30 80 / 55%) 0%, rgb(6 11 36 / 70%) 100%);
border: 1px solid var(--border, rgb(0 180 255 / 12%));
border-radius: 10px;
}
/* 迷你图表标签文字(6 处使用) */
.mc-hd {
margin-bottom: 2px;
font-size: 10px;
opacity: 0.35;
}
/* 迷你图表大数字(5 处使用) */
.mc-value {
margin-bottom: 0;
font-size: 16px;
font-weight: 700;
line-height: 1;
}
/* 迷你图表单位(4 处使用) */
.mc-unit {
margin-left: 2px;
font-size: 9px;
font-weight: 400;
opacity: 0.4;
}
}