fix(h5): show raw skin age, support Harmony touch pickers and add WeChat logout
This commit is contained in:
@@ -56,5 +56,10 @@ window.AccountClient = (function () {
|
||||
return v;
|
||||
});
|
||||
}
|
||||
return { request: request, init: init, browser: browser, guest: guest, ensureGuest: ensureGuest, user: function () { return user; } };
|
||||
function logout() {
|
||||
return request('api/auth/logout', {}).then(function (v) {
|
||||
user = null; guestReady = false; rememberKind(); return v;
|
||||
});
|
||||
}
|
||||
return { request: request, init: init, browser: browser, guest: guest, ensureGuest: ensureGuest, logout: logout, user: function () { return user; } };
|
||||
})();
|
||||
|
||||
+30
-4
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
/* ─── 屏幕管理:页面内不放返回按钮,前进时 pushState,微信/浏览器返回键经 popstate 回到上一屏 ─── */
|
||||
var currentScreen = 's-login';
|
||||
var currentScreen = 's-login', reportOwner = null, logoutBusy = false;
|
||||
var fontStylesLoaded = false;
|
||||
function loadAppFont() {
|
||||
if (fontStylesLoaded) return;
|
||||
@@ -79,6 +79,11 @@
|
||||
label.textContent = user ? (user.kind === 'wechat' ? '微信已登录' : '游客模式') : '尚未登录';
|
||||
label.parentElement.dataset.kind = user ? user.kind : '';
|
||||
});
|
||||
document.querySelectorAll('[data-action="logout-wechat"]').forEach(function (button) {
|
||||
button.hidden = !user || user.kind !== 'wechat';
|
||||
button.disabled = logoutBusy;
|
||||
button.textContent = logoutBusy ? '正在退出…' : '退出登录';
|
||||
});
|
||||
}
|
||||
function loginSucceeded() {
|
||||
showScreen('s-splash', 'replace');
|
||||
@@ -94,6 +99,7 @@
|
||||
}
|
||||
function showScreen(id, nav) { // nav: 'push' 新增历史 | 'replace' 替换当前历史 | 缺省不动历史
|
||||
if (id !== 's-login' && !AccountClient.user() && !(/[?&]mock=1/.test(location.search))) id = 's-login';
|
||||
if (id === 's-report' && !IS_MOCK && reportOwner !== (AccountClient.user() && AccountClient.user().id)) id = 's-splash';
|
||||
if (id === 's-profile' || profileOwner !== ProfileStore.key(AccountClient.user())) restoreProfile();
|
||||
if (['s-profile', 's-cam', 's-report'].includes(id)) loadAppFont();
|
||||
if (id === 's-profile' || id === 's-cam') prepareScanArtwork();
|
||||
@@ -1401,6 +1407,7 @@
|
||||
}
|
||||
|
||||
function renderReport(vm, frontPhoto, sidePhoto, payload) {
|
||||
reportOwner = AccountClient.user() && AccountClient.user().id;
|
||||
// 头部:综合评分(vm.total 缺失时回退三维度 S 均分×10)
|
||||
var total = vm.total;
|
||||
if (total == null) {
|
||||
@@ -1412,10 +1419,11 @@
|
||||
$('#stat-score').innerHTML = total != null ? '<b>' + total + '</b><i>分</i>' : '<b>--</b><i>分</i>';
|
||||
var ring = $('#ring-fg');
|
||||
ring.style.strokeDashoffset = total != null ? 326.73 * (1 - Math.max(0, Math.min(100, total)) / 100) : 326.73;
|
||||
$('#stat-age').innerHTML = vm.skinAgeDisplay != null ? '<b>' + vm.skinAgeDisplay + '</b><i>岁</i>' : '<b>--</b><i>岁</i>';
|
||||
var displayedAge = SkinScore.reportSkinAge(vm, payload && payload.result);
|
||||
$('#stat-age').innerHTML = displayedAge != null ? '<b>' + displayedAge + '</b><i>岁</i>' : '<b>--</b><i>岁</i>';
|
||||
// 肌龄对比胶囊(新版设计「比实际年龄年轻2岁」);缺实际年龄或肌龄时不展示
|
||||
var pill = $('#stat-age-note');
|
||||
var diff = vm.skinAgeDisplay != null && vm.realAge ? vm.skinAgeDisplay - vm.realAge : null;
|
||||
var diff = displayedAge != null && vm.realAge ? displayedAge - vm.realAge : null;
|
||||
pill.hidden = diff == null;
|
||||
pill.textContent = diff == null ? '' : diff < 0 ? '比实际年龄年轻' + (-diff) + '岁' : diff > 0 ? '比实际年龄大' + diff + '岁' : '与实际年龄相当';
|
||||
|
||||
@@ -1618,6 +1626,7 @@
|
||||
},
|
||||
'history': function () { openHistory(); },
|
||||
'history-retry': function () { renderHistoryList(); },
|
||||
'logout-wechat': logoutWechat,
|
||||
'open-wechat-login': function () {
|
||||
historySeq++;
|
||||
$('#login-consent').checked = false;
|
||||
@@ -1668,8 +1677,9 @@
|
||||
var btn = event.target.closest('[data-report-id]');
|
||||
if (!btn || btn.disabled) return;
|
||||
btn.disabled = true;
|
||||
var seq = historySeq, owner = AccountClient.user() && AccountClient.user().id;
|
||||
AccountClient.request('api/reports/' + encodeURIComponent(btn.dataset.reportId)).then(function (data) {
|
||||
if (currentScreen !== 's-history') return;
|
||||
if (currentScreen !== 's-history' || seq !== historySeq || owner !== (AccountClient.user() && AccountClient.user().id)) 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');
|
||||
@@ -1677,6 +1687,22 @@
|
||||
});
|
||||
|
||||
var loginBusy = false;
|
||||
function logoutWechat() {
|
||||
var user = AccountClient.user();
|
||||
if (logoutBusy || !user || user.kind !== 'wechat') return;
|
||||
if (!window.confirm('确认退出微信登录?\n历史报告将保留,重新微信登录后可查看。')) return;
|
||||
logoutBusy = true; renderAccountStatus();
|
||||
AccountClient.logout().then(function () {
|
||||
historySeq++; reportOwner = null;
|
||||
$('#history-list').textContent = ''; $('#history-count').textContent = ''; $('#identity-note').textContent = '';
|
||||
$('#login-consent').checked = false;
|
||||
$('#login-message').textContent = '已退出微信登录,历史报告已保留。';
|
||||
showScreen('s-login', 'replace');
|
||||
$('[data-action="login-wechat"]').focus({ preventScroll: true });
|
||||
}).catch(function (e) { toast('退出失败:' + e.message); }).finally(function () {
|
||||
logoutBusy = false; renderAccountStatus();
|
||||
});
|
||||
}
|
||||
function loginAllowed() {
|
||||
if (loginBusy) return false;
|
||||
if (!$('#login-consent').checked) { toast('请先阅读并同意用户协议和隐私政策'); return false; }
|
||||
|
||||
+12
-8
@@ -689,14 +689,11 @@
|
||||
var notes = [];
|
||||
var realAge = val(profile.realAge);
|
||||
|
||||
// 肌肤年龄:原值不改写,展示值钳制到 realAge±3 并留痕
|
||||
// 生物可视年龄直接使用算法原始肌龄,不再按实际年龄钳制。
|
||||
var skinAge = val(r.skin_age);
|
||||
var skinAgeDisplay = skinAge, ageClampNote = '';
|
||||
if (realAge == null) {
|
||||
notes.push('未提供实际年龄:不修正展示肌龄;产品推荐仍使用接口原始肌龄');
|
||||
} else if (skinAge != null) {
|
||||
skinAgeDisplay = Math.min(Math.max(skinAge, realAge - 3), realAge + 3);
|
||||
if (skinAgeDisplay !== skinAge) ageClampNote = '触发(原' + skinAge + '→' + skinAgeDisplay + ')';
|
||||
notes.push('未提供实际年龄:肌龄展示与产品推荐均使用接口原始肌龄');
|
||||
}
|
||||
|
||||
var skinTypeCn = skinTypeCnOf(r, profile); // {skin_type_cn}:接口枚举优先,回退用户自选肤质
|
||||
@@ -898,7 +895,14 @@
|
||||
right: { right_jawline_info: { right_jawline_angle: 113.4, right_jawline_angle_level: 1, right_jawline_quality: 1 } } // 偏离 2.6° → 94.8
|
||||
};
|
||||
|
||||
// Historical snapshots may retain an old clamped display value. Read their
|
||||
// original age without modifying the stored snapshot or recalculating copy.
|
||||
function reportSkinAge(vm, result) {
|
||||
if (vm && Object.prototype.hasOwnProperty.call(vm, 'skinAgeRaw')) return val(vm.skinAgeRaw);
|
||||
return val(result && result.skin_age);
|
||||
}
|
||||
return {
|
||||
reportSkinAge: reportSkinAge,
|
||||
buildViewModel: buildViewModel,
|
||||
recommendProduct: recommendProduct,
|
||||
MOCK_RESULT: MOCK_RESULT,
|
||||
@@ -941,7 +945,7 @@ if (typeof require === 'function' && typeof module !== 'undefined' && require.ma
|
||||
ok(vm.cats.acne.P === 85, '痘痘P = min(分级映射85, 100−14.5=85.5) = 85');
|
||||
ok(vm.cats.wrinkles.S === 7.69 && vm.cats.smooth.S === 9.45 && vm.cats.tone.S === 9.45, 'S=7+3×P/100 两位小数(含 9.445→9.45 浮点修正)');
|
||||
ok(vm.dims.firm.P === 23 && vm.dims.vita.P === 81.5 && vm.dims.stab.P === 85, '维度木桶 min:紧塑力23 / 活力感81.5 / 稳定力85');
|
||||
ok(vm.skinAgeRaw === 20 && vm.skinAgeDisplay === 22 && vm.ageClampNote === '触发(原20→22)', '肌肤年龄钳制 20→22 留痕,原值不改写');
|
||||
ok(vm.skinAgeRaw === 20 && vm.skinAgeDisplay === 20 && vm.ageClampNote === '', '肌肤年龄直接展示原值20,不按实际年龄钳制');
|
||||
ok(vm.group === '25+组', '原始肌龄20 → 25+组');
|
||||
ok(vm.essence.name === '25+束紧精华' && vm.recommendation.tier === 1,
|
||||
'肌龄小于32,即使皱纹低分也固定25+,不再使用最差项升档');
|
||||
@@ -974,14 +978,14 @@ if (typeof require === 'function' && typeof module !== 'undefined' && require.ma
|
||||
&& vm2.dims.stab.P === Math.min(vm2.cats.sens.P, vm2.cats.acne.P), '三维度 P = min(旗下两分类)');
|
||||
ok(vm2.group === '25+组', '实际年龄40不影响推荐,原始肌龄20仍为25+组');
|
||||
|
||||
console.log('== T3 钳制边界 ==');
|
||||
console.log('== T3 原始肌龄展示 ==');
|
||||
var r3 = clone(SS.MOCK_RESULT);
|
||||
r3.skin_age = { value: 24 };
|
||||
var vm3 = SS.buildViewModel(r3, SS.MOCK_SIDE, { realAge: 25 });
|
||||
ok(vm3.skinAgeDisplay === 24 && vm3.ageClampNote === '', '±3 以内不钳制、不留痕');
|
||||
r3.skin_age = { value: 40 };
|
||||
var vm3b = SS.buildViewModel(r3, SS.MOCK_SIDE, { realAge: 25 });
|
||||
ok(vm3b.skinAgeDisplay === 28 && vm3b.ageClampNote === '触发(原40→28)' && vm3b.skinAgeRaw === 40, '上边界钳制 40→28');
|
||||
ok(vm3b.skinAgeDisplay === 40 && vm3b.ageClampNote === '' && vm3b.skinAgeRaw === 40, '原始肌龄40不因实际年龄25而改变');
|
||||
|
||||
console.log('== T4 最差项不再覆盖肤龄推荐 ==');
|
||||
var r4 = clone(SS.MOCK_RESULT);
|
||||
|
||||
Reference in New Issue
Block a user