From 1a1b1ce66beb8a0f495830022445ea045400593d Mon Sep 17 00:00:00 2001 From: zhaichao Date: Mon, 24 Aug 2026 14:48:08 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/assets/styles/design-system.scss | 326 ++++++++++++++++++ frontend/src/assets/styles/design-tokens.scss | 147 ++++++++ frontend/src/assets/styles/index.scss | 6 + .../views/classHour/jointTraining/index.vue | 20 -- frontend/src/views/classHour/plan/index.vue | 24 -- .../src/views/classHour/subsidy/index.vue | 19 - .../src/views/schedule/planImport/index.vue | 10 - frontend/src/views/student/score/index.vue | 18 - .../src/views/student/shiftTeam/index.vue | 23 -- .../src/views/student/statusChange/index.vue | 18 - .../src/views/student/studentInfo/index.vue | 23 -- .../views/student/warningCondition/index.vue | 17 - .../src/views/student/warningResult/index.vue | 25 -- .../views/subjectMajor/discipline/index.vue | 14 - .../src/views/subjectMajor/major/index.vue | 17 - .../views/teachBusiness/syllabus/index.vue | 10 - .../teachBusiness/teachingTask/index.vue | 15 - .../teachBusiness/trainingPlan/index.vue | 10 - .../src/views/teachBusiness/venue/index.vue | 10 - .../src/views/teachOffice/elective/index.vue | 32 -- .../views/teachOffice/shiftSemester/index.vue | 9 - .../src/views/teachOffice/subject/index.vue | 6 - .../src/views/teachOffice/taskPlan/index.vue | 12 - .../src/views/teachOffice/teacher/index.vue | 44 --- frontend/src/views/textbook/index.vue | 19 - 25 files changed, 479 insertions(+), 395 deletions(-) create mode 100644 frontend/src/assets/styles/design-system.scss create mode 100644 frontend/src/assets/styles/design-tokens.scss diff --git a/frontend/src/assets/styles/design-system.scss b/frontend/src/assets/styles/design-system.scss new file mode 100644 index 000000000..5aa758136 --- /dev/null +++ b/frontend/src/assets/styles/design-system.scss @@ -0,0 +1,326 @@ +// ============================================================ +// 全局设计系统(Design System) +// 收敛 Element 基元 + 统一页面结构基线 + 工具类 +// 依赖 design-tokens.scss 中的 $token 与 --token +// +// 用途:业务页面优先复用以下区块/工具类,删除各自 scoped 中重复定义, +// 实现全站风格、排版、按钮统一。仅需在 index.scss 末尾引入一次。 +// ============================================================ + +// ------------------------- 1. Element 基元收敛 ------------------------- + +// 1.1 按钮——统一主色、尺寸、圆角、hover/active 反馈与外光晕 +.el-button { + --el-button-primary-text: #fff; + border-radius: var(--token-radius-md, 4px); + transition: background-color 0.2s, border-color 0.2s, color 0.2s, + box-shadow 0.2s; + + // 主按钮统一教育绿 + &.el-button--primary { + background-color: var(--token-color-primary, #00875a); + border-color: var(--token-color-primary, #00875a); + color: #fff; + + &:hover, + &:focus { + background-color: var(--token-color-primary-hover, #0b9e6c); + border-color: var(--token-color-primary-hover, #0b9e6c); + } + + &:active { + background-color: var(--token-color-primary-active, #006b47); + border-color: var(--token-color-primary-active, #006b47); + } + + &.is-plain { + color: var(--token-color-primary, #00875a); + background-color: var(--token-color-primary-lighter, #f0f9f4); + border-color: var(--token-color-primary, #00875a); + + &:hover, + &:focus { + color: #fff; + background-color: var(--token-color-primary, #00875a); + } + } + + &.is-disabled { + background-color: #a0cfb8; + border-color: #a0cfb8; + color: rgba(255, 255, 255, 0.9); + } + } + + // 危险按钮 + &.el-button--danger { + background-color: var(--token-color-danger, #ff4949); + border-color: var(--token-color-danger, #ff4949); + &:hover, + &:focus { + background-color: #ff6b6b; + border-color: #ff6b6b; + } + } + + // 聚焦外光晕(键盘可访问性),除 text 按钮外 + &:focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--token-color-primary-ring, rgba(0, 135, 90, 0.35)); + } + + // 表格内文字操作按钮保持紧凑 + &.el-button--text { + font-size: var(--token-font-size-sm, 13px); + padding: 0 4px; + + &.danger-link { + color: var(--token-color-danger, #ff4949); + } + } +} + +// 1.2 卡片——统一无内阴影、body 内边距 +.el-card { + border-color: var(--token-color-border-lighter, #ebeef5); + &.is-always-shadow { + box-shadow: var(--token-shadow-card, 0 1px 4px rgba(0, 0, 0, 0.06)); + } +} + +// 1.3 表格——统一表头背景与居中、行 hover、斑马 +.el-table { + th.el-table__cell { + background-color: var(--token-color-fill, #f5f7fa); + color: var(--token-color-text-main, #303133); + font-weight: 600; + } + + th.el-table__cell > .cell { + text-align: center; + } + + .el-table__body tr.hover-row > td.el-table__cell, + .el-table__body tr:hover > td.el-table__cell { + background-color: var(--token-color-primary-lighter, #f0f9f4); + } + + &.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell { + background-color: var(--token-color-fill-light, #f0f2f5); + } +} + +// 1.4 表单——统一 label 对齐与错误/必填语义 +.el-form { + .el-form-item { + .el-form-item__label { + color: var(--token-color-text-regular, #606266); + } + .el-form-item__error { + color: var(--token-color-danger, #ff4949); + } + } +} + +// 1.5 分页——统一置于底部右侧 +.el-pagination { + color: var(--token-color-text-regular, #606266); + &.pagination-right { + justify-content: flex-end; + } +} + +// ------------------------- 2. 页面结构基线 ------------------------- +// 说明:这些类名与历史业务页 scoped 中的自定义类名保持同名, +// 统一后应删除页面内的 scoped 重复定义,避免覆盖。 + +// 2.1 基础页面容器 +.page-shell { + padding: var(--token-space-xl, 20px); + box-sizing: border-box; +} + +// 2.2 页面标题 +.page-title { + font-size: var(--token-font-size-xl, 20px); + font-weight: 600; + color: var(--token-color-text-main, #303133); + margin-bottom: var(--token-space-lg, 16px); +} + +// 2.3 区块/卡片容器——统一白底 + 圆角 + 轻阴影 + 内边距 +.section-card { + background: #fff; + border-radius: var(--token-radius-xl, 8px); + box-shadow: var(--token-shadow-light, 0 1px 4px rgba(0, 0, 0, 0.08)); + padding: var(--token-space-lg, 16px) var(--token-space-xl, 20px); + margin-bottom: var(--token-space-lg, 16px); + + &:last-child { + margin-bottom: 0; + } +} + +// 兼容:el-card 封装的查询/表格卡 +.search-card.el-card, +.table-card.el-card { + border-radius: var(--token-radius-xl, 8px); +} +.search-card, +.table-card { + margin-bottom: var(--token-space-lg, 16px); +} + +.search-card { + .search-form { + .el-form-item { + margin-bottom: 0; + } + } + + // 底部操作区(右侧对齐) + .search-actions, + .search-footer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: var(--token-space-md, 12px); + } + + // 仅明确标记为页脚的操作区才带轻分割线(避免误伤页面自有分割结构) + .search-footer { + margin-top: var(--token-space-md, 12px); + padding-top: var(--token-space-md, 12px); + border-top: 1px solid var(--token-color-border-lighter, #ebeef5); + } +} + +.table-card { + .pagination-wrap, + .pagination-wrapper, + .pagination { + display: flex; + justify-content: flex-end; + margin-top: var(--token-space-md, 12px); + } +} + +// 2.4 查询控件——统一纵向排列的表单:标签右对齐 + 控件自适应 +.query-field { + display: flex; + align-items: center; + margin-bottom: var(--token-space-sm, 8px); + min-height: 32px; + + .q-label { + width: 120px; + flex-shrink: 0; + font-size: var(--token-font-size-sm, 13px); + color: var(--token-color-text-main, #303133); + text-align: right; + margin-right: var(--token-space-sm, 8px); + white-space: nowrap; + + &.no-check { + margin-left: var(--token-space-2xl, 24px); + } + } + + .q-control { + flex: 1; + min-width: 0; + } +} + +// 2.5 操作/导出按钮行——统一 flex + 等比间距 + 自动换行 +.action-row, +.export-bar { + display: flex; + flex-wrap: wrap; + gap: var(--token-space-md, 12px); +} + +.action-row + .action-row { + margin-top: var(--token-space-md, 12px); +} + +// 2.6 列表工具栏——左右分组 +.list-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--token-space-md, 12px); + flex-wrap: wrap; + margin-bottom: var(--token-space-lg, 16px); + + .left-group, + .right-group { + display: flex; + align-items: center; + gap: var(--token-space-md, 12px); + flex-wrap: wrap; + } +} + +// 列表标题 +.list-title, +.section-title { + font-size: var(--token-font-size-lg, 16px); + font-weight: 600; + color: var(--token-color-text-main, #303133); + margin-bottom: var(--token-space-md, 12px); +} + +// 2.7 危险文字(删除/停用等) +.text-danger, +.danger-text, +.danger-link { + color: var(--token-color-danger, #ff4949); +} + +// ------------------------- 3. 工具类 ------------------------- +// 字体 +.text-center { text-align: center; } +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-primary { color: var(--token-color-primary, #00875a); } +.text-regular { color: var(--token-color-text-regular, #606266); } +.text-secondary { color: var(--token-color-text-secondary, #909399); } + +.font-sm { font-size: var(--token-font-size-sm, 13px); } +.font-lg { font-size: var(--token-font-size-lg, 16px); } + +// 布局 +.flex { display: flex; align-items: center; } +.flex-center { display: flex; align-items: center; justify-content: center; } +.flex-between { display: flex; align-items: center; justify-content: space-between; } +.flex-end { display: flex; align-items: center; justify-content: flex-end; } +.flex-wrap { flex-wrap: wrap; } +.flex-1 { flex: 1; min-width: 0; } +.w-full { width: 100%; } + +// 间距(上/下) +.mt-0 { margin-top: 0; } +.mt-xs { margin-top: var(--token-space-xs, 4px); } +.mt-sm { margin-top: var(--token-space-sm, 8px); } +.mt-md { margin-top: var(--token-space-md, 12px); } +.mt-lg { margin-top: var(--token-space-lg, 16px); } +.mt-xl { margin-top: var(--token-space-xl, 20px); } +.mb-0 { margin-bottom: 0; } +.mb-xs { margin-bottom: var(--token-space-xs, 4px); } +.mb-sm { margin-bottom: var(--token-space-sm, 8px); } +.mb-md { margin-bottom: var(--token-space-md, 12px); } +.mb-lg { margin-bottom: var(--token-space-lg, 16px); } +.mb-xl { margin-bottom: var(--token-space-xl, 20px); } + +// 圆角/阴影 +.radius-sm { border-radius: var(--token-radius-sm, 2px); } +.radius-md { border-radius: var(--token-radius-md, 4px); } +.radius-lg { border-radius: var(--token-radius-lg, 6px); } +.radius-xl { border-radius: var(--token-radius-xl, 8px); } +.shadow-light { box-shadow: var(--token-shadow-light, 0 1px 4px rgba(0, 0, 0, 0.08)); } + +// 等宽 +.mono { + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; +} \ No newline at end of file diff --git a/frontend/src/assets/styles/design-tokens.scss b/frontend/src/assets/styles/design-tokens.scss new file mode 100644 index 000000000..3349142f9 --- /dev/null +++ b/frontend/src/assets/styles/design-tokens.scss @@ -0,0 +1,147 @@ +// ============================================================ +// 设计 Token 体系(Design Tokens) +// 全站统一颜色 / 字体 / 间距 / 圆角 / 阴影 / 边框 / 层级 / 断点 +// 约定: +// 1. 业务代码禁止散落魔法数字,一律引用下方 $token / --token +// 2. SCSS 变量供样式内使用,:root 自定义属性供内联/动态样式使用 +// ============================================================ +@import './variables.scss'; + +// ------------------------- 色彩 Token ------------------------- +// 主色:教育绿 +$token-color-primary: #00875a; // 主色 +$token-color-primary-hover: #0b9e6c; // hover +$token-color-primary-active: #006b47; // active +$token-color-primary-light: #e6f4ea; // 浅底(标签/选中背景) +$token-color-primary-lighter: #f0f9f4; // 更浅(hover 背景/斑马) +$token-color-primary-ring: rgba(0, 135, 90, 0.35); // 聚焦外光晕 + +// 语义色 +$token-color-danger: #ff4949; // 危险 +$token-color-success: #13ce66; // 成功 +$token-color-warning: #ffba00; // 警告 +$token-color-info: #909399; // 中性信息 + +// 中性灰阶(对齐 Element 三级文字) +$token-color-text-main: #303133; // 主要文字 +$token-color-text-regular: #606266; // 常规文字 +$token-color-text-secondary: #909399; // 次要文字 +$token-color-text-placeholder: #c0c4cc; // 占位 +$token-color-border: #dcdfe6; // 边框 +$token-color-border-light: #e4e7ed; // 浅边框 +$token-color-border-lighter: #ebeef5; // 极浅边框(分割线) +$token-color-fill: #f5f7fa; // 填充/底灰 +$token-color-fill-light: #f0f2f5; // 更浅填充 + +// ------------------------- 字体 Token ------------------------- +$token-font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', + 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; +$token-font-size-xs: 12px; +$token-font-size-sm: 13px; +$token-font-size-base: 14px; +$token-font-size-lg: 16px; +$token-font-size-xl: 20px; + +// ------------------------- 间距 Token ------------------------- +$token-space-xs: 4px; +$token-space-sm: 8px; +$token-space-md: 12px; +$token-space-lg: 16px; +$token-space-xl: 20px; +$token-space-2xl: 24px; + +// ------------------------- 圆角 Token ------------------------- +$token-radius-sm: 2px; +$token-radius-md: 4px; +$token-radius-lg: 6px; +$token-radius-xl: 8px; +$token-radius-circle: 50%; + +// ------------------------- 阴影 Token ------------------------- +$token-shadow-light: 0 1px 4px rgba(0, 0, 0, 0.08); +$token-shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06); +$token-shadow-popup: 0 2px 12px rgba(0, 0, 0, 0.12); + +// ------------------------- 层级 Token ------------------------- +$token-z-index-dropdown: 2000; // 下拉/气泡 +$token-z-index-mask: 2001; // 遮罩 +$token-z-index-modal: 2002; // 弹窗 +$token-z-index-toast: 3000; // 消息/通知 + +// ------------------------- 断点 Token ------------------------- +$token-breakpoint-xs: 480px; +$token-breakpoint-sm: 768px; +$token-breakpoint-md: 992px; +$token-breakpoint-lg: 1200px; +$token-breakpoint-xl: 1920px; + +// ------------------------- 暴露给运行时(CSS 变量) ------------------------- +:root { + --token-color-primary: #{$token-color-primary}; + --token-color-primary-hover: #{$token-color-primary-hover}; + --token-color-primary-active: #{$token-color-primary-active}; + --token-color-primary-light: #{$token-color-primary-light}; + --token-color-primary-lighter: #{$token-color-primary-lighter}; + --token-color-primary-ring: #{$token-color-primary-ring}; + + --token-color-danger: #{$token-color-danger}; + --token-color-success: #{$token-color-success}; + --token-color-warning: #{$token-color-warning}; + --token-color-info: #{$token-color-info}; + + --token-color-text-main: #{$token-color-text-main}; + --token-color-text-regular: #{$token-color-text-regular}; + --token-color-text-secondary: #{$token-color-text-secondary}; + --token-color-text-placeholder: #{$token-color-text-placeholder}; + --token-color-border: #{$token-color-border}; + --token-color-border-light: #{$token-color-border-light}; + --token-color-border-lighter: #{$token-color-border-lighter}; + --token-color-fill: #{$token-color-fill}; + --token-color-fill-light: #{$token-color-fill-light}; + + --token-font-family: #{$token-font-family}; + --token-font-size-xs: #{$token-font-size-xs}; + --token-font-size-sm: #{$token-font-size-sm}; + --token-font-size-base: #{$token-font-size-base}; + --token-font-size-lg: #{$token-font-size-lg}; + --token-font-size-xl: #{$token-font-size-xl}; + + --token-space-xs: #{$token-space-xs}; + --token-space-sm: #{$token-space-sm}; + --token-space-md: #{$token-space-md}; + --token-space-lg: #{$token-space-lg}; + --token-space-xl: #{$token-space-xl}; + --token-space-2xl: #{$token-space-2xl}; + + --token-radius-sm: #{$token-radius-sm}; + --token-radius-md: #{$token-radius-md}; + --token-radius-lg: #{$token-radius-lg}; + --token-radius-xl: #{$token-radius-xl}; + + --token-shadow-light: #{$token-shadow-light}; + --token-shadow-card: #{$token-shadow-card}; + --token-shadow-popup: #{$token-shadow-popup}; + + --token-z-index-dropdown: #{$token-z-index-dropdown}; + --token-z-index-mask: #{$token-z-index-mask}; + --token-z-index-modal: #{$token-z-index-modal}; + --token-z-index-toast: #{$token-z-index-toast}; +} + +// ------------------------- 暴露给 JS 模块 ------------------------- +:export { + colorPrimary: $token-color-primary; + colorPrimaryHover: $token-color-primary-hover; + colorPrimaryActive: $token-color-primary-active; + colorPrimaryLight: $token-color-primary-light; + colorPrimaryLighter: $token-color-primary-lighter; + colorDanger: $token-color-danger; + colorSuccess: $token-color-success; + colorWarning: $token-color-warning; + fontFamily: $token-font-family; + breakpointXs: $token-breakpoint-xs; + breakpointSm: $token-breakpoint-sm; + breakpointMd: $token-breakpoint-md; + breakpointLg: $token-breakpoint-lg; + breakpointXl: $token-breakpoint-xl; +} \ No newline at end of file diff --git a/frontend/src/assets/styles/index.scss b/frontend/src/assets/styles/index.scss index 27d028ab4..f1344433b 100644 --- a/frontend/src/assets/styles/index.scss +++ b/frontend/src/assets/styles/index.scss @@ -245,3 +245,9 @@ aside { margin-bottom: 10px; } } + +/* ================= 全局设计系统 ================= + 居于最末加载,收敛 Element 基元与页面结构基线 + (Token 体系在 design-tokens.scss,页面统一类名在 design-system.scss) */ +@import './design-tokens.scss'; +@import './design-system.scss'; diff --git a/frontend/src/views/classHour/jointTraining/index.vue b/frontend/src/views/classHour/jointTraining/index.vue index 40e74a7f8..9515adbf5 100644 --- a/frontend/src/views/classHour/jointTraining/index.vue +++ b/frontend/src/views/classHour/jointTraining/index.vue @@ -455,13 +455,7 @@ export default { // ==================== 1. 查询条件区域 ==================== .search-card { - margin-bottom: 16px; - .search-form { - .w-full { - width: 100%; - } - .search-actions { display: flex; justify-content: flex-end; @@ -473,8 +467,6 @@ export default { // ==================== 2. 操作区域 ==================== .upload-card { - margin-bottom: 16px; - .upload-row { display: flex; align-items: center; @@ -501,24 +493,12 @@ export default { // ==================== 3. 数据表格 ==================== .table-card { - margin-bottom: 16px; - .table-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 12px; } - - .pagination { - display: flex; - justify-content: flex-end; - margin-top: 12px; - } - - .text-danger { - color: #f56c6c; - } } // ==================== 4. 新增/修改表单 ==================== diff --git a/frontend/src/views/classHour/plan/index.vue b/frontend/src/views/classHour/plan/index.vue index 626e0e2e0..c366fda47 100644 --- a/frontend/src/views/classHour/plan/index.vue +++ b/frontend/src/views/classHour/plan/index.vue @@ -641,30 +641,6 @@ export default { margin-bottom: 12px; } - // ==================== 1. 查询区域 ==================== - .search-card { - margin-bottom: 16px; - - .search-form { - .el-form-item { - margin-bottom: 0; - } - } - } - - // ==================== 2. 数据表格 ==================== - .table-card { - .pagination { - display: flex; - justify-content: flex-end; - margin-top: 12px; - } - - .text-danger { - color: #f56c6c; - } - } - // ==================== 3. 新增/修改表单 ==================== .add-form { max-height: 62vh; diff --git a/frontend/src/views/classHour/subsidy/index.vue b/frontend/src/views/classHour/subsidy/index.vue index b4a16d849..6ce1e7af7 100644 --- a/frontend/src/views/classHour/subsidy/index.vue +++ b/frontend/src/views/classHour/subsidy/index.vue @@ -251,22 +251,9 @@ export default { diff --git a/frontend/src/views/schedule/planImport/index.vue b/frontend/src/views/schedule/planImport/index.vue index d86c285a6..401ddc265 100644 --- a/frontend/src/views/schedule/planImport/index.vue +++ b/frontend/src/views/schedule/planImport/index.vue @@ -327,10 +327,6 @@ export default { diff --git a/frontend/src/views/student/score/index.vue b/frontend/src/views/student/score/index.vue index fa2acd7ea..bf1804de8 100644 --- a/frontend/src/views/student/score/index.vue +++ b/frontend/src/views/student/score/index.vue @@ -218,18 +218,6 @@ export default { \ No newline at end of file diff --git a/frontend/src/views/student/shiftTeam/index.vue b/frontend/src/views/student/shiftTeam/index.vue index 9a93fd308..08069f415 100644 --- a/frontend/src/views/student/shiftTeam/index.vue +++ b/frontend/src/views/student/shiftTeam/index.vue @@ -627,31 +627,14 @@ export default { diff --git a/frontend/src/views/subjectMajor/discipline/index.vue b/frontend/src/views/subjectMajor/discipline/index.vue index 14213bc6b..a77d6a0a1 100644 --- a/frontend/src/views/subjectMajor/discipline/index.vue +++ b/frontend/src/views/subjectMajor/discipline/index.vue @@ -494,15 +494,7 @@ export default { diff --git a/frontend/src/views/textbook/index.vue b/frontend/src/views/textbook/index.vue index b5115772b..1826f1804 100644 --- a/frontend/src/views/textbook/index.vue +++ b/frontend/src/views/textbook/index.vue @@ -1468,10 +1468,6 @@ export default { diff --git a/frontend/src/views/index.vue b/frontend/src/views/index.vue index 53cb8c8c3..6b60de5b0 100644 --- a/frontend/src/views/index.vue +++ b/frontend/src/views/index.vue @@ -1,8 +1,89 @@