综合案例:学员注册页面
这一节不引入 CSS、JavaScript 或后端框架,只使用前面学过的 HTML,完成一张结构清楚、能够原生校验的学员注册页面。
明确案例目标
页面需要包含:
- 完整的 HTML 文档骨架。
- 页面导航和课程说明。
- 本地头像占位图与帮助链接。
- 报名步骤列表和课程信息表格。
- 分组合理的注册表单。
- 浏览器原生必填、长度、类型与范围校验。
规划页面结构
body
├── header
│ ├── 标题
│ └── nav
├── main
│ ├── 课程说明 section
│ ├── 报名步骤 section
│ ├── 课程安排 section
│ └── 注册表单 section
└── footer先确定内容层级,再填写标签。此时不讨论颜色、间距和横向布局。
搭建文档骨架与页头
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FastAPI Web 全栈课程报名</title>
</head>
<body>
<header>
<p>正心教育 · Web 全栈基础</p>
<h1>FastAPI Web 全栈课程报名</h1>
<nav aria-label="页面导航">
<a href="#course-intro">课程说明</a>
<a href="#schedule">课程安排</a>
<a href="#register">填写资料</a>
</nav>
</header>
<main>
<!-- 页面主要内容 -->
</main>
<footer>
<small>© 2026 正心教育</small>
</footer>
</body>
</html>导航链接通过片段标识跳到页面内对应的 id。
添加课程说明与本地图片
<section id="course-intro">
<h2>课程说明</h2>
<img
src="./images/avatar-placeholder.svg"
alt="默认学员头像占位图"
width="120"
height="120"
>
<p>
本课程从零学习 Web 前端,也可为后续后端项目开发做好准备。
</p>
<p>
填写前可以先阅读
<a href="#register">报名要求</a>。
</p>
</section>图片与页面一起放在案例目录,不依赖外部图片服务。移动整个目录后仍能正常显示。
展示报名步骤与课程安排
<section>
<h2>报名步骤</h2>
<ol>
<li>阅读课程说明。</li>
<li>选择班级和学习方式。</li>
<li>填写并核对个人资料。</li>
<li>提交报名信息。</li>
</ol>
</section>
<section id="schedule">
<h2>课程安排</h2>
<table id="class-schedule">
<caption>可选班级</caption>
<thead>
<tr>
<th scope="col">班级</th>
<th scope="col">上课时间</th>
<th scope="col">学习方式</th>
</tr>
</thead>
<tbody id="class-schedule-body">
<tr>
<th scope="row">A01</th>
<td>周一、周三晚</td>
<td>线上</td>
</tr>
<tr>
<th scope="row">A02</th>
<td>周六全天</td>
<td>线下</td>
</tr>
</tbody>
</table>
</section>列表表示有顺序的流程,表格表示具有行列关系的班级数据。
完成注册表单
<section id="register">
<h2>填写注册资料</h2>
<p>标记为“必填”的项目必须填写。</p>
<form id="student-registration-form" action="/students" method="post" enctype="multipart/form-data">
<fieldset>
<legend>基本信息</legend>
<p>
<label for="name">姓名(必填)</label>
<input
id="name"
name="name"
type="text"
minlength="2"
maxlength="20"
autocomplete="name"
required
>
</p>
<p>
<label for="email">邮箱(必填)</label>
<input
id="email"
name="email"
type="email"
autocomplete="email"
required
>
</p>
<p>
<label for="mobile">手机号(必填)</label>
<input
id="mobile"
name="mobile"
type="tel"
autocomplete="tel"
required
>
</p>
<p>
<label for="age">年龄</label>
<input id="age" name="age" type="number" min="16" max="80">
</p>
</fieldset>
<fieldset>
<legend>课程选择</legend>
<p>
<label for="class-code">班级(必填)</label>
<select id="class-code" name="class_code" required>
<option value="">请选择班级</option>
<option value="A01">A01 · 周一、周三晚</option>
<option value="A02">A02 · 周六全天</option>
</select>
</p>
<fieldset>
<legend>学习方式(必填)</legend>
<label>
<input name="study_mode" type="radio" value="online" required>
线上学习
</label>
<label>
<input name="study_mode" type="radio" value="offline">
线下学习
</label>
</fieldset>
<fieldset>
<legend>希望补充的基础</legend>
<label>
<input name="interests" type="checkbox" value="html">
HTML
</label>
<label>
<input name="interests" type="checkbox" value="css">
CSS
</label>
<label>
<input name="interests" type="checkbox" value="javascript">
JavaScript
</label>
</fieldset>
</fieldset>
<fieldset>
<legend>补充资料</legend>
<p>
<label for="avatar">头像</label>
<input id="avatar" name="avatar" type="file" accept="image/*">
</p>
<p>
<label for="remark">备注</label>
<textarea
id="remark"
name="remark"
rows="5"
maxlength="200"
placeholder="例如:希望参加周末班"
></textarea>
</p>
</fieldset>
<p>
<label>
<input name="agreement" type="checkbox" value="accepted" required>
我已核对信息并同意报名说明(必填)
</label>
</p>
<p>
<button type="submit">提交报名</button>
<button type="reset">恢复初始值</button>
</p>
</form>
</section>认识本案例的字段契约
这张表单是“课程报名”场景,class_code、study_mode 和 interests 是报名字段;它们不等同于后续学员 CRUD 接口中的 class_name、disabled 和 course。后续 JavaScript 会在具体页面中把表单数据整理成对应的数据对象,不能把显示文字、表单字段名和 API 字段名混为一谈。
可选字段也有不同的空值情况:未填写文本通常是空字符串,未勾选的复选框可能不会提交字段,未选择文件则没有文件对象。p01 只定义这些字段,空值整理和对象转换放到后续 JavaScript 章节。
当前没有 /students 后端接口,表单通过原生校验后可能进入不存在的地址。这不影响本节目标:先确认 HTML 结构和浏览器校验正确。
逐项验收页面
完整页面源码见本节末尾;将文件按导入路径保存后,打开 .code/p01/c08-student-form-case/01-student-form/index.html 并依次检查:
- 页面标题和中文是否正常显示。
- 顶部三个链接是否跳到正确区域。
- 本地头像占位图是否显示。
- 不填写必填项时,提交是否被浏览器阻止。
- 错误邮箱、过小年龄是否触发校验。
- 单选按钮是否只能选择一个学习方式。
- “恢复初始值”是否恢复到页面加载时的状态。
- 浏览器开发者工具中,标签嵌套是否正确。
复盘知识来源
| 页面内容 | 对应知识 |
|---|---|
| 文档骨架、注释和路径 | 基础标签 |
| 标题、段落、强调文字 | 文本标签 |
| 图片、导航和页内跳转 | 链接与图片 |
| 步骤和班级安排 | 列表与表格 |
| 文本字段和提交方式 | 表单基础 |
| 单选、多选、下拉和文件 | 更多表单控件 |
| 页面区域和浏览器校验 | HTML5 语义与原生校验 |
AI 协作任务:检查表单结构
把当前 HTML 文件交给任意 AI 编程助手时,不要只说“帮我优化”。可以这样说明任务:
目标:检查学员注册页的 HTML 结构和原生表单校验。
上下文:这是零基础课程 p01 综合案例,目前只学习 HTML。
约束:不能添加 CSS、JavaScript、框架或接口;保留现有字段名。
验收:标题层级合理,label 与控件关联,图片有 alt,必填项和输入类型正确。
请先列出问题,再一次只修改一类问题,并解释修改位置。接受建议前先对照本章知识,修改后重新打开页面,使用鼠标和键盘填写表单,并故意漏填必填项。只有浏览器原生校验正常、页面内容没有丢失,任务才算完成。AI 建议如果加入样式或脚本,应明确拒绝,因为它超出了当前 Part 的边界。
案例验收
完成本节案例后,对照下面的标准检查这张学员登记页。
验收:
- 页面有清楚的标题层级、表单分组和字段标签。
- 必填项、邮箱、年龄和长度约束可以触发浏览器原生校验。
- 图片、表格和表单字段的辅助说明关系正确。
可复制的完整页面源码
将两个文件按显示的相对路径放入 .code/p01/c08-student-form-case/01-student-form/,再用浏览器打开 index.html。
完整页面源码:.code/p01/c08-student-form-case/01-student-form/
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FastAPI Web 全栈课程报名</title>
</head>
<body>
<header>
<p>正心教育 · Web 全栈基础</p>
<h1>FastAPI Web 全栈课程报名</h1>
<nav aria-label="页面导航">
<a href="#course-intro">课程说明</a>
<a href="#schedule">课程安排</a>
<a href="#register">填写资料</a>
</nav>
</header>
<main>
<section id="course-intro">
<h2>课程说明</h2>
<img
src="./images/avatar-placeholder.svg"
alt="默认学员头像占位图"
width="120"
height="120"
>
<p>本课程用于补充 Web 前端基础,为后续 FastAPI 项目开发做准备。</p>
<p>填写前可以先阅读<a href="#register">报名要求</a>。</p>
</section>
<section>
<h2>报名步骤</h2>
<ol>
<li>阅读课程说明。</li>
<li>选择班级和学习方式。</li>
<li>填写并核对个人资料。</li>
<li>提交报名信息。</li>
</ol>
</section>
<section id="schedule">
<h2>课程安排</h2>
<table id="class-schedule">
<caption>可选班级</caption>
<thead>
<tr>
<th scope="col">班级</th>
<th scope="col">上课时间</th>
<th scope="col">学习方式</th>
</tr>
</thead>
<tbody id="class-schedule-body">
<tr>
<th scope="row">A01</th>
<td>周一、周三晚</td>
<td>线上</td>
</tr>
<tr>
<th scope="row">A02</th>
<td>周六全天</td>
<td>线下</td>
</tr>
</tbody>
</table>
</section>
<section id="register">
<h2>填写注册资料</h2>
<p>标记为“必填”的项目必须填写。</p>
<form id="student-registration-form" action="/students" method="post" enctype="multipart/form-data">
<fieldset>
<legend>基本信息</legend>
<p>
<label for="name">姓名(必填)</label>
<input id="name" name="name" type="text" minlength="2" maxlength="20" autocomplete="name" required>
</p>
<p>
<label for="email">邮箱(必填)</label>
<input id="email" name="email" type="email" autocomplete="email" required>
</p>
<p>
<label for="mobile">手机号(必填)</label>
<input id="mobile" name="mobile" type="tel" autocomplete="tel" required>
</p>
<p>
<label for="age">年龄</label>
<input id="age" name="age" type="number" min="16" max="80">
</p>
</fieldset>
<fieldset>
<legend>课程选择</legend>
<p>
<label for="class-code">班级(必填)</label>
<select id="class-code" name="class_code" required>
<option value="">请选择班级</option>
<option value="A01">A01 · 周一、周三晚</option>
<option value="A02">A02 · 周六全天</option>
</select>
</p>
<fieldset>
<legend>学习方式(必填)</legend>
<label><input name="study_mode" type="radio" value="online" required> 线上学习</label>
<label><input name="study_mode" type="radio" value="offline"> 线下学习</label>
</fieldset>
<fieldset>
<legend>希望补充的基础</legend>
<label><input name="interests" type="checkbox" value="html"> HTML</label>
<label><input name="interests" type="checkbox" value="css"> CSS</label>
<label><input name="interests" type="checkbox" value="javascript"> JavaScript</label>
</fieldset>
</fieldset>
<fieldset>
<legend>补充资料</legend>
<p>
<label for="avatar">头像</label>
<input id="avatar" name="avatar" type="file" accept="image/*">
</p>
<p>
<label for="remark">备注</label>
<textarea id="remark" name="remark" rows="5" maxlength="200" placeholder="例如:希望参加周末班"></textarea>
</p>
</fieldset>
<p>
<label>
<input name="agreement" type="checkbox" value="accepted" required>
我已核对信息并同意报名说明(必填)
</label>
</p>
<p>
<button type="submit">提交报名</button>
<button type="reset">恢复初始值</button>
</p>
</form>
</section>
</main>
<footer>
<p>报名问题:<a href="mailto:support@example.com">support@example.com</a></p>
<small>© 2026 正心教育</small>
</footer>
</body>
</html><svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120" role="img" aria-labelledby="title description">
<title id="title">默认学员头像</title>
<desc id="description">圆形头像轮廓占位图</desc>
<rect width="120" height="120" rx="16" fill="#e8eef7"/>
<circle cx="60" cy="43" r="22" fill="#6b7c93"/>
<path d="M24 104c3-23 17-35 36-35s33 12 36 35" fill="#6b7c93"/>
</svg>:::
小结
- 写页面前先规划语义结构和内容层级。
- 列表、表格和表单解决不同类型的信息表达问题。
- 路径应尽量使用可随案例目录一起移动的相对路径。
- 原生校验改善填写体验,但最终数据仍由后端验证。
- 这一版页面只负责 HTML;下一部分再使用 CSS 改善视觉和布局。