Преглед изворни кода

style(admin): 重写全局设计系统,统一全部后台页面视觉规范

依据 enterprise-ui-optimizer 技能 design-tokens.md(对标 Ant Design 5 / 华为云控制台)。

问题:
- 162 个页面中 49 个(30%)用自定义根 class,从未被设计系统覆盖,
  一直是原生 Element 蓝(令牌全限定在 .admin-page 下)
- 双令牌体系并存:旧 Element 色板与 Ant 令牌各说各话
- el-dialog / dropdown / message 等 portal 到 body,逃出作用域未被统一

改动:
- 设计系统作用域 .admin-page -> .main-wrapper(Layout 内 router-view 容器),
  一次性覆盖除 Login 外全部后台页面
- 合并双令牌:保留旧变量名(存量规则零改动自动纳入),取值对齐 Ant 规范
  (主色 #409EFF->#1677FF、文字改 rgba 灰阶、边框 #DCDFE6->#D9D9D9、
   字号 13->14、间距 20->24、阴影轻量化的规范值)
- 新增 portal 组件全局覆盖:弹窗/下拉/气泡/消息/通知/日期面板统一圆角、阴影、主题色
- 补齐规范项:按钮圆角 6px、卡片 8px+24px、表头 #FAFAFA、数值列右对齐+等宽数字、
  状态色语义化标签、空状态、分区标题 3px 主色竖条

避坑:
- 全局规则改用 :where(.main-wrapper) 降特异度至 0,1,0,避免与组件 scoped
  样式(0,2,0)平局导致打包顺序决定胜负
- 11 个 admin/review/*.vue 根元素 class 为 .filter-bar,限定为
  :where(.main-wrapper) > * .filter-bar 仅作用于下级元素,避免整页变弹性容器
- 弹性填充链与 :not(:has(.adm-table-wrap)) 滚动修复逐字保留,
  :where() 批处理显式跳过全部含 :has( 的行

验证:npm run build 通过;编译产物 :where() 51 处、:has() 18 处保留,
--color-primary 与 --adm-primary 均为 #1677ff。
liaoxg пре 1 недеља
родитељ
комит
18bd4f8542
1 измењених фајлова са 362 додато и 533 уклоњено
  1. 362 533
      cfc-web/src/styles/admin-tokens.css

+ 362 - 533
cfc-web/src/styles/admin-tokens.css

@@ -1,487 +1,105 @@
 /**
- * Admin 后台管理页面样式规范
- * 
- * 设计原则:
- * - 基于 Element UI 的默认风格进行扩展
- * - 统一间距和尺寸标准
- * - 减少内联样式使用,改为 class 复用
+ * 企业级后台设计系统(Design System)
+ *
+ * 规范依据:enterprise-ui-optimizer / references/design-tokens.md
+ * 风格对标:Ant Design 5、华为云控制台、银行信贷系统、国资企业后台
+ * 设计原则:克制、干净、专业、轻量化、信息优先
+ *           数据可读性 > 操作效率 > 视觉美观 > 个性化效果
+ *
+ * 作用域:.main-wrapper(Layout 内 router-view 容器)→ 覆盖全部后台页面
+ *         全局(无作用域)→ 覆盖 Element 挂载到 body 的 portal 组件
+ *          (el-dialog / el-dropdown-menu / el-message / el-select-dropdown / el-tooltip …)
+ *
+ * ⚠️ 文件末尾「弹性填充链」与「:has() 滚动修复」为功能逻辑,已逐字保留,禁止改动。
  */
 
