| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- (() => {
- 'use strict';
- const data = window.WORKSHOP_MANUAL_DATA;
- const storageKey = 'ksf-workshop-manual-r12-local-final-20261110';
- let completed;
- try { completed = new Set(JSON.parse(localStorage.getItem(storageKey) || '[]').filter(id => data.steps.some(s => s.id === id))); }
- catch { completed = new Set(); }
- const byId = id => document.getElementById(id);
- const escape = value => String(value).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
- const fullPrompt = step => step.prompt || `${data.common}\n\n本节要回答:${step.question}\n\n先复用:${step.inputs}\n执行细则:${step.skillReference}\n\n本节任务:\n${step.task}\n\n本节应交付:\n${step.deliverables.map(d => `• ${d}`).join('\n')}`;
- let current = 0;
- let toastTimer;
- function toast(message) {
- byId('toast').textContent = message;
- byId('toast').classList.add('show');
- clearTimeout(toastTimer);
- toastTimer = setTimeout(() => byId('toast').classList.remove('show'), 2800);
- }
- function closeMenu() {
- document.querySelector('.sidebar').classList.remove('open');
- document.querySelector('.menu-button').setAttribute('aria-expanded', 'false');
- byId('menu-overlay').hidden = true;
- }
- function navigation() {
- byId('navigation').innerHTML = data.groups.map(group => `<div class="nav-group"><div class="nav-label">${escape(group.label)}</div>${data.steps.filter(s => s.group === group.id).map(s => `<a class="nav-item" href="#${s.id}" ${data.steps[current].id === s.id ? 'aria-current="step"' : ''}><span class="nav-num">${escape(s.number.replace('准备 ', ''))}</span><span>${escape(s.title)}</span>${completed.has(s.id) ? '<span class="nav-done" aria-label="已核对">✓</span>' : ''}</a>`).join('')}</div>`).join('');
- byId('progress-label').textContent = `${completed.size} / ${data.steps.length} 项已核对`;
- }
- function render(scroll = false) {
- const id = location.hash.slice(1);
- const found = data.steps.findIndex(s => s.id === id);
- current = found >= 0 ? found : 0;
- const step = data.steps[current];
- const group = data.groups.find(g => g.id === step.group);
- byId('group-label').textContent = group.label;
- const version = step.id === 'install' ? `<p class="version-note">工作坊 ${escape(data.release.version)} · VOC 0.3.42 <a href="${escape(data.release.guide)}" target="_blank" rel="noopener">安装说明 ↗</a></p>` : '';
- byId('chapter').innerHTML = `<header class="chapter-head"><h1><span class="chapter-index">${escape(step.number)}</span><span id="chapter-title">${escape(step.title)}</span></h1><label class="checkpoint" title="本机个人进度,不代替实际验收"><input type="checkbox" id="completed" ${completed.has(step.id) ? 'checked' : ''}>已核对</label></header>
- <div class="chapter-body"><p class="action-copy">${escape(step.action)}</p><div class="outputs-box"><b>产出</b><ul class="outputs">${step.deliverables.map(d => `<li>${escape(d)}</li>`).join('')}</ul></div>${version}
- <div class="prompt-box"><div class="prompt-top"><b>任务提示词</b><button class="copy-button" id="copy-prompt">复制提示词</button></div><pre class="prompt-text" id="prompt-text">${escape(fullPrompt(step))}</pre></div>
- <div class="quality"><h2>检查要点</h2><ul>${step.checks.map(c => `<li>${escape(c)}</li>`).join('')}</ul></div></div>
- <div class="chapter-footer"><button class="step-button" id="previous" ${current === 0 ? 'disabled' : ''}>← ${current > 0 ? escape(data.steps[current - 1].title) : '开始之前'}</button><button class="step-button step-next" id="next" ${current === data.steps.length - 1 ? 'disabled' : ''}>${current < data.steps.length - 1 ? escape(data.steps[current + 1].title) : '本组提案已整理'} →</button></div>`;
- byId('copy-prompt').addEventListener('click', () => copy(fullPrompt(step)));
- byId('completed').addEventListener('change', event => {
- event.target.checked ? completed.add(step.id) : completed.delete(step.id);
- try { localStorage.setItem(storageKey, JSON.stringify([...completed])); } catch { toast('浏览器未允许保存本机进度'); }
- navigation();
- });
- byId('previous').addEventListener('click', () => { location.hash = data.steps[current - 1].id; });
- byId('next').addEventListener('click', () => { location.hash = data.steps[current + 1].id; });
- navigation();
- closeMenu();
- document.title = `${step.title} · 康师傅工作坊操作手册`;
- if (scroll) {
- document.querySelector('.work-area').scrollIntoView({ behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'instant' : 'smooth', block: 'start' });
- byId('chapter').focus({ preventScroll: true });
- }
- }
- async function copy(text) {
- try {
- if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);
- else {
- const field = document.createElement('textarea');
- field.value = text;
- field.style.position = 'fixed';
- field.style.left = '-9999px';
- document.body.append(field);
- field.select();
- const ok = document.execCommand('copy');
- field.remove();
- if (!ok) throw new Error('COPY_UNAVAILABLE');
- }
- toast('完整提示词已复制,可发送给数字人');
- } catch {
- const selection = getSelection();
- const range = document.createRange();
- range.selectNodeContents(byId('prompt-text'));
- selection.removeAllRanges();
- selection.addRange(range);
- toast('请复制已选中的完整提示词');
- }
- }
- document.querySelector('.menu-button').addEventListener('click', event => {
- const open = !document.querySelector('.sidebar').classList.contains('open');
- document.querySelector('.sidebar').classList.toggle('open', open);
- event.currentTarget.setAttribute('aria-expanded', String(open));
- byId('menu-overlay').hidden = !open;
- });
- byId('menu-overlay').addEventListener('click', closeMenu);
- document.addEventListener('keydown', event => { if (event.key === 'Escape') closeMenu(); });
- window.addEventListener('hashchange', () => render(true));
- render(false);
- })();
|