/* Presentation only: original scores and recommendation rules remain in SkinScore. */ (function(root, factory) { if (typeof module === 'object' && module.exports) module.exports = factory(require('./report-copy.js')); else root.ReportUI = factory(root.ReportCopy); })(typeof window !== 'undefined' ? window : this, function(Copy) { 'use strict'; var defs = [ ['contour', '轮廓', 'firm', 'contour'], ['wrinkles', '皱纹', 'firm', 'wrinkles'], ['color', '肤色', 'vita', 'tone'], ['smooth', '平滑度', 'vita', 'smooth'], ['water', '水分', 'vita', 'tone'], ['sens', '泛红', 'stab', 'sens'], ['acne', '痘痘', 'stab', 'acne'] ]; var products = [ { tier: 1, match: '25+束紧精华', title: "1'me 主场精华 I", name: '以我本然有光蓄活', image: 'assets/img/ui-20261012/product-25.jpg' }, { tier: 2, match: '35+支撑精华', title: "1'me 主场精华 II", name: '以我自在有势挺括', image: 'assets/img/ui-20261012/product-35.jpg' }, { tier: 3, match: '50+重塑精华', title: "1'me 主场精华 III", name: '以我从容有态构塑', image: 'assets/img/ui-20261012/product-50.jpg' } ]; var levelMap = { '预警': '改善', '改善': '良好', '良好': '优秀' }; var dimNames = { firm: '紧塑力', vita: '透亮感', stab: '稳韧力' }; var focusTips = { firm: '强化紧塑力,提拉轮廓抚平细纹。', vita: '提升透亮感,补水提亮加强防晒。', stab: '增强稳韧力,舒缓泛红强韧屏障。' }; function numeric(value) { return typeof value === 'number' && isFinite(value); } // Read saved raw dimension values, never re-score historical reports or use // the legacy focus.name (which contains the old dimension terminology). function focusOf(vm) { var selected = null; ['firm', 'vita', 'stab'].forEach(function(key) { var dim = vm.dims && vm.dims[key]; if (dim && numeric(dim.P) && (!selected || dim.P < selected.score)) selected = { key: key, score: dim.P, name: dimNames[key], tip: focusTips[key] }; }); return selected; } function customerText(view) { if (view.key === 'wrinkles') return view.text.split('\n分区明细')[0]; if (view.key !== 'contour') return view.text; // Also cover immutable old presentations, without modifying stored copy. return view.text.replace(/侧脸角度数据未完整获取[;;]?/g, '') .replace('本次未检出有效数据,请重新拍摄后查看。', '本次未获取轮廓有效数据。'); } function scalar(value) { return numeric(value) ? value : value && numeric(value.value) ? value.value : null; } var wrinkleDefs = [ ['forehead', '抬头纹(额头纹)', ['forehead_wrinkle_info'], 'forehead_count'], ['left_nasolabial', '左法令纹', ['left_nasolabial_wrinkle_info', 'left_nasolabial_fold_info'], 'left_nasolabial_count'], ['right_nasolabial', '右法令纹', ['right_nasolabial_wrinkle_info', 'right_nasolabial_fold_info'], 'right_nasolabial_count'], ['left_crowsfeet', '左鱼尾纹', ['left_crowsfeet_wrinkle_info'], 'left_crowsfeet_count'], ['right_crowsfeet', '右鱼尾纹', ['right_crowsfeet_wrinkle_info'], 'right_crowsfeet_count'], ['left_eye', '左眼部细纹', ['left_eye_wrinkle_info'], 'left_undereye_count'], ['right_eye', '右眼部细纹', ['right_eye_wrinkle_info'], 'right_undereye_count'], ['left_mouth', '左嘴角纹', ['left_mouth_wrinkle_info'], 'left_mouth_count'], ['right_mouth', '右嘴角纹', ['right_mouth_wrinkle_info'], 'right_mouth_count'], ['glabella', '眉间纹', ['glabella_wrinkle_info'], 'glabella_count'], ['left_cheek', '左脸颊纹', ['left_cheek_wrinkle_info'], 'left_cheek_count'], ['right_cheek', '右脸颊纹', ['right_cheek_wrinkle_info'], 'right_cheek_count'] ]; var wrinkleMetrics = [ ['wrinkle_score', '皱纹分'], ['wrinkle_severity_level', '严重等级'], ['wrinkle_deep_num', '深纹条数'], ['wrinkle_shallow_num', '浅纹条数'], ['wrinkle_norm_length', '标准化长度'], ['wrinkle_norm_depth', '标准化深度'], ['wrinkle_pixel_density', '像素密度'], ['wrinkle_area_ratio', '面积比例'], ['wrinkle_deep_ratio', '深纹比例'] ]; function wrinkleDetails(result) { result = result || {}; return wrinkleDefs.map(function (def) { var node = null; def[2].some(function (key) { if (result[key] && typeof result[key] === 'object') { node = result[key]; return true; } return false; }); var values = {}; wrinkleMetrics.forEach(function (metric) { values[metric[0]] = scalar(node && node[metric[0]]); }); // Region counts are a valid fallback; a missing count is never zero. if (values.wrinkle_deep_num == null) values.wrinkle_deep_num = scalar(result.wrinkle_count && result.wrinkle_count[def[3]]); var fine = result.fine_line || result.fine_line_count; if (values.wrinkle_shallow_num == null) values.wrinkle_shallow_num = scalar(fine && fine[def[3]]); return { key: def[0], name: def[1], metrics: values }; }); } function fill(template, vars) { return template.replace(/\{([a-z_]+)\}/g, function(_, key) { var v = vars[key]; return v == null ? '未获取' : numeric(v) ? String(Math.round(v * 100) / 100) : String(v); }).replace(/\s+/g, ' ').trim(); } function count(result, key) { var v = result[key]; return v && numeric(v.count) ? v.count : null; } function build(vm, result) { result = result || {}; var views = defs.map(function(d) { var cat = vm.cats[d[3]], vars = Object.assign({}, cat.vars || {}); var level = levelMap[cat.level], valid = cat.P != null; if (d[0] === 'color' && vars.melanin == null) valid = false; if (d[0] === 'water' && vars.water_score == null) valid = false; var template = valid && level ? Copy.conclusion[d[0]][level] : ''; if (d[0] === 'contour') { var severity = vars.dark_circle_sev; var description = numeric(severity) ? ['无明显黑眼圈', '黑眼圈轻度', '黑眼圈中度', '黑眼圈重度'][severity] : null; template = template.replace(/黑眼圈轻\/中\/重度(根据分数给出判断)/g, description || '黑眼圈数据未获取'); if (vars.la == null || vars.ra == null) template = template.replace(/左右下颌线角度(?:分别为左|左) \{la\}°\/ 右 \{ra\}°[;;]/g, ''); } if (d[0] === 'acne') { var pustules = count(result, 'acne_pustule'), nodules = count(result, 'acne_nodule'); var heavy = [pustules > 0 ? '脓包' : '', nodules > 0 ? '结节' : ''].filter(Boolean); vars.heavy_note = heavy.length ? ',含' + heavy.join('、') + '等中重度皮损,炎症反应明显' : ''; if (heavy.length) { level = '改善'; template = Copy.conclusion.acne[level]; } else if (vars.acne_cnt > 0 && level === '优秀') { level = '良好'; template = Copy.conclusion.acne[level]; } if (vars.acne_cnt == null) template = '本次未获取完整皮损计数,请结合检测图片查看。'; else if (vars.acne_cnt === 0) template = '本次未检出明显皮损,请结合检测图片查看。'; } // Missing counts are unknown, not zero: remove only the unavailable metric. if (vars.fl_cnt == null) template = template.replace(/、细纹 \{fl_cnt\} 条/g, '(细纹计数未获取)'); if (vars.wr_cnt == null) template = template.replace(/皱纹 \{wr_cnt\} 条/g, '皱纹计数未获取'); if (vars.water_area == null) template = template.replace(/缺水面积 \{water_area\}%[、,]/g, '缺水面积未获取;'); if (vars.water_sev == null) template = template.replace(/肌肤缺水比较严重 \{water_sev\},/g, ''); if (vars.sens_area == null) template = template.replace(/泛红面积(?:达 | )\{sens_area\}%[,;]/g, '泛红面积未获取;'); if (vars.pore_cnt == null) template = template.replace(/粗大毛孔 \{pore_cnt\} 颗/g, '毛孔计数未获取'); if (vars.blackhead_cnt == null) template = template.replace(/黑头 \{blackhead_cnt\} 颗/g, '黑头计数未获取'); if (d[0] === 'smooth' && valid && level === '优秀') { // A high aggregate score does not mean every localized pore is absent. if (vars.pore_cnt == null || vars.blackhead_cnt == null) template = '肌肤质感整体较好,但毛孔或黑头计数未完整获取,请结合检测图查看。'; else if (vars.pore_cnt > 0 || vars.blackhead_cnt > 0) template = '肌肤质感整体较好;本次检测到粗大毛孔 {pore_cnt} 颗、黑头 {blackhead_cnt} 颗,局部仍有可改善之处。'; } var details = d[0] === 'wrinkles' ? wrinkleDetails(result) : null; var advice = level ? Copy.care[d[0]][level] : null; return { key: d[0], name: d[1], dim: d[2], source: d[3], P: cat.P, level: level || null, text: valid ? fill(template, vars) : '本次未检出有效数据,请重新拍摄后查看。', details: details, valid: valid, advice: valid ? advice : null }; }); // Preserve selection of the two weakest original categories. Tone expands into // color+water advice, with thresholds inherited from the same original category. var ranked = Object.keys(vm.cats).filter(function(k) { return vm.cats[k].P != null; }) .sort(function(a,b) { return vm.cats[a].P - vm.cats[b].P; }).slice(0,2); var chosen = views.filter(function(v) { return ranked.indexOf(v.source) >= 0 && v.advice; }); var care = chosen.map(function(v) { return { name: v.name, text: v.advice.care + v.advice.lifestyle }; }); var product = products.filter(function(p) { return p.match === vm.essence.name; })[0] || null; return { version: Copy.version + '+feedback-2026-10-12', dims: dimNames, views: views, care: care, product: product, focus: focusOf(vm) }; } return { build: build, products: products, levelMap: levelMap, dimNames: dimNames, wrinkleDetails: wrinkleDetails, focusOf: focusOf, customerText: customerText }; });