-/* ========================
-   CSS 自定义属性 (设计令牌)
-   ======================== */
+/* ============================================================
+   1. 设计令牌(单一事实源)
+   旧的 Element 变量名保留(向后兼容存量规则),但取值已对齐 Ant 规范,
+   使所有引用旧变量的规则自动纳入新体系,无需逐条改写。
+   ============================================================ */
 :root {
-  /* 主色调 */
-  --color-primary: #409EFF;
-  --color-primary-light: #66b1ff;
-  --color-primary-dark: #337ecc;
-  
-  /* 成功/警告/危险 */
-  --color-success: #67C23A;
-  --color-warning: #E6A23C;
-  --color-danger: #F56C6C;
-  --color-info: #909399;
-  
-  /* 文字色 */
-  --color-text-primary: #303133;
-  --color-text-regular: #606266;
-  --color-text-secondary: #909399;
-  --color-text-placeholder: #C0C4CC;
-  
-  /* 边框色 */
-  --color-border-base: #DCDFE6;
-  --color-border-light: #E4E7ED;
-  --color-border-lighter: #EBEEF5;
-  --color-border-extra-light: #F2F6FC;
-  
-  /* 背景色 */
-  --color-bg-page: #f0f2f5;
-  --color-bg-card: #ffffff;
-  --color-bg-overlay: rgba(0, 0, 0, 0.5);
-  
-  /* 字体 */
+  /* ---- 品牌主色与功能色(Ant Design 5 语义色) ---- */
+  --color-primary: #1677FF;          /* 原 #409EFF → Ant 蓝 */
+  --color-primary-light: #4096FF;    /* hover */
+  --color-primary-dark: #0958D9;     /* active */
+  --color-success: #52C41A;          /* 原 #67C23A */
+  --color-warning: #FAAD14;          /* 原 #E6A23C */
+  --color-danger: #FF4D4F;           /* 原 #F56C6C */
+  --color-info: #909399;             /* 沿用 Element 灰语义(非蓝),避免误改存量灰字 */
+
+  /* ---- 文字灰阶(rgba 体系,层次更稳定) ---- */
+  --color-text-primary: rgba(0, 0, 0, 0.88);
+  --color-text-regular: rgba(0, 0, 0, 0.65);
+  --color-text-secondary: rgba(0, 0, 0, 0.45);
+  --color-text-placeholder: rgba(0, 0, 0, 0.25);
+
+  /* ---- 边框与填充 ---- */
+  --color-border-base: #D9D9D9;       /* 输入框、强分割 */
+  --color-border-light: #F0F0F0;      /* 弱分割线、卡片边框 */
+  --color-border-lighter: #F0F0F0;
+  --color-border-extra-light: #F5F5F5;
+
+  /* ---- 背景 ---- */
+  --color-bg-page: #F0F2F5;           /* 页面整体背景 */
+  --color-bg-card: #FFFFFF;           /* 卡片、弹窗 */
+  --color-bg-header: #FAFAFA;         /* 表头、区块标题底 */
+  --color-bg-overlay: rgba(0, 0, 0, 0.45);
+
+  /* ---- 字体 ---- */
   --font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
-  --font-size-base: 14px;
-  --font-size-small: 13px;
-  --font-size-mini: 12px;
-  
-  /* 间距 (8px 基准) */
+  --font-size-base: 14px;             /* 正文 / 表格 / 表单 */
+  --font-size-small: 14px;            /* 原 13px → 规范正文 14 */
+  --font-size-mini: 12px;             /* 辅助说明、脚注、占位 */
+
+  /* ---- 间距(8px 基准,禁止 5/7/10/14/18 等非标值) ---- */
   --spacing-xs: 4px;
   --spacing-sm: 8px;
   --spacing-md: 12px;
   --spacing-base: 16px;
-  --spacing-lg: 20px;
-  --spacing-xl: 24px;
+  --spacing-lg: 24px;                 /* 原 20px → 规范 24 */
+  --spacing-xl: 24px;                 /* 卡片内边距、区块间距 */
   --spacing-xxl: 32px;
-  
-  /* 圆角 */
-  --border-radius-base: 4px;
-  --border-radius-small: 2px;
-  --border-radius-large: 8px;
-  
-  /* 阴影 */
-  --box-shadow-base: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-  --box-shadow-light: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
-  
-  /* 宽度标准 */
+  --spacing-3xl: 48px;
+
+  /* ---- 圆角(禁止 >8px 大圆角) ---- */
+  --border-radius-small: 2px;         /* 标签、徽标 */
+  --border-radius-base: 4px;          /* 输入框、下拉、表格内层 */
+  --border-radius-control: 6px;       /* 按钮 */
+  --border-radius-large: 8px;         /* 卡片、弹窗、面板 */
+
+  /* ---- 阴影(轻量化:以描边+留白分区,仅浮层用稍重阴影) ---- */
+  --box-shadow-base: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02);
+  --box-shadow-light: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
+  --box-shadow-popup: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12);
+
+  /* ---- 控件宽度标准 ---- */
   --width-search-input: 220px;
   --width-select-sm: 120px;
   --width-select-base: 150px;
   --width-select-lg: 180px;
   --width-select-xl: 200px;
