feat(report): unify display grades and add contour side photos
This commit is contained in:
+46
-7
@@ -1164,7 +1164,7 @@
|
||||
popHistorySilently();
|
||||
return;
|
||||
}
|
||||
renderReport(vm, photos[0], photos[1], payload); // img1=正脸实拍,payload 供检测可视化取 face_maps/marks
|
||||
renderReport(vm, photos[0], photos[1], payload, photos); // photos 已统一为正/左/右
|
||||
if (IS_MOCK) toast('演示报告,不保存至我的报告');
|
||||
else if (AccountClient.user() && AccountClient.user().kind !== 'wechat') toast('游客报告仅供本次查看,不保存历史记录');
|
||||
else if (!payload.report_saved) toast('报告生成成功,但保存失败,请勿关闭页面并联系管理员');
|
||||
@@ -1315,6 +1315,13 @@
|
||||
if (top + ch < rectangle.top + rectangle.height) top = rectangle.top + rectangle.height - ch;
|
||||
return { left: left, top: top, width: cw, height: ch, focused: true };
|
||||
}
|
||||
function vizShiftContentDown(crop, rectangle, fraction) {
|
||||
if (!crop.focused || !rectangle || !fraction) return crop;
|
||||
// Move the source window upward, so its subject moves DOWN inside the
|
||||
// fixed frame. Keep the full face in bounds and never create a blank edge.
|
||||
var minimumTop = Math.max(0, rectangle.top + rectangle.height - crop.height);
|
||||
return Object.assign({}, crop, { top: Math.max(minimumTop, crop.top - crop.height * fraction) });
|
||||
}
|
||||
// 从 result 提取某分类的标记集合:polygon 与 rectangle 都保留,缺该标记类型则跳过
|
||||
function vizCollectMarks(markDefs, result) {
|
||||
var out = [];
|
||||
@@ -1389,6 +1396,8 @@
|
||||
var finish = function (overlays) {
|
||||
if (epoch !== vizEpoch) return;
|
||||
var crop = vizFaceCrop(photo.naturalWidth, photo.naturalHeight, opts && opts.faceRectangle);
|
||||
var originalTop = crop.top;
|
||||
crop = vizShiftContentDown(crop, opts && opts.faceRectangle, opts && opts.contentDown);
|
||||
var scale = Math.min(1, 960 / crop.width);
|
||||
var w = Math.round(crop.width * scale), h = Math.round(crop.height * scale);
|
||||
var cv = document.createElement('canvas');
|
||||
@@ -1440,6 +1449,7 @@
|
||||
try {
|
||||
imgEl.src = cv.toDataURL('image/jpeg', 0.9); imgEl.hidden = false;
|
||||
imgEl.dataset.crop = crop.focused ? 'face' : 'full';
|
||||
imgEl.dataset.contentOffset = String((originalTop - crop.top) / crop.height);
|
||||
imgEl.dataset.overlayAlpha = String(opts && typeof opts.alpha === 'number' ? opts.alpha : 1);
|
||||
} catch (e) { /* 合成失败保留占位 */ }
|
||||
};
|
||||
@@ -1501,11 +1511,39 @@
|
||||
// 照片类(轮廓/光滑/痘痘):无照片 → 保留占位;有坐标则 canvas 叠加,无坐标纯照片
|
||||
if (!frontPhoto) continue;
|
||||
var marks = cfg.marks ? vizCollectMarks(cfg.marks, result) : [];
|
||||
vizCompose(frontPhoto, null, marks, imgEl, { faceRectangle: rectangle });
|
||||
// The visible contour frame is 90% of the source height: 9% of the
|
||||
// source equals a 10% downward shift relative to that visible frame.
|
||||
vizCompose(frontPhoto, null, marks, imgEl, { faceRectangle: rectangle, contentDown: key === 'contour' ? .09 : 0 });
|
||||
}
|
||||
}
|
||||
|
||||
function renderReport(vm, frontPhoto, sidePhoto, payload) {
|
||||
var contourPhotoEpoch = 0;
|
||||
function renderContourSides(reportPhotos) {
|
||||
var epoch = ++contourPhotoEpoch, wrap = $('#contour-sides');
|
||||
function refreshHeight() {
|
||||
var card = wrap.closest('.card-detail'), state = detailSwipes.get(card);
|
||||
if (state) state.height(Number(card.querySelector('.dtab.active').dataset.tab));
|
||||
}
|
||||
wrap.hidden = true;
|
||||
['left', 'right'].forEach(function(view, i) {
|
||||
var img = $('#contour-' + view), figure = img.closest('figure');
|
||||
img.onload = img.onerror = null; img.removeAttribute('src'); figure.hidden = true;
|
||||
var src = reportPhotos && reportPhotos[i + 1];
|
||||
if (!src) return;
|
||||
img.draggable = false;
|
||||
img.onload = function() { if (epoch === contourPhotoEpoch) { figure.hidden = false; wrap.hidden = false; refreshHeight(); } };
|
||||
img.onerror = function() {
|
||||
if (epoch !== contourPhotoEpoch) return;
|
||||
figure.hidden = true; img.removeAttribute('src');
|
||||
wrap.hidden = !wrap.querySelector('figure:not([hidden])');
|
||||
refreshHeight();
|
||||
};
|
||||
img.src = src;
|
||||
});
|
||||
refreshHeight();
|
||||
}
|
||||
function renderReport(vm, frontPhoto, sidePhoto, payload, reportPhotos) {
|
||||
renderContourSides(reportPhotos || [frontPhoto, sidePhoto, null]);
|
||||
reportOwner = AccountClient.user() && AccountClient.user().id;
|
||||
// Historical snapshots also use their saved dimensions, never the old API total.
|
||||
var total = SkinScore.weightedTotal(vm.dims);
|
||||
@@ -1529,7 +1567,7 @@
|
||||
['firm', 'vita', 'stab'].forEach(function (k) {
|
||||
setDim(k, vm.dims[k], k === worstDimKey ? 'worst' : k === bestDimKey ? 'best' : '');
|
||||
});
|
||||
var presentation = payload && payload.presentation || ReportUI.build(vm, payload && payload.result);
|
||||
var presentation = ReportUI.forDisplay(vm, payload && payload.result, payload && payload.presentation);
|
||||
var focus = ReportUI.focusOf(vm), focusEl = $('#dims-note');
|
||||
focusEl.hidden = !focus;
|
||||
focusEl.replaceChildren();
|
||||
@@ -1654,7 +1692,8 @@
|
||||
card.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('.dtab');
|
||||
if (btn) { setDetailTab(card, btn.dataset.tab); return; }
|
||||
var img = e.target.closest('.ditem-img');
|
||||
var sideButton = e.target.closest('.contour-side-photo');
|
||||
var img = sideButton ? sideButton.querySelector('img') : e.target.closest('.ditem-img');
|
||||
if (img && img.src && !img.hidden && img.closest('.dpanel').classList.contains('active')) {
|
||||
$('#zoom-img').src = img.src;
|
||||
openOverlay('p-zoom');
|
||||
@@ -1779,7 +1818,7 @@
|
||||
AccountClient.request('api/reports/' + encodeURIComponent(btn.dataset.reportId)).then(function (data) {
|
||||
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 }));
|
||||
renderReport(entry.vm, entry.photos[0], entry.photos[1], Object.assign({}, entry.payload, { presentation: entry.presentation }), entry.photos);
|
||||
showScreen('s-report', 'push');
|
||||
}).catch(function (e) { toast(e.message); }).finally(function () { btn.disabled = false; });
|
||||
});
|
||||
@@ -1905,7 +1944,7 @@
|
||||
renderReport(SkinScore.buildViewModel(mockRes, mockSide, {
|
||||
realAge: +($('#f-age').value || 0) || 28, // 调试档案缺省 28 岁
|
||||
skinType: SKINS[skinIndex].name
|
||||
}), fx ? fx.photos[0] : null, null, fx ? { result: mockRes, side: mockSide, face_rectangle: fx.face_rectangle } : null);
|
||||
}), fx ? fx.photos[0] : null, null, fx ? { result: mockRes, side: mockSide, face_rectangle: fx.face_rectangle } : null, fx && fx.photos);
|
||||
} catch (e) { /* 调试入口异常不阻塞正式流程 */ }
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user