From a957b39cc0eda2bdc45e4d3eebcc65bcbd5ef719 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 16 Jun 2026 20:12:58 +0800 Subject: [PATCH] feat(frontend): harden home CTA, Portal, public, and admin pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Landing: 主 CTA “先做快速审核” 升级为主按钮,移除多余的次按钮,添加 hero-note 与 hero-divider 让首屏节奏更稳 - Landing: hero-points 收口为 3 个差异化卖点,第 1 个加 lead 背景,右侧风险卡降饱和、避免压过主 CTA - Landing: 删去旧“志愿方案审计更聚焦/先下单后补资料/站内可追踪交付”重复卡片 - Public: /privacy /service-terms /deletion-policy 接 portal-ui.css 并改成双段 panel + 列表结构 - Portal: /portal/{token}/notifications /portal/{token}/deletion-request 接 portal-ui.css 与新的工具条 / 表单字段化 - Admin: /admin/orders/new 接 portal-ui.css,添加“人工补录更直接/与后台订单主链一致/字段业务语义化”信任块 - Admin: /admin/notifications /admin/ops-alerts 接 portal-ui.css,添加 meta-grid 摘要并把 “payload / details” 改名为“通知内容摘要 / 详细上下文” - Tests: 锁住 shared CSS、关键产品化文案、Portal 与 admin 页结构断言 --- admin/routes/notifications.py | 45 +++--- admin/routes/ui.py | 37 +++-- admin/routes/web_public.py | 161 +++++++++++--------- admin/tests/test_admin_ui_pages.py | 2 + admin/tests/test_notification_audit_page.py | 2 + admin/tests/test_notifications_admin.py | 3 + admin/tests/test_ops_alerts_admin.py | 3 + admin/tests/test_order_info_form.py | 2 + admin/tests/test_web_public.py | 13 +- 9 files changed, 165 insertions(+), 103 deletions(-) diff --git a/admin/routes/notifications.py b/admin/routes/notifications.py index 4845e6e..cce7cc1 100644 --- a/admin/routes/notifications.py +++ b/admin/routes/notifications.py @@ -177,18 +177,21 @@ def ops_alert_audit_page( ) rows_html = "".join(rows) or "暂无运维告警" html = f""" -运维告警审计 - -
-
+运维告警审计 + +
+

运维告警审计

-

日志路径:{escape(settings.ops_alert_log_path)}

-

SMTP 告警收件人数量:{len(settings.alert_recipients)}

-

IM Webhook 数量:{len(settings.alert_webhook_urls)}

+

集中查看通知链路中的异常告警、摘要与上下文,方便快速判断是否需要人工介入。

+
+
告警来源文件{escape(settings.ops_alert_log_path)}
+
SMTP 告警收件人数量{len(settings.alert_recipients)}
+
IM Webhook 数量{len(settings.alert_webhook_urls)}
+
-
- - +
+
时间类型标题摘要details
+ {rows_html}
时间类型标题摘要详细上下文
@@ -299,17 +302,21 @@ def notification_audit_admin_page( ) rows_html = "".join(rows) or "暂无通知事件" html = f""" -后台通知审计 - -
-
+后台通知审计 + +
+

后台通知审计

-

总数:{payload['total']}

-

筛选:order_id={escape(str(order_id or '-'))} / status={escape(str(status or '-'))} / channel={escape(str(channel or '-'))}

+

集中查看订单通知状态、重试情况与失败原因,便于客服或运营快速定位交付链路问题。

