feat: enhance learning dashboard visuals
This commit is contained in:
@@ -216,6 +216,79 @@
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
.learning-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 14px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.learning-panel {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
padding: 18px;
|
||||
background: rgba(255,255,255,0.65);
|
||||
box-shadow: inset 0 4px 12px rgba(18,32,51,0.06);
|
||||
}
|
||||
.learning-panel h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.learning-panel p {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.flow-steps {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.flow-step {
|
||||
padding: 10px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(20,100,199,0.25);
|
||||
background: rgba(13,143,124,0.06);
|
||||
}
|
||||
.flow-step strong {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
.chain-pill {
|
||||
display: inline-flex;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--line);
|
||||
background: #ffffff;
|
||||
font-size: 12px;
|
||||
margin-right: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.insight-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.insight-card {
|
||||
border-radius: 20px;
|
||||
padding: 18px;
|
||||
border: 1px solid var(--line);
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.9), rgba(255,255,255,0.7));
|
||||
min-height: 200px;
|
||||
position: relative;
|
||||
}
|
||||
.insight-card strong {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-size: 22px;
|
||||
color: var(--brand);
|
||||
}
|
||||
.insight-card ul {
|
||||
margin: 10px 0 0 16px;
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.layout { grid-template-columns: 1fr; }
|
||||
.grid, .pipeline, .metrics { grid-template-columns: 1fr 1fr; }
|
||||
@@ -395,6 +468,78 @@
|
||||
</div>
|
||||
<div class="empty" id="emptyState" style="display: none; margin-top: 16px;">当前关键词没有匹配到实验。</div>
|
||||
</section>
|
||||
|
||||
<section class="card learning-panel-wrapper">
|
||||
<div class="eyebrow" id="learningBoardEyebrow">可视化学习</div>
|
||||
<h2 id="learningBoardTitle">Struts2 请求与鉴权流程图</h2>
|
||||
<p id="learningBoardDesc">通过时序卡片展示请求到达、拦截、执行和结果之间的关系,并把登录保护链路具体化,增强讲解能力。</p>
|
||||
<div class="learning-grid">
|
||||
<article class="learning-panel">
|
||||
<div class="eyebrow" id="timelineEyebrow">请求生命周期</div>
|
||||
<h3 id="timelineTitle">请求进入 → Action → 结果</h3>
|
||||
<p id="timelineDesc">Struts2 将 URL 映射到 Action,执行之前先跑拦截器,再决定返回的 JSP 或 JSON。</p>
|
||||
<div class="flow-steps">
|
||||
<div class="flow-step">
|
||||
<strong id="timelineStep1">1. 请求进来</strong>
|
||||
<p id="timelineStep1Text">Dispatcher 解析 namespace 与 action,构造参数并执行</p>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong id="timelineStep2">2. 拦截器链</strong>
|
||||
<p id="timelineStep2Text">`AuthInterceptor` 检测 Session,其他拦截器做参数/校验/文件准备</p>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong id="timelineStep3">3. 结果输出</strong>
|
||||
<p id="timelineStep3Text">Action 返回 SUCCESS/INPUT,Struts 渲染 JSP 或 JSON,浏览器读取响应</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="learning-panel">
|
||||
<div class="eyebrow" id="chainEyebrow">Action → Interceptor → Result</div>
|
||||
<h3 id="chainTitle">链式控制:操作 → 鉴权 → 渲染</h3>
|
||||
<p id="chainDesc">每个请求都必须穿过这个三段式,理解它能帮助你解释 Struts2 的核心执行模型。</p>
|
||||
<div class="chain-pill" id="chainAction">Action</div>
|
||||
<div class="chain-pill" id="chainInterceptor">Interceptor</div>
|
||||
<div class="chain-pill" id="chainResult">Result</div>
|
||||
<p id="chainExplain">Action 负责业务,拦截器负责验证/鉴权,Result 负责视图渲染或 JSON 返回。</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="eyebrow" id="insightEyebrow">学习洞察</div>
|
||||
<h2 id="insightTitle">关键链路 & 实验对照</h2>
|
||||
<div class="insight-grid">
|
||||
<article class="insight-card">
|
||||
<div class="eyebrow">登录保护链路</div>
|
||||
<h3 id="loginCardTitle">Session 登录 + 拦截器</h3>
|
||||
<p id="loginCardDesc">运用 `LoginAction`、`AuthInterceptor` 和 `DashboardAction` 来完成端到端登录和受保护导航。</p>
|
||||
<ul>
|
||||
<li id="loginCardItem1">输入 admin / 123456 写入 Session</li>
|
||||
<li id="loginCardItem2">`AuthInterceptor` 拦截未登录访问</li>
|
||||
<li id="loginCardItem3">登录后跳转到专属仪表盘再进实验</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="insight-card">
|
||||
<div class="eyebrow">表单绑定 & 校验</div>
|
||||
<h3 id="bindingCardTitle">数据绑定观察卡片</h3>
|
||||
<p id="bindingCardDesc">从 `userFormPage` 到 `submitUser`,展示字段绑定、校验触发和成功结果的行为差异。</p>
|
||||
<ul>
|
||||
<li id="bindingCardItem1">字段名:Action 属性与表单 `name` 一一对应</li>
|
||||
<li id="bindingCardItem2">验证逻辑:`UserAction#submit` 里点亮不同结果</li>
|
||||
<li id="bindingCardItem3">成功后跳转 `user/success.jsp` 显示绑定概览</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="insight-card">
|
||||
<div class="eyebrow">JSON 对照模块</div>
|
||||
<h3 id="jsonCardTitle">AJAX vs REST</h3>
|
||||
<p id="jsonCardDesc">并列演示 `ajax.action`(AJAX JSON)和 `api/users.action`(REST JSON)的输出差异,便于讲解 Struts 如何扩展接口。</p>
|
||||
<ul>
|
||||
<li id="jsonCardItem1">`ajax.action` 面向浏览器交互,返回 `success` + data</li>
|
||||
<li id="jsonCardItem2">`api/users.action` 保持 REST 语义,便于讲解 JSON 策略</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@@ -489,7 +634,39 @@
|
||||
cardJsonTitle: "AJAX 与 REST 风格返回",
|
||||
cardJsonText: "保留 JSON 动作和 REST 风格示例,用来说明经典 MVC 项目如何逐步演进到接口输出。",
|
||||
cardJsonAjax: "打开 AJAX JSON",
|
||||
cardJsonRest: "打开 REST JSON"
|
||||
cardJsonRest: "打开 REST JSON",
|
||||
learningBoardEyebrow: "可视化学习",
|
||||
learningBoardTitle: "Struts2 请求与鉴权流程图",
|
||||
learningBoardDesc: "用时序卡片展示请求穿越 Action、拦截器和 Result 的全链路,让讲解更直观。",
|
||||
timelineEyebrow: "请求生命周期",
|
||||
timelineTitle: "请求进入 → Action → 结果",
|
||||
timelineDesc: "Struts2 先解析 action,再跑拦截器,最后决定 JSP 或 JSON 的渲染。",
|
||||
timelineStep1: "1. 请求进来",
|
||||
timelineStep1Text: "Dispatcher 解析 namespace、action,注入参数并准备执行。",
|
||||
timelineStep2: "2. 拦截器链",
|
||||
timelineStep2Text: "`AuthInterceptor` 检查 Session,其它拦截器做校验/上传处理。",
|
||||
timelineStep3: "3. 结果输出",
|
||||
timelineStep3Text: "Action 返回 SUCCESS/INPUT,Struts 渲染 JSP 或 JSON 回应。",
|
||||
chainEyebrow: "Action → Interceptor → Result",
|
||||
chainTitle: "链式控制:操作 → 鉴权 → 渲染",
|
||||
chainDesc: "中间拦截器决定请求是否通过,结果决定渲染视图或接口响应。",
|
||||
chainExplain: "Action 负责业务,Interceptor 掌握鉴权细节,Result 才真正交给浏览器。",
|
||||
insightEyebrow: "学习洞察",
|
||||
insightTitle: "关键链路与实验对照",
|
||||
loginCardTitle: "Session 登录 + 拦截器",
|
||||
loginCardDesc: "用 `LoginAction`、`AuthInterceptor` 和仪表盘串起端到端链路。",
|
||||
loginCardItem1: "输入 admin / 123456 写入 Session",
|
||||
loginCardItem2: "`AuthInterceptor` 拦截未登录访问",
|
||||
loginCardItem3: "登录后跳转仪表盘再进入实验页",
|
||||
bindingCardTitle: "表单绑定观察卡片",
|
||||
bindingCardDesc: "从用户表单到 `submitUser`,看字段绑定与校验行为如何变化。",
|
||||
bindingCardItem1: "字段名与 Action 属性一一对应",
|
||||
bindingCardItem2: "`UserAction#submit` 根据校验返回不同结果",
|
||||
bindingCardItem3: "成功后跳转 `user/success.jsp` 显示汇总",
|
||||
jsonCardTitle: "AJAX vs REST JSON",
|
||||
jsonCardDesc: "对照 `ajax.action` 与 `api/users.action` 的输出,讲解 Struts JSON 扩展。",
|
||||
jsonCardItem1: "`ajax.action` 面向浏览器交互,返回 success + 数据",
|
||||
jsonCardItem2: "`api/users.action` 保持 REST 格式,便于接口讲解"
|
||||
},
|
||||
html: {
|
||||
helperList: "<li>先看登录和仪表盘,理解最经典的 Session 鉴权链路。</li><li>再看表单、校验、上传,理解 Action 如何接收与处理输入。</li><li>最后再看 AJAX 和 REST 风格返回,理解 Struts2 的扩展能力。</li>"
|
||||
@@ -562,7 +739,39 @@
|
||||
cardJsonTitle: "AJAX and REST responses",
|
||||
cardJsonText: "Keeps JSON and REST-style samples to show how a classic MVC project can evolve into API output.",
|
||||
cardJsonAjax: "Open AJAX JSON",
|
||||
cardJsonRest: "Open REST JSON"
|
||||
cardJsonRest: "Open REST JSON",
|
||||
learningBoardEyebrow: "Visual learning",
|
||||
learningBoardTitle: "Struts2 request & auth flow",
|
||||
learningBoardDesc: "Timeline cards highlight how requests traverse Action, Interceptor and Result so the flow is easier to explain.",
|
||||
timelineEyebrow: "Request lifecycle",
|
||||
timelineTitle: "Inbound → Action → Result",
|
||||
timelineDesc: "The dispatcher resolves the action, runs interceptors, then chooses which JSP or JSON should render.",
|
||||
timelineStep1: "1. Request arrives",
|
||||
timelineStep1Text: "Dispatcher maps namespace/action, binds parameters, prepares execution.",
|
||||
timelineStep2: "2. Interceptor chain",
|
||||
timelineStep2Text: "`AuthInterceptor` enforces session while others handle validation/uploads.",
|
||||
timelineStep3: "3. Result output",
|
||||
timelineStep3Text: "Action returns SUCCESS/INPUT and Struts renders JSP or JSON back to the browser.",
|
||||
chainEyebrow: "Action → Interceptor → Result",
|
||||
chainTitle: "Control chain: ops → auth → render",
|
||||
chainDesc: "Interceptors decide if the request passes; the result determines view or API output.",
|
||||
chainExplain: "Action owns business logic, Interceptor handles guardrails, Result delivers the final payload.",
|
||||
insightEyebrow: "Learning insights",
|
||||
insightTitle: "Key links & lab comparison",
|
||||
loginCardTitle: "Session login + interceptor",
|
||||
loginCardDesc: "`LoginAction`, `AuthInterceptor`, and the dashboard string together a safe auth chapter.",
|
||||
loginCardItem1: "Submit admin / 123456 to write session",
|
||||
loginCardItem2: "`AuthInterceptor` blocks unauthenticated access",
|
||||
loginCardItem3: "Successful login lands on dashboard before labs",
|
||||
bindingCardTitle: "Form binding snapshot",
|
||||
bindingCardDesc: "Observe how the user form, validation, and result respond to field binding and checks.",
|
||||
bindingCardItem1: "Form field names mirror Action properties",
|
||||
bindingCardItem2: "`UserAction#submit` routes based on validation",
|
||||
bindingCardItem3: "Success page shows a structured summary",
|
||||
jsonCardTitle: "AJAX vs REST JSON",
|
||||
jsonCardDesc: "Line up `ajax.action` and `api/users.action` to explain Struts JSON modes.",
|
||||
jsonCardItem1: "`ajax.action` targets browser interactions with success + payload",
|
||||
jsonCardItem2: "`api/users.action` stays RESTy for API teaching"
|
||||
},
|
||||
html: {
|
||||
helperList: "<li>Start with login and the dashboard to understand the classic session-auth path.</li><li>Move to forms, validation, and upload to see how actions receive and process input.</li><li>Finish with AJAX and REST-style responses to explain Struts2 extension points.</li>"
|
||||
|
||||
Reference in New Issue
Block a user