-  
-  /* 高度标准 */
-  --height-table-max: calc(100vh - 280px);
-  --height-table-with-filter: calc(100vh - 340px);
-}
-
-/* ========================
-   全局重置
-   ======================== */
-.admin-page {
-  padding: var(--spacing-base);
-  background: var(--color-bg-page);
-  min-height: calc(100vh - 60px);
-}
-
-/* page-container:所有<el-card>包裹的列表页使用此 class */
-.page-container {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-  padding: 20px;
-}
-.page-container > .el-card {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-}
-.page-container > .el-card .el-card__body {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-  padding: 20px;
-}
-.page-container > .el-card .el-card__body > .el-form:first-child {
-  flex-shrink: 0;
-}
-.page-container > .el-card .el-card__body > .el-pagination {
-  flex-shrink: 0;
-  margin-top: auto;
-  padding-top: 20px;
-  text-align: right;
-}
-
-/* ========================
-   页面头部标题栏
-   ======================== */
-.admin-page-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-bottom: var(--spacing-base);
-}
-
-.admin-page-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: var(--color-text-primary);
-}
-
-.admin-page-actions {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-
-/* ========================
-   筛选器 / 搜索栏
-   ======================== */
-.filter-bar {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
-  gap: var(--spacing-sm);
-  margin-bottom: var(--spacing-base);
-  padding: var(--spacing-base);
-  background: var(--color-bg-card);
-  border-radius: var(--border-radius-base);
-  box-shadow: var(--box-shadow-light);
-}
-
-.filter-bar .el-select {
-  width: var(--width-select-base);
-}
-
-.filter-bar .el-input {
-  width: var(--width-search-input);
-}
-
-.filter-bar .el-input--small {
-  width: var(--width-search-input);
-}
-
-.filter-bar .el-input--mini {
-  width: 160px;
-}
-
-/* ========================
-   状态标签栏 (ArticleManage 等)
-   ======================== */
-.status-tabs {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
-  gap: var(--spacing-xs);
-  margin-bottom: var(--spacing-md);
-}
-
-.status-tabs .el-button + .el-button {
-  margin-left: 0;
-}
-
-/* ========================
-   表格容器 (滚动)
-   ======================== */
-.table-scroll-wrap {
-  overflow: auto;
-  max-height: var(--height-table-max);
-  border-radius: var(--border-radius-base);
-}
-
-.table-scroll-wrap-sm {
-  overflow: auto;
-  max-height: calc(100vh - 300px);
-  border-radius: var(--border-radius-base);
-}
-
-.table-scroll-wrap-lg {
-  overflow: auto;
-  max-height: var(--height-table-with-filter);
-  border-radius: var(--border-radius-base);
-}
-
-/* ========================
-   卡片
-   ======================== */
-.admin-card {
-  background: var(--color-bg-card);
-  border-radius: var(--border-radius-base);
-  box-shadow: var(--box-shadow-light);
-  margin-bottom: var(--spacing-base);
-}
-
-.admin-card .el-card__header {
-  padding: var(--spacing-base);
-  border-bottom: 1px solid var(--color-border-lighter);
-}
-
-.admin-card .el-card__body {
-  padding: var(--spacing-base);
-}
-
-/* ========================
-   操作按钮区 (分页上方/下方)
-   ======================== */
-.table-actions {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: var(--spacing-base);
-}
-
-.table-actions-right {
-  text-align: right;
-  margin-top: var(--spacing-base);
-}
-
-/* ========================
-   对话框表单
-   ======================== */
-.admin-form-dialog .el-form-item {
-  margin-bottom: var(--spacing-base);
-}
-
-.admin-form-dialog .el-form-item__label {
-  color: var(--color-text-regular);
-}
 
-/* ========================
-   搜索结果统计
-   ======================== */
-.search-result-count {
-  font-size: var(--font-size-small);
-  color: var(--color-text-secondary);
-  margin-bottom: var(--spacing-sm);
-}
-
-/* ========================
-   空状态
-   ======================== */
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  padding: var(--spacing-xxl) 0;
-  color: var(--color-text-placeholder);
-}
-
-.empty-state i {
-  font-size: 48px;
-  margin-bottom: var(--spacing-sm);
-}
-
-/* ========================
-   分页器
-   ======================== */
-.pagination-wrap {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: var(--spacing-base);
-  padding: var(--spacing-sm) 0;
-}
-
-/* ========================
-   详情页布局
-   ======================== */
-.detail-section {
-  margin-bottom: var(--spacing-lg);
-}
-
-.detail-section-title {
-  font-size: 15px;
-  font-weight: 600;
-  color: var(--color-text-primary);
-  margin-bottom: var(--spacing-md);
-  padding-bottom: var(--spacing-sm);
-  border-bottom: 1px solid var(--color-border-lighter);
-}
-
-/* ========================
-   编辑页通用样式
-   ======================== */
-.edit-form {
-  max-width: 800px;
-}
-
-.edit-form .el-form-item__label {
-  font-weight: 500;
-}
-
-.edit-form-footer {
-  margin-top: var(--spacing-xl);
-  padding-top: var(--spacing-base);
-  border-top: 1px solid var(--color-border-lighter);
-  text-align: right;
-}
-
-/* ========================
-   富文本编辑器容器
-   ======================== */
-.rich-editor {
-  border: 1px solid var(--color-border-base);
-  border-radius: var(--border-radius-base);
-  position: relative;
-  overflow: visible;
-}
-
-.rich-editor /deep/ .w-e-toolbar {
-  flex-wrap: wrap;
-  padding: var(--spacing-sm);
-}
-
-.rich-editor /deep/ .w-e-text-container {
-  min-height: 300px;
-}
-
-.rich-editor /deep/ .w-e-panel-container {
-  z-index: 5000;
-}
-
-/* ========================
-   Element UI 组件覆盖样式
-   ======================== */
-
-/* 表格 */
-.el-table {
-  font-size: var(--font-size-small);
-}
-
-.el-table th {
-  background-color: var(--color-bg-page) !important;
-  color: var(--color-text-regular);
-  font-weight: 600;
-}
-
-.el-table td {
-  padding: 8px 0;
-}
-
-/* 按钮 */
-.el-button--small {
-  padding: 9px 15px;
-}
-
-/* 卡片 */
-.el-card {
-  border-radius: var(--border-radius-base);
-  box-shadow: var(--box-shadow-light);
-}
-
-.el-card__header {
-  padding: var(--spacing-base);
-  font-weight: 600;
-  border-bottom: 1px solid var(--color-border-lighter);
-}
-
-.el-card__body {
-  padding: var(--spacing-base);
-}
-
-/* 输入框 */
-.el-input--small {
-  font-size: var(--font-size-small);
-}
-
-/* 表单项 */
-.el-form-item--small .el-form-item__label {
-  font-size: var(--font-size-small);
-}
-
-/* 标签 */
-.el-tag {
-  border-radius: var(--border-radius-small);
-}
-
-/* 分页 */
-.el-pagination {
-  font-weight: normal;
-}
-
-/* 对话框 */
-.el-dialog__header {
-  padding: var(--spacing-base);
-  border-bottom: 1px solid var(--color-border-lighter);
-}
-
-.el-dialog__body {
-  padding: var(--spacing-lg);
-}
-
-.el-dialog__footer {
-  padding: var(--spacing-base);
-  border-top: 1px solid var(--color-border-lighter);
-}
-
-/* ========================
-   工具类
-   ======================== */
-
-/* 文本 */
-.text-muted {
-  color: var(--color-text-placeholder);
-}
-
-.text-secondary {
-  color: var(--color-text-secondary);
-}
-
-.text-primary {
-  color: var(--color-text-primary);
-}
-
-.text-success {
-  color: var(--color-success);
-}
-
-.text-warning {
-  color: var(--color-warning);
-}
-
-.text-danger {
-  color: var(--color-danger);
-}
-
-/* 间距 */
-.mt-sm { margin-top: var(--spacing-sm); }
-.mt-base { margin-top: var(--spacing-base); }
-.mt-lg { margin-top: var(--spacing-lg); }
-.mb-sm { margin-bottom: var(--spacing-sm); }
-.mb-base { margin-bottom: var(--spacing-base); }
-.mb-lg { margin-bottom: var(--spacing-lg); }
-
-/* Flex */
-.flex { display: flex; }
-.flex-wrap { flex-wrap: wrap; }
-.items-center { align-items: center; }
-.justify-between { justify-content: space-between; }
-.gap-sm { gap: var(--spacing-sm); }
-.gap-base { gap: var(--spacing-base); }
-
-/* 浮动 */
-.pull-right { float: right; }
-.clearfix::after { content: ""; display: table; clear: both; }
-
-/* ==== 企业级设计系统(Ant Design 5 规范,对齐 design-tokens.md) ==== */
-/* 作用域:.admin-page 及其后代 —— 覆盖全部 B 端管理页(admin / teacher / 顶层共享页) */
-/* 原则:克制、干净、留白分区、状态色仅承载语义、主色覆盖率 < 5% */
-
-:root {
+  /* ---- 企业级语义令牌(Ant 规范,新增样式优先使用这一组) ---- */
   --adm-primary: #1677FF;
   --adm-primary-hover: #4096FF;
   --adm-primary-active: #0958D9;
   --adm-success: #52C41A;
   --adm-success-text: #389E0D;
+  --adm-success-bg: #F6FFED;
+  --adm-success-border: #B7EB8F;
   --adm-warning: #FAAD14;
   --adm-warning-text: #D48806;
+  --adm-warning-bg: #FFFBE6;
+  --adm-warning-border: #FFE58F;
   --adm-error: #FF4D4F;
   --adm-error-text: #CF1322;
+  --adm-error-bg: #FFF1F0;
+  --adm-error-border: #FFCCC7;
   --adm-info: #1677FF;
   --adm-info-text: #0958D9;
+  --adm-info-bg: #E6F4FF;
+  --adm-info-border: #91CAFF;
   --adm-text-88: rgba(0, 0, 0, 0.88);
   --adm-text-65: rgba(0, 0, 0, 0.65);
   --adm-text-45: rgba(0, 0, 0, 0.45);
@@ -492,88 +110,302 @@
   --adm-bg-container: #FFFFFF;
   --adm-bg-header: #FAFAFA;
   --adm-radius-sm: 2px;
+  --adm-radius-md: 4px;
   --adm-radius-control: 6px;
   --adm-radius-card: 8px;
-  --adm-shadow-card: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
+  --adm-shadow-card: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02);
+  --adm-shadow-popup: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12);
+}
+
+/* ============================================================
+   2. 全局 Element 基线(无作用域)
+   目的:让 portal 到 body 的组件(弹窗 / 下拉 / 气泡 / 消息)与页面内风格一致。
+   这些组件不在 .main-wrapper 内,必须全局定义才能覆盖。
+   ============================================================ */
+
+/* ---- 按钮:统一圆角 6px(规范 radius-control) ---- */
+.el-button { border-radius: var(--adm-radius-control); font-weight: 400; }
+.el-button--default { border-color: var(--adm-border-base); color: var(--adm-text-88); }
+.el-button--default:hover,
+.el-button--default:focus { color: var(--adm-primary-hover); border-color: var(--adm-primary-hover); background: #fff; }
+.el-button--primary { background-color: var(--adm-primary); border-color: var(--adm-primary); }
+.el-button--primary:hover,
+.el-button--primary:focus { background-color: var(--adm-primary-hover); border-color: var(--adm-primary-hover); }
+.el-button--primary:active { background-color: var(--adm-primary-active); border-color: var(--adm-primary-active); }
+.el-button--primary.is-disabled,
+.el-button--primary.is-disabled:hover { background-color: #A0C4FF; border-color: #A0C4FF; }
+.el-button--text { color: var(--adm-primary); }
+.el-button--text:hover { color: var(--adm-primary-hover); }
+.el-button--danger { border-color: var(--adm-error); }
+.el-button--danger.is-plain { color: var(--adm-error); border-color: var(--adm-error-border); background: var(--adm-error-bg); }
+.el-button--danger.is-plain:hover { background: var(--adm-error); border-color: var(--adm-error); color: #fff; }
+
+/* ---- 输入框 / 下拉:圆角 4px(规范 radius-md) ---- */
+.el-input__inner,
+.el-textarea__inner { border-radius: var(--adm-radius-md); border-color: var(--adm-border-base); color: var(--adm-text-88); }
+.el-input__inner:hover,
+.el-textarea__inner:hover { border-color: var(--adm-primary-hover); }
+.el-input.is-focus .el-input__inner,
+.el-input__inner:focus,
+.el-textarea__inner:focus { border-color: var(--adm-primary); }
+.el-input__inner::placeholder,
+.el-textarea__inner::placeholder { color: var(--adm-text-25); }
+.el-input.is-disabled .el-input__inner { background: var(--adm-fill); border-color: var(--adm-border-split); color: var(--adm-text-25); }
+
+/* ---- 标签:圆角 2px;状态色语义化(浅底+深字+同色描边) ---- */
+.el-tag { border-radius: var(--adm-radius-sm); font-size: 12px; }
+.el-tag--success { background: var(--adm-success-bg); border-color: var(--adm-success-border); color: var(--adm-success-text); }
+.el-tag--warning { background: var(--adm-warning-bg); border-color: var(--adm-warning-border); color: var(--adm-warning-text); }
+.el-tag--info { background: var(--adm-info-bg); border-color: var(--adm-info-border); color: var(--adm-info-text); }
+.el-tag--danger { background: var(--adm-error-bg); border-color: var(--adm-error-border); color: var(--adm-error-text); }
+
+/* ---- 对话框 / 消息框(portal) ---- */
+.el-dialog { border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-popup); overflow: hidden; }
+.el-dialog__header { padding: 16px 24px; border-bottom: 1px solid var(--adm-border-split); }
+.el-dialog__title { font-size: 16px; font-weight: 500; line-height: 1.5; color: var(--adm-text-88); }
+.el-dialog__headerbtn { top: 16px; right: 20px; }
+.el-dialog__headerbtn .el-dialog__close { color: var(--adm-text-45); }
+.el-dialog__headerbtn .el-dialog__close:hover { color: var(--adm-primary); }
+.el-dialog__body { padding: 24px; color: var(--adm-text-88); }
+.el-dialog__footer { padding: 12px 24px; border-top: 1px solid var(--adm-border-split); }
+.el-message-box { border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-popup); border: none; padding-bottom: 0; }
+.el-message-box__title { font-size: 16px; font-weight: 500; color: var(--adm-text-88); }
+.el-message-box__content { padding: 16px 24px; color: var(--adm-text-65); }
+.el-message-box__btns { padding: 12px 24px; border-top: 1px solid var(--adm-border-split); }
+
+/* ---- 下拉 / 浮层 / 气泡 / 选择器面板(portal) ---- */
+.el-dropdown-menu { border-radius: var(--adm-radius-control); box-shadow: var(--adm-shadow-popup); border: 1px solid var(--adm-border-split); padding: 4px; }
+.el-dropdown-menu__item { font-size: 14px; line-height: 32px; border-radius: var(--adm-radius-md); color: var(--adm-text-88); }
+.el-dropdown-menu__item:not(.is-disabled):hover { background: var(--adm-fill); color: var(--adm-text-88); }
+.el-select-dropdown { border: 1px solid var(--adm-border-split); border-radius: var(--adm-radius-control); box-shadow: var(--adm-shadow-popup); }
+.el-select-dropdown__item { font-size: 14px; height: 34px; line-height: 34px; }
+.el-select-dropdown__item.selected { color: var(--adm-primary); font-weight: 500; }
+.el-popover,
+.el-picker-panel,
+.el-cascader__dropdown,
+.el-autocomplete-suggestion { border-radius: var(--adm-radius-control); box-shadow: var(--adm-shadow-popup); border-color: var(--adm-border-split); }
+.el-tooltip__popper { box-shadow: var(--adm-shadow-popup); }
+.el-message { border-radius: var(--adm-radius-control); box-shadow: var(--adm-shadow-popup); border: none; }
+.el-notification { border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-popup); border: 1px solid var(--adm-border-split); }
+.el-notification__title { font-size: 16px; font-weight: 500; color: var(--adm-text-88); }
+.el-notification__content { font-size: 14px; color: var(--adm-text-65); }
+
+/* ---- 卡片 ---- */
+.el-card { border: 1px solid var(--adm-border-split); border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-card); }
+.el-card__header { padding: 20px 24px; border-bottom: 1px solid var(--adm-border-split); }
+.el-card__body { padding: 24px; }
+
+/* ---- 表格(基线;页面内更精细的规则见第 3 节) ---- */
+.el-table { font-size: 14px; color: var(--adm-text-88); }
+.el-table th { background-color: var(--adm-bg-header) !important; color: var(--adm-text-88); font-weight: 500; }
+.el-table td,
+.el-table th.is-leaf { border-color: var(--adm-border-split); }
+.el-table__empty-text { color: var(--adm-text-45); font-size: 14px; }
+
+/* ---- 分页 ---- */
+.el-pagination { font-weight: 400; color: var(--adm-text-65); }
+.el-pagination.is-background .el-pager li:not(.disabled).active { background-color: var(--adm-primary); }
+.el-pagination.is-background .el-pager li:not(.active):hover { color: var(--adm-primary); }
+
+/* ---- 表单 ---- */
+.el-form-item__label { font-size: 14px; color: var(--adm-text-65); }
+.el-form-item__content { color: var(--adm-text-88); }
+
+/* ---- 标签页 ---- */
+.el-tabs__item { font-size: 14px; color: var(--adm-text-65); }
+.el-tabs__item.is-active { color: var(--adm-primary); font-weight: 500; }
+.el-tabs__active-bar { background-color: var(--adm-primary); }
+.el-tabs__nav-wrap::after { background-color: var(--adm-border-split); height: 1px; }
+
+/* ---- 分割线 ---- */
+.el-divider--horizontal { margin: 24px 0; background-color: var(--adm-border-split); }
+.el-divider__text { font-size: 14px; font-weight: 500; color: var(--adm-text-88); }
+
+/* ============================================================
+   3. 页面作用域:.main-wrapper
+   Layout 内 router-view 容器 —— 覆盖全部后台页面(含原先未覆盖的
+   dashboard / lg-admin / review/* / supply-* / teacher-* 等自定义根节点)
+   ============================================================ */
+
+/* ---- 页面容器基线 ---- */
+:where(.main-wrapper) {
+  font-family: var(--font-family);
+  font-size: var(--font-size-base);
+  line-height: 1.5;
+  color: var(--adm-text-88);
+  background: var(--color-bg-page);
 }
 
