feat: add Vue2 exercises for dynamic styles, lifecycle methods, component communication, and course management dashboard

- Implement dynamic styles and event handling in Vue2 with a card component.
- Create lifecycle methods exercise to simulate async data loading and instance destruction.
- Develop a component communication exercise with props, events, and slots.
- Build a comprehensive course management dashboard with filtering, statistics, and component interactions.
This commit is contained in:
charlie
2026-03-23 10:09:29 +08:00
parent 00d3c9e4c6
commit 3435848495
48 changed files with 1705 additions and 48 deletions

View File

@@ -1,10 +1,7 @@
import "./style.css";
import { lessons } from "./lessons";
function getRequiredElement<T extends Element>(
selector: string,
parent: ParentNode = document,
): T {
function getRequiredElement<T extends Element>(selector: string, parent: ParentNode = document): T {
const element = parent.querySelector<T>(selector);
if (!element) {
@@ -35,34 +32,21 @@ app.innerHTML = `
<p id="lesson-focus" class="focus"></p>
<p id="lesson-summary" class="summary"></p>
</section>
<section class="grid">
<section >
<article class="card">
<div class="card-head">
<h3>知识点</h3>
</div>
<ul id="lesson-points" class="point-list"></ul>
</article>
<article class="card">
<div class="card-head">
<h3>运行结果</h3>
<button id="run-demo" class="run-button" type="button">运行演示</button>
</div>
<div id="lesson-output" class="output-panel"></div>
</article>
</section>
<section class="grid code-grid">
<section >
<article class="card code-card">
<div class="card-head">
<h3>Starter</h3>
</div>
<pre><code id="starter-code"></code></pre>
</article>
<article class="card code-card">
<div class="card-head">
<h3>Answer</h3>
</div>
<pre><code id="answer-code"></code></pre>
</article>
</section>
</main>
</div>
@@ -73,10 +57,7 @@ const title = getRequiredElement<HTMLElement>("#lesson-title");
const focus = getRequiredElement<HTMLElement>("#lesson-focus");
const summary = getRequiredElement<HTMLElement>("#lesson-summary");
const pointList = getRequiredElement<HTMLElement>("#lesson-points");
const output = getRequiredElement<HTMLElement>("#lesson-output");
const starterCode = getRequiredElement<HTMLElement>("#starter-code");
const answerCode = getRequiredElement<HTMLElement>("#answer-code");
const runButton = getRequiredElement<HTMLButtonElement>("#run-demo");
let activeLessonId = lessons[0]?.id ?? "";
@@ -101,12 +82,6 @@ function renderLessonList(): void {
.join("");
}
function renderOutput(lines: string[]): void {
output.innerHTML = lines
.map((line) => `<div class="output-line">${line}</div>`)
.join("");
}
function renderActiveLesson(): void {
const lesson = lessons.find((item) => item.id === activeLessonId);
@@ -117,12 +92,8 @@ function renderActiveLesson(): void {
title.textContent = `${lesson.id}. ${lesson.title}`;
focus.textContent = lesson.focus;
summary.textContent = lesson.summary;
pointList.innerHTML = lesson.keyPoints
.map((item) => `<li>${item}</li>`)
.join("");
pointList.innerHTML = lesson.keyPoints.map((item) => `<li>${item}</li>`).join("");
starterCode.textContent = lesson.starterCode;
answerCode.textContent = lesson.answerCode;
renderOutput(lesson.runDemo());
renderLessonList();
}
@@ -138,14 +109,4 @@ nav.addEventListener("click", (event) => {
renderActiveLesson();
});
runButton.addEventListener("click", () => {
const lesson = lessons.find((item) => item.id === activeLessonId);
if (!lesson) {
return;
}
renderOutput(lesson.runDemo());
});
renderActiveLesson();