1 line
18 KiB
HTML
1 line
18 KiB
HTML
<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<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><String>[]</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<T></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> |