r104/app.js · 约 20271 字符
1/* 小草问卷调查 H5 —— 单页 hash 路由 + 底部 Tab */2(function () {3 'use strict';45 // ---------- 数据 ----------6 var USER = { name: '小草用户', phone: '138****6677', points: 1280, level: '黄金调研员', answers: 36, published: 5 };78 var CATS = [9 { ic: '📋', lb: '市场调研' }, { ic: '🎓', lb: '学术问卷' },10 { ic: '🎉', lb: '活动报名' }, { ic: '💡', lb: '产品反馈' },11 { ic: '🏢', lb: '员工测评' }, { ic: '🏥', lb: '健康调查' },12 { ic: '🛒', lb: '消费洞察' }, { ic: '❤️', lb: '满意度' }13 ];1415 var QS = [16 { id: 'q1', ti: '2026 新能源汽车消费意愿调研', de: '了解用户对新能源车的购买计划与关注因素', cat: '市场调研', thumb: '🚗', grad: 'linear-gradient(135deg,#15c39a,#1aa6e8)', qn: 12, join: 3820, rew: '+20 积分', tags: ['热门', '有奖'] },17 { id: 'q2', ti: '大学生线上学习习惯调查', de: '高校学习行为研究,匿名填写,结果公开', cat: '学术问卷', thumb: '🎓', grad: 'linear-gradient(135deg,#6c5ce7,#a05cff)', qn: 15, join: 1560, rew: '+15 积分', tags: ['匿名', '学术'] },18 { id: 'q3', ti: '城市马拉松志愿者报名', de: '2026 春季马拉松志愿者信息收集与岗位分配', cat: '活动报名', thumb: '🏃', grad: 'linear-gradient(135deg,#ff9a3c,#ff6a3c)', qn: 8, join: 890, rew: '纪念衫', tags: ['招募中'] },19 { id: 'q4', ti: 'APP 改版体验反馈收集', de: '新版首页与发布流程的满意度与改进建议', cat: '产品反馈', thumb: '📱', grad: 'linear-gradient(135deg,#1aa6e8,#15c39a)', qn: 10, join: 2410, rew: '+10 积分', tags: ['官方'] },20 { id: 'q5', ti: '员工敬业度年度测评', de: '企业内部匿名敬业度调研,助力组织健康', cat: '员工测评', thumb: '🏢', grad: 'linear-gradient(135deg,#3a7bd5,#15c39a)', qn: 20, join: 640, rew: '内部', tags: ['企业'] },21 { id: 'q6', ti: '社区养老服务需求调查', de: '倾听居民对社区养老服务的真实需求', cat: '健康调查', thumb: '🏥', grad: 'linear-gradient(135deg,#26c6a4,#1aa6e8)', qn: 14, join: 1120, rew: '+12 积分', tags: ['公益'] }22 ];2324 var MINE = [25 { ti: '门店顾客满意度回访', qn: 9, join: 326, status: '收集中', prog: 0.82 },26 { ti: '新品口味偏好测试', qn: 11, join: 158, status: '收集中', prog: 0.44 },27 { ti: '会员权益调研', qn: 7, join: 502, status: '已结束', prog: 1 },28 { ti: '节日活动意向调查', qn: 6, join: 211, status: '草稿', prog: 0.1 },29 { ti: '配送时效满意度', qn: 8, join: 433, status: '已结束', prog: 1 }30 ];3132 var ARTS = [33 { id: 'a1', cv: '问卷设计', ti: '写好一份问卷的 6 个基本原则', am: '阅读 3.2k · 收藏 480', de: '从目标到题目的结构化方法' },34 { id: 'a2', cv: '提高回收', ti: '如何让问卷回收率提升 3 倍', am: '阅读 2.1k · 收藏 312', de: '激励、渠道与节奏' },35 { id: 'a3', cv: '数据分析', ti: '问卷数据入门:从交叉分析到可视化', am: '阅读 1.8k · 收藏 276', de: '读懂你的受访者' },36 { id: 'a4', cv: '合规隐私', ti: '匿名与隐私:问卷合规必读', am: '阅读 1.5k · 收藏 198', de: '保护每一位填写者' }37 ];3839 var MSGS = [40 { md: '📊', mt: '问卷回收提醒', mx: '您发布的《门店顾客满意度回访》新增 12 份有效回答。', ti: '10 分钟前' },41 { md: '🎁', mt: '积分到账', mx: '完成《APP 改版体验反馈》获得 10 积分,当前 1280 分。', ti: '2 小时前' },42 { md: '✅', mt: '审核通过', mx: '您提交的《会员权益调研》已通过审核并公开发布。', ti: '昨天' },43 { md: '💡', mt: '小贴士', mx: '设置合理的答题时长(3 分钟内)可显著提升完成率。', ti: '3 天前' }44 ];4546 var Q_DEMO = [47 { t: '您最常使用问卷的场景是?', type: 'single', opts: ['工作调研', '学术研究', '活动报名', '产品反馈'] },48 { t: '您期望一份问卷的填写时长?', type: 'single', opts: ['1 分钟以内', '3 分钟以内', '5 分钟以上'] },49 { t: '您最关注问卷的哪些方面?(可多选)', type: 'multi', opts: ['题目设计', '匿名保护', '奖励回馈', '数据可信'] },50 { t: '对小草问卷调查的期待或建议', type: 'text' }51 ];5253 // ---------- 工具 ----------54 function go(route) { location.hash = route; }55 function goAttr(r) { return 'data-go="' + r + '"'; }56 function alertAttr(m) { return 'data-alert="' + m + '"'; }57 function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }5859 function homeTop() {60 return '<div class="top"><div class="row"><div><h1>小草问卷调查</h1><div class="sub">轻松发布 · 高效回收</div></div>' +61 '<div class="iconbtn" ' + goAttr('message') + '>🔔</div></div>' +62 '<div class="search"><span>🔍</span><input placeholder="搜索问卷 / 话题" /></div></div>';63 }64 function innTop(t) {65 return '<div class="innTop"><span class="back" ' + goAttr('home') + '>‹</span><span class="t">' + esc(t) + '</span></div>';66 }6768 // ---------- 页面主体 ----------69 function homeBody() {70 var stats = '<div class="stats"><div class="st"><b>1.2w+</b><span>累计问卷</span></div>' +71 '<div class="st"><b>86w+</b><span>回收份数</span></div>' +72 '<div class="st"><b>98%</b><span>用户好评</span></div></div>';73 var cats = '<div class="section-t">问卷分类 <span class="more" ' + goAttr('list') + '>全部 ›</span></div><div class="cats">';74 CATS.forEach(function (c, i) { cats += '<div class="c" ' + goAttr('list?cat=' + i) + '><div class="ic">' + c.ic + '</div><div class="lb">' + c.lb + '</div></div>'; });75 cats += '</div>';76 var list = '<div class="section-t">热门问卷 <span class="more" ' + goAttr('list') + '>查看更多 ›</span></div>';77 QS.slice(0, 4).forEach(function (q) { list += qCard(q); });78 return stats + cats + list;79 }8081 function qCard(q) {82 return '<div class="card nb qcard" ' + goAttr('fill?id=' + q.id) + '>' +83 '<div class="thumb" style="background:' + q.grad + '">' + q.thumb + '</div>' +84 '<div class="info"><div class="ti">' + esc(q.ti) + '</div><div class="de">' + esc(q.de) + '</div>' +85 '<div class="tags">' + q.tags.map(function (t) { return '<span>' + esc(t) + '</span>'; }).join('') + '</div>' +86 '<div class="meta"><span>📝 ' + q.qn + ' 题</span><span>👥 ' + q.join + '</span><span class="rew">' + q.rew + '</span></div></div></div>';87 }8889 function listBody(q) {90 var title = '全部问卷';91 if (q && q.cat !== undefined && CATS[q.cat]) title = CATS[q.cat].lb;92 var body = '<div class="section-t" style="margin-top:14px">为你推荐</div>';93 QS.forEach(function (x) { body += qCard(x); });94 return innTop(title) + body;95 }9697 function fillBody(q) {98 var item = null;99 if (q && q.id) { for (var i = 0; i < QS.length; i++) if (QS[i].id === q.id) item = QS[i]; }100 item = item || QS[0];101 var h = innTop('填写问卷') +102 '<div class="card nb"><div style="font-weight:700;font-size:16px">' + esc(item.ti) + '</div>' +103 '<div class="tags" style="margin-top:8px"><span>' + esc(item.cat) + '</span><span>' + item.qn + ' 题</span><span class="rew" style="background:#fff3e8;color:#ff8a00">' + item.rew + '</span></div>' +104 '<div class="prog"><i style="width:0%"></i></div><div style="font-size:11px;color:#9aa3b0;margin-top:6px">预计 2 分钟 · 匿名保护</div></div>';105 Q_DEMO.forEach(function (qq, idx) {106 h += '<div class="q"><div class="qt"><span class="num">' + (idx + 1) + '.</span>' + esc(qq.t) + (qq.type !== 'text' ? '<span class="req">*</span>' : '') + '</div>';107 if (qq.type === 'text') {108 h += '<textarea class="field" style="margin:8px 0 0;width:100%;border:1px solid #e3e8f0;border-radius:10px;padding:11px;font-size:14px;outline:none" placeholder="请输入您的想法…"></textarea>';109 } else {110 qq.opts.forEach(function (o) {111 h += '<div class="opt" data-single="' + (qq.type === 'single' ? '1' : '0') + '"><span class="' + (qq.type === 'single' ? 'dot' : 'dot box') + '"></span>' + esc(o) + '</div>';112 });113 }114 h += '</div>';115 });116 h += '<div class="btn" data-fill-submit="1">提交问卷</div>';117 return h;118 }119120 function doneBody() {121 return '<div class="done"><div class="circle">✓</div><h2>提交成功,感谢参与!</h2>' +122 '<p>您获得 <b style="color:#ff8a00">+10 积分</b>,可在「我的」中查看</p>' +123 '<div class="btn ghost" ' + goAttr('home') + '>返回首页</div>' +124 '<div class="btn" ' + goAttr('knowledge') + '>读一篇问卷知识</div></div>';125 }126127 function publishBody() {128 var h = innTop('发布问卷') +129 '<div class="field"><label>问卷标题</label><input placeholder="例如:门店顾客满意度回访" /></div>' +130 '<div class="field"><label>问卷简介</label><textarea placeholder="一句话说明这份问卷的用途"></textarea></div>' +131 '<div class="field"><label>所属分类</label><select><option>市场调研</option><option>学术问卷</option><option>活动报名</option><option>产品反馈</option><option>员工测评</option><option>满意度</option></select></div>' +132 '<div class="section-t" style="margin:14px 16px 0">题目 <span class="more">已添加 2 题</span></div>';133 [['单选题','您对本次服务的整体满意度?'], ['多选题','您关注哪些方面?']].forEach(function (p) {134 h += '<div class="q"><div class="qt"><span class="num">●</span>' + p[0] + '</div><div class="opt"><span class="dot"></span>示例选项 A</div><div class="opt"><span class="dot"></span>示例选项 B</div></div>';135 });136 h += '<div class="qadd"><input placeholder="输入新题目后点击添加" /><div class="btn sm" ' + alertAttr('已添加题目') + '>添加</div></div>';137 h += '<div class="field" style="margin-top:0"><label>答题奖励</label><select><option>+10 积分</option><option>+20 积分</option><option>红包 1 元</option><option>无奖励</option></select></div>';138 h += '<div class="btn" data-publish="1">发布并公开</div>';139 return h;140 }141142 function mineBody() {143 var h = innTop('我的问卷') + '<div class="kpis"><div class="k"><b>' + USER.published + '</b><span>已发布</span></div>' +144 '<div class="k"><b>2.6k</b><span>总回收</span></div><div class="k"><b>92%</b><span>平均完成率</span></div></div>';145 MINE.forEach(function (m) {146 h += '<div class="card nb"><div style="display:flex;justify-content:space-between;align-items:center"><b>' + esc(m.ti) + '</b>' +147 '<span style="font-size:11px;color:#15a07e;background:#eafaf5;padding:2px 8px;border-radius:8px">' + m.status + '</span></div>' +148 '<div class="meta" style="display:flex;gap:12px;font-size:12px;color:#8a93a3;margin:8px 0 6px"><span>📝 ' + m.qn + ' 题</span><span>👥 ' + m.join + ' 份</span></div>' +149 '<div class="prog"><i style="width:' + Math.round(m.prog * 100) + '%"></i></div>' +150 '<div style="margin-top:10px"><span class="btn sm ghost" ' + goAttr('dash') + '>查看数据</span></div></div>';151 });152 return h;153 }154155 function dashBody() {156 var days = ['一', '二', '三', '四', '五', '六', '日'];157 var vals = [40, 62, 55, 80, 72, 95, 88];158 var max = 100;159 var bars = '<div class="bars">';160 vals.forEach(function (v, i) {161 var hh = Math.round(v / max * 120);162 bars += '<div class="b" style="height:' + hh + 'px"><i>' + v + '</i><em>周' + days[i] + '</em></div>';163 });164 bars += '</div>';165 return innTop('数据看板') +166 '<div class="kpis"><div class="k"><b>2,630</b><span>总回收</span></div><div class="k"><b>92%</b><span>完成率</span></div><div class="k"><b>4.7</b><span>平均分</span></div></div>' +167 '<div class="card nb"><div style="font-weight:700;margin-bottom:10px">近 7 天回收趋势</div>' + bars + '</div>' +168 '<div class="card nb"><div style="font-weight:700;margin-bottom:10px">满意度分布</div><div class="pie">' +169 '<div class="ring"></div><div class="lg"><div><b style="background:#15c39a"></b>非常满意 42%</div><div><b style="background:#1aa6e8"></b>满意 29%</div><div><b style="background:#ffb347"></b>一般 29%</div></div></div></div>' +170 '<div class="card nb" style="font-size:12px;color:#8a93a3">数据每小时更新,支持导出 Excel 与交叉分析。</div>';171 }172173 function knowledgeBody() {174 var h = innTop('问卷知识') + '<div class="section-t" style="margin-top:14px">精选阅读</div>';175 ARTS.forEach(function (a) {176 h += '<div class="card nb art" ' + goAttr('article?id=' + a.id) + '>' +177 '<div class="cv">' + a.cv + '</div><div style="flex:1;min-width:0"><div class="at">' + esc(a.ti) + '</div><div class="am">' + a.am + '</div></div></div>';178 });179 return h;180 }181182 function articleBody(q) {183 var a = ARTS[0];184 if (q && q.id) { for (var i = 0; i < ARTS.length; i++) if (ARTS[i].id === q.id) a = ARTS[i]; }185 return innTop('文章详情') +186 '<div class="card nb"><div style="font-weight:800;font-size:18px">' + esc(a.ti) + '</div>' +187 '<div class="am" style="font-size:12px;color:#9aa3b0;margin-top:6px">' + a.am + '</div></div>' +188 '<div class="card nb rich"><h3>一、先想清楚目标</h3><p>每一份好问卷都始于明确的目标:你想知道什么、决策依据是什么。目标越清晰,题目越聚焦。</p>' +189 '<div class="callout">💡 小技巧:用一句话写下「这份问卷要帮我决定什么」,它能筛掉 80% 的无效题目。</div>' +190 '<h3>二、题目要短而具体</h3><p>避免引导性、双重含义与专业术语。一道题只问一件事,选项互斥且覆盖全面。</p>' +191 '<h3>三、控制长度与节奏</h3><p>3 分钟以内、10 题左右是完成率最高的区间;开头放简单题,敏感题靠后。</p>' +192 '<h3>四、重视匿名与回馈</h3><p>明确告知匿名保护,并设置合理奖励,能显著提升回收质量与数量。</p></div>' +193 '<div class="btn ghost" ' + goAttr('knowledge') + '>返回知识列表</div>';194 }195196 function meBody() {197 return '<div class="profile"><div class="av">🌱</div><div><div class="pn">' + USER.name + '</div>' +198 '<div class="pp">' + USER.level + ' · ' + USER.phone + '</div></div></div>' +199 '<div class="kpis"><div class="k"><b>' + USER.points + '</b><span>我的积分</span></div><div class="k"><b>' + USER.answers + '</b><span>已答问卷</span></div><div class="k"><b>' + USER.published + '</b><span>已发布</span></div></div>' +200 '<div class="menu">' +201 '<div class="mi" ' + goAttr('mine') + '><span class="mi-ic">📋</span><span class="mi-t">我的问卷</span><span class="mi-r">›</span></div>' +202 '<div class="mi" ' + goAttr('dash') + '><span class="mi-ic">📊</span><span class="mi-t">数据看板</span><span class="mi-r">›</span></div>' +203 '<div class="mi" ' + goAttr('message') + '><span class="mi-ic">🔔</span><span class="mi-t">消息中心</span><span class="mi-r">›</span></div>' +204 '<div class="mi" ' + goAttr('publish') + '><span class="mi-ic">➕</span><span class="mi-t">发布问卷</span><span class="mi-r">›</span></div>' +205 '<div class="mi" ' + goAttr('about') + '><span class="mi-ic">ℹ️</span><span class="mi-t">关于小草</span><span class="mi-r">›</span></div></div>';206 }207208 function messageBody() {209 var h = innTop('消息中心');210 MSGS.forEach(function (m) {211 h += '<div class="card nb msg"><div class="md">' + m.md + '</div><div style="flex:1"><div class="mt">' + esc(m.mt) + '</div><div class="mx">' + esc(m.mx) + '</div><div class="mti">' + m.ti + '</div></div></div>';212 });213 return h;214 }215216 function aboutBody() {217 return '<div class="about"><div class="logo">🌱</div><h3>小草问卷调查</h3><p>轻松发布问卷,高效回收数据。<br>覆盖市场调研、学术问卷、活动报名、员工测评等场景,<br>让每一次提问都有价值。</p>' +218 '<div class="menu" style="text-align:left;margin-top:18px"><div class="mi"><span class="mi-ic">📞</span><span class="mi-t">商务合作</span><span class="mi-r">400-888-0666</span></div>' +219 '<div class="mi"><span class="mi-ic">🌐</span><span class="mi-t">官网</span><span class="mi-r">www.chixujiaoyu.com</span></div>' +220 '<div class="mi"><span class="mi-ic">⚖️</span><span class="mi-t">隐私与合规</span><span class="mi-r">›</span></div></div>' +221 '<p style="margin-top:18px;font-size:11px">版本 v2.6.0 · 西安小草网络 出品</p></div>';222 }223224 // ---------- 路由表 ----------225 var TAB = { home: 'home', list: 'home', fill: 'home', done: 'home', publish: 'publish', knowledge: 'knowledge', article: 'knowledge', me: 'me', message: 'me', mine: 'me', dash: 'me', about: 'me' };226 var ROUTES = {227 home: function () { return { top: homeTop(), body: homeBody(), tab: 'home' }; },228 list: function (q) { return { top: '', body: listBody(q), tab: 'home' }; },229 fill: function (q) { return { top: '', body: fillBody(q), tab: 'home' }; },230 done: function () { return { top: '', body: doneBody(), tab: 'home' }; },231 publish: function () { return { top: '', body: publishBody(), tab: 'publish' }; },232 knowledge: function () { return { top: '', body: knowledgeBody(), tab: 'knowledge' }; },233 article: function (q) { return { top: '', body: articleBody(q), tab: 'knowledge' }; },234 me: function () { return { top: '', body: meBody(), tab: 'me' }; },235 message: function () { return { top: '', body: messageBody(), tab: 'me' }; },236 mine: function () { return { top: '', body: mineBody(), tab: 'me' }; },237 dash: function () { return { top: '', body: dashBody(), tab: 'me' }; },238 about: function () { return { top: '', body: aboutBody(), tab: 'me' }; }239 };240241 function tabBar(active) {242 var items = [243 { k: 'home', ic: '🏠', lb: '广场' },244 { k: 'publish', ic: '➕', lb: '发布' },245 { k: 'knowledge', ic: '📚', lb: '知识' },246 { k: 'me', ic: '👤', lb: '我的' }247 ];248 var h = '<div class="tab">';249 items.forEach(function (it) {250 h += '<div class="ti ' + (it.k === active ? 'active' : '') + '" ' + goAttr(it.k) + '><span class="ti-ic">' + it.ic + '</span>' + it.lb + '</div>';251 });252 return h + '</div>';253 }254255 function render() {256 var h = location.hash.replace(/^#/, '') || 'home';257 var parts = h.split('?');258 var route = parts[0] || 'home';259 var q = {};260 if (parts[1]) parts[1].split('&').forEach(function (kv) { var a = kv.split('='); q[decodeURIComponent(a[0])] = decodeURIComponent(a[1] || ''); });261 var fn = ROUTES[route] || ROUTES.home;262 var r = fn(q);263 var tab = TAB[route] || 'home';264 var html = (r.top || '') + (r.body || '') + tabBar(tab);265 document.getElementById('app').innerHTML = html;266 window.scrollTo(0, 0);267 }268269 // ---------- 事件委托 ----------270 document.addEventListener('click', function (e) {271 var t = e.target;272 while (t && t !== document) {273 if (t.getAttribute && t.getAttribute('data-go')) { e.preventDefault(); go(t.getAttribute('data-go')); return; }274 if (t.getAttribute && t.getAttribute('data-alert')) { e.preventDefault(); alert(t.getAttribute('data-alert')); return; }275 if (t.getAttribute && t.getAttribute('data-fill-submit')) { e.preventDefault(); go('done'); return; }276 if (t.getAttribute && t.getAttribute('data-publish')) { e.preventDefault(); alert('问卷已提交审核,预计 1 小时内公开'); go('mine'); return; }277 if (t.classList && t.classList.contains('opt')) {278 var single = t.getAttribute('data-single');279 if (single === '1') {280 var sibs = t.parentNode.querySelectorAll('.opt');281 for (var i = 0; i < sibs.length; i++) sibs[i].classList.remove('sel');282 t.classList.add('sel');283 } else { t.classList.toggle('sel'); }284 return;285 }286 t = t.parentNode;287 }288 });289290 window.addEventListener('hashchange', render);291 render();292})();293