feat: add TypeScript lessons and learning panel
- Introduced a new script to check TypeScript lesson files for errors. - Created a main TypeScript file to render lessons and their details. - Added lesson definitions with starter and answer codes. - Implemented a user interface for navigating and running lessons. - Styled the application with CSS for a better user experience. - Updated README to reflect the new TypeScript section and usage instructions.
This commit is contained in:
34
04-dom-events-async/10-final-dashboard/README.md
Normal file
34
04-dom-events-async/10-final-dashboard/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 练习 10:综合页面
|
||||
|
||||
## 目标
|
||||
|
||||
把 DOM、事件和异步知识拼起来,做一个可以真实互动的小页面。
|
||||
|
||||
## 项目名称
|
||||
|
||||
学习面板
|
||||
|
||||
## 任务
|
||||
|
||||
请完成一个控制台之外可见的页面交互,要求至少包含:
|
||||
|
||||
- 一个课程列表区域
|
||||
- 一个表单区域
|
||||
- 一个添加课程的交互
|
||||
- 一个点击课程切换完成状态的交互
|
||||
- 一个异步加载提示或远程数据模拟
|
||||
- 清晰的状态文案更新
|
||||
|
||||
## 建议顺序
|
||||
|
||||
1. 先看 HTML 结构
|
||||
2. 先写元素获取和渲染函数
|
||||
3. 再写表单提交和点击事件
|
||||
4. 最后补异步加载逻辑
|
||||
|
||||
## 文件
|
||||
|
||||
- [starter.html](/Users/lijiaqing/home/wwwroot/front-end-example/04-dom-events-async/10-final-dashboard/starter.html)
|
||||
- [starter.js](/Users/lijiaqing/home/wwwroot/front-end-example/04-dom-events-async/10-final-dashboard/starter.js)
|
||||
- [answer.html](/Users/lijiaqing/home/wwwroot/front-end-example/04-dom-events-async/10-final-dashboard/answer.html)
|
||||
- [answer.js](/Users/lijiaqing/home/wwwroot/front-end-example/04-dom-events-async/10-final-dashboard/answer.js)
|
||||
82
04-dom-events-async/10-final-dashboard/answer.html
Normal file
82
04-dom-events-async/10-final-dashboard/answer.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>学习面板</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 32px;
|
||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: linear-gradient(180deg, #eef4ff 0%, #f7faff 100%);
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: min(900px, calc(100% - 24px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero,
|
||||
.panel,
|
||||
.form-panel {
|
||||
margin-top: 18px;
|
||||
padding: 24px;
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dbe4f0;
|
||||
}
|
||||
|
||||
.lesson-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: #f8fbff;
|
||||
border: 1px solid #dbe4f0;
|
||||
}
|
||||
|
||||
.lesson-item.done {
|
||||
background: #dcfce7;
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="page">
|
||||
<section class="hero">
|
||||
<h1>DOM + 事件 + 异步学习面板</h1>
|
||||
<p id="status-text">正在初始化页面...</p>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>课程列表</h2>
|
||||
<button id="load-btn" type="button">模拟加载课程</button>
|
||||
<ul id="lesson-list"></ul>
|
||||
</section>
|
||||
|
||||
<section class="form-panel">
|
||||
<h2>添加课程</h2>
|
||||
<form id="lesson-form">
|
||||
<input id="lesson-input" type="text" placeholder="输入课程名称" />
|
||||
<button type="submit">添加</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="./answer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
84
04-dom-events-async/10-final-dashboard/answer.js
Normal file
84
04-dom-events-async/10-final-dashboard/answer.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const lessons = [
|
||||
{ title: "获取元素", done: false },
|
||||
{ title: "修改 DOM", done: true },
|
||||
];
|
||||
|
||||
function fakeLoadExtraLessons() {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve([
|
||||
{ title: "事件委托", done: false },
|
||||
{ title: "异步渲染", done: false },
|
||||
]);
|
||||
}, 900);
|
||||
});
|
||||
}
|
||||
|
||||
const statusText = document.getElementById("status-text");
|
||||
const loadButton = document.getElementById("load-btn");
|
||||
const lessonList = document.getElementById("lesson-list");
|
||||
const lessonForm = document.getElementById("lesson-form");
|
||||
const lessonInput = document.getElementById("lesson-input");
|
||||
|
||||
function renderLessons() {
|
||||
lessonList.innerHTML = "";
|
||||
|
||||
lessons.forEach(function (lesson, index) {
|
||||
const item = document.createElement("li");
|
||||
item.className = "lesson-item";
|
||||
|
||||
if (lesson.done) {
|
||||
item.classList.add("done");
|
||||
}
|
||||
|
||||
item.dataset.index = index;
|
||||
item.textContent = lesson.title;
|
||||
lessonList.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
lessonList.addEventListener("click", function (event) {
|
||||
const currentItem = event.target.closest(".lesson-item");
|
||||
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = Number(currentItem.dataset.index);
|
||||
lessons[index].done = !lessons[index].done;
|
||||
renderLessons();
|
||||
});
|
||||
|
||||
lessonForm.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
const value = lessonInput.value.trim();
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
lessons.push({
|
||||
title: value,
|
||||
done: false,
|
||||
});
|
||||
|
||||
lessonInput.value = "";
|
||||
statusText.textContent = "已新增一门课程";
|
||||
renderLessons();
|
||||
});
|
||||
|
||||
loadButton.addEventListener("click", async function () {
|
||||
statusText.textContent = "正在异步加载课程...";
|
||||
|
||||
const newLessons = await fakeLoadExtraLessons();
|
||||
newLessons.forEach(function (lesson) {
|
||||
lessons.push(lesson);
|
||||
});
|
||||
|
||||
statusText.textContent = "额外课程加载完成";
|
||||
renderLessons();
|
||||
});
|
||||
|
||||
statusText.textContent = "页面初始化完成";
|
||||
renderLessons();
|
||||
82
04-dom-events-async/10-final-dashboard/starter.html
Normal file
82
04-dom-events-async/10-final-dashboard/starter.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>学习面板</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 32px;
|
||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: linear-gradient(180deg, #eef4ff 0%, #f7faff 100%);
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: min(900px, calc(100% - 24px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero,
|
||||
.panel,
|
||||
.form-panel {
|
||||
margin-top: 18px;
|
||||
padding: 24px;
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dbe4f0;
|
||||
}
|
||||
|
||||
.lesson-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: #f8fbff;
|
||||
border: 1px solid #dbe4f0;
|
||||
}
|
||||
|
||||
.lesson-item.done {
|
||||
background: #dcfce7;
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="page">
|
||||
<section class="hero">
|
||||
<h1>DOM + 事件 + 异步学习面板</h1>
|
||||
<p id="status-text">正在初始化页面...</p>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>课程列表</h2>
|
||||
<button id="load-btn" type="button">模拟加载课程</button>
|
||||
<ul id="lesson-list"></ul>
|
||||
</section>
|
||||
|
||||
<section class="form-panel">
|
||||
<h2>添加课程</h2>
|
||||
<form id="lesson-form">
|
||||
<input id="lesson-input" type="text" placeholder="输入课程名称" />
|
||||
<button type="submit">添加</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="./starter.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
22
04-dom-events-async/10-final-dashboard/starter.js
Normal file
22
04-dom-events-async/10-final-dashboard/starter.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const lessons = [
|
||||
{ title: "获取元素", done: false },
|
||||
{ title: "修改 DOM", done: true },
|
||||
];
|
||||
|
||||
function fakeLoadExtraLessons() {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve([
|
||||
{ title: "事件委托", done: false },
|
||||
{ title: "异步渲染", done: false },
|
||||
]);
|
||||
}, 900);
|
||||
});
|
||||
}
|
||||
|
||||
// 任务:
|
||||
// 1. 获取页面里的关键元素
|
||||
// 2. 写一个 renderLessons 函数,把 lessons 渲染到列表
|
||||
// 3. 点击列表项时切换 done 状态
|
||||
// 4. 提交表单时阻止默认提交,并新增课程
|
||||
// 5. 点击加载按钮时显示加载状态,并把异步返回的课程追加到列表
|
||||
Reference in New Issue
Block a user