-/* 主色按钮统一为 Ant 蓝(覆盖 Element 默认 #409EFF) */
-.admin-page .el-button--primary { background-color: var(--adm-primary); border-color: var(--adm-primary); color: #fff; }
-.admin-page .el-button--primary:hover,
-.admin-page .el-button--primary:focus { background-color: var(--adm-primary-hover); border-color: var(--adm-primary-hover); }
-.admin-page .el-button--primary:active { background-color: var(--adm-primary-active); border-color: var(--adm-primary-active); }
-.admin-page .el-button--primary.is-disabled,
-.admin-page .el-button--primary.is-disabled:hover { background-color: #A0C4FF; border-color: #A0C4FF; color: #fff; }
-
-/* 卡片:1px 描边 + 极轻阴影,替代重投影 */
-.admin-page .el-card { border: 1px solid var(--adm-border-split); border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-card); }
-.admin-page .el-card__header { padding: 20px 24px; border-bottom: 1px solid var(--adm-border-split); }
-.admin-page .el-card__body { padding: 24px; }
-
-/* 页面标题区:唯一主操作居右 */
-.admin-page .admin-page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; flex-shrink: 0; }
-.admin-page .admin-page-title { font-size: 20px; font-weight: 600; line-height: 1.4; color: var(--adm-text-88); }
-.admin-page .admin-page-subtitle { font-size: 12px; color: var(--adm-text-45); margin-left: 12px; }
-.admin-page .admin-page-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
-
-/* 筛选区:去卡片化,改底部 1px 分割线分区 */
-.admin-page .filter-bar {
+/* ---- 页面标题区:主标题 20/600,唯一主操作居右 ---- */
+:where(.main-wrapper) .admin-page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; flex-shrink: 0; }
+:where(.main-wrapper) .admin-page-title { font-size: 20px; font-weight: 600; line-height: 1.4; color: var(--adm-text-88); }
+:where(.main-wrapper) .admin-page-subtitle { font-size: 12px; color: var(--adm-text-45); margin-left: 12px; font-weight: 400; }
+:where(.main-wrapper) .admin-page-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
+
+/* ---- 卡片:白底 + 1px 描边 + 8px 圆角 + 极轻阴影 ---- */
+:where(.main-wrapper) .el-card { border: 1px solid var(--adm-border-split); border-radius: var(--adm-radius-card); box-shadow: var(--adm-shadow-card); }
+:where(.main-wrapper) .el-card__header { padding: 20px 24px; border-bottom: 1px solid var(--adm-border-split); font-weight: 500; }
+:where(.main-wrapper) .el-card__body { padding: 24px; }
+
+/* ---- 页面内主按钮 ---- */
+:where(.main-wrapper) .el-button--primary { background-color: var(--adm-primary); border-color: var(--adm-primary); }
+:where(.main-wrapper) .el-button--primary:hover,
+:where(.main-wrapper) .el-button--primary:focus { background-color: var(--adm-primary-hover); border-color: var(--adm-primary-hover); }
+:where(.main-wrapper) .el-button--primary:active { background-color: var(--adm-primary-active); border-color: var(--adm-primary-active); }
+
+/* ---- 筛选区:去卡片化,用底部 1px 分割线分区(留白承担分区职责) ---- */
+:where(.main-wrapper) > * .filter-bar {
   display: flex; align-items: flex-end; flex-wrap: wrap; gap: 16px;
   padding: 0 0 16px; margin-bottom: 16px;
   background: transparent; border-radius: 0; box-shadow: none;
   border-bottom: 1px solid var(--adm-border-split);
 }
