From 62d885ab725a3ddc8d119ef7d6d978fa1aac9680 Mon Sep 17 00:00:00 2001 From: Karlcarlo Date: Sun, 4 Oct 2026 08:34:55 +0000 Subject: [PATCH] =?UTF-8?q?feat(=E6=88=91=E7=9A=84):=20=E6=84=8F=E8=A7=81?= =?UTF-8?q?=E5=8F=8D=E9=A6=88=E2=80=94=E2=80=94=E5=8A=9F=E8=83=BD=E9=97=AE?= =?UTF-8?q?=E9=A2=98/=E5=BB=BA=E8=AE=AE/=E5=85=B6=E4=BB=96=20+=20=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=20+=20=E8=81=94=E7=B3=BB=E6=96=B9=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E6=88=91=E6=8F=90=E4=BA=A4=E8=BF=87=E7=9A=84=20(v26100423)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端 box_app 新增 /api/mp/public/mama-box/feedback(POST/GET)与管理端 /api/mama-box/feedbacks Co-Authored-By: Claude Opus 5.5 --- index.html | 35 +++++++++++++++++++- js/app.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++-- mama-box.css | 31 ++++++++++++++++++ 3 files changed, 155 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 771fcf7..6bfa0ba 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ 兜爸心选 · 孕育陪伴 - + @@ -1174,6 +1174,8 @@ + + + + diff --git a/js/app.js b/js/app.js index 073341c..0bbf39c 100644 --- a/js/app.js +++ b/js/app.js @@ -3,7 +3,7 @@ (function () { 'use strict'; /* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */ - var VER = '26100422'; + var VER = '26100423'; const $ = (s, c) => (c || document).querySelector(s); const $$ = (s, c) => Array.from((c || document).querySelectorAll(s)); @@ -839,6 +839,91 @@ })); modalScrim.addEventListener('click', closeModal); + /* ================= 意见反馈(2026-10-04) ================= + 用户:「在我的页面里加一个客服反馈,功能上的反馈、功能上的建议」。 + 入口:「我的」常用服务 #meFeedback;「帮助与反馈」里的「提交反馈或建议」行(data-fb)。 + 提交:POST /api/mp/public/mama-box/feedback?openid=&unionid= {kind,text,images,contact,ctx} + 服务端规则(box_app FeedbackService):5~500 字、截图只认本站 /api/upload 地址、每人每天 10 条。 + 截图:前端先压到长边 1600 的 JPEG 再传(手机原图动辄 5MB+),最多 3 张。 */ + (function () { + const M = $('#modalFeedback'); if (!M) return; + const ta = $('#fbText'), cnt = $('#fbCount'), add = $('#fbAdd'), file = $('#fbFile'), box = $('#fbImgs'), btn = $('#fbSubmit'); + let kind = 'idea', imgs = [], busy = 0; // imgs: [{el, url|null}] + const API = '/api/mp/public/mama-box/feedback'; + const ident = () => { const o = hashParam('openid'), u = hashParam('unionid'); + return o ? 'openid=' + encodeURIComponent(o) + (u ? '&unionid=' + encodeURIComponent(u) : '') : ''; }; + $$('.fb-kind', M).forEach(b => b.addEventListener('click', () => { + kind = b.dataset.kind; + $$('.fb-kind', M).forEach(x => { x.classList.toggle('on', x === b); x.setAttribute('aria-checked', x === b ? 'true' : 'false'); }); + ta.placeholder = kind === 'bug' ? '哪个功能、怎么操作的时候出了问题?截个图会更快定位哦' : kind === 'idea' ? '想要什么新功能,或者哪里可以做得更好?' : '想对我们说的话…'; + })); + ta.addEventListener('input', () => { cnt.textContent = ta.value.length + '/500'; }); + const syncAdd = () => add.classList.toggle('hide', imgs.length >= 3); + function shrink(f) { + return new Promise(res => { + const u = URL.createObjectURL(f), im = new Image(); + im.onload = () => { + const k = Math.min(1, 1600 / Math.max(im.width, im.height)), c = document.createElement('canvas'); + c.width = Math.round(im.width * k); c.height = Math.round(im.height * k); + c.getContext('2d').drawImage(im, 0, 0, c.width, c.height); URL.revokeObjectURL(u); + c.toBlob(b => res(b || f), 'image/jpeg', .85); + }; + im.onerror = () => { URL.revokeObjectURL(u); res(f); }; + im.src = u; + }); + } + file.addEventListener('change', () => { + const f = file.files && file.files[0]; file.value = ''; + if (!f || imgs.length >= 3) return; + const it = { url: null, el: el('span', 'fb-thumb up') }; + const pv = document.createElement('img'); pv.alt = ''; pv.src = URL.createObjectURL(f); it.el.appendChild(pv); + const x = el('button', '', '×'); x.type = 'button'; x.setAttribute('aria-label', '删除这张'); + x.addEventListener('click', () => { imgs = imgs.filter(i => i !== it); it.el.remove(); syncAdd(); }); + it.el.appendChild(x); box.insertBefore(it.el, add); imgs.push(it); syncAdd(); busy++; + shrink(f).then(b => { const fd = new FormData(); fd.append('file', b, 'feedback.jpg'); return fetch('/api/upload', { method: 'POST', body: fd }); }) + .then(r => r.json()).then(j => { it.url = j && j.result && j.result.url || null; if (!it.url) throw 0; it.el.classList.remove('up'); }) + .catch(() => { toast('截图上传失败,可以删掉重新选'); it.el.classList.remove('up'); it.el.classList.add('bad'); }) + .finally(() => { busy--; }); + }); + function renderMine(list) { + const wrap = $('#fbMine'), L = $('#fbMineList'); + if (!list || !list.length) { wrap.hidden = true; return; } + wrap.hidden = false; L.innerHTML = ''; + list.slice(0, 5).forEach(f => { + const d = el('div', 'fb-item'), h = el('div', 'fb-item-h'); + h.appendChild(el('b', '', f.kindName)); h.appendChild(el('span', '', fmtWhen(f.at))); + h.appendChild(el('span', 'fb-st' + (f.status === 'done' ? ' done' : ''), f.status === 'done' ? '已处理' : '已收到')); + d.appendChild(h); d.appendChild(el('div', '', f.text)); + if (f.reply) d.appendChild(el('div', 'fb-reply', '兜爸心选回复:' + f.reply)); + L.appendChild(d); + }); + } + function loadMine() { const q = ident(); if (!q) return; + fetch(API + '?' + q).then(r => r.json()).then(j => renderMine(j && j.result && j.result.list)).catch(() => {}); } + function open() { openModal('modalFeedback'); loadMine(); } + const entry = $('#meFeedback'); if (entry) entry.addEventListener('click', open); + document.addEventListener('click', e => { const r = e.target.closest && e.target.closest('[data-fb]'); if (r) { e.preventDefault(); open(); } }); + btn.addEventListener('click', () => { + const text = ta.value.trim(); + if (text.length < 5) { toast('再多写几个字吧(至少 5 个字)'); ta.focus(); return; } + if (busy) { toast('截图还在上传,稍等一下'); return; } + const q = ident(); if (!q) { toast('请从小程序里打开后再提交'); return; } + btn.disabled = true; btn.textContent = '提交中…'; + fetch(API + '?' + q, { method: 'POST', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ kind, text, images: imgs.map(i => i.url).filter(Boolean), contact: $('#fbContact').value.trim(), + ctx: { ver: VER, ua: navigator.userAgent.slice(0, 300), from: 'me' } }) }) + .then(r => r.json()).then(j => { + if (!j || j.code !== 200) { toast((j && j.message) || '提交失败,请稍后再试'); return; } + toast('收到啦,谢谢你的反馈 💛'); + ta.value = ''; cnt.textContent = '0/500'; $('#fbContact').value = ''; + imgs.forEach(i => i.el.remove()); imgs = []; syncAdd(); + loadMine(); + }) + .catch(() => toast('网络不太好,请稍后再试')) + .finally(() => { btn.disabled = false; btn.textContent = '提交'; }); + }); + })(); + /* ================= 孕期信息 ================= 预产期是唯一的「源」:填了它就按今天倒推出孕周 + 额外天数; 没填时那两格 disabled(置灰不可编辑)。算出来之后用户可以微调, @@ -1569,7 +1654,7 @@ groups: [ { t: '联系我们', rows: [ ['row', '在线客服', '9:00 – 21:00'], - ['row', '提交反馈或建议', '截图一起发给我们'], + ['row', '提交反馈或建议', '功能问题、建议都可以', 'fb'], // 2026-10-04 真能提交了 → 打开 #modalFeedback ['row', '加入内测群', '抢先体验新功能'] ] } ], @@ -1616,6 +1701,9 @@ const rows = g.rows.map(r => { /* .st-para 而不是 .st-txt:后者是首页成长脚印在用的类名 */ if (r[0] === 'txt') return '

' + r[1] + '

'; + if (r[0] === 'row' && r[3] === 'fb') + return ''; if (r[0] === 'row') return '