3057955ea4
: Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
336 lines
19 KiB
HTML
336 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
|
||
<meta charset="UTF-8">
|
||
<link rel="stylesheet" href="../shared/fonts.css">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||
<title>意见反馈 · 傻瓜比价</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||
html, body { height: 100%; overflow: hidden; }
|
||
/* 【去框】原型 body 黑底 + flex 居中(浏览器预览用)。WebView 里直接铺页面色、不居中。 */
|
||
body {
|
||
font-family: 'PuHuiTi', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||
background: #F5F5F5;
|
||
color: #1A1A1A;
|
||
}
|
||
/* 【去框】原型 .device 是 375x812 预览框(圆角+阴影+固定宽高)→ 全屏自适应容器。 */
|
||
.device { position: relative; width: 100%; height: 100%; background: #F5F5F5; overflow: hidden; }
|
||
|
||
/* 意见反馈屏:灰底白卡(原型 #feedback background #F5F5F5) */
|
||
#feedback { position: absolute; inset: 0; display: flex; flex-direction: column; background: #F5F5F5; }
|
||
|
||
/* header —— 返回 | 标题 | 反馈历史(原型 .header space-between;去框后顶部内距由 40 改 10,状态栏交给原生) */
|
||
.header { display: flex; align-items: center; justify-content: space-between; height: 48px; padding: 0 16px; background: #F5F5F5; flex-shrink: 0; position: relative; }
|
||
.btn-back { background: none; border: none; cursor: pointer; padding: 0; display: flex; align-items: center; }
|
||
.btn-back svg { width: 24px; height: 24px; display: block; }
|
||
.btn-back:active { opacity: .5; }
|
||
.header-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; line-height: 1.4; color: #1A1A1A; }
|
||
.fb-history-entry { background: none; border: none; cursor: pointer; font-family: inherit; font-size: 14px; font-weight: 400; color: #666666; padding: 6px 0; white-space: nowrap; transition: opacity .15s; }
|
||
.fb-history-entry:active { opacity: .6; }
|
||
|
||
/* body —— 滚动区,确定按钮 margin-top:auto 贴底 */
|
||
.fb-body { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 8px 16px 0; display: flex; flex-direction: column; gap: 16px; }
|
||
.fb-body::-webkit-scrollbar { display: none; }
|
||
.fb-card { background: #FFFFFF; border-radius: 14px; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
|
||
|
||
/* 顶部引导文案 + 奖励 chip */
|
||
.fb-intro { margin: -4px 2px 0; }
|
||
.fb-intro-text { font-size: 13px; font-weight: 400; line-height: 1.6; color: #666666; margin: 0; }
|
||
.fb-intro-reward { display: inline-flex; align-items: center; gap: 5px; margin-top: 10px; padding: 5px 10px; background: #FFF8E1; border-radius: 8px; font-size: 12px; font-weight: 600; color: #7A4F00; }
|
||
.fb-intro-reward .coin { width: 14px; height: 14px; display: block; flex-shrink: 0; }
|
||
|
||
.fb-section-label { font-size: 14px; font-weight: 400; line-height: 1.5; color: #1A1A1A; margin: 4px 0 8px; }
|
||
.fb-section-label .req { color: #E02E24; margin-left: 2px; }
|
||
.fb-section-label .count { color: #999999; font-weight: 400; font-size: 13px; margin-left: 4px; }
|
||
|
||
.fb-textarea { width: 100%; min-height: 140px; background: #FFFFFF; border: none; border-radius: 14px; padding: 14px 16px; font-size: 14px; font-weight: 400; color: #1A1A1A; line-height: 1.5; resize: none; outline: none; font-family: inherit; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
|
||
.fb-textarea::placeholder { color: #CCCCCC; font-weight: 400; }
|
||
|
||
.fb-upload-row { display: flex; flex-wrap: wrap; gap: 10px; }
|
||
.fb-upload-tile { width: 72px; height: 72px; background: #FFFFFF; border-radius: 14px; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
|
||
.fb-thumb { position: relative; width: 72px; height: 72px; border-radius: 14px; overflow: hidden; flex-shrink: 0; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
|
||
.fb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||
.fb-thumb-del { position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; border-radius: 50%; background: rgba(0,0,0,.55); color: #FFFFFF; border: none; font-size: 13px; line-height: 1; padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
||
|
||
/* 加群二维码卡 */
|
||
.fb-qr-card { display: flex; gap: 14px; align-items: center; padding: 14px; margin-top: 8px; }
|
||
.fb-qr { width: 88px; height: 88px; background: #FFFFFF; border-radius: 8px; flex-shrink: 0; display: block; border: 1px solid #F0F0F0; overflow: hidden; }
|
||
.fb-qr svg, .fb-qr img { width: 100%; height: 100%; display: block; }
|
||
.fb-qr img { object-fit: cover; }
|
||
.fb-qr-text { font-size: 13px; font-weight: 400; line-height: 1.6; color: #1A1A1A; }
|
||
.fb-qr-text .quote { font-weight: 600; }
|
||
|
||
/* 确定按钮(DESIGN cta-primary:17/700 + radius 24 + 黄渐变 + 高光 + 暖阴影) */
|
||
.fb-submit { width: 100%; border: none; padding: 14px 28px; border-radius: 24px; font-size: 17px; font-weight: 700; line-height: 1; font-family: inherit; background: linear-gradient(180deg, #FFE066 0%, #FFC400 100%); color: #1A1A1A; box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35); cursor: not-allowed; opacity: .45; transition: opacity .15s; }
|
||
.fb-submit.active { opacity: 1; cursor: pointer; }
|
||
.fb-submit:disabled { cursor: not-allowed; }
|
||
.fb-submit-bar { flex-shrink: 0; padding: 8px 16px 16px; background: #F5F5F5; }
|
||
|
||
/* 居中 toast */
|
||
.toast { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,.82); color: #fff; font-size: 14px; padding: 10px 18px; border-radius: 10px; z-index: 9999; opacity: 0; pointer-events: none; transition: opacity .2s; max-width: 70%; text-align: center; line-height: 1.5; }
|
||
.toast.show { opacity: 1; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="device" id="app">
|
||
<div class="screen active" id="feedback">
|
||
<div class="header">
|
||
<button class="btn-back" onclick="goBack()" aria-label="返回">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</button>
|
||
<span class="header-title">意见反馈</span>
|
||
<button class="fb-history-entry" onclick="goHistory()" aria-label="反馈历史">反馈历史</button>
|
||
</div>
|
||
|
||
<div class="fb-body">
|
||
<div class="fb-intro">
|
||
<p class="fb-intro-text">您的建议和问题我们都会认真查看,评价越真实详细,金币奖励越高,感谢您帮我们做得更好~</p>
|
||
<div class="fb-intro-reward">
|
||
<img class="coin" src="assets/welfare/index/checkin-coin-single.png" alt="" aria-hidden="true">
|
||
反馈采纳后,即得金币奖励
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="fb-section-label">问题和意见<span class="req">*</span></div>
|
||
<textarea class="fb-textarea" id="fbContent" inputmode="text" maxlength="200" placeholder="说说您的建议或问题,以便我们提供更好的服务(15个字以上)"></textarea>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="fb-section-label">上传截图<span class="count" id="fbUpCount">(0/6)</span></div>
|
||
<div class="fb-upload-row" id="fbUpRow">
|
||
<div class="fb-upload-tile" id="fbAddTile" role="button" aria-label="上传截图" onclick="document.getElementById('fbFileInput').click()">
|
||
<svg width="26" height="26" viewBox="0 0 24 24" fill="none"><path d="M12 5V19M5 12H19" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
<input type="file" id="fbFileInput" accept="image/*" multiple style="display: none;">
|
||
</div>
|
||
|
||
<div class="fb-card fb-qr-card" id="fbQrCard" style="display: none;">
|
||
<div class="fb-qr" id="fbQrSlot">
|
||
<svg viewBox="0 0 88 88" xmlns="http://www.w3.org/2000/svg" aria-label="二维码">
|
||
<rect width="88" height="88" fill="#FFFFFF"/>
|
||
<g fill="#1A1A1A">
|
||
<rect x="6" y="6" width="20" height="20"/><rect x="9" y="9" width="14" height="14" fill="#FFFFFF"/><rect x="12" y="12" width="8" height="8"/>
|
||
<rect x="62" y="6" width="20" height="20"/><rect x="65" y="9" width="14" height="14" fill="#FFFFFF"/><rect x="68" y="12" width="8" height="8"/>
|
||
<rect x="6" y="62" width="20" height="20"/><rect x="9" y="65" width="14" height="14" fill="#FFFFFF"/><rect x="12" y="68" width="8" height="8"/>
|
||
<rect x="30" y="6" width="3" height="3"/><rect x="36" y="6" width="3" height="3"/><rect x="42" y="6" width="3" height="3"/><rect x="51" y="6" width="3" height="3"/><rect x="57" y="6" width="3" height="3"/>
|
||
<rect x="30" y="12" width="3" height="3"/><rect x="39" y="12" width="3" height="3"/><rect x="48" y="12" width="3" height="3"/><rect x="54" y="12" width="3" height="3"/>
|
||
<rect x="33" y="18" width="3" height="3"/><rect x="42" y="18" width="3" height="3"/><rect x="51" y="18" width="3" height="3"/><rect x="57" y="18" width="3" height="3"/>
|
||
<rect x="30" y="24" width="3" height="3"/><rect x="36" y="24" width="3" height="3"/><rect x="45" y="24" width="3" height="3"/><rect x="54" y="24" width="3" height="3"/>
|
||
<rect x="6" y="30" width="3" height="3"/><rect x="15" y="30" width="3" height="3"/><rect x="24" y="30" width="3" height="3"/><rect x="33" y="30" width="3" height="3"/><rect x="42" y="30" width="3" height="3"/><rect x="48" y="30" width="3" height="3"/><rect x="60" y="30" width="3" height="3"/><rect x="69" y="30" width="3" height="3"/><rect x="78" y="30" width="3" height="3"/>
|
||
<rect x="9" y="36" width="3" height="3"/><rect x="18" y="36" width="3" height="3"/><rect x="27" y="36" width="3" height="3"/><rect x="39" y="36" width="3" height="3"/><rect x="51" y="36" width="3" height="3"/><rect x="63" y="36" width="3" height="3"/><rect x="72" y="36" width="3" height="3"/>
|
||
<rect x="6" y="42" width="3" height="3"/><rect x="12" y="42" width="3" height="3"/><rect x="21" y="42" width="3" height="3"/><rect x="30" y="42" width="3" height="3"/><rect x="36" y="42" width="3" height="3"/><rect x="45" y="42" width="3" height="3"/><rect x="54" y="42" width="3" height="3"/><rect x="66" y="42" width="3" height="3"/><rect x="75" y="42" width="3" height="3"/>
|
||
<rect x="15" y="48" width="3" height="3"/><rect x="24" y="48" width="3" height="3"/><rect x="33" y="48" width="3" height="3"/><rect x="42" y="48" width="3" height="3"/><rect x="51" y="48" width="3" height="3"/><rect x="60" y="48" width="3" height="3"/><rect x="69" y="48" width="3" height="3"/><rect x="78" y="48" width="3" height="3"/>
|
||
<rect x="9" y="54" width="3" height="3"/><rect x="18" y="54" width="3" height="3"/><rect x="30" y="54" width="3" height="3"/><rect x="39" y="54" width="3" height="3"/><rect x="48" y="54" width="3" height="3"/><rect x="57" y="54" width="3" height="3"/><rect x="66" y="54" width="3" height="3"/><rect x="75" y="54" width="3" height="3"/>
|
||
<rect x="33" y="60" width="3" height="3"/><rect x="42" y="60" width="3" height="3"/><rect x="51" y="60" width="3" height="3"/><rect x="60" y="60" width="3" height="3"/><rect x="72" y="60" width="3" height="3"/>
|
||
<rect x="30" y="66" width="3" height="3"/><rect x="39" y="66" width="3" height="3"/><rect x="48" y="66" width="3" height="3"/><rect x="57" y="66" width="3" height="3"/><rect x="69" y="66" width="3" height="3"/><rect x="78" y="66" width="3" height="3"/>
|
||
<rect x="33" y="72" width="3" height="3"/><rect x="45" y="72" width="3" height="3"/><rect x="54" y="72" width="3" height="3"/><rect x="63" y="72" width="3" height="3"/><rect x="75" y="72" width="3" height="3"/>
|
||
<rect x="36" y="78" width="3" height="3"/><rect x="42" y="78" width="3" height="3"/><rect x="48" y="78" width="3" height="3"/><rect x="57" y="78" width="3" height="3"/><rect x="66" y="78" width="3" height="3"/><rect x="72" y="78" width="3" height="3"/>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="fb-qr-text">
|
||
<div id="fbQrTitle">长按图片保存二维码</div>
|
||
<div>直通<span class="quote" id="fbQrGroup">「傻瓜比价官方群」</span></div>
|
||
<div id="fbQrSub">一起唠嗑共创、解锁新玩法</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="fb-submit-bar">
|
||
<button class="fb-submit" id="fbSubmit" disabled>确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script src="../shared/bridge.js"></script>
|
||
<script src="../shared/api.js"></script>
|
||
<script>
|
||
(function () {
|
||
var content = document.getElementById('fbContent');
|
||
var submit = document.getElementById('fbSubmit');
|
||
var fileInput = document.getElementById('fbFileInput');
|
||
var row = document.getElementById('fbUpRow');
|
||
var addTile = document.getElementById('fbAddTile');
|
||
var countEl = document.getElementById('fbUpCount');
|
||
var toastEl = document.getElementById('toast');
|
||
if (!content || !submit) return;
|
||
|
||
var MAX_IMG = 6;
|
||
var files = []; // 已选 File 对象(提交时压缩后 multipart 上传)
|
||
var urls = []; // 与 files 对应的 objectURL(缩略图预览,删除时回收)
|
||
var submitting = false;
|
||
var toastTimer = null;
|
||
|
||
function showToast(msg) {
|
||
toastEl.textContent = msg;
|
||
toastEl.classList.add('show');
|
||
if (toastTimer) clearTimeout(toastTimer);
|
||
toastTimer = setTimeout(function () { toastEl.classList.remove('show'); }, 1600);
|
||
}
|
||
|
||
// 确定按钮:正文非空即可点(文案虽提示「15个字以上」但不强制下限,对齐原型 sync)
|
||
function sync() {
|
||
var ok = content.value.trim().length >= 1 && !submitting;
|
||
submit.classList.toggle('active', ok);
|
||
submit.disabled = !ok;
|
||
}
|
||
content.addEventListener('input', sync);
|
||
|
||
// 渲染已选截图缩略图(末尾保留「+」继续上传 tile)
|
||
function renderShots() {
|
||
row.querySelectorAll('.fb-thumb').forEach(function (t) { t.remove(); });
|
||
files.forEach(function (f, i) {
|
||
var thumb = document.createElement('div');
|
||
thumb.className = 'fb-thumb';
|
||
var img = document.createElement('img');
|
||
img.src = urls[i]; img.alt = '截图' + (i + 1);
|
||
var del = document.createElement('button');
|
||
del.className = 'fb-thumb-del'; del.type = 'button';
|
||
del.setAttribute('aria-label', '删除'); del.textContent = '×';
|
||
del.onclick = function () {
|
||
try { URL.revokeObjectURL(urls[i]); } catch (e) {}
|
||
files.splice(i, 1); urls.splice(i, 1); renderShots();
|
||
};
|
||
thumb.appendChild(img); thumb.appendChild(del);
|
||
row.insertBefore(thumb, addTile);
|
||
});
|
||
if (countEl) countEl.textContent = '(' + files.length + '/' + MAX_IMG + ')';
|
||
addTile.style.display = files.length >= MAX_IMG ? 'none' : 'flex';
|
||
}
|
||
|
||
// 点「+」→ 系统相册多选 → 回填缩略图,最多 6 张
|
||
if (fileInput) {
|
||
fileInput.addEventListener('change', function () {
|
||
Array.prototype.slice.call(fileInput.files || []).forEach(function (f) {
|
||
if (files.length >= MAX_IMG) return;
|
||
if (!/^image\//.test(f.type)) return;
|
||
files.push(f);
|
||
urls.push(URL.createObjectURL(f));
|
||
});
|
||
renderShots();
|
||
fileInput.value = ''; // 允许重复选择同一张
|
||
});
|
||
}
|
||
|
||
// 提交前逐张压缩(长边 ≤1600,jpeg .85);解码失败兜底原 file
|
||
function compressImage(file) {
|
||
return new Promise(function (resolve) {
|
||
try {
|
||
var url = URL.createObjectURL(file);
|
||
var img = new Image();
|
||
img.onload = function () {
|
||
try {
|
||
var max = 1600, w = img.width, h = img.height;
|
||
if (w > max || h > max) { var s = Math.min(max / w, max / h); w = Math.round(w * s); h = Math.round(h * s); }
|
||
var canvas = document.createElement('canvas');
|
||
canvas.width = w; canvas.height = h;
|
||
canvas.getContext('2d').drawImage(img, 0, 0, w, h);
|
||
URL.revokeObjectURL(url);
|
||
canvas.toBlob(function (blob) { resolve(blob || file); }, 'image/jpeg', 0.85);
|
||
} catch (e) { URL.revokeObjectURL(url); resolve(file); }
|
||
};
|
||
img.onerror = function () { URL.revokeObjectURL(url); resolve(file); };
|
||
img.src = url;
|
||
} catch (e) { resolve(file); }
|
||
});
|
||
}
|
||
|
||
// 加群二维码卡:接 /feedback/config(运营可配开关+图+三行文案)
|
||
function renderQr(cfg) {
|
||
var card = document.getElementById('fbQrCard');
|
||
// 明确关闭才隐藏;未拉到(null)按默认渲染,避免空白闪烁(对齐原生 QrCard)
|
||
if (cfg && cfg.enabled === false) { card.style.display = 'none'; return; }
|
||
card.style.display = 'flex';
|
||
var title = (cfg && cfg.title) || '长按图片保存二维码';
|
||
var group = (cfg && cfg.group_name) || '傻瓜比价官方群';
|
||
var sub = (cfg && cfg.subtitle) || '一起唠嗑共创、解锁新玩法';
|
||
document.getElementById('fbQrTitle').textContent = title;
|
||
document.getElementById('fbQrGroup').textContent = '「' + group + '」';
|
||
document.getElementById('fbQrSub').textContent = sub;
|
||
var imgUrl = cfg && cfg.image_url;
|
||
if (imgUrl) {
|
||
var slot = document.getElementById('fbQrSlot');
|
||
if (slot) {
|
||
var el = document.createElement('div');
|
||
el.className = 'fb-qr'; el.id = 'fbQrSlot';
|
||
var im = document.createElement('img');
|
||
im.src = imgUrl; im.alt = '官方群二维码';
|
||
el.appendChild(im);
|
||
slot.parentNode.replaceChild(el, slot);
|
||
}
|
||
}
|
||
}
|
||
renderQr(null); // 先按默认渲染
|
||
if (window.SGApi) { SGApi.get('/feedback/config').then(renderQr).catch(function () {}); }
|
||
|
||
// 提交反馈:真接 POST /api/v1/feedback(multipart:content + 逐张 images)
|
||
function submitFeedback() {
|
||
if (submit.disabled || submitting) return;
|
||
var text = content.value.trim();
|
||
if (!text) { showToast('请填写问题和意见'); return; }
|
||
|
||
var hasBridge = window.SGBridge && SGBridge.hasNative && typeof SGBridge.getToken === 'function';
|
||
if (!hasBridge) { // 浏览器预览(无原生桥):不真发
|
||
console.log('[stub] submitFeedback(浏览器预览,无 Bridge,不真发)', { content: text, images: files.length });
|
||
showToast('感谢您的反馈');
|
||
return;
|
||
}
|
||
|
||
submitting = true; sync();
|
||
var oldText = submit.textContent; submit.textContent = '提交中…';
|
||
(async function () {
|
||
try {
|
||
var token = SGBridge.getToken();
|
||
var blobs = [];
|
||
for (var i = 0; i < files.length; i++) { blobs.push(await compressImage(files[i])); }
|
||
var fd = new FormData();
|
||
fd.append('content', text);
|
||
blobs.forEach(function (b, i) { fd.append('images', b, 'fb_' + (i + 1) + '.jpg'); });
|
||
// 只放 Authorization,不手动设 Content-Type(让浏览器自动带 multipart boundary)
|
||
var res = await fetch('/api/v1/feedback', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: fd });
|
||
if (res.ok) {
|
||
// 成功:页内居中 toast 显示后再 closePage 回我的(native 未实现 SGBridge.toast,故不走原生 toast)
|
||
showToast('感谢您的反馈');
|
||
setTimeout(function () { if (window.SGBridge && SGBridge.closePage) SGBridge.closePage(); }, 1200);
|
||
return; // 保持按钮禁用,等页面关闭,避免重复提交
|
||
} else if (res.status === 401) {
|
||
if (SGBridge.requestLogin) SGBridge.requestLogin();
|
||
showToast('请先登录');
|
||
} else {
|
||
var detail = '';
|
||
try { var j = await res.json(); detail = (j && j.detail) ? j.detail : ''; } catch (e) {}
|
||
showToast(detail || ('提交失败(' + res.status + '),请稍后再试'));
|
||
}
|
||
} catch (e) {
|
||
showToast('网络异常,提交失败');
|
||
}
|
||
// 失败兜底:恢复按钮,允许重试(成功分支已 return,不会走到这)
|
||
submitting = false; submit.textContent = oldText; sync();
|
||
})();
|
||
}
|
||
submit.addEventListener('click', submitFeedback);
|
||
|
||
sync();
|
||
|
||
// 顶栏返回 / 反馈历史入口
|
||
window.goBack = function () {
|
||
if (window.SGBridge && SGBridge.closePage) { SGBridge.closePage(); }
|
||
else if (history.length > 1) { history.back(); }
|
||
};
|
||
window.goHistory = function () {
|
||
if (window.SGBridge && SGBridge.navigate) { SGBridge.navigate('feedbackHistory'); }
|
||
else { console.log('[nav] feedbackHistory'); }
|
||
};
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|