/* 计算器分步模式(11-04 用户:「其他计算器都按生娃计算器那种模式:点进去一步一步填,最后出答案」) 用法:页面原来的计算逻辑不动(照样实时往结果框里写),本脚本只管「显示哪一块」: CKW.init({ root: '.calc', res: '#res', after: '#scroll > section.calc ~ *' }) · root 里每个 .fld 是一题;.tabs(算法切换)单独一题,题目取 data-q;包在 div 里的 .fld 会被拆开,带 .hide 的跳过; .calc 直属的 .hint 跟着上一题。 · 必填:有 input/select/.seg 的题要先填;标签里写了「选填」或 .fld 带 data-opt 的不拦。 · 只有一组 .seg / .tabs 的题,点完自动下一题(同生娃计算器)。 · 最后一题按钮是「看结果」:结果框里还是 .empty(没填全 / 数不对)就不放行,把那句提示显示出来。 · 结果屏:只出结果框 + after 选中的内容(详细说明等)+「重新算」(回第 1 题,填过的都留着)。 · 记住停在哪一屏(AGENTS 规则:测试 / 计算器再进来回到最后停的屏),键 mmbox.wz.v1,按页面路径分。 */ (function (g) { 'use strict'; var KEY = 'mmbox.wz.v1'; function load() { try { return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch (e) { return {}; } } function save(v) { try { var m = load(); m[location.pathname] = v; localStorage.setItem(KEY, JSON.stringify(m)); } catch (e) {} } function hidden(el) { return el.classList.contains('hide') || el.hidden; } function txt(el) { return el ? el.textContent.replace(/\s+/g, ' ').trim() : ''; } function init(o) { var root = typeof o.root === 'string' ? document.querySelector(o.root) : o.root; var res = typeof o.res === 'string' ? document.querySelector(o.res) : o.res; if (!root || !res || root.__wz) return; root.__wz = 1; var after = o.after ? [].slice.call(document.querySelectorAll(o.after)).filter(function (e) { return !/toast|mask/.test(e.className + ' ' + e.id); }) : []; var sc = document.getElementById('scroll') || document.scrollingElement; root.classList.add('wz'); /* 顶部进度 + 底部按钮 */ var top = document.createElement('div'); top.className = 'wz-top'; top.innerHTML = ''; var bot = document.createElement('div'); bot.className = 'wz-bot'; bot.innerHTML = '
'; var again = document.createElement('div'); again.className = 'wz-again'; again.innerHTML = '填过的都还在,改哪项点哪项'; root.insertBefore(top, root.firstChild); root.appendChild(bot); res.parentNode.insertBefore(again, res.nextSibling); var nEl = top.querySelector('.wz-n'), dEl = top.querySelector('.wz-dots'), err = bot.querySelector('.wz-err'), prev = bot.querySelector('.wz-prev'), next = bot.querySelector('.wz-next'), tip = bot.querySelector('.wz-tip'); /* 题目单元:每次现算(页面切 tabs 会显示 / 隐藏不同的 .fld) */ function units() { var out = []; [].forEach.call(root.children, function (c) { if (c === top || c === bot || c === res || c === again || hidden(c)) return; if (c.classList.contains('fld') || c.classList.contains('tabs')) out.push([c]); else if (c.querySelector('.fld')) [].forEach.call(c.querySelectorAll('.fld'), function (f) { if (!hidden(f)) out.push([f]); }); else if (out.length) out[out.length - 1].push(c); /* 跟在题后的 .hint / 「再加一个孩子」等 */ else out.push([c]); }); return out; } /* 能被藏的块:root 的直属子元素(除进度 / 按钮 / 结果框)+ 壳里的 .fld;题里面的东西不碰 */ function all() { var out = []; [].forEach.call(root.children, function (c) { if (c === top || c === bot || c === res || c === again) return; out.push(c); if (!c.classList.contains('fld') && !c.classList.contains('tabs')) [].push.apply(out, c.querySelectorAll('.fld')); }); return out; } function single(u) { /* 只有一组按钮可选 → 选完自动下一题 */ var el = u[0]; if (el.classList.contains('tabs')) return true; return el.querySelectorAll('.seg').length === 1 && !el.querySelector('input,select,.stp'); } function missing(u) { var el = u[0]; if (el.hasAttribute('data-opt') || /选填/.test(txt(el.querySelector('.lb')))) return ''; var bad = ''; [].forEach.call(el.querySelectorAll('input,select'), function (i) { if (bad || i.type === 'hidden' || i.disabled) return; var lb = i.closest('div') && i.closest('div').querySelector('.lb'); if (lb && /选填/.test(txt(lb))) return; if (!String(i.value).trim()) bad = '先填一下「' + (txt(lb || el.querySelector('.lb')).replace(/选填.*$/, '') || '这一项') + '」'; }); if (!bad) [].forEach.call(el.querySelectorAll('.seg'), function (s) { if (!bad && !s.querySelector('.on')) bad = '先选一下' + (txt(el.querySelector('.lb')) ? '「' + txt(el.querySelector('.lb')).replace(/\s.*$/, '') + '」' : ''); }); return bad; } var i = 0, mode = 'q'; function show() { var us = units(); if (i >= us.length) i = us.length - 1; if (i < 0) i = 0; all().forEach(function (e) { e.classList.add('wz-off'); }); [].forEach.call(root.querySelectorAll('.wz-in'), function (e) { e.classList.remove('wz-in'); }); root.classList.toggle('wz-res', mode === 'r'); document.documentElement.classList.toggle('wz-q', mode !== 'r'); /* 答题时页面底部后加的相关推荐 / 评论区也先不出 */ after.forEach(function (e) { e.classList.toggle('wz-hold', mode !== 'r'); }); err.textContent = ''; if (mode === 'r') { res.classList.remove('wz-pop'); void res.offsetWidth; res.classList.add('wz-pop'); save('r'); sc.scrollTop = 0; return; } var u = us[i]; u.forEach(function (e) { e.classList.remove('wz-off'); e.classList.add('wz-in'); if (e.parentNode !== root) e.parentNode.classList.remove('wz-off'); /* 包在 #fLmp 这类壳里的题 */ }); var q = u[0].classList.contains('tabs') ? (u[0].getAttribute('data-q') || '先选一种算法') : ''; u[0].setAttribute('data-wzq', q); nEl.textContent = '第 ' + (i + 1) + ' / ' + us.length + ' 步'; dEl.innerHTML = us.map(function (_, k) { return ''; }).join(''); prev.hidden = !i; var last = i === us.length - 1; next.textContent = last ? '看结果 ›' : '下一步 ›'; next.classList.toggle('go', last); tip.textContent = single(u) && !last ? '选完自动下一步' : ''; save(i); sc.scrollTop = 0; } function go(d) { var us = units(); if (d > 0) { var m = missing(us[i]); if (m) { err.textContent = m; root.classList.remove('wz-shake'); void root.offsetWidth; root.classList.add('wz-shake'); return; } if (i === us.length - 1) { var e = res.querySelector('.empty'); if (e) { err.textContent = txt(e); return; } mode = 'r'; return show(); } } i += d; show(); } prev.addEventListener('click', function () { go(-1); }); next.addEventListener('click', function () { go(1); }); again.querySelector('.wz-re').addEventListener('click', function () { mode = 'q'; i = 0; show(); }); /* 单选题点完自动下一步(等页面自己的点击处理先跑完) */ root.addEventListener('click', function (e) { if (mode !== 'q') return; var b = e.target.closest('.seg button,.tabs button'); if (!b) return; var us = units(), u = us[i]; if (!u || u.indexOf(b.closest('.fld,.tabs')) < 0 || !single(u) || i === us.length - 1) return; setTimeout(function () { if (mode === 'q' && units()[i] && units()[i][0] === u[0]) go(1); }, 320); }); var sv = load()[location.pathname]; if (sv === 'r' && !res.querySelector('.empty')) mode = 'r'; else if (typeof sv === 'number') i = sv; show(); } g.CKW = { init: init }; })(window);