Files
gaokao-volunteer-system/admin/static/portal-ui.css
Hermes Agent 4202eba208
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
feat(frontend): 三阶段18项前端优化全部完成
第一阶段(致命缺陷修复):
- 复核结果页: 真分享(navigator.share+clipboard兜底)+保存草稿(localStorage), 删除假wxhelp外链
- 通用状态组件库: portal-ui.css新增state-loading/skeleton/empty/error/toast/sr-only
- my-orders/my-reports接入skeleton占位+state-empty

第二阶段(核心功能补齐):
- C方案鉴权: my-orders/my-reports/compare-reports删除find_by_phone手机号直查,
  改为portal token验证, 堵死P0-2越权漏洞
- 首页表单4字段即时校验(province/score/subjects/goal) + portal info 3字段校验
- cwb页面从固定文案升级为基于crowd_db的三档真实建议(冲刺+20/稳妥0/保底-20)

第三阶段(产品化提升):
- 新增/score-line-query分数线查询页(从crowd_db读本科线)
- 新增/rank-estimator位次估算页(基于benchmarks插值)
- /data-query扩展为4入口(分数线/位次/政策/同分段)
- 报告资产页补复制链接+系统分享
- 全局导航移除my-orders/my-reports(C方案)
- 从所有导航删除越权入口

验证: pytest 1306 passed, ruff All checks passed, 浏览器逐页验证通过
2026-06-27 13:23:08 +08:00

