nex_math/frontend/src/styles.css

2071 lines
39 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

:root {
--brand: #5b5ce2;
--sider-top: #20224a;
--sider-bottom: #15162e;
--content-bg: #f5f6fb;
--text-secondary: #6f7080;
--border-soft: #ececf5;
/* 中性面:与 theme.ts 的 palette 一一对应 */
--surface: #ffffff;
--surface-2: #fafaff;
--surface-hover: #f6f6fd;
--tab-rail: #f2f3fa;
--text-strong: #1f2033;
--text-muted: #b9bacb;
--header-bg: rgba(255, 255, 255, 0.88);
--scrollbar: #c9cae6;
--track: #f1f2f6;
--track-raised: #ffffff;
--brand-soft: rgba(91, 92, 226, 0.08);
--success-soft: #f0fdf4;
--success-soft-border: #bbf7d0;
--danger-soft: #fef2f2;
--danger-soft-border: #fecaca;
--warning-soft: #fffbeb;
/* 内容页布局尺度:顶栏高度、内容列左右留白、内容列最大宽度 */
--layout-header-h: 56px;
--content-pad: 14px;
/* 底部留白单独一个变量:.app-content 的下边距比四周略大一点,
阅读器高度等「满屏高度」计算时统一引用它,避免出现几像素的竖直滚动条。 */
--content-pad-end: calc(var(--content-pad) + 4px);
--content-max: 1680px;
}
/* 深色模式:只换中性色,品牌色与语义色保持不变 */
:root[data-theme="dark"] {
--content-bg: #12141f;
--text-secondary: #9b9db4;
--border-soft: #2a2c40;
--surface: #1a1c2a;
--surface-2: #1f2132;
--surface-hover: #22243a;
--tab-rail: #16182a;
--text-strong: #e9eaf3;
--text-muted: #5d5f78;
--header-bg: rgba(26, 28, 42, 0.88);
--scrollbar: #3a3c55;
--track: #24263c;
--track-raised: #3a3e5c;
--brand-soft: rgba(91, 92, 226, 0.22);
--success-soft: rgba(22, 163, 74, 0.14);
--success-soft-border: rgba(22, 163, 74, 0.4);
--danger-soft: rgba(220, 38, 38, 0.14);
--danger-soft-border: rgba(220, 38, 38, 0.4);
--warning-soft: rgba(212, 136, 6, 0.14);
}
body {
margin: 0;
background: var(--content-bg);
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Microsoft YaHei", sans-serif;
-webkit-font-smoothing: antialiased;
}
#root {
min-height: 100vh;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: var(--scrollbar);
border-radius: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
/* ---------------- 侧边栏外壳 ---------------- */
.app-sider {
transition: all 0.2s ease;
}
.sider {
display: flex;
flex-direction: column;
height: 100%;
background: linear-gradient(180deg, var(--sider-top) 0%, var(--sider-bottom) 100%);
}
.sider-brand {
position: relative;
display: flex;
align-items: center;
gap: 8px;
height: 56px;
padding: 0 10px 0 12px;
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
color: #fff;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
transition: padding 0.2s ease;
}
/* 收拢时只留 logo,并且要保证 logo 居中 */
.sider--collapsed .sider-brand {
justify-content: center;
padding: 0;
}
.sider-brand-mark {
display: grid;
place-items: center;
width: 30px;
height: 30px;
flex-shrink: 0;
border-radius: 9px;
background: var(--brand);
color: #fff;
font-size: 17px;
font-weight: 800;
box-shadow: 0 4px 12px rgba(91, 92, 226, 0.45);
transition:
transform 0.18s ease,
opacity 0.16s ease;
}
.sider-brand-text {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14.5px;
font-weight: 700;
}
/* 菜单头部的收拢按钮:与平台名同一行,靠右 */
.sider-brand-collapse {
margin-inline-start: auto;
flex-shrink: 0;
width: 30px;
height: 30px;
padding: 0;
color: rgba(255, 255, 255, 0.66);
}
/* 收拢态:logo 与展开按钮叠在同一位置,hover / 聚焦时原地换人 */
.sider-brand-logo {
position: relative;
display: grid;
place-items: center;
width: 30px;
height: 30px;
}
.sider-brand-toggle {
display: grid;
place-items: center;
width: 30px;
height: 30px;
padding: 0;
flex-shrink: 0;
}
.sider-brand-logo .sider-brand-toggle {
position: absolute;
inset: 0;
opacity: 0;
visibility: hidden;
}
.sider-brand-logo:hover .sider-brand-mark,
.sider-brand-logo:focus-within .sider-brand-mark {
opacity: 0;
visibility: hidden;
}
.sider-brand-logo:hover .sider-brand-toggle,
.sider-brand-logo:focus-within .sider-brand-toggle {
opacity: 1;
visibility: visible;
}
.sider-brand .sider-brand-toggle:not(:disabled):hover {
color: #fff;
background: rgba(255, 255, 255, 0.14);
}
.sider-scroll {
flex: 1;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
padding: 10px 0 12px;
}
.sider-scroll::-webkit-scrollbar {
width: 6px;
}
.sider-scroll::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.16);
border-radius: 6px;
}
.sider-menu.ant-menu-dark .ant-menu-item-group-title,
.sider-scroll .ant-menu-dark .ant-menu-item-group-title {
padding-inline: 22px;
letter-spacing: 0.6px;
}
/* 收拢态下的分组分隔线与弹出子菜单 */
.sider-scroll .ant-menu-dark .ant-menu-item-divider {
margin-inline: 16px;
border-color: rgba(255, 255, 255, 0.12);
}
.sider-footer {
flex-shrink: 0;
padding: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.sider-user {
display: flex;
align-items: center;
gap: 10px;
padding: 7px 8px;
border-radius: 10px;
cursor: pointer;
background: rgba(255, 255, 255, 0.07);
transition: background 0.2s;
}
.sider-user:hover {
background: rgba(255, 255, 255, 0.14);
}
.sider-user--collapsed {
justify-content: center;
padding: 6px 0;
}
.sider-user-name {
color: #fff;
font-size: 13px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sider-user-role {
color: rgba(255, 255, 255, 0.5);
font-size: 11px;
line-height: 1.4;
}
/* 移动端抽屉中的侧栏:去掉 antd 默认留白 */
.app-sider-drawer .ant-drawer-body {
background: linear-gradient(180deg, var(--sider-top) 0%, var(--sider-bottom) 100%);
}
/* ---------------- 顶栏(移动端在此唤出菜单抽屉) ---------------- */
.app-header {
display: flex;
align-items: center;
gap: 12px;
position: sticky;
top: 0;
z-index: 20;
/* 与 .app-content-inner 同一条基线:内容列被 max-width 收窄时,
顶栏控件同步内收(推导:内容列左偏移 = pad + (100% - 2*pad - 上限)/2
= (100% - 上限)/2,未被夹住时就是 pad,故取两者较大值)。 */
padding-inline: max(
var(--content-pad),
calc((100% - var(--content-max)) / 2)
) !important;
background: var(--header-bg);
border-bottom: 1px solid var(--border-soft);
backdrop-filter: saturate(180%) blur(8px);
}
/* 移动端的菜单入口:带描边的小方块,避免被当成正文里的返回按钮 */
.header-toggle {
flex-shrink: 0;
display: grid;
place-items: center;
width: 38px;
height: 32px;
border: 1px solid var(--border-soft);
border-radius: 10px;
background: var(--surface-2);
color: var(--text-secondary);
}
.header-toggle:hover {
color: var(--brand);
border-color: rgba(91, 92, 226, 0.35);
background: var(--surface-hover);
}
/* 状态切换时图标轻微横向位移,强化「推走 / 拉回」的方向感 */
.header-toggle-icon {
display: inline-grid;
place-items: center;
animation: header-toggle-in 0.2s ease;
}
.header-toggle-glyph {
display: block;
}
@keyframes header-toggle-in {
from {
transform: translateX(-3px);
opacity: 0.35;
}
to {
transform: none;
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.header-toggle-icon {
animation: none;
}
}
/* 平台名只在侧栏文字被收拢掉时出现,避免与侧栏品牌行重复 */
.app-header-brand {
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 14px;
font-weight: 700;
color: var(--text-strong);
}
.header-divider {
flex-shrink: 0;
width: 1px;
height: 22px;
margin-inline: -3px;
background: var(--border-soft);
}
/* 功能页面 header:与下方模块面板同款面板(同底色、同描边、同圆角),
标题 + 描述在左,该功能的主操作按钮在右 */
.page-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px 16px;
min-height: 54px;
margin-bottom: 16px;
padding: 10px 20px;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 12px;
}
.page-header-heading {
min-width: 0;
flex: 1;
}
.page-header .page-header-title {
margin: 0;
font-size: 15px;
font-weight: 600;
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.page-header .page-header-desc {
display: block;
margin-top: 1px;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.page-header-dot {
color: var(--text-muted);
}
/* 功能页头右侧的主操作:由 HeaderActions portal 进来,空的时候不占位 */
.page-header-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.page-header-actions:empty {
display: none;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
margin-inline-start: auto;
flex-shrink: 0;
}
.header-control {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
min-width: 38px;
height: 38px;
padding-inline: 8px;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
color: var(--text-secondary);
}
/* 明暗模式:圆角方块 + 轨道底色,图标本身表示当前可执行的动作 */
.mode-toggle {
width: 38px;
font-size: 18px;
color: var(--text-strong);
background: var(--track);
}
.mode-toggle:hover {
color: var(--brand);
background: var(--surface-hover);
}
/* 语言切换:分段控件,选中项抬起成同圆角的小方块(与明暗按钮同高) */
.header-actions .lang-switch.ant-segmented {
padding: 3px;
border-radius: 12px;
background: var(--track);
}
.header-actions .lang-switch .ant-segmented-item {
border-radius: 9px;
color: var(--text-secondary);
transition: color 0.18s ease;
}
.header-actions .lang-switch .ant-segmented-item-label {
min-width: 36px;
height: 32px;
padding-inline: 10px;
font-size: 14px;
font-weight: 600;
line-height: 32px;
}
.header-actions .lang-switch .ant-segmented-item:hover:not(.ant-segmented-item-selected) {
color: var(--text-strong);
}
.header-actions .lang-switch .ant-segmented-item-selected,
.header-actions .lang-switch .ant-segmented-thumb {
background: var(--track-raised);
box-shadow: 0 1px 2px rgba(15, 17, 45, 0.14);
}
.header-actions .lang-switch .ant-segmented-item-selected {
color: var(--text-strong);
}
/* 内容页边距:留白随视口变宽略微加大,但宽度优先给内容,
避免高分辨屏上「中间一条窄柱、两侧大片空白」。 */
@media (min-width: 900px) {
:root {
--content-pad: 20px;
}
}
@media (min-width: 1600px) {
:root {
--content-pad: 24px;
}
}
@media (min-width: 2200px) {
:root {
--content-pad: 28px;
}
}
.app-content {
padding: var(--content-pad);
padding-block-end: var(--content-pad-end);
}
.app-content-inner {
max-width: min(100%, var(--content-max));
margin: 0 auto;
}
/* 页面切换淡入 */
.view-fade {
animation: view-fade 0.24s ease both;
}
@keyframes view-fade {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: none;
}
}
/* ---------------- 登录页 ---------------- */
.login-bg {
min-height: 100vh;
display: grid;
place-items: center;
padding: 16px;
background: linear-gradient(135deg, #5b5ce2 0%, #312e81 60%, #1e1b4b 100%);
}
/* 登录页右上角的语言 / 明暗切换:渐变背景上用浅色控件 */
.login-bg > .header-actions {
position: fixed;
inset-block-start: 18px;
inset-inline-end: 20px;
z-index: 2;
}
.login-bg .mode-toggle,
.login-bg .lang-switch.ant-segmented {
--track: rgba(255, 255, 255, 0.18);
--track-raised: #ffffff;
color: rgba(255, 255, 255, 0.9);
}
.login-bg .mode-toggle {
color: #ffffff;
}
.login-bg .mode-toggle:hover {
--track: rgba(255, 255, 255, 0.3);
color: #ffffff;
}
.login-bg .lang-switch .ant-segmented-item {
color: rgba(255, 255, 255, 0.72);
}
.login-bg .lang-switch .ant-segmented-item:hover:not(.ant-segmented-item-selected) {
color: #ffffff;
}
.login-bg .lang-switch .ant-segmented-item-selected,
.login-bg .lang-switch .ant-segmented-thumb {
background: #ffffff;
color: #312e81;
}
.login-card {
width: 400px;
max-width: 100%;
border-radius: 16px;
box-shadow: 0 24px 64px rgba(15, 16, 46, 0.35);
}
.login-brand-mark {
display: inline-grid;
width: 52px;
height: 52px;
border-radius: 15px;
font-size: 27px;
}
/* ---------------- 通用页面块 ---------------- */
.page-stack {
display: flex;
flex-direction: column;
gap: 16px;
}
.panel-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin-bottom: 16px;
}
.section-title {
display: flex;
align-items: center;
gap: 6px;
margin: 0 0 8px;
font-size: 14px;
font-weight: 600;
color: var(--text-strong);
}
/* 指标卡:可选图标 + 数值 */
.metric-card .ant-statistic-title {
margin-bottom: 6px;
}
.metric-icon {
display: grid;
place-items: center;
width: 42px;
height: 42px;
flex-shrink: 0;
border-radius: 12px;
background: rgba(91, 92, 226, 0.1);
color: var(--brand);
font-size: 19px;
}
.metric-icon--success {
background: rgba(22, 163, 74, 0.1);
color: #16a34a;
}
.metric-icon--warning {
background: rgba(212, 136, 6, 0.12);
color: #d48806;
}
/* 掌握度概览:一条分布条看完四档,段宽按知识点数量占比;图例可点进知识地图 */
.mastery-distribution {
flex: 1;
min-width: 240px;
}
.mastery-band {
display: flex;
height: 10px;
overflow: hidden;
border-radius: 999px;
background: var(--surface-2);
}
/* 轻量列表:抽屉章节 / 结果回看 / 推荐步骤统一节奏 */
.data-rows {
display: flex;
flex-direction: column;
}
.data-row {
padding: 12px 2px;
border-bottom: 1px solid var(--border-soft);
}
.data-row:last-child {
border-bottom: none;
padding-bottom: 2px;
}
.data-row--clickable {
cursor: pointer;
transition: background 0.2s;
}
.data-row--clickable:hover {
background: var(--surface-2);
}
/* 加载态:整页 / 局部两种高度 */
.async-state {
padding: 48px 0;
text-align: center;
}
.async-state--compact {
padding: 24px 0;
}
.async-skeleton {
padding: 8px 2px 12px;
}
/* 表格里的纯文本次要信息 */
.cell-sub {
font-size: 12px;
color: var(--text-secondary);
}
/* 次要说明常是整句话,限制一行,否则整列会被撑得比屏幕还宽 */
.ant-table-cell .cell-sub {
max-width: 360px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* 选项字母前缀,错题本 / 练习记录 / 答题共用 */
.option-letter {
display: inline-block;
width: 26px;
color: var(--text-secondary);
}
/* 答题选项行 */
.option-row {
height: auto !important;
min-height: 44px;
padding: 10px 16px !important;
text-align: left !important;
justify-content: flex-start !important;
white-space: normal !important;
}
.option-row-letter {
display: inline-block;
width: 24px;
color: inherit;
opacity: 0.7;
}
/* 答题题干:练习流程统一排版 */
.question-stem {
font-size: 17px;
font-weight: 600;
line-height: 1.7;
margin: 20px 0 14px;
}
/* 回看选项行(错题本 / 练习记录 / 题库预览),与答题行同一套排版语义 */
.option-review {
display: flex;
align-items: flex-start;
gap: 6px;
padding: 8px 12px;
border: 1px solid var(--border-soft);
border-radius: 8px;
background: var(--surface);
line-height: 1.7;
transition: background 0.2s, border-color 0.2s;
}
.option-review--correct {
background: var(--success-soft);
border-color: var(--success-soft-border);
}
.option-review--wrong {
background: var(--danger-soft);
border-color: var(--danger-soft-border);
}
.option-review-icon {
margin-left: auto;
flex-shrink: 0;
font-size: 15px;
line-height: 1.7;
}
/* 知识图谱管理:左侧结构树 + 右侧详情 */
.graph-main {
flex: 1 1 620px;
min-width: min(520px, 100%);
}
.graph-detail {
width: 380px;
max-width: 100%;
position: sticky;
top: 76px;
}
.tree-box {
max-height: 620px;
overflow: auto;
padding: 12px;
border: 1px solid var(--border-soft);
border-radius: 10px;
background: var(--surface);
}
/* ---------------- 教材 / 课程:竖向 Tab + 等高卡片 + 分页 ---------------- */
/*
* Tab 与内容必须读起来是一块东西:外壳是一张卡,导航条是嵌在卡内的分段面板,
* 选中项抬成与内容区同底色的白色小块 —— 看到高亮就知道右边显示的是它的内容。
*/
.resource-tabs.ant-tabs-left,
.resource-tabs.ant-tabs-top {
overflow: hidden;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 16px;
}
.resource-tabs .ant-tabs-content {
padding: 0;
}
/* 竖向:只有 2 个标签,不需要 antd 的滚动裁剪 */
.resource-tabs.ant-tabs-left > .ant-tabs-nav,
.resource-tabs.ant-tabs-left .ant-tabs-nav-wrap {
overflow: visible;
}
/* 导航条浮在卡片内侧:四边留白,不再和内容区抢那条分隔线 */
.resource-tabs.ant-tabs-left > .ant-tabs-nav {
flex: 0 0 186px;
width: 186px;
align-self: stretch;
position: static;
margin: 10px 0 10px 10px;
padding: 8px;
border-radius: 12px;
background: var(--tab-rail);
}
.resource-tabs.ant-tabs-left > .ant-tabs-body-holder {
flex: 1;
min-width: 0;
padding: 18px 20px 20px 22px;
}
/* antd 给左侧形态的内容区留了 24px,改由 body-holder 统一控制 */
.resource-tabs.ant-tabs-left > .ant-tabs-body-holder > .ant-tabs-content {
padding: 0;
}
/* 窄屏:同一条分段面板改卧在卡片顶部 */
.resource-tabs.ant-tabs-top > .ant-tabs-nav {
margin: 10px 10px 0;
padding: 6px;
border-radius: 12px;
background: var(--tab-rail);
}
.resource-tabs.ant-tabs-top > .ant-tabs-body-holder {
padding: 16px 18px 18px;
}
.resource-tabs .ant-tabs-nav-list {
width: 100%;
}
/* antd 自带的导航分隔线由卡片描边代替,留着会再多切一刀 */
.resource-tabs .ant-tabs-nav::before {
display: none;
}
.resource-tabs .ant-tabs-tab {
justify-content: flex-start;
align-self: stretch;
height: 40px;
margin: 0 0 2px !important;
padding: 0 12px !important;
border-radius: 9px;
/* 标签只有名称,禁止折行 */
white-space: nowrap;
color: var(--text-secondary);
transition:
background 0.18s ease,
box-shadow 0.18s ease,
color 0.18s ease;
}
.resource-tabs .ant-tabs-tab:last-child {
margin-bottom: 0 !important;
}
.resource-tabs .ant-tabs-tab:hover {
color: var(--text-strong);
background: var(--surface-hover);
}
.resource-tabs .ant-tabs-tab .ant-tabs-tab-btn {
width: 100%;
color: inherit;
}
/* 选中项:白底小块浮在面板上,底色与右侧内容一致 */
.resource-tabs .ant-tabs-tab-active,
.resource-tabs .ant-tabs-tab-active:hover {
background: var(--surface);
box-shadow: 0 1px 3px rgba(15, 17, 45, 0.14);
}
.resource-tabs .ant-tabs-tab-active .ant-tabs-tab-btn,
.resource-tabs .ant-tabs-tab-active .ant-tabs-tab-btn .anticon {
color: var(--brand);
font-weight: 600;
}
/* 顶部形态:两项均分宽度 */
.resource-tabs.ant-tabs-top .ant-tabs-tab {
flex: 1 1 0;
margin-inline-end: 0 !important;
}
.resource-tabs .ant-tabs-ink-bar {
display: none;
}
.resource-tab-label {
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
}
/* 内容区是实底卡片,内层卡片靠描边和淡投影保持层次 */
.resource-tabs .equal-card {
box-shadow: 0 1px 3px rgba(15, 17, 45, 0.05);
}
/* 卡片等高:栅格列与卡片一起撑满,内容多少不再影响高度 */
.card-grid {
--card-h: 352px;
align-items: stretch;
}
.card-grid > .ant-col {
display: flex;
}
.card-grid .equal-card {
width: 100%;
height: var(--card-h);
display: flex;
flex-direction: column;
}
.card-grid .equal-card .ant-card-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
/* 断点与 Col 的 xl 对齐:单列时没有对齐压力,固定高度只会撑出一片空白 */
@media (max-width: 1199px) {
.card-grid .equal-card {
height: auto;
min-height: 260px;
}
}
/* 弹性留白:把页脚和操作区顶到卡片底部,不同内容量的卡片同一条基线 */
.card-fill {
flex: 1;
min-height: 0;
}
/* 页脚不参与拉伸,章节目录单行显示,剩下的并入 +N */
.card-foot {
flex: none;
}
.card-tags {
display: flex;
gap: 6px;
}
.card-tags .ant-tag {
max-width: 150px;
margin-inline-end: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-pin {
padding-top: 12px;
}
.card-clamp {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
/* flex-basis 取的是未裁剪的整段文字高度,允许压缩时会把第二行切半,这里锁定两行高 */
flex: none;
}
.card-pager {
margin-top: 16px;
}
/* 数学公式 */
.math-inline {
font-size: 1em;
}
.math-block {
overflow-x: auto;
overflow-y: hidden;
padding: 4px 2px;
}
.katex-display {
margin: 0.25em 0;
}
/* ---------------- 电子书阅读器 ---------------- */
.ebook-reader {
display: flex;
flex-direction: column;
/* 顶栏 + 内容区上下留白之外的全部空间,保证页面本身不产生竖直滚动 */
height: calc(
100vh - var(--layout-header-h) - var(--content-pad) - var(--content-pad-end)
);
min-height: 480px;
overflow: hidden;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 12px;
}
.ebook-bar {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
min-height: 52px;
padding: 8px 16px;
border-bottom: 1px solid var(--border-soft);
}
.ebook-bar-title {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 0;
}
.ebook-name {
flex: 1;
min-width: 0;
font-weight: 600;
}
.ebook-bar-actions {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.ebook-body {
position: relative;
display: flex;
flex: 1;
min-height: 0;
}
/* PDF:虚拟滚动容器,页面绝对定位在占位轨道上 */
.ebook-pdf {
flex: 1;
overflow: auto;
padding: 24px 0;
}
.ebook-pdf-strip {
position: relative;
margin: 0 auto;
}
.ebook-pdf-page {
position: absolute;
background: #ffffff;
border-radius: 2px;
box-shadow: 0 1px 6px rgba(15, 17, 45, 0.12);
}
.ebook-pdf-canvas {
display: block;
width: 100%;
height: 100%;
}
/* EPUB:epub.js 往这里塞 iframe,尺寸交给它自己算 */
.ebook-epub {
flex: 1;
min-width: 0;
}
.ebook-epub iframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}
.ebook-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--surface) 82%, transparent);
}
.ebook-overlay-inner {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.ebook-overlay-text {
color: var(--text-secondary);
font-size: 13px;
}
.ebook-foot {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
padding: 8px 16px;
border-top: 1px solid var(--border-soft);
}
.ebook-caption {
color: var(--text-secondary);
font-size: 12px;
white-space: nowrap;
}
.ebook-progress {
flex: 1;
margin: 0;
}
.ebook-keys-hint {
opacity: 0.7;
}
.ebook-counter {
min-width: 92px;
text-align: center;
font-size: 12px;
color: var(--text-secondary);
}
.ebook-font-btn {
font-weight: 600;
}
.ebook-toc-list {
display: flex;
flex-direction: column;
}
.ebook-toc-item {
width: 100%;
text-align: start;
overflow: hidden;
}
@media (max-width: 768px) {
:root {
--content-pad-end: calc(var(--content-pad) + 2px);
}
.app-content {
padding: var(--content-pad);
padding-block-end: var(--content-pad-end);
}
.ebook-reader {
min-height: 420px;
}
.ebook-counter {
min-width: 0;
}
.ebook-keys-hint {
display: none;
}
.app-header {
gap: 8px;
padding-inline: 12px !important;
}
/* 窄屏放不下按钮文字:主操作只留图标,文案保留给读屏与无障碍名称 */
.page-header {
margin-bottom: 12px;
padding: 10px 14px;
}
.page-header-actions .ant-btn > span:not(.anticon):not(.ant-btn-icon) {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.page-header-actions .ant-btn {
min-width: 34px;
padding-inline: 0;
}
}
/* ---------------- 今日任务:自评卡片与结算区 ---------------- */
/* 掌握度自评三档:图标 + 标签 + 说明,选中态给品牌色描边与浅底 */
.daily-rates {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 10px;
}
.daily-rate {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 14px;
text-align: start;
background: var(--surface-2);
border: 1px solid var(--border-soft);
border-radius: 12px;
cursor: pointer;
transition:
background 0.2s,
border-color 0.2s;
}
.daily-rate:hover:not(:disabled) {
background: var(--surface-hover);
border-color: var(--brand);
}
.daily-rate:disabled {
opacity: 0.65;
cursor: default;
}
.daily-rate--active {
background: var(--brand-soft);
border-color: var(--brand);
box-shadow: inset 0 0 0 1px var(--brand);
}
.daily-rate-icon {
color: var(--brand);
font-size: 18px;
}
.daily-rate-text {
display: flex;
flex: 1;
flex-direction: column;
gap: 2px;
}
.daily-rate-label {
color: var(--text-strong);
font-weight: 600;
}
.daily-rate-desc {
color: var(--text-secondary);
font-size: 12px;
line-height: 1.5;
}
.daily-rate-check {
color: var(--brand);
font-size: 15px;
}
/* 完成态结算块:章节、自评、完成时刻、连续天数一次看完 */
.daily-done {
display: flex;
gap: 14px;
padding: 14px 16px;
background: var(--success-soft);
border: 1px solid var(--success-soft-border);
border-radius: 14px;
}
.daily-done-icon {
color: #16a34a;
font-size: 26px;
}
/* 结算区:主按钮独占一行,提示语跟在按钮下面 */
.daily-foot {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 确认框里的章节名可能很长:限宽换行,避免气泡顶出内容区 */
.daily-confirm-desc {
max-width: 260px;
}
/* 重点按钮:今日任务的唯一主操作,尺寸要压过页面其余按钮 */
.daily-submit.ant-btn {
min-height: 52px;
border-radius: 12px;
box-shadow: 0 10px 22px rgba(91, 92, 226, 0.26);
font-size: 16px;
font-weight: 600;
}
.daily-submit.ant-btn:disabled {
box-shadow: none;
}
/* ---------------- 分屏工作台(题库 / 记录 / 错题 / 答题) ---------------- */
.split-view {
display: flex;
align-items: stretch;
overflow: hidden;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 14px;
box-shadow: 0 1px 2px rgba(31, 32, 51, 0.04);
}
.split-aside {
flex: 0 0 auto;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--surface-2);
}
.split-view--left .split-aside {
border-inline-end: 1px solid var(--border-soft);
}
.split-view--right .split-aside {
border-inline-start: 1px solid var(--border-soft);
}
.split-main {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 窄屏:左右分屏退回上下堆叠,清单只占一小段高度 */
.split-view--stack {
flex-direction: column;
height: auto !important;
}
.split-view--stack .split-aside {
border-inline-end: 0;
border-inline-start: 0;
border-bottom: 1px solid var(--border-soft);
width: 100% !important;
max-height: 40vh;
}
.split-view--stack .split-main {
min-height: 60vh;
}
.split-resizer {
flex: 0 0 7px;
cursor: col-resize;
position: relative;
background: transparent;
touch-action: none;
}
.split-resizer::after {
content: "";
position: absolute;
inset: 0 3px;
border-radius: 3px;
background: var(--border-soft);
transition: background 0.16s ease;
}
.split-resizer:hover::after,
.split-resizer--active::after {
background: var(--brand);
}
body.split-dragging {
cursor: col-resize;
user-select: none;
}
/* 分屏里的一栏 */
.pane {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.pane-head {
flex: 0 0 auto;
padding: 12px 14px;
border-bottom: 1px solid var(--border-soft);
background: var(--surface);
}
.split-aside > .pane > .pane-head {
background: var(--surface-2);
}
.pane-head-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.pane-title {
font-size: 14px;
font-weight: 600;
color: var(--text-strong);
min-width: 0;
}
.pane-extra {
flex-shrink: 0;
}
.pane-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 10px;
}
.pane-toolbar:empty {
display: none;
}
/* 题库侧栏的三个筛选下拉尽量等分一行;栏太窄时整体换行,
而不是把「难度不限」压成「难度…」;
菜单宽度靠 popupMatchSelectWidth={false} 单独放开,长选项不会被裁。 */
.bank-filter-select {
flex: 1 1 92px;
min-width: 92px;
}
.pane-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 10px 12px 18px;
}
.pane--flat .pane-body {
padding: 0;
overflow: hidden;
display: flex;
}
.pane-foot {
flex: 0 0 auto;
padding: 10px 14px;
border-top: 1px solid var(--border-soft);
background: var(--surface);
}
/* 清单条目 */
.master-item {
display: block;
width: 100%;
text-align: start;
padding: 10px 12px;
margin-bottom: 6px;
border: 1px solid transparent;
border-radius: 10px;
background: transparent;
color: inherit;
cursor: pointer;
transition: background 0.16s ease, border-color 0.16s ease;
}
.master-item:hover {
background: var(--surface-hover);
}
.master-item--active {
background: var(--brand-soft);
border-color: rgba(91, 92, 226, 0.35);
}
.master-item-title {
display: flex;
align-items: center;
gap: 6px;
font-weight: 600;
color: var(--text-strong);
line-height: 1.5;
}
.master-item-sub {
margin-top: 3px;
font-size: 12px;
color: var(--text-secondary);
line-height: 1.6;
}
.master-group {
margin: 6px 0 4px;
padding: 0 4px;
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
}
/* 得分环 / 计数的数字都用等宽数字,纵向对比才整齐 */
.tabular {
font-variant-numeric: tabular-nums;
}
/* ---------------- 试卷排版 ---------------- */
.paper {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
min-width: 0;
}
.paper-head {
flex: 0 0 auto;
padding: 14px 20px 10px;
border-bottom: 1px solid var(--border-soft);
background: var(--surface);
}
.paper-head-main {
min-width: 0;
}
.paper-title {
font-size: 16px;
font-weight: 650;
color: var(--text-strong);
line-height: 1.5;
}
.paper-meta {
margin-top: 2px;
font-size: 12.5px;
color: var(--text-secondary);
line-height: 1.7;
}
.paper-head-side {
flex-shrink: 0;
}
.paper-score {
display: flex;
flex-direction: column;
align-items: flex-end;
padding: 0 12px;
border-inline-start: 1px solid var(--border-soft);
}
.paper-score-value {
font-size: 22px;
font-weight: 700;
line-height: 1.1;
color: var(--brand);
font-variant-numeric: tabular-nums;
}
.paper-score-total {
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
}
.paper-score-label {
font-size: 11.5px;
color: var(--text-secondary);
}
.paper-head-tools {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
margin-top: 12px;
}
.paper-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 14px 18px 24px;
}
/* 一道题 */
.paper-question {
display: flex;
gap: 12px;
padding: 14px 10px 16px;
border-radius: 12px;
transition: background 0.16s ease, box-shadow 0.16s ease;
}
.paper-question + .paper-question {
border-top: 1px dashed var(--border-soft);
border-radius: 12px 12px 0 0;
}
.paper-question--active {
background: var(--surface-2);
box-shadow: inset 3px 0 0 var(--brand);
}
.paper-question-rail {
flex: 0 0 auto;
padding-top: 1px;
}
.paper-question-no {
display: inline-grid;
place-items: center;
min-width: 28px;
height: 28px;
padding: 0 6px;
border-radius: 50%;
background: var(--track);
color: var(--text-strong);
font-size: 13px;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.paper-question--correct .paper-question-no {
background: var(--success-soft);
color: var(--success-soft-border);
color: #16a34a;
}
.paper-question--wrong .paper-question-no {
background: var(--danger-soft);
color: #dc2626;
}
.paper-question-main {
flex: 1 1 auto;
min-width: 0;
}
.paper-question-head {
min-height: 24px;
margin-bottom: 2px;
}
.paper-question-actions {
margin-inline-start: auto;
}
.paper-stem {
font-size: 15.5px;
font-weight: 600;
line-height: 1.85;
color: var(--text-strong);
margin: 6px 0 12px;
}
.paper-options {
max-width: 760px;
}
.paper-answer {
margin-top: 12px;
padding: 10px 12px;
border-radius: 10px;
background: var(--surface-2);
border: 1px solid var(--border-soft);
max-width: 760px;
}
.paper-explain-body {
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed var(--border-soft);
line-height: 1.85;
color: var(--text-strong);
}
.paper-explain-toggle {
border: 0;
background: transparent;
padding: 0;
cursor: pointer;
color: var(--brand);
font-size: 13px;
}
/* 答题卡 */
.answer-sheet {
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
}
.answer-sheet-chips {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.answer-chip {
position: relative;
display: inline-flex;
align-items: center;
gap: 3px;
min-width: 32px;
height: 30px;
padding: 0 8px;
border: 1px solid var(--border-soft);
border-radius: 9px;
background: var(--surface);
color: var(--text-strong);
font-size: 13px;
font-variant-numeric: tabular-nums;
cursor: pointer;
transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
}
.answer-chip:hover {
transform: translateY(-1px);
border-color: var(--brand);
}
.answer-chip--active {
border-color: var(--brand);
box-shadow: 0 0 0 2px var(--brand-soft);
}
.answer-chip-icon {
font-size: 11px;
}
.answer-chip--correct {
background: var(--success-soft);
border-color: var(--success-soft-border);
color: #16a34a;
}
.answer-chip--wrong {
background: var(--danger-soft);
border-color: var(--danger-soft-border);
color: #dc2626;
}
.answer-chip--done {
border-color: rgba(91, 92, 226, 0.45);
color: var(--brand);
}
.answer-chip--pending {
border-style: dashed;
color: var(--text-muted);
}
.answer-sheet-legend {
display: flex;
align-items: center;
gap: 10px;
font-size: 12px;
color: var(--text-secondary);
}
.answer-sheet-legend-toggle {
border: 0;
background: transparent;
padding: 0;
cursor: pointer;
color: var(--text-secondary);
font-size: 12px;
text-decoration: underline dotted;
}
.answer-sheet-legend-items {
display: flex;
}
.answer-sheet-legend-items span {
display: inline-flex;
align-items: center;
gap: 4px;
}
.answer-dot {
width: 9px;
height: 9px;
border-radius: 3px;
display: inline-block;
}
.answer-dot--correct {
background: var(--success-soft);
border: 1px solid #16a34a;
}
.answer-dot--wrong {
background: var(--danger-soft);
border: 1px solid #dc2626;
}
.answer-dot--done {
background: var(--brand-soft);
border: 1px solid var(--brand);
}
.answer-dot--pending {
background: transparent;
border: 1px dashed var(--text-muted);
}
.answer-sheet-summary {
margin-inline-start: auto;
display: flex;
align-items: center;
gap: 10px;
font-size: 12.5px;
color: var(--text-secondary);
}
/* 答题中的提交条 */
.paper-submit {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
/* 题库卡片里的章节导航 */
.chapter-nav-count {
font-variant-numeric: tabular-nums;
color: var(--text-secondary);
font-size: 12px;
}
/* 试卷头部:标题与视图切换并排 */
.paper-head-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
/* 题干下方的主体(选项 / 作答控件) */
.paper-body-slot {
min-width: 0;
}
/* 可折叠解析 */
.paper-explain {
margin-top: 12px;
max-width: 760px;
}
.paper-explain-caret {
margin-inline-start: 6px;
font-size: 10px;
color: var(--text-muted);
}
/* 我的作答 / 正确答案 对照 */
.answer-compare {
align-items: center;
row-gap: 4px;
}
.answer-chip-no {
font-variant-numeric: tabular-nums;
}
/* 清单里的可点分组(教材名)与更紧凑的子项(章节) */
.master-group-button {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding: 5px 8px;
border: 0;
border-radius: 8px;
background: transparent;
color: var(--text-strong);
font: inherit;
text-align: start;
cursor: pointer;
transition: background 0.16s ease, color 0.16s ease;
}
.master-group-button:hover {
background: var(--surface-hover);
}
.master-group-button--active {
color: var(--brand);
}
.master-item--tight {
padding: 7px 10px;
margin-bottom: 2px;
border-radius: 8px;
}
.master-item--tight .master-item-title {
font-weight: 500;
font-size: 13px;
}
/* 侧栏底部的小字提示 */
.aside-hint {
margin-top: 14px;
padding: 10px 4px 0;
border-top: 1px dashed var(--border-soft);
line-height: 1.7;
}
/* 生成任务进度条(挂在分屏上方,高度变化由 ResizeObserver 收敛) */
.bank-task {
margin-bottom: 12px;
padding: 12px 16px 4px;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 12px;
}
/* 成绩单摘要 */
.result-score {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 8px;
}
.result-score-value {
font-size: 30px;
font-weight: 700;
line-height: 1.1;
color: var(--brand);
font-variant-numeric: tabular-nums;
}
.result-score-total {
font-size: 15px;
font-weight: 500;
color: var(--text-secondary);
}
.result-score-label {
font-size: 12px;
color: var(--text-secondary);
}
.result-knowledge {
margin-top: 14px;
padding-top: 4px;
border-top: 1px dashed var(--border-soft);
}
.result-knowledge-row {
gap: 8px;
padding: 4px 0;
font-size: 13px;
}
.result-knowledge-name {
flex: 1 1 auto;
min-width: 0;
color: var(--text-strong);
}
/* 章节测试:组卷条件收成上方一条窄栏,高度留给答题工作台 */
.test-launcher {
padding: 12px 14px;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 14px;
box-shadow: 0 1px 2px rgba(31, 32, 51, 0.04);
}
.test-launcher-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.test-launcher-select {
width: 260px;
max-width: 100%;
}
.test-launcher-meta {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed var(--border-soft);
}
.test-launcher-tags {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
.test-launcher-note {
flex: 1 1 240px;
min-width: 0;
font-size: 12.5px;
}
@media (max-width: 1200px) {
.paper-head {
padding: 12px 14px 8px;
}
.paper-body {
padding: 14px 12px 26px;
}
.paper-question {
padding: 16px 6px 18px;
}
}