+
+
总数{payload['total']}
+
当前筛选order_id={escape(str(order_id or '-'))} / status={escape(str(status or '-'))} / channel={escape(str(channel or '-'))}
+
查看重点优先关注失败原因、尝试次数与最后尝试时间,确认是否需要人工补发。
+
-
- - +
+
订单渠道事件状态尝试次数最后尝试失败原因payload
+ {rows_html}
订单渠道事件状态尝试次数最后尝试失败原因通知内容摘要
diff --git a/admin/routes/ui.py b/admin/routes/ui.py index eac9542..7eb89b8 100644 --- a/admin/routes/ui.py +++ b/admin/routes/ui.py @@ -79,25 +79,38 @@ def _render_admin_new_order_page() -> str: f"" for p in province_options ) return f""" -后台手动添加订单 +后台手动添加订单 + -
-

后台手动添加订单

-

请先在仪表盘登录,页面会自动读取同一 token 调用 /api/orders

+
+
后台录单

后台手动添加订单

用于人工服务场景下的补录、客服录单或线下沟通后的订单补建。页面会读取当前登录态调用 /api/orders

+
+
+
人工补录更直接适合客服、学校渠道或线下沟通后补建订单。
+
与后台订单主链一致录入后仍会进入统一订单、通知与交付链路。
+
字段尽量按业务语义展示避免让后台录单页继续像内部测试表单。
+
-
+
@@ -106,7 +119,7 @@ button {{ border:none;border-radius:12px;background:#1f6feb;color:#fff;font-weig
-

+
""" diff --git a/admin/routes/web_public.py b/admin/routes/web_public.py index aa2a878..a54a009 100644 --- a/admin/routes/web_public.py +++ b/admin/routes/web_public.py @@ -661,14 +661,17 @@ def _render_footer_links(token: str | None = None) -> str: @router.get("/privacy", include_in_schema=False) def privacy_page(token: str | None = None) -> HTMLResponse: body = ( - "隐私政策" - "" - "
" - "

隐私政策

" - "

我们收集下单、资料填写、支付与交付所需的最小信息,仅用于高考志愿填报服务,不用于营销出售。

" - "

如需撤回资料或申请删除,可使用“删除申请 / 数据删除说明”入口提交请求。

" + "" + "" + "隐私政策" + "" + "" + "
" + "
隐私说明

隐私政策

" + "

我们只收集下单、资料填写、支付与交付所需的最小信息,用于志愿服务流程,不用于营销出售或无关用途。

" + "

你可以预期什么

  • 支付前只收必要下单信息
  • 详细资料在支付后通过资料向导分步补充
  • 隐私政策、服务说明与删除申请入口全程可见
  • 如需撤回资料或申请删除,可通过删除申请入口提交请求
" + _render_footer_links(token) - + "
" + + "
" ) return HTMLResponse(body) @@ -676,13 +679,17 @@ def privacy_page(token: str | None = None) -> HTMLResponse: @router.get("/service-terms", include_in_schema=False) def service_terms_page(token: str | None = None) -> HTMLResponse: body = ( - "服务说明与免责声明" - "" - "
" - "

服务说明与免责声明

" - "

本服务提供志愿填报辅助建议与报告,不承诺录取结果;监护人需知情并同意提交资料。

" + "" + "" + "服务说明与免责声明" + "" + "" + "
" + "
服务边界

服务说明与免责声明

" + "

本服务提供志愿填报辅助建议、方案审计与交付支持,不承诺录取结果;提交资料前请确认监护人与考生已知情。

" + "

下单前请了解

  • 我们优先帮助你审计现有方案与风险点
  • 支付后可继续补充详细资料与附件
  • 交付状态、通知与报告入口会在站内持续更新
  • 如需撤回资料或删除交付物,可通过删除申请入口提交请求
" + _render_footer_links(token) - + "
" + + "
" ) return HTMLResponse(body) @@ -690,13 +697,17 @@ def service_terms_page(token: str | None = None) -> HTMLResponse: @router.get("/deletion-policy", include_in_schema=False) def deletion_policy_page() -> HTMLResponse: body = ( - "删除申请 / 数据删除说明" - "" - "
" - "

删除申请 / 数据删除说明

" - "

如需申请删除订单资料、附件或交付物,请在支付后的 Portal 中提交删除申请,客服会核验订单与监护人信息后处理。

" + "" + "" + "删除申请 / 数据删除说明" + "" + "" + "
" + "
数据删除

删除申请 / 数据删除说明

" + "

如需申请删除订单资料、附件或交付物,可在支付后的 Portal 中提交删除申请;系统会保留必要的审计记录,并由人工核验后处理。

" + "

你可以怎么做

  • 在 Portal 中提交删除申请
  • 填写申请人姓名、联系方式、删除范围与原因
  • 确认监护人已知情并同意发起删除申请
  • 处理结果会回到站内状态链路中查看
" + _render_footer_links() - + "
" + + "
" ) return HTMLResponse(body) @@ -729,25 +740,32 @@ def _render_landing_page() -> str: }} * {{ box-sizing: border-box; }} body {{ margin: 0; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; background: linear-gradient(180deg,#0e1a2b 0,#13243d 38%,var(--bg) 38%,var(--bg) 100%); color: var(--text); }} - .wrap {{ max-width: 1180px; margin: 0 auto; padding: 32px 20px 72px; }} - .hero {{ display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(320px, .8fr); gap: 24px; align-items: stretch; }} - .hero-copy {{ color: #ecf4ff; padding: 28px 8px 12px 0; }} + .wrap {{ max-width: 1180px; margin: 0 auto; padding: 40px 20px 72px; }} + .hero {{ display: grid; grid-template-columns: minmax(0, 1.22fr) minmax(340px, .78fr); gap: 32px; align-items: stretch; }} + .hero-copy {{ color: #ecf4ff; padding: 40px 8px 18px 0; }} .eyebrow {{ display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 999px; background: rgba(223,247,241,.12); border: 1px solid rgba(223,247,241,.24); color: #c9fff3; font-size: 13px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }} - h1 {{ margin: 18px 0 14px; font-size: clamp(34px, 6vw, 52px); line-height: 1.08; letter-spacing: -0.03em; }} - .sub {{ margin: 0; max-width: 680px; color: #b8c8e4; line-height: 1.8; font-size: 17px; }} - .hero-actions {{ display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }} + h1 {{ margin: 18px 0 14px; max-width: 760px; font-size: clamp(36px, 6vw, 56px); line-height: 1.04; letter-spacing: -0.04em; }} + .sub {{ margin: 0; max-width: 700px; color: #b8c8e4; line-height: 1.82; font-size: 17px; }} + .hero-actions {{ display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; align-items: center; }} .btn {{ display: inline-flex; align-items: center; justify-content: center; min-height: 46px; padding: 0 18px; border-radius: 14px; text-decoration: none; font-weight: 700; transition: .18s ease; }} - .btn-primary {{ background: var(--primary); color: #fff; box-shadow: 0 12px 28px rgba(31,111,235,.28); }} - .btn-primary:hover {{ background: var(--primary-dark); }} - .btn-secondary {{ background: rgba(255,255,255,.08); color: #fff; border: 1px solid rgba(255,255,255,.18); }} + .btn-primary {{ min-height: 54px; padding: 0 28px; font-size: 17px; background: linear-gradient(135deg,#2d7cff,#0f4fd6); color: #fff; box-shadow: 0 22px 40px rgba(31,111,235,.42), inset 0 1px 0 rgba(255,255,255,.18); letter-spacing: .01em; }} + .btn-primary:hover {{ background: linear-gradient(135deg,#276fe7,#0d45bf); transform: translateY(-1px); }} + .btn-secondary {{ background: rgba(255,255,255,.08); color: #fff; border: 1px solid rgba(255,255,255,.18); min-height: 44px; padding: 0 14px; font-size: 14px; }} .btn-secondary:hover {{ background: rgba(255,255,255,.14); }} + .btn-text {{ color:#cfe0ff; padding: 0 6px; min-height: 44px; font-size: 14px; text-decoration: underline; text-underline-offset: 4px; }} + .hero-note {{ margin-top: 12px; color:#8fb0df; font-size:13px; line-height:1.6; }} + .hero-risk-band {{ display:grid; gap:6px; margin-bottom:16px; padding:12px 14px; border-radius:14px; background: rgba(255,255,255,.7); border:1px solid #f3d49f; }} + .hero-risk-band strong {{ font-size:14px; color:#7a5a00; }} + .hero-risk-band span {{ color:#7a5c24; font-size:13px; line-height:1.55; }} .hero-trust {{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin-top:18px; }} .hero-trust-item {{ padding:12px 14px; border-radius:14px; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.12); color:#d9e7ff; font-size:13px; line-height:1.5; }} .hero-trust-item strong {{ display:block; color:#fff; margin-bottom:4px; font-size:14px; }} .hero-points {{ display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 12px; margin-top: 24px; }} .point {{ padding: 14px; border-radius: 16px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); }} + .point.lead {{ background: rgba(31,111,235,.18); border-color: rgba(125,180,255,.42); }} .point strong {{ display: block; color: #fff; margin-bottom: 6px; font-size: 15px; }} .point span {{ color: #b8c8e4; font-size: 13px; line-height: 1.6; }} + .hero-divider {{ margin: 28px 0 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,.18), transparent); }} .hero-panel {{ background: rgba(255,255,255,.98); border: 1px solid rgba(215,227,241,.88); border-radius: var(--radius-xl); box-shadow: var(--shadow); padding: 24px; align-self: end; }} .hero-panel h2 {{ margin: 0 0 10px; font-size: 22px; }} .hero-panel p {{ margin: 0 0 16px; color: var(--muted); line-height: 1.7; }} @@ -788,29 +806,32 @@ def _render_landing_page() -> str:
新高考志愿填报 · 志愿决策支持

高考志愿填报智能规划服务

先审计现有志愿方案,再判断是否踩线、扎堆或梯度失衡,再决定要不要进入完整规划。先完成线上下单,再进入资料向导补充详细信息,后续可在站内查看报告与交付进度。

-
- 查看服务套餐 - 了解服务流程 + -
-
先审计后规划先判断现有方案有没有明显风险,再决定要不要进入完整规划。
-
风险重点可解释重点识别踩线、扎堆、梯度失衡和结构异常,不只给结果。
-
进度站内可查资料、通知、报告、下载入口都能在站内持续追踪。
-
隐私与删除入口可见隐私政策、服务说明与删除申请入口全程可访问。
+

适合已经拿到一版志愿方案、想先判断有没有明显风险的人。

+
+
先审计后规划先判断现有方案有没有明显风险,再决定要不要进入完整规划。
+
风险重点可解释重点识别踩线、扎堆、梯度失衡和结构异常,不只给结果。
+
进度站内可查资料、通知、报告、下载入口都能在站内持续追踪。
+
隐私与删除入口可见隐私政策、服务说明与删除申请入口全程可访问。
-
-
志愿方案审计更聚焦重点帮你识别方案是否踩线、是否扎堆、梯度是否失衡,以及哪些风险需要进一步人工确认。
-
先下单后补资料先确认联系方式与考生基础信息,支付后进入 4 步资料向导。
-
站内可追踪交付支持资料进度、通知记录、报告在线查看与 PDF 下载入口。
+
+
+
先把方案看清,再决定要不要重做如果你已经拿到一版方案,先做审核看是否踩线、扎堆、梯度失衡,再决定是否进入完整规划。
+
重点可解释不只给一份结论,更说明为什么这样选、需要确认什么。
+
资料、通知、报告都在站内支付后只需按提示补充资料,状态、通知、报告、下载入口全部站内可查。
-