371 lines
7.3 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
--portal-bg: #f3f7fb;
--portal-surface: #ffffff;
--portal-surface-soft: #f7fbff;
--portal-border: #d7e3f1;
--portal-text: #142235;
--portal-muted: #5b6b88;
--portal-primary: #1f6feb;
--portal-primary-dark: #194fb6;
--portal-primary-soft: rgba(31, 111, 235, 0.12);
--portal-success: #0f766e;
--portal-success-soft: #dff7f1;
--portal-warning: #8a5a00;
--portal-warning-soft: #fff7e6;
--portal-shadow: 0 18px 42px rgba(20, 34, 53, 0.08);
--portal-radius-xl: 24px;
--portal-radius-lg: 18px;
--portal-radius-md: 14px;
}
* {
box-sizing: border-box;
}
body {
color: var(--portal-text);
}
a {
color: var(--portal-primary);
}
a:hover {
color: var(--portal-primary-dark);
}
.portal-panel {
background: var(--portal-surface);
border: 1px solid var(--portal-border);
border-radius: var(--portal-radius-xl);
box-shadow: var(--portal-shadow);
}
.portal-eyebrow {
display: inline-flex;
padding: 6px 10px;
border-radius: 999px;
background: var(--portal-success-soft);
color: var(--portal-success);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.portal-muted {
color: var(--portal-muted);
}
.portal-button,
.portal-button-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 46px;
padding: 0 18px;
border-radius: var(--portal-radius-md);
text-decoration: none;
font-weight: 700;
border: none;
cursor: pointer;
}
.portal-button {
background: var(--portal-primary);
color: #fff;
}
.portal-button:hover {
background: var(--portal-primary-dark);
color: #fff;
}
.portal-button-secondary {
background: #edf3ff;
color: var(--portal-primary-dark);
}
.portal-button-secondary:hover {
background: #dfeaff;
color: var(--portal-primary-dark);
}
.portal-field input,
.portal-field textarea,
.portal-field select {
width: 100%;
padding: 12px;
border-radius: 12px;
border: 1px solid #cfd7e6;
font-size: 14px;
background: #fff;
color: var(--portal-text);
}
.portal-field input:focus,
.portal-field textarea:focus,
.portal-field select:focus {
outline: none;
border-color: var(--portal-primary);
box-shadow: 0 0 0 4px var(--portal-primary-soft);
}
.portal-helper {
color: var(--portal-muted);
font-size: 12px;
line-height: 1.6;
}
.portal-notice {
padding: 16px 18px;
border-radius: 16px;
background: var(--portal-warning-soft);
border: 1px solid #f4d39b;
color: var(--portal-warning);
line-height: 1.7;
}
.portal-status-grid {
display: grid;
gap: 12px;
}
.portal-status-item {
padding: 14px 16px;
border-radius: 16px;
background: var(--portal-surface-soft);
border: 1px solid var(--portal-border);
}
.global-nav {
position: sticky;
top: 0;
z-index: 100;
background: rgba(14,26,43,.92);
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(255,255,255,.08);
padding: 12px 20px;
}
.global-nav-inner {
max-width: 1180px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
}
.global-nav-brand {
font-size: 18px;
font-weight: 700;
color: #fff;
text-decoration: none;
}
.global-nav-links {
display: flex;
gap: 20px;
align-items: center;
}
.global-nav-link {
color: #b8c8e4;
text-decoration: none;
font-size: 14px;
font-weight: 500;
}
.global-nav-link:hover {
color: #fff;
}
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 2px solid var(--portal-primary);
outline-offset: 2px;
}
@media (max-width: 768px) {
.global-nav {
padding: 10px 14px;
}
.global-nav-brand {
font-size: 16px;
}
.global-nav-links {
gap: 12px;
}
.global-nav-link {
font-size: 13px;
}
}
/* ============================================================
通用状态组件库2026-06-26 第一阶段 P1-2
覆盖loading / skeleton / empty / error / toast / spinner
所有用户端页面可直接 <div class="state-..."> 引用。
============================================================ */
/* —— Loading 容器 —— */
.state-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 36px 18px;
text-align: center;
color: #5b6b88;
background: #f8fbff;
border: 1px solid #d7e3f1;
border-radius: 16px;
}
.state-loading__spinner {
width: 28px;
height: 28px;
border: 3px solid #dbe7f5;
border-top-color: #1f6feb;
border-radius: 50%;
animation: state-spin 0.9s linear infinite;
}
.state-loading__text {
font-size: 14px;
line-height: 1.6;
margin: 0;
}
@keyframes state-spin {
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.state-loading__spinner {
animation-duration: 2.5s;
}
}
/* —— Skeleton 占位(首屏数据未到时用)—— */
.state-skeleton {
display: block;
padding: 18px;
background: #f4f7fb;
border: 1px solid #e1e8f3;
border-radius: 14px;
}
.state-skeleton__line {
display: block;
height: 14px;
margin: 10px 0;
border-radius: 7px;
background: linear-gradient(90deg, #e6ecf5 0%, #f3f6fb 50%, #e6ecf5 100%);
background-size: 200% 100%;
animation: state-shimmer 1.4s ease-in-out infinite;
}
.state-skeleton__line--short { width: 38%; }
.state-skeleton__line--mid { width: 64%; }
.state-skeleton__line--long { width: 90%; }
@keyframes state-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
.state-skeleton__line {
animation: none;
background: #e6ecf5;
}
}
/* —— Empty 空状态 —— */
.state-empty {
padding: 28px 20px;
border-radius: 16px;
background: #f8fbff;
border: 1px solid #d7e3f1;
color: #5b6b88;
text-align: center;
}
.state-empty__title {
margin: 0 0 6px;
font-size: 16px;
font-weight: 700;
color: #2c3e5b;
}
.state-empty__hint {
margin: 0;
font-size: 13px;
line-height: 1.7;
}
/* —— Error 错误状态 —— */
.state-error {
padding: 20px 18px;
border-radius: 14px;
background: #fff5f5;
border: 1px solid #f5c2c7;
color: #b42318;
}
.state-error__title {
margin: 0 0 6px;
font-size: 15px;
font-weight: 700;
}
.state-error__hint {
margin: 0;
font-size: 13px;
line-height: 1.7;
color: #8a1f15;
}
.state-error a {
color: #b42318;
text-decoration: underline;
}
/* —— Toast全局aria-live=polite—— */
.state-toast-stack {
position: fixed;
left: 50%;
bottom: 24px;
transform: translateX(-50%);
display: flex;
flex-direction: column;
gap: 8px;
z-index: 9999;
pointer-events: none;
}
.state-toast {
pointer-events: auto;
min-width: 220px;
max-width: 90vw;
padding: 10px 14px;
border-radius: 10px;
background: rgba(20, 34, 53, .92);
color: #fff;
font-size: 13px;
line-height: 1.5;
box-shadow: 0 12px 30px rgba(20, 34, 53, .18);
opacity: 0;
transform: translateY(8px);
transition: opacity .2s ease, transform .2s ease;
}
.state-toast--visible {
opacity: 1;
transform: translateY(0);
}
.state-toast--success { background: #1f7a4d; }
.state-toast--error { background: #b42318; }
.state-toast--info { background: #1f6feb; }
/* —— 工具类sr-only可访问性配套—— */
.state-sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}