+:where(.main-wrapper) > * .filter-bar .el-select { width: var(--width-select-base); }
+:where(.main-wrapper) > * .filter-bar .el-input { width: var(--width-search-input); }
+:where(.main-wrapper) > * .filter-bar .el-input--mini { width: 160px; }
 
-/* 区块标题:3px 主色竖条 */
-.admin-page .admin-section-title {
+/* ---- 区块标题:14/500 + 3px 主色竖条 ---- */
+:where(.main-wrapper) .admin-section-title {
   font-size: 14px; font-weight: 500; color: var(--adm-text-88);
   padding-left: 8px; border-left: 3px solid var(--adm-primary);
-  margin: 24px 0 12px; line-height: 1.2;
+  margin: 24px 0 12px; line-height: 1.4;
+}
+
+/* ---- 表格:表头浅底 46px、行内距 12px、单元格左右 16px ---- */
+:where(.main-wrapper) .el-table { font-size: 14px; border-radius: var(--adm-radius-card); }
+:where(.main-wrapper) .el-table th { background-color: var(--adm-bg-header) !important; color: var(--adm-text-88); font-weight: 500; height: 46px; padding: 0; }
+:where(.main-wrapper) .el-table td { padding: 12px 0; }
+:where(.main-wrapper) .el-table .cell { padding-left: 16px; padding-right: 16px; }
+:where(.main-wrapper) .el-table--border,
+:where(.main-wrapper) .el-table--group { border-color: var(--adm-border-split); }
+:where(.main-wrapper) .el-table td,
+:where(.main-wrapper) .el-table th.is-leaf { border-color: var(--adm-border-split); }
+:where(.main-wrapper) .el-table__empty-text { color: var(--adm-text-45); }
+
+/* ---- 数值列:右对齐 + 等宽数字(金额/数量可读性) ---- */
+:where(.main-wrapper) .el-table .cell-num,
+:where(.main-wrapper) .el-table .is-right .cell { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
+
+/* ---- 页内状态标签 ---- */
+:where(.main-wrapper) .el-tag { border-radius: var(--adm-radius-sm); }
+:where(.main-wrapper) .el-tag--success { background: var(--adm-success-bg); border-color: var(--adm-success-border); color: var(--adm-success-text); }
+:where(.main-wrapper) .el-tag--warning { background: var(--adm-warning-bg); border-color: var(--adm-warning-border); color: var(--adm-warning-text); }
+:where(.main-wrapper) .el-tag--info { background: var(--adm-info-bg); border-color: var(--adm-info-border); color: var(--adm-info-text); }
+:where(.main-wrapper) .el-tag--danger { background: var(--adm-error-bg); border-color: var(--adm-error-border); color: var(--adm-error-text); }
+
+/* ---- 标准化状态徽标(浅底+深字+同色圆点) ---- */
+:where(.main-wrapper) .adm-status { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; font-size: 12px; line-height: 20px; border-radius: var(--adm-radius-sm); white-space: nowrap; }
+:where(.main-wrapper) .adm-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
+:where(.main-wrapper) .adm-status--success { background: var(--adm-success-bg); color: var(--adm-success-text); }
+:where(.main-wrapper) .adm-status--info { background: var(--adm-info-bg); color: var(--adm-info-text); }
+:where(.main-wrapper) .adm-status--warning { background: var(--adm-warning-bg); color: var(--adm-warning-text); }
+:where(.main-wrapper) .adm-status--error { background: var(--adm-error-bg); color: var(--adm-error-text); }
+:where(.main-wrapper) .adm-status--default { background: var(--adm-fill); color: var(--adm-text-65); }
+
+/* ---- 分页:右下角 ---- */
+:where(.main-wrapper) .el-pagination { padding: 16px 0 0; text-align: right; font-weight: 400; flex-shrink: 0; }
+
+/* ---- 表单 ---- */
+:where(.main-wrapper) .el-form-item__label { font-size: 14px; color: var(--adm-text-65); font-weight: 500; }
+
+/* ---- 详情页 / 编辑页分区 ---- */
+:where(.main-wrapper) .detail-section { margin-bottom: 24px; }
+:where(.main-wrapper) .detail-section-title {
+  font-size: 15px; font-weight: 500; color: var(--adm-text-88);
+  margin-bottom: 12px; padding-bottom: 8px;
+  border-bottom: 1px solid var(--adm-border-split);
 }
+:where(.main-wrapper) .edit-form-footer { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--adm-border-split); text-align: right; }
+
+/* ---- 空状态 ---- */
+:where(.main-wrapper) .empty-state {
+  display: flex; flex-direction: column; align-items: center; justify-content: center;
+  padding: 48px 0; color: var(--adm-text-25);
+}
+:where(.main-wrapper) .empty-state i { font-size: 48px; margin-bottom: 8px; }
+
+/* ---- 搜索结果计数 ---- */
+:where(.main-wrapper) .search-result-count { font-size: 12px; color: var(--adm-text-45); margin-bottom: 8px; }
+
+/* ---- 通用辅助 ---- */
+:where(.main-wrapper) .text-muted { color: var(--adm-text-45); }
+:where(.main-wrapper) .adm-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
+:where(.main-wrapper) > .header { flex-shrink: 0; }
+
+/* ============================================================
+   4. 存量页面类兼容(.admin-page / .page-container)
+   这些类是 .main-wrapper 的后代,第 3 节规则已覆盖;此处仅保留其
+   布局语义(页面内边距),并把卡片内距统一到规范 24px。
+   ============================================================ */
+.admin-page { padding: var(--spacing-base); background: var(--color-bg-page); min-height: calc(100vh - 60px); }
+
+.page-container { flex: 1; display: flex; flex-direction: column; min-height: 0; padding: 20px; }
+.page-container > .el-card { flex: 1; display: flex; flex-direction: column; min-height: 0; }
+.page-container > .el-card .el-card__body { flex: 1; display: flex; flex-direction: column; min-height: 0; padding: 24px; }
+.page-container > .el-card .el-card__body > .el-form:first-child { flex-shrink: 0; }
+.page-container > .el-card .el-card__body > .el-pagination { flex-shrink: 0; margin-top: auto; padding-top: 20px; text-align: right; }
+
+/* ============================================================
+   5. 工具类(保留存量用法)
+   ============================================================ */
+.text-muted { color: var(--adm-text-45); }
+.text-secondary { color: var(--adm-text-65); }
+.text-primary { color: var(--adm-text-88); }
+.text-success { color: var(--adm-success-text); }
+.text-warning { color: var(--adm-warning-text); }
+.text-danger { color: var(--adm-error-text); }
+
+.mt-sm { margin-top: var(--spacing-sm); }
+.mt-base { margin-top: var(--spacing-base); }
+.mt-lg { margin-top: var(--spacing-lg); }
+.mb-sm { margin-bottom: var(--spacing-sm); }
+.mb-base { margin-bottom: var(--spacing-base); }
+.mb-lg { margin-bottom: var(--spacing-lg); }
+
+.flex { display: flex; }
+.flex-wrap { flex-wrap: wrap; }
+.items-center { align-items: center; }
+.justify-between { justify-content: space-between; }
+.gap-sm { gap: var(--spacing-sm); }
+.gap-base { gap: var(--spacing-base); }
+
+.pull-right { float: right; }
+.clearfix::after { content: ""; display: table; clear: both; }
+
+/* 状态标签栏(ArticleManage 等) */
+.status-tabs { display: flex; align-items: center; flex-wrap: wrap; gap: var(--spacing-xs); margin-bottom: var(--spacing-md); }
+.status-tabs .el-button + .el-button { margin-left: 0; }
+
+/* 操作按钮区 */
+.table-actions { display: flex; align-items: center; justify-content: space-between; margin-top: var(--spacing-base); }
+.table-actions-right { text-align: right; margin-top: var(--spacing-base); }
+
+/* 分页容器 */
+.pagination-wrap { display: flex; justify-content: flex-end; margin-top: var(--spacing-base); padding: var(--spacing-sm) 0; }
+
+/* 编辑表单宽度约束 */
+.edit-form { max-width: 800px; }
+.edit-form .el-form-item__label { font-weight: 500; }
+
+/* 富文本容器 */
+.rich-editor { border: 1px solid var(--adm-border-base); border-radius: var(--adm-radius-md); position: relative; overflow: visible; }
+.rich-editor /deep/ .w-e-toolbar { flex-wrap: wrap; padding: var(--spacing-sm); }
+.rich-editor /deep/ .w-e-text-container { min-height: 300px; }
+.rich-editor /deep/ .w-e-panel-container { z-index: 5000; }
+
+/* 表格滚动容器(存量用法,保留) */
+.table-scroll-wrap { overflow: auto; max-height: calc(100vh - 280px); border-radius: var(--adm-radius-card); }
+.table-scroll-wrap-sm { overflow: auto; max-height: calc(100vh - 300px); border-radius: var(--adm-radius-card); }
+.table-scroll-wrap-lg { overflow: auto; max-height: calc(100vh - 340px); border-radius: var(--adm-radius-card); }
+
+/* 对话框表单 */
+.admin-form-dialog .el-form-item { margin-bottom: var(--spacing-base); }
+.admin-form-dialog .el-form-item__label { color: var(--adm-text-65); }
 
-/* 表格:表头浅底、行高舒适、单元格留白 */
-.admin-page .el-table { font-size: 14px; border-radius: var(--adm-radius-card); }
-.admin-page .el-table th { background-color: var(--adm-bg-header) !important; color: var(--adm-text-88); font-weight: 500; height: 46px; padding: 0; }
-.admin-page .el-table td { padding: 12px 0; }
-.admin-page .el-table .cell { padding-left: 16px; padding-right: 16px; }
-.admin-page .el-table--border, .admin-page .el-table--group { border-color: var(--adm-border-split); }
-.admin-page .el-table td, .admin-page .el-table th.is-leaf { border-color: var(--adm-border-split); }
-
-/* 数值列:右对齐 + 等宽数字 */
-.admin-page .el-table .cell-num,
-.admin-page .el-table .is-right .cell { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
-
-/* 状态标签:浅底 + 深字 + 同色描边(语义化,替代纯文字着色) */
-.admin-page .el-tag { border-radius: var(--adm-radius-sm); }
-.admin-page .el-tag--success { background: #F6FFED; border-color: #B7EB8F; color: var(--adm-success-text); }
-.admin-page .el-tag--warning { background: #FFFBE6; border-color: #FFE58F; color: var(--adm-warning-text); }
-.admin-page .el-tag--info { background: #E6F4FF; border-color: #91CAFF; color: var(--adm-info-text); }
-.admin-page .el-tag--danger { background: #FFF1F0; border-color: #FFCCC7; color: var(--adm-error-text); }
-
-/* 标准化状态徽标(自定义 span,替代纯文字着色) */
-.admin-page .adm-status { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; font-size: 12px; line-height: 20px; border-radius: var(--adm-radius-sm); white-space: nowrap; }
-.admin-page .adm-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
-.admin-page .adm-status--success { background: #F6FFED; color: var(--adm-success-text); }
-.admin-page .adm-status--info { background: #E6F4FF; color: var(--adm-info-text); }
-.admin-page .adm-status--warning { background: #FFFBE6; color: var(--adm-warning-text); }
-.admin-page .adm-status--error { background: #FFF1F0; color: var(--adm-error-text); }
-.admin-page .adm-status--default { background: var(--adm-fill); color: var(--adm-text-65); }
-
-/* 对话框 */
-.admin-page .el-dialog__header { padding: 16px 24px; border-bottom: 1px solid var(--adm-border-split); }
-.admin-page .el-dialog__title { font-size: 16px; font-weight: 500; color: var(--adm-text-88); }
-.admin-page .el-dialog__body { padding: 24px; }
-.admin-page .el-dialog__footer { padding: 12px 24px; border-top: 1px solid var(--adm-border-split); }
-
-/* 分页 */
-.admin-page .el-pagination { padding: 16px 0 0; text-align: right; font-weight: normal; flex-shrink: 0; }
-
-/* 表单 */
-.admin-page .el-form-item__label { font-size: 14px; color: var(--adm-text-65); font-weight: 500; }
-.admin-page .el-divider__text { font-size: 14px; font-weight: 500; color: var(--adm-text-88); }
-.admin-page .el-divider--horizontal { margin: 24px 0; }
-
-/* 表格容器:弹性撑满(替代 JS 计算 max-height),仅表格自身滚动 */
+/* ============================================================
+   6. 弹性填充链(★ 功能逻辑,逐字保留,禁止改动 ★)
+   作用:列表页仅表格内部滚动,页面不产生多余滚动条。
+   ============================================================ */
 .admin-page .adm-table-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; }
 .admin-page .adm-table-wrap > .el-table { flex: 1; min-height: 0; height: 100%; }
 
@@ -582,7 +414,6 @@
 .admin-page .adm-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
 .admin-page > .header { flex-shrink: 0; }
 
-/* ==== 弹性填充链增强(自动填充,兼容任意层级卡片/标签页嵌套) ==== */
 /* 让 admin-page / page-container 内任意层级的卡片成为弹性填充容器(覆盖 Layout.vue 的直接子级选择器) */
 .admin-page .el-card.el-card,
 .page-container .el-card.el-card {
@@ -668,13 +499,11 @@
   min-height: 0;
 }
 
-/* ==== 非表格页(表单/编辑/详情)滚动修复 ==== */
-/* 背景:Layout.vue 与上文 blanket 规则无差别强制 .admin-page / .el-card / .el-card__body
-   进入 flex:1 + min-height:0 撑满链(本意是让列表页「只有表格内部滚动」)。
-   副作用:无表格的长表单页(如商品编辑 ProductEdit)内容被容器裁切且没有滚动条,
-   下半部分无法编辑。
-   修复:仅当页面内存在 .adm-table-wrap 时才进入撑满链;否则页面与卡片恢复自然高度,
-   由外层 .el-main(overflow-y:auto)统一整体滚动。 */
+/* ============================================================
+   7. 非表格页滚动修复(★ 功能逻辑,逐字保留,禁止改动 ★)
+   背景:无表格的长表单页曾被 flex 撑满链裁切且无滚动条,下半部分无法编辑。
+   修复:仅当页面内存在 .adm-table-wrap 时才进入撑满链;否则恢复自然高度。
+   ============================================================ */
 .admin-page:not(:has(.adm-table-wrap)),
 .page-container:not(:has(.adm-table-wrap)) {
   flex: 0 0 auto;