mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-08 18:49:09 +00:00
chore: 完成多批次代码优化与重构
- 重构工作流模块目录结构,迁移代码文件 - 修复类型断言空值安全问题,添加 ! 操作符 - 优化样式类名,替换 flex-cc 为标准 flex 工具类 - 更新路由标签简化文案,移除冗余注释 - 调整 ruff 配置,放宽行长度限制 - 更新 README 与多语言文案,优化项目描述 - 修复表单、图表组件的类型与样式问题 - 简化搜索表单、数据卡片的布局代码
This commit is contained in:
@@ -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;
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
+22
-1
@@ -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;
|
||||
+18
-2
@@ -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",
|
||||
)
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user