Files
2026-08-04 18:07:40 +08:00

1 line
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Dart & Flutter 学习测验 (30题)</title> <style> * { margin:0; padding:0; box-sizing:border-box; font-family:'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif; } body { min-height:100vh; background: linear-gradient(135deg,#02569B 0%, #0277BD 50%, #13B9FD 100%); display:flex; align-items:center; justify-content:center; padding:20px; } .app { width:100%; max-width:760px; } .header { background:#fff; border-radius:16px 16px 0 0; padding:22px 28px 16px; box-shadow:0 10px 40px rgba(0,0,0,.25); } .header h1 { font-size:22px; color:#02569B; display:flex; align-items:center; gap:10px; } .header h1 span.logo { width:34px;height:34px;border-radius:50%; background:linear-gradient(135deg,#13B9FD,#02569B); color:#fff;display:inline-flex;align-items:center;justify-content:center; font-size:18px;font-weight:800; } .meta { display:flex; justify-content:space-between; align-items:center; margin-top:14px; font-size:13px; color:#666; } .bar { height:8px; background:#e3f2fd; border-radius:99px; margin-top:8px; overflow:hidden; } .bar-fill { height:100%; width:0%; background:linear-gradient(90deg,#13B9FD,#02569B); transition:width .4s ease; } .card { background:#fff; padding:28px; border-radius:0 0 16px 16px; box-shadow:0 10px 40px rgba(0,0,0,.25); min-height:420px; } .q-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; } .q-num { font-size:15px; color:#0288D1; font-weight:700; } .badge { font-size:11px; padding:3px 10px; border-radius:99px; font-weight:700; letter-spacing:.5px; } .lv1{ background:#e3f2fd; color:#0277BD; } .lv2{ background:#e8f5e9; color:#2e7d32; } .lv3{ background:#fff3e0; color:#e65100; } .lv4{ background:#fce4ec; color:#c2185b; } .lv5{ background:#f3e5f5; color:#7b1fa2; } .lv6{ background:#ffebee; color:#b71c1c; } .q-title { font-size:17px; line-height:1.7; color:#263238; margin-bottom:20px; font-weight:600; } .q-title code { background:#f5f5f5; padding:1px 6px; border-radius:5px; color:#0277BD; font-family:'Consolas',monospace; } .opts { display:flex; flex-direction:column; gap:12px; } .opt { border:2px solid #e0e0e0; border-radius:12px; padding:14px 18px; cursor:pointer; font-size:15px; color:#37474f; transition:all .15s; background:#fff; text-align:left; } .opt:hover:not(:disabled) { border-color:#13B9FD; background:#e1f5fe; } .opt.selected { border-color:#0277BD; background:#e1f5fe; } .opt.correct { border-color:#2e7d32; background:#e8f5e9; font-weight:700; } .opt.wrong { border-color:#c62828; background:#ffebee; } .opt:disabled { cursor:default; opacity:.92; } .opt .key { font-weight:800; margin-right:8px; color:#0277BD; } .explain { margin-top:20px; padding:16px 18px; border-radius:12px; font-size:14px; line-height:1.8; display:none; border-left:5px solid; } .explain.ok { background:#e8f5e9; border-color:#2e7d32; color:#1b5e20; display:block; } .explain.no { background:#ffebee; border-color:#c62828; color:#b71c1c; display:block; } .explain b { font-size:13px; } .btns { display:flex; justify-content:flex-end; gap:12px; margin-top:22px; } .btn { border:none; border-radius:10px; padding:12px 28px; font-size:15px; font-weight:700; cursor:pointer; transition:transform .1s, opacity .2s; } .btn:active { transform:scale(.96); } .btn-primary { background:linear-gradient(135deg,#13B9FD,#02569B); color:#fff; } .btn-ghost { background:#eceff1; color:#455a64; } .btn:disabled { opacity:.4; cursor:not-allowed; } /* 结果页 */ .result { display:none; text-align:center; padding-top:10px; } .score-circle { width:150px; height:150px; border-radius:50%; margin:10px auto 20px; display:flex; flex-direction:column; align-items:center; justify-content:center; background:conic-gradient(#13B9FD calc(var(--p)*1%), #e3f2fd 0); } .score-circle .inner { width:120px; height:120px; border-radius:50%; background:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center; } .score-circle .num { font-size:38px; font-weight:800; color:#02569B; } .score-circle .lbl { font-size:13px; color:#90a4ae; } .grade { font-size:26px; font-weight:800; margin-bottom:6px; } .grade-sub { color:#78909c; font-size:14px; margin-bottom:18px; } .review { text-align:left; max-height:260px; overflow-y:auto; border-top:1px solid #eceff1; padding-top:14px; } .review .item { font-size:13px; padding:8px 6px; border-bottom:1px dashed #eceff1; color:#455a64; line-height:1.6; } .review .ok { color:#2e7d32; font-weight:700; } .review .no { color:#c62828; font-weight:700; } </style> </head> <body> <div class="app"> <div class="header"> <h1><span class="logo">D</span>Dart & Flutter 学习测验</h1> <div class="meta"> <span id="progressText">第 1 / 30 题</span> <span id="scoreText" style="color:#13B9FD;font-weight:700;">得分:0</span> </div> <div class="bar"><div class="bar-fill" id="bar"></div></div> </div> <div class="card"> <!-- 题目卡片 --> <div id="quizBox"> <div class="q-head"> <span class="q-num" id="qNum"></span> <span class="badge" id="qBadge"></span> </div> <div class="q-title" id="qTitle"></div> <div class="opts" id="opts"></div> <div class="explain" id="explain"></div> <div class="btns"> <button class="btn btn-ghost" id="prevBtn" onclick="prev()">上一题</button> <button class="btn btn-primary" id="nextBtn" onclick="next()">下一题 →</button> </div> </div> <div class="result" id="resultBox"> <div class="score-circle" id="circle"> <div class="inner"><div class="num" id="finalScore"></div><div class="lbl">/ 30 分</div></div> </div> <div class="grade" id="grade"></div> <div class="grade-sub" id="gradeSub"></div> <div class="review" id="review"></div> <div class="btns" style="justify-content:center;margin-top:20px;"> <button class="btn btn-primary" onclick="restart()">🔄 重新测验</button> </div> </div> </div> </div><script> const questions = [ // ===== 阶段1 基础语法 ===== { lv:1, q:'Dart 中声明"类型自动推断"的变量,用哪个关键字?', opts:['var','let','const','dynamic'], ans:0, exp:'Dart 用 <b>var</b> 由编译器自动推断类型;<b>let</b> 是 JS 的;<b>const</b> 表示编译期常量。' }, { lv:1, q:'Dart 中表示 64 位整数的数据类型是?', opts:['integer','int','number','long'], ans:1, exp:'Dart 用 <b>int</b> 表示整数(64位),<b>double</b> 表示浮点数,两者父类是 <b>num</b>。' }, { lv:1, q:'以下哪个是 Dart 中正确的单行注释写法?', opts:['# 注释','/* 注释 */','// 注释','<!-- 注释 -->'], ans:2, exp:'Dart 单行注释用 <b>//</b>,多行用 <b>/* */</b>。<b>#</b> 是 Python,#! 在 Dart 中只是 shebang 声明。' }, { lv:1, q:'Dart 程序的入口函数是?', opts:['runApp()','main()','init()','start()'], ans:1, exp:'入口是 <b>main()</b>。Flutter 中通常写成 <code>void main() => runApp(MyApp());</code>' }, { lv:1, q:'要在控制台输出文字,应调用哪个函数?', opts:['console.log()','System.out.println()','echo','print()'], ans:3, exp:'Dart 用 <b>print()</b> 输出到控制台,比如 <code>print("Hello Dart");</code>' }, { lv:1, q:'<code>int x = 10;</code> 要打印出 <code>x=10</code>,以下哪种写法正确?', opts:['print("x=" + x);','print("x=$x");','print(\'x=${x}\');','以上都可以'], ans:3, exp:'Dart 中 <b>+</b> 拼接字符串、<b>$x</b> 插值、<b>${x}</b> 表达式插值都合法,三种写法均能输出 <code>x=10</code>。' }, // ===== 阶段2 控制流与函数 ===== { lv:2, q:'以下哪个 <b>不是</b> Dart 的循环写法?', opts:['for (var i=0; i<5; i++)','while (ok)','foreach (x in list)','do { } while (ok)'], ans:2, exp:'Dart <b>没有</b> <code>foreach(x in list)</code> 语法,应写 <code>for (var x in list)</code> 或 <code>list.forEach(...)</code>。' }, { lv:2, q:'执行 <code>for (var i=0; i<5; i++) print(i);</code> 会输出?', opts:['1 2 3 4 5','0 1 2 3 4','0 1 2 3 4 5','5 4 3 2 1'], ans:1, exp:'i 从 0 开始,条件 <code>i&lt;5</code>,所以输出 <b>0 1 2 3 4</b>。' }, { lv:2, q:'Dart 的 switch 语句中,每个 case 结束必须以什么结尾(否则编译报错)?', opts:['end','return / break / throw','continue','逗号'], ans:1, exp:'Dart 每个非空 case 必须以 <b>break、return 或 throw</b> 等结束,不能像 C 语言那样自动穿透。' }, { lv:2, q:'定义一个返回两数之和的函数,哪个写法正确?', opts:['int add(int a,int b){ return a+b; }','add(int a,int b) => a+b;','void add(int a,int b){ return a+b; }','A 和 B 都可以'], ans:3, exp:'A 是传统写法,B 是<b>箭头函数</b>简写(返回表达式),两者都合法;C 返回 void 却 return 值,编译错误。' }, { lv:2, q:'以下哪个是<b>可选命名参数</b>的正确声明?', opts:['void f({String name}) {}','void f([String name]) {}','void f(String? name) {}','void f(String name="") {}'], ans:0, exp:'<b>{}</b> 包裹的是命名参数(调用用 <code>f(name: x)</code>);<b>[]</b> 是可选位置参数;<b>?</b> 是空安全;最后一个是默认值写法但不属于命名参数。' }, { lv:2, q:'函数 <code>void f({int a = 1}) {}</code>,以下调用哪个正确?', opts:['f()','f(1)','f(a: 1)','A 和 C 都可以'], ans:3, exp:'命名参数有默认值时可省略(A 合法);必须<b>按名调用</b> <code>f(a: 1)</code>(C 合法);<code>f(1)</code> 用位置传参给命名参数会报错。' }, // ===== 阶段3 集合 ===== { lv:3, q:'Dart 中表示"数组/列表"的类型是?', opts:['Array','ArrayList','List','Vector'], ans:2, exp:'Dart 用 <b>List</b>(可增长数组),字面量写法 <code>[1,2,3]</code>。' }, { lv:3, q:'创建包含 1、2、3 的列表,正确的是?', opts:['{1, 2, 3}','[1, 2, 3]','(1, 2, 3)','List(1,2,3)'], ans:1, exp:'<b>[]</b> 是 List;<b>{}</b> 是 Set/Map;<b>()</b> 不是集合字面量。' }, { lv:3, q:'<code>var list = [10, 20, 30]; print(list[1]);</code> 输出?', opts:['10','20','30','报错'], ans:1, exp:'Dart 索引从 <b>0</b> 开始,list[1] 是第二个元素 <b>20</b>。' }, { lv:3, q:'对 List 每个元素做处理并<b>返回一个新集合</b>的方法是?', opts:['forEach()','map()','where()','add()'], ans:1, exp:'<b>map()</b> 对每个元素变换并返回 Iterable;<b>forEach()</b> 只遍历返回 void;<b>where()</b> 是过滤。' }, { lv:3, q:'<code>var a=[1,2,3]; var b=[0, ...a];</code> 那么 <code>b.length</code> 是?', opts:['3','4','7','报错'], ans:1, exp:'<b>...a</b> 是扩展运算符,把 a 展开成 1,2,3,加上开头的 0,b = [0,1,2,3],长度 <b>4</b>。' }, { lv:3, q:'以下哪种集合类型<b>保证元素不重复</b>?', opts:['List','Map','Set','Queue'], ans:2, exp:'<b>Set</b> 存储唯一元素,重复添加会被忽略;Map 存储键值对,键也不重复。' }, // ===== 阶段4 面向对象 ===== { lv:4, q:'定义类 <code>class Dog { String name; }</code> 后,创建一个实例哪个正确?', opts:['Dog d = Dog();','var d = new Dog;','Dog d = new Dog();','以上都可以'], ans:3, exp:'Dart 2 之后 <b>new 关键字可选</b>,且省略括号的 <code>Dog()</code> 也合法,三种写法都能创建实例。' }, { lv:4, q:'子类继承父类使用哪个关键字?', opts:['with','implements','extends','inherits'], ans:2, exp:'继承用 <b>extends</b>;<b>implements</b> 是实现接口;<b>with</b> 用于混入 Mixin。' }, { lv:4, q:'包含未实现方法、<b>不能被直接实例化</b>的类,用什么关键字声明?', opts:['abstract','interface','base','pure'], ans:0, exp:'<b>abstract</b> 抽象类可含未实现方法,不能直接 new,需子类实现后实例化。' }, { lv:4, q:'在构造函数/实例方法中,<code>this</code> 指向?', opts:['当前实例','父类对象','类本身','静态成员'], ans:0, exp:'<b>this</b> 指向当前实例对象,用于区分参数与成员变量,如 <code>this.name = name</code>。' }, { lv:4, q:'创建一个只能装 String 的 List,哪个写法正确?', opts:['List<String> names = [];','List names = <String>[];','var names = <String>[];','以上都是'], ans:3, exp:'Dart 泛型语法多样:类型标注式、泛型字面量 <code>&lt;String&gt;[]</code> 都合法,三种写法结果相同。' }, { lv:4, q:'抽象类 Animal 有抽象方法 <code>void speak()</code>,子类 Dog 必须?', opts:['重写 speak() 提供实现','调用父类构造','删除 speak','什么也不用做'], ans:0, exp:'子类必须用 <code>@override</code> 实现所有抽象方法,否则 Dog 也是抽象类无法实例化。' }, // ===== 阶段5 空安全与异步 ===== { lv:5, q:'Dart 空安全中,声明一个<b>可能为 null</b> 的字符串类型是?', opts:['String','String?','String!','nullable String'], ans:1, exp:'在类型后加 <b>?</b> 表示可空:<code>String? s = null;</code>;不加 ? 的 String 保证非空。' }, { lv:5, q:'空值合并运算符 <code>??</code> 的作用是?', opts:['若左边为 null 则返回右边','强制把 null 变为非空','判断对象是否相等','抛出空异常'], ans:0, exp:'<code>a ?? b</code>:a 不为 null 用 a,否则用 b。常配合 <code>??=</code> 设置默认值。' }, { lv:5, q:'使用 <code>async/await</code> 时,异步函数一般返回什么类型?', opts:['Stream<T>','Future<T>','void','T'], ans:1, exp:'async 函数返回 <b>Future&lt;T&gt;</b>,await 会挂起等待该 Future 完成后再继续执行。' }, { lv:5, q:'执行以下代码,输出顺序是?<br><code>Future.delayed(Duration(seconds:1), () => print("A"));<br>print("B");</code>', opts:['A 后 B','B 后 A','同时输出','只输出 A'], ans:1, exp:'Future.delayed 是异步任务,先执行同步的 <code>print("B")</code>,1 秒后再输出 A。' }, // ===== 阶段6 Flutter 入门 ===== { lv:6, q:'Flutter 中用于<b>监听持续不断的数据流</b>(如多次事件)的是?', opts:['Future','Stream','List','Map'], ans:1, exp:'<b>Stream</b> 可监听多次异步事件;Future 只代表一次性的结果。' }, { lv:6, q:'Flutter 中构建界面的<b>基本单位</b>是?', opts:['Activity','View','Widget','Component'], ans:2, exp:'Flutter 一切皆 <b>Widget</b>,UI 由 Widget 树组合而成(如 Text、Container、Column)。' }, { lv:6, q:'界面状态需要<b>随数据变化而更新</b>时,应使用哪类组件?', opts:['StatelessWidget','StatefulWidget','Container','Scaffold'], ans:1, exp:'有动态状态用 <b>StatefulWidget</b>(配合 State 里的 setState 刷新);无状态用 StatelessWidget。' }, ]; let cur = 0; // 当前题索引 let score = 0; let userAnswers = new Array(questions.length).fill(null); // 每题选择的索引 let answered = new Array(questions.length).fill(false); const lvName = ['','⭐ 基础','⭐⭐ 控制流与函数','⭐⭐⭐ 集合','⭐⭐⭐⭐ 面向对象','⭐⭐⭐⭐⭐ 空安全与异步','🚀 Flutter 入门']; const lvClass = ['','lv1','lv2','lv3','lv4','lv5','lv6']; function render() { const q = questions[cur]; document.getElementById('qNum').textContent = `第 ${cur+1} / ${questions.length} 题`; document.getElementById('qBadge').textContent = lvName[q.lv]; document.getElementById('qBadge').className = 'badge ' + lvClass[q.lv]; document.getElementById('qTitle').innerHTML = q.q; const optsBox = document.getElementById('opts'); optsBox.innerHTML = ''; const keys = ['A','B','C','D']; q.opts.forEach((o, i) => { const btn = document.createElement('button'); btn.className = 'opt'; btn.innerHTML = `<span class="key">${keys[i]}.</span>${o}`; btn.onclick = () => choose(i, btn); // 若已答过,恢复选中/判分样式 if (answered[cur]) { btn.disabled = true; if (i === q.ans) btn.classList.add('correct'); if (i === userAnswers[cur] && i !== q.ans) btn.classList.add('wrong'); } optsBox.appendChild(btn); }); const expBox = document.getElementById('explain'); if (answered[cur]) { const isOK = userAnswers[cur] === q.ans; expBox.className = 'explain ' + (isOK ? 'ok' : 'no'); expBox.innerHTML = (isOK ? '✅ 回答正确!' : '❌ 回答错误,正确答案是 <b>' + keys[q.ans] + '</b>。<br>') + '📖 解析:' + q.exp; } else { expBox.className = 'explain'; expBox.innerHTML = ''; } document.getElementById('prevBtn').disabled = cur === 0; document.getElementById('nextBtn').textContent = cur === questions.length - 1 ? '查看成绩 🎯' : '下一题 →'; document.getElementById('progressText').textContent = `第 ${cur+1} / ${questions.length} 题`; document.getElementById('bar').style.width = ((cur+1)/questions.length*100) + '%'; document.getElementById('scoreText').textContent = `得分:${score}`; } function choose(i, btn) { if (answered[cur]) return; answered[cur] = true; userAnswers[cur] = i; const q = questions[cur]; if (i === q.ans) score++; render(); } function next() { if (cur < questions.length - 1) { cur++; render(); } else showResult(); } function prev() { if (cur > 0) { cur--; render(); } } function showResult() { document.getElementById('quizBox').style.display = 'none'; const rb = document.getElementById('resultBox'); rb.style.display = 'block'; const pct = score / questions.length * 100; document.getElementById('circle').style.setProperty('--p', pct.toFixed(1)); document.getElementById('finalScore').textContent = score; let grade, sub; if (pct >= 90) { grade = '🏆 大神级'; sub = 'Dart 掌握得非常扎实,可以放心进入 Flutter 实战了!'; } else if (pct >= 75) { grade = '🌟 优秀'; sub = '基础很牢,建议把错题解析再看一遍,查漏补缺。'; } else if (pct >= 60) { grade = '👍 良好'; sub = '整体不错,但还有薄弱点,针对性复习错题对应的章节。'; } else if (pct >= 40) { grade = '📖 及格边缘'; sub = '需要加强巩固,建议重点复习错题所在的难度阶段。'; } else { grade = '💪 仍需努力'; sub = '建议重新通读 Dart 基础 + 面向对象部分再测一次。'; } document.getElementById('grade').textContent = grade; document.getElementById('gradeSub').textContent = sub; // 错题回顾 const keys = ['A','B','C','D']; const review = document.getElementById('review'); let html = '<div style="font-weight:700;color:#02569B;margin-bottom:6px;">📋 答题回顾</div>'; questions.forEach((q, i) => { const isOK = userAnswers[i] === q.ans; html += `<div class="item"> <b>${i+1}.</b> ${q.q.replace(/<br>/g,' ').slice(0,46)}... ${isOK ? '<span class="ok">✔ 对</span>' : '<span class="no">✘ 错(你的答案:' + (userAnswers[i]==null?'未答':keys[userAnswers[i]]) + ',正确:' + keys[q.ans] + ')</span>'} </div>`; }); review.innerHTML = html; } function restart() { cur = 0; score = 0; userAnswers = new Array(questions.length).fill(null); answered = new Array(questions.length).fill(false); document.getElementById('resultBox').style.display = 'none'; document.getElementById('quizBox').style.display = 'block'; render(); } render(); </script></body> </html>