feat(h5): align October UI and add WeChat auth with saved reports
This commit is contained in:
+135
-126
@@ -4,6 +4,22 @@
|
||||
|
||||
var $ = function (sel, root) { return (root || document).querySelector(sel); };
|
||||
|
||||
// The PSD geometry uses the same 375 × 812 coordinate space as the capture mask.
|
||||
(window.CameraGeometry || []).forEach(function (geometry, index) {
|
||||
document.querySelectorAll('#cam-mask path[data-shape="' + index + '"]').forEach(function (path) {
|
||||
path.setAttribute('d', geometry.outline);
|
||||
});
|
||||
var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
group.setAttribute('data-shape', index);
|
||||
group.setAttribute('class', 'cam-feature-lines');
|
||||
geometry.guides.forEach(function (d) {
|
||||
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.setAttribute('d', d);
|
||||
group.appendChild(path);
|
||||
});
|
||||
$('#cam-mask').appendChild(group);
|
||||
});
|
||||
|
||||
/* ─── Toast ─── */
|
||||
var toastEl = $('#toast'), toastTimer = null;
|
||||
function toast(msg) {
|
||||
@@ -15,12 +31,15 @@
|
||||
}
|
||||
|
||||
/* ─── 屏幕管理:页面内不放返回按钮,前进时 pushState,微信/浏览器返回键经 popstate 回到上一屏 ─── */
|
||||
var currentScreen = 's-splash';
|
||||
var currentScreen = 's-login';
|
||||
function showScreen(id, nav) { // nav: 'push' 新增历史 | 'replace' 替换当前历史 | 缺省不动历史
|
||||
if (id !== 's-login' && !AccountClient.user() && !(/[?&]mock=1/.test(location.search))) id = 's-login';
|
||||
document.querySelectorAll('.screen').forEach(function (s) {
|
||||
s.classList.toggle('active', s.id === id);
|
||||
});
|
||||
var sc = document.getElementById(id);
|
||||
window.scrollTo(0, 0);
|
||||
document.body.scrollTop = 0;
|
||||
sc.scrollTop = 0;
|
||||
currentScreen = id;
|
||||
if (id === 's-report') resetReport(); // 每次进入报告页都还原初始状态
|
||||
@@ -33,7 +52,7 @@
|
||||
window.addEventListener('popstate', function (e) {
|
||||
if (Date.now() < popSilentUntil) { popSilentUntil = 0; return; }
|
||||
var to = (e.state && e.state.screen) || 's-splash';
|
||||
['p-privacy', 'p-photoguide', 'p-policy', 'p-zoom', 'p-history'].forEach(closeOverlay);
|
||||
['p-privacy', 'p-photoguide', 'p-policy', 'p-zoom'].forEach(closeOverlay);
|
||||
if (currentScreen === 's-cam') leaveCamera();
|
||||
if (to === 's-cam') { to = 's-profile'; history.replaceState({ screen: to }, ''); } // 「前进」回拍照页不自动开相机
|
||||
if (to !== currentScreen) showScreen(to);
|
||||
@@ -92,11 +111,11 @@
|
||||
|
||||
/* ─── 肤质轮播 ─── */
|
||||
var SKINS = [
|
||||
{ img: 'assets/img/skin-0-dry.png', name: '干性肌肤', desc: '全脸干燥,容易紧绷、缺乏光泽' },
|
||||
{ img: 'assets/img/skin-1-mixeddry.png', name: '混合偏干肌肤', desc: '两颊偏干,T区油脂较少或偶尔出油' },
|
||||
{ img: 'assets/img/skin-2-neutral.png', name: '中性肌肤', desc: '水油平衡,肤感舒适,无明显干燥出油' },
|
||||
{ img: 'assets/img/skin-3-oily.png', name: '油性肌肤', desc: '全脸易出油,容易泛油光、毛孔明显' },
|
||||
{ img: 'assets/img/skin-4-mixedoily.png', name: '混合偏油肌肤', desc: 'T区易出油,两颊相对清爽或轻微干燥' }
|
||||
{ img: 'assets/img/ui-20261009/skin-0-dry.png', name: '干性肌肤', desc: '全脸干燥,容易紧绷、<br>缺乏光泽' },
|
||||
{ img: 'assets/img/ui-20261009/skin-1-mixeddry.png', name: '混合偏干肌肤', desc: '两颊偏干,T区油脂较少或偶尔出油' },
|
||||
{ img: 'assets/img/ui-20261009/skin-2-neutral.png', name: '中性肌肤', desc: '水油平衡,肤感舒适,无明显干燥出油' },
|
||||
{ img: 'assets/img/ui-20261009/skin-3-oily.png', name: '油性肌肤', desc: '全脸易出油,容易泛油光、毛孔明显' },
|
||||
{ img: 'assets/img/ui-20261009/skin-4-mixedoily.png', name: '混合偏油肌肤', desc: 'T区易出油,两颊相对清爽或轻微干燥' }
|
||||
];
|
||||
var skinIndex = 0;
|
||||
var track = $('#skin-track'), dotsBox = $('#skin-dots');
|
||||
@@ -711,7 +730,7 @@
|
||||
// 左脸出镜=头向自己的右侧转(target 'left',产物作 left_image)。
|
||||
// mask 为 index.html 取景轮廓编号(1=左脸出镜 耳在画面左,2=右脸出镜 耳在画面右,均按镜像预览绘制)
|
||||
var CAM_STEPS = [
|
||||
{ step: '1/3<br>请正对屏幕', guides: ['正脸居中', '取下眼镜', '光线充足'], target: 'front', mask: 0,
|
||||
{ step: '1/3<br>请正脸出镜', guides: ['正脸居中', '取下眼镜', '光线充足'], target: 'front', mask: 0,
|
||||
done: '正脸拍摄完成' },
|
||||
{ step: '2/3<br>请右脸出镜', guides: ['右脸居中', '取下眼镜', '光线充足'], target: 'right', mask: 2,
|
||||
done: '右脸拍摄完成', preview: '下一张:右脸出镜<br>请向左转头' },
|
||||
@@ -826,7 +845,7 @@
|
||||
}
|
||||
var vm = null;
|
||||
try {
|
||||
vm = SkinScore.buildViewModel(payload.result, payload.side, {
|
||||
vm = payload.vm || SkinScore.buildViewModel(payload.result, payload.side, {
|
||||
realAge: profile.age,
|
||||
skinType: profile.skinType
|
||||
});
|
||||
@@ -838,7 +857,8 @@
|
||||
return;
|
||||
}
|
||||
renderReport(vm, photos[0], photos[1], payload); // img1=正脸实拍,payload 供检测可视化取 face_maps/marks
|
||||
storeReport(payload, photos, profile); // 以检测为单位存三张照片+分析结果(本地历史)
|
||||
if (IS_MOCK) toast('演示报告,不保存至我的报告');
|
||||
else if (!payload.report_saved) toast('报告生成成功,但保存失败,请勿关闭页面并联系管理员');
|
||||
photos = []; // 报告已渲染(图片 src 已挂):释放三连拍 dataURL 内存
|
||||
showScreen('s-report', 'replace'); // 报告顶替拍照页:返回键回档案页,不再回到相机
|
||||
}, wait);
|
||||
@@ -879,7 +899,7 @@
|
||||
}
|
||||
var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
||||
var timer = setTimeout(function () { if (ctrl) ctrl.abort(); }, ANALYZE_TIMEOUT_MS);
|
||||
fetch('/api/skin-analyze', {
|
||||
fetch('api/skin-analyze', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -897,7 +917,7 @@
|
||||
clearTimeout(timer);
|
||||
if (seq !== camSeq) return; // 流程已被重置,丢弃过期响应
|
||||
if (!data.ok) done(data.message || '检测失败,请重试');
|
||||
else done(null, { result: data.result, side: data.side });
|
||||
else done(null, data);
|
||||
})
|
||||
.catch(function (err) {
|
||||
clearTimeout(timer);
|
||||
@@ -952,7 +972,8 @@
|
||||
{ key: 'mole', color: '#282828' }, // 痣
|
||||
{ key: 'acne', color: '#D30C0C' } // 痤疮
|
||||
] },
|
||||
tone: { img: '#viz-tone', blendMap: 'water_area' }, // 肤色水润:正脸×水分图正片叠底(water_area,接口返回)
|
||||
water: { img: '#viz-water', blendMap: 'water_area' },
|
||||
tone: { img: '#viz-tone', blendMap: 'brown_area' }, // 肤色水润:正脸×水分图正片叠底(water_area,接口返回)
|
||||
sens: { img: '#viz-sens', blendMap: 'red_area' }, // 敏感:正脸×红区图正片叠底(直挂白底过淡不可读)
|
||||
acne: { img: '#viz-acne', photo: true, marks: [ // 痘痘:正脸+闭口粉刺/痤疮标记
|
||||
{ key: 'closed_comedones', color: '#E8A13D' }, // 闭口粉刺
|
||||
@@ -1029,10 +1050,13 @@
|
||||
}
|
||||
|
||||
// canvas 合成:照片绘制到自然尺寸 → (可选压暗)→ (可选盖透明增强图层)→ 画坐标标记 → dataURL 挂到卡片 <img>
|
||||
var vizEpoch = 0;
|
||||
function vizCompose(photoURL, overlayURL, marks, imgEl, opts) {
|
||||
var epoch = vizEpoch;
|
||||
vizLoadImage(photoURL, function (photo) {
|
||||
if (!photo) return; // 照片不可用:保留占位
|
||||
var finish = function (overlay) {
|
||||
if (epoch !== vizEpoch) return;
|
||||
var w = photo.naturalWidth, h = photo.naturalHeight;
|
||||
var cv = document.createElement('canvas');
|
||||
cv.width = w; cv.height = h;
|
||||
@@ -1080,6 +1104,8 @@
|
||||
|
||||
// 报告页 6 张检测图统一入口:任一输入缺失 → 该卡保留既有占位(不报错)
|
||||
function renderViz(payload, frontPhoto) {
|
||||
vizEpoch++;
|
||||
document.querySelectorAll('.ditem-img').forEach(function (img) { img.hidden = true; img.removeAttribute('src'); });
|
||||
var result = payload && payload.result;
|
||||
var maps = (result && result.face_maps) || {};
|
||||
for (var key in VIZ_CAT) {
|
||||
@@ -1140,19 +1166,10 @@
|
||||
['firm', 'vita', 'stab'].forEach(function (k) {
|
||||
setDim(k, vm.dims[k], k === worstDimKey ? 'worst' : k === bestDimKey ? 'best' : '');
|
||||
});
|
||||
// 关注行(设计稿两行:「近期优先关注:」灰 + 维度名深色下划线,下行为灰色口号)
|
||||
var DIM_SLOGAN = {
|
||||
'紧塑力': '紧塑提拉力,轮廓上扬不松垮。',
|
||||
'活力感': '活力透亮感,水润澎弹有光泽。',
|
||||
'稳定力': '强韧稳态力,锁水保湿抵御刺激。' // 设计稿原文
|
||||
};
|
||||
$('#dims-note').innerHTML = vm.focus.name
|
||||
? '<span class="dims-note-l1">近期优先关注:<b>' + esc(vm.focus.name) + '</b></span><br><span class="dims-note-l2">' + esc(DIM_SLOGAN[vm.focus.name] || '') + '</span>'
|
||||
: '<span class="dims-note-l1">近期优先关注:<b>---</b></span>';
|
||||
|
||||
// 三个 Tab 面板:各所辖两个分类(S + 档位徽章 + 话术正文)
|
||||
var keys = ['contour', 'wrinkles', 'smooth', 'tone', 'sens', 'acne'];
|
||||
for (var i = 0; i < keys.length; i++) setCat(keys[i], vm.cats[keys[i]]);
|
||||
var presentation = payload && payload.presentation || ReportUI.build(vm, payload && payload.result);
|
||||
$('#dims-note').textContent = '';
|
||||
var textIds = { contour: '#ditem-para1', wrinkles: '#ditem-para2', color: '#tx-tone', smooth: '#tx-smooth', water: '#tx-water', sens: '#tx-sens', acne: '#tx-acne' };
|
||||
presentation.views.forEach(function (v) { $(textIds[v.key]).textContent = v.text; });
|
||||
|
||||
// 检测可视化:轮廓=正脸实拍,其余按 VIZ_CAT 取 face_maps 图层或叠加 marks(payload 缺失时各卡保留占位)
|
||||
renderViz(payload, frontPhoto);
|
||||
@@ -1170,27 +1187,18 @@
|
||||
notesEl.style.display = 'none';
|
||||
}
|
||||
|
||||
// 护理建议(V2.3 §8 五段:肤质定制/最差2分类/精华/生活方式);综合分析与生活方式通栏已按需求移除
|
||||
// 展示对齐 Figma:纯「序号、正文」,不显示段名【tag】、不加粗
|
||||
var careBox = $('#care-list');
|
||||
if (careBox) {
|
||||
careBox.innerHTML = (vm.careAdvice || []).map(function (s, i) {
|
||||
return '<p class="care-item">' + (i + 1) + '、' + esc(s.text) + '</p>';
|
||||
}).join('');
|
||||
$('#care-list').innerHTML = presentation.care.map(function (s, i) {
|
||||
return '<p class="care-item">' + (i + 1) + '、' + esc(s.text) + '</p>';
|
||||
}).join('') || '<p class="care-item">本次有效数据不足,暂无个性化护理建议。</p>';
|
||||
var product = presentation.product;
|
||||
$('.card-product').hidden = !product;
|
||||
$('.prod-title').hidden = !product;
|
||||
if (product) {
|
||||
$('#product-title').textContent = product.title;
|
||||
$('#product-name').textContent = product.name + '双仓精华液';
|
||||
$('#product-image').src = product.image;
|
||||
$('#product-image').alt = product.name + '双仓精华液';
|
||||
}
|
||||
|
||||
// 精华推荐卡
|
||||
// 精华推荐卡只放产品名 + 命中判据:推荐正文已是护理建议第 4 段(V2.3 §8.5),卡内不再重复同一句
|
||||
$('#essence-name').textContent = vm.essence.name;
|
||||
// 命中判据:引擎内部名 → 用户可读措辞(引擎值被真值回归逐字锁定,仅展示层映射)
|
||||
var TRIGGER_LABEL = {
|
||||
'均分档': vm.avgS != null ? '六项平均分 ' + vm.avgS : '六项平均分',
|
||||
'最差项约束': vm.worst ? vm.worst.name + '得分偏低' : '单项得分偏低',
|
||||
'年龄组档': vm.group ? '年龄段 ' + vm.group.replace('组', '') : '年龄段'
|
||||
};
|
||||
$('#essence-triggers').textContent = vm.essence.triggers.length
|
||||
? '推荐依据:' + vm.essence.triggers.map(function (t) { return TRIGGER_LABEL[t] || t; }).join('、')
|
||||
: '';
|
||||
}
|
||||
|
||||
/* ─── 相机可用后加载检测模型(加载完成前快门即手动模式) ─── */
|
||||
@@ -1252,7 +1260,10 @@
|
||||
|
||||
/* ─── 动作分发 ─── */
|
||||
var actions = {
|
||||
'open-privacy': function () { openOverlay('p-privacy'); },
|
||||
'open-policy': function () { openOverlay('p-policy'); },
|
||||
'login-browser': function () { loginBrowser(); },
|
||||
'login-wechat': function () { loginWechat(); },
|
||||
'open-privacy': function () { if (!AccountClient.user()) { showScreen('s-login', 'replace'); return; } openOverlay('p-privacy'); },
|
||||
'close-privacy': function () { closeOverlay('p-privacy'); },
|
||||
'close-policy': function () { closeOverlay('p-policy'); },
|
||||
'agree-continue': function () {
|
||||
@@ -1291,13 +1302,7 @@
|
||||
startCamera(); // 复用启停与预热(camSeqCam 防竞态)
|
||||
},
|
||||
'history': function () { openHistory(); },
|
||||
'close-history': function () { closeOverlay('p-history'); },
|
||||
'clear-history': function () {
|
||||
saveReports([]);
|
||||
renderHistoryList();
|
||||
toast('历史报告已清空');
|
||||
},
|
||||
'more': function () { toast('了解更多功能开发中'); }
|
||||
'history-retry': function () { renderHistoryList(); }
|
||||
};
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
@@ -1307,84 +1312,78 @@
|
||||
if (fn) fn();
|
||||
});
|
||||
|
||||
/* ─── 报告历史(localStorage):以检测为单位,存三张照片+分析结果(数据/坐标/文案),
|
||||
不含 face_maps 整幅图层(体积原因);上限 5 条,超配额逐条淘汰 ─── */
|
||||
var REPORTS_KEY = 'juzi.reports.v1', REPORTS_MAX = 5;
|
||||
function loadReports() {
|
||||
try { var v = JSON.parse(localStorage.getItem(REPORTS_KEY)); return Array.isArray(v) ? v : []; }
|
||||
catch (e) { return []; }
|
||||
}
|
||||
function saveReports(list) {
|
||||
try { localStorage.setItem(REPORTS_KEY, JSON.stringify(list)); return true; }
|
||||
catch (e) { return false; } // QuotaExceeded:交给调用方淘汰重试
|
||||
}
|
||||
function storeReport(payload, ph, profile) {
|
||||
if (!payload || !payload.result) return;
|
||||
var result = Object.assign({}, payload.result);
|
||||
delete result.face_maps; // 历史不存图层;可视化中图层类(皱纹/肤色/敏感)回看时降级为占位,坐标叠加类(光滑/痘痘)仍可用
|
||||
var entry = {
|
||||
id: 'r' + Date.now(), ts: Date.now(), profile: profile,
|
||||
total: payload.result.score_info && payload.result.score_info.total_score,
|
||||
skinAge: payload.result.skin_age && payload.result.skin_age.value,
|
||||
photos: [ph[0] || null, ph[1] || null, ph[2] || null],
|
||||
payload: { result: result, side: payload.side || null, face_rectangle: payload.face_rectangle || null }
|
||||
};
|
||||
var list = loadReports();
|
||||
list.unshift(entry);
|
||||
while (list.length > REPORTS_MAX) list.pop();
|
||||
while (!saveReports(list) && list.length > 1) list.pop(); // 淘汰至可写
|
||||
saveReports(list);
|
||||
}
|
||||
function fmtTime(ts) {
|
||||
var d = new Date(ts);
|
||||
function p(n) { return (n < 10 ? '0' : '') + n; }
|
||||
return d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate() + ' ' + p(d.getHours()) + ':' + p(d.getMinutes());
|
||||
}
|
||||
/* Reports and complete immutable snapshots live on the authenticated backend. */
|
||||
var historySeq = 0;
|
||||
function renderHistoryList() {
|
||||
var box = $('#history-list'), list = loadReports();
|
||||
if (!list.length) {
|
||||
box.innerHTML = '<p class="history-empty">暂无历史报告<br>完成一次肌肤检测后会自动保存在这里</p>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = list.map(function (e) {
|
||||
var sub = [e.profile && e.profile.age ? e.profile.age + '岁' : '', e.profile && e.profile.skinType || '']
|
||||
.filter(Boolean).join(' · ');
|
||||
var score = e.total != null ? e.total : '--';
|
||||
var img = e.photos && e.photos[0] ? '<img class="his-thumb" src="' + e.photos[0] + '" alt="">' : '<i class="his-thumb"></i>';
|
||||
return '<div class="his-item" data-id="' + e.id + '">' + img +
|
||||
'<div class="his-main"><b>' + fmtTime(e.ts) + '</b><span>' + sub + ' · 肌龄' + (e.skinAge != null ? e.skinAge : '--') + '岁</span></div>' +
|
||||
'<div class="his-score"><b>' + score + '</b><span>分</span></div>' +
|
||||
'<button class="his-del" data-del="' + e.id + '">删除</button></div>';
|
||||
}).join('');
|
||||
var seq = ++historySeq, box = $('#history-list');
|
||||
box.textContent = '正在读取报告…';
|
||||
AccountClient.request('api/reports').then(function (data) {
|
||||
if (seq !== historySeq) return;
|
||||
$('#history-count').textContent = '您最近已进行过' + data.total + '次面部问题检测';
|
||||
$('#identity-note').textContent = AccountClient.user().kind === 'wechat'
|
||||
? '保留最近 5 份报告,同一微信账号可跨手机查看。'
|
||||
: '保留最近 5 份报告。本机身份仅限当前浏览器;更换手机或清除浏览器数据后无法找回。';
|
||||
if (!data.items.length) { box.innerHTML = '<p class="history-empty">暂无报告<br>完成一次检测后,报告会自动保存在这里。</p>'; return; }
|
||||
box.innerHTML = data.items.map(function (item) {
|
||||
var date = new Date(item.created_at).toLocaleDateString('sv-SE');
|
||||
return '<article class="card history-item"><p><span>测试时间</span> ' + esc(date) +
|
||||
'</p><button class="history-detail" data-report-id="' + esc(item.id) + '">查看详情</button></article>';
|
||||
}).join('');
|
||||
}).catch(function (e) {
|
||||
if (seq !== historySeq) return;
|
||||
$('#history-count').textContent = '暂时无法读取';
|
||||
box.textContent = e.message + ',请刷新列表重试。';
|
||||
});
|
||||
}
|
||||
function openHistory() {
|
||||
showScreen('s-history', 'push');
|
||||
renderHistoryList();
|
||||
openOverlay('p-history');
|
||||
}
|
||||
$('#history-list').addEventListener('click', function (e) {
|
||||
var del = e.target.closest('[data-del]');
|
||||
if (del) { // 删除单条(不打开报告)
|
||||
e.stopPropagation();
|
||||
saveReports(loadReports().filter(function (it) { return it.id !== del.dataset.del; }));
|
||||
renderHistoryList();
|
||||
return;
|
||||
}
|
||||
var item = e.target.closest('.his-item');
|
||||
if (!item) return;
|
||||
var entry = loadReports().filter(function (it) { return it.id === item.dataset.id; })[0];
|
||||
if (!entry) return;
|
||||
var vm = null;
|
||||
try {
|
||||
vm = SkinScore.buildViewModel(entry.payload.result, entry.payload.side, {
|
||||
realAge: entry.profile && entry.profile.age, skinType: entry.profile && entry.profile.skinType
|
||||
});
|
||||
} catch (err) { vm = null; }
|
||||
if (!vm) { toast('该记录已损坏'); return; }
|
||||
renderReport(vm, entry.photos && entry.photos[0], null, entry.payload);
|
||||
closeOverlay('p-history');
|
||||
showScreen('s-report', currentScreen === 's-report' ? undefined : 'push');
|
||||
$('#history-list').addEventListener('click', function (event) {
|
||||
var btn = event.target.closest('[data-report-id]');
|
||||
if (!btn || btn.disabled) return;
|
||||
btn.disabled = true;
|
||||
AccountClient.request('api/reports/' + encodeURIComponent(btn.dataset.reportId)).then(function (data) {
|
||||
if (currentScreen !== 's-history') return;
|
||||
var entry = data.report;
|
||||
renderReport(entry.vm, entry.photos[0], entry.photos[1], Object.assign({}, entry.payload, { presentation: entry.presentation }));
|
||||
showScreen('s-report', 'push');
|
||||
}).catch(function (e) { toast(e.message); }).finally(function () { btn.disabled = false; });
|
||||
});
|
||||
|
||||
var loginBusy = false;
|
||||
function loginAllowed() {
|
||||
if (loginBusy) return false;
|
||||
if (!$('#login-consent').checked) { toast('请先阅读并同意用户协议和隐私政策'); return false; }
|
||||
return true;
|
||||
}
|
||||
function loginBrowser() {
|
||||
if (!loginAllowed()) return;
|
||||
loginBusy = true;
|
||||
$('#login-message').textContent = '正在登录…';
|
||||
AccountClient.browser(true).then(function () {
|
||||
showScreen('s-splash', 'replace');
|
||||
}).catch(function (e) { $('#login-message').textContent = e.message; })
|
||||
.finally(function () { loginBusy = false; });
|
||||
}
|
||||
function loginWechat() {
|
||||
if (!loginAllowed()) return;
|
||||
loginBusy = true;
|
||||
AccountClient.request('api/auth/config').then(function (data) {
|
||||
if (!data.wechat) {
|
||||
$('#login-message').textContent = data.reason + '。报告仍会保存至服务端。';
|
||||
return;
|
||||
}
|
||||
if (!/MicroMessenger/i.test(navigator.userAgent)) {
|
||||
$('#login-message').textContent = '请在微信内打开本页面,或使用本机浏览器登录。';
|
||||
return;
|
||||
}
|
||||
return AccountClient.request('api/auth/wechat', { consent: true }).then(function (data) {
|
||||
location.assign(data.url);
|
||||
});
|
||||
}).catch(function (e) { $('#login-message').textContent = e.message; })
|
||||
.finally(function () { loginBusy = false; });
|
||||
}
|
||||
/* ─── 页面隐藏/来电生命周期:隐藏即停相机与全部定时器,回前台从当前步恢复(保留 photos[] 与 camStep) ─── */
|
||||
function handleCamHidden() {
|
||||
if (currentScreen !== 's-cam') return;
|
||||
@@ -1423,8 +1422,18 @@
|
||||
})();
|
||||
|
||||
/* ─── 初始化 ─── */
|
||||
history.replaceState({ screen: 's-splash' }, ''); // 刷新后从启动页开始,历史栈的屏标记与之对齐
|
||||
history.replaceState({ screen: 's-login' }, ''); // 刷新后从启动页开始,历史栈的屏标记与之对齐
|
||||
renderCamStep();
|
||||
if (!(/[?&]mock=1/.test(location.search) && /[?&]screen=report/.test(location.search))) {
|
||||
loginBusy = true;
|
||||
AccountClient.init().then(function () {
|
||||
if (AccountClient.user()) showScreen('s-splash', 'replace');
|
||||
else $('#login-message').textContent = /[?&]login_error=/.test(location.search)
|
||||
? '微信登录未完成,可重试或使用本机浏览器登录。'
|
||||
: '本机登录仅限当前浏览器,不支持换手机找回。';
|
||||
}).catch(function (e) { $('#login-message').textContent = e.message; })
|
||||
.finally(function () { loginBusy = false; });
|
||||
}
|
||||
// 仅调试用:?mock=1&screen=report 直渲 mock 报告页(供无头浏览器截图验证布局,非正式入口)
|
||||
// 有 MOCK_FIXTURE 时走 fixture(检测可视化同步出图),否则回退 SkinScore 内置样本
|
||||
if (IS_MOCK && /[?&]screen=report/.test(location.search) && typeof SkinScore !== 'undefined') {
|
||||
|
||||
Reference in New Issue
Block a user