- server.js: face_maps 改为白名单 5 图层完整 dataURL 透传(REPORT_MAPS,按魔数判 mime),marks 坐标随 result 原样透传;实测 return_marks 不支持毛孔/黑头等扩展标记 - skin-score.js: 按需求文档 V2.2 逐字审计修正 9 处(黑眼圈 dark_circle_severity、oil_desc/spot_flag §7.2 拼接、skintone 中文映射、轮廓预警档前缀、痘痘 heavy_note、50+精华全文、综合文案数值格式),契约引用 V2.0→V2.2 - app.js/index.html/css: 报告页 6 分类检测可视化(轮廓=正脸实拍、皱纹=正脸压暗+皱纹增强线、光滑=正脸+色斑/痣/痤疮标记、肤色=色沉图、敏感=红区图、痘痘=正脸+闭口/痤疮标记),数据驱动 VIZ_CAT 配置,优雅降级 - h5/test/: Excel 两组三视图真值回归 84/84(含案例2 AY 列扁平化数据重建 fixture) - h5/assets/mock/: 真实案例1 报告页 fixture(坐标与照片同比例缩放),?mock=1&screen=report 直渲 - e2e: Chrome 无头闭环 55/55(mock 全流程/直渲/真实 API 降级链路),截图存档 preview/e2e-20260926 - 自测: skin-score 83/83,fixture 冒烟 35/35
335 lines
17 KiB
HTML
335 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no">
|
|
<meta name="format-detection" content="telephone=no">
|
|
<meta name="description" content="1'me 在线肌肤测试:拍摄正面与左右侧面照片,即时获取六维肌肤状态分析与个性化护理建议。">
|
|
<meta name="theme-color" content="#101823">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta property="og:title" content="1'me 在线肌肤测试">
|
|
<meta property="og:description" content="拍摄正面与左右侧面照片,即时获取六维肌肤状态分析与个性化护理建议。">
|
|
<title>1'me 在线肌肤测试</title>
|
|
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
|
|
<link rel="preload" as="image" href="assets/img/bg-splash.jpg">
|
|
<link rel="stylesheet" href="css/style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ═══════════ 01 启动页 ═══════════ -->
|
|
<section class="screen active" id="s-splash">
|
|
<div class="splash-bg"></div>
|
|
<p class="splash-slogan">Life in Full<br>为更好的生命表现</p>
|
|
<div class="splash-bottom">
|
|
<div class="brand-row">
|
|
<img src="assets/svg/品牌-1me.svg" alt="1'me" class="brand-logo">
|
|
<span class="brand-text">肌肤状态检测</span>
|
|
</div>
|
|
<button class="btn btn-primary" data-action="open-privacy">开始检测</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ═══════════ 03 档案填写 ═══════════ -->
|
|
<section class="screen" id="s-profile">
|
|
<div class="profile-bg"></div>
|
|
<header class="nav">
|
|
<button class="nav-back" data-action="back-splash" aria-label="返回"><i class="chev left"></i></button>
|
|
<span class="nav-title">1'me在线肌肤测试</span>
|
|
</header>
|
|
<div class="profile-body">
|
|
<h1 class="page-title">您的肌肤状态</h1>
|
|
|
|
<div class="form-row two">
|
|
<div class="field">
|
|
<label class="field-label">您的年龄</label>
|
|
<input id="f-age" class="field-input" type="text" inputmode="numeric" maxlength="2" placeholder="请输入">
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label">您的性别</label>
|
|
<div class="field-select-wrap">
|
|
<select id="f-gender" class="field-input field-select">
|
|
<option value="" selected disabled>请选择</option>
|
|
<option>女</option><option>男</option>
|
|
</select>
|
|
<i class="chev down"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="field-label">您的所在地区</label>
|
|
<div class="field-select-wrap">
|
|
<select id="f-region" class="field-input field-select">
|
|
<option value="" selected disabled>请选择</option>
|
|
</select>
|
|
<i class="chev down"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="field-label">您的日常肤质</label>
|
|
<div class="skin-card">
|
|
<div class="skin-axis">
|
|
<span class="axis-item"><i class="chip" style="background:#EAE6E1"></i>偏干</span>
|
|
<span class="axis-item"><i class="chip" style="background:#DBF6D6"></i>水油平衡</span>
|
|
<span class="axis-item"><i class="chip" style="background:#F5F078"></i>偏油</span>
|
|
</div>
|
|
<div class="skin-carousel" id="skin-carousel">
|
|
<div class="skin-track" id="skin-track"></div>
|
|
</div>
|
|
<div class="skin-dots" id="skin-dots"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="btn btn-primary btn-submit" data-action="submit-profile">提交</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ═══════════ 05-07 拍照页 ═══════════ -->
|
|
<section class="screen" id="s-cam">
|
|
<div class="cam-bg" id="cam-bg"></div>
|
|
<header class="nav nav-dark">
|
|
<button class="nav-back" data-action="cam-back" aria-label="返回"><i class="chev left"></i></button>
|
|
<span class="nav-title">1'me在线肌肤测试</span>
|
|
</header>
|
|
<div class="cam-dots" id="cam-dots"><i></i><i></i><i></i></div>
|
|
<p class="cam-step" id="cam-step">1/3<br>请单人正脸出镜</p>
|
|
<!-- 取景蒙版:SVG mask 镂空压暗 + 轮廓线框;data-step 切换三种轮廓
|
|
0=正脸(含耳部) 1=右脸出镜(头向左转,镜像画面中鼻朝左) 2=左脸出镜(头向右转,鼻朝右) -->
|
|
<svg class="cam-mask" id="cam-mask" data-step="0" viewBox="0 0 375 812" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
|
|
<defs>
|
|
<mask id="cam-cut" maskUnits="userSpaceOnUse" x="0" y="0" width="375" height="812">
|
|
<rect x="0" y="0" width="375" height="812" fill="#fff"/>
|
|
<g fill="#000">
|
|
<path data-shape="0" d="M187.5 240 C252 240 300 285 302 365 C313 367 318 381 315 397 C312 412 305 418 299 420 C294 488 246 578 187.5 586 C129 578 81 488 76 420 C70 418 63 412 60 397 C57 381 62 367 73 365 C75 285 123 240 187.5 240 Z"/>
|
|
<path data-shape="1" transform="translate(375 0) scale(-1 1)" d="M170 245 C230 240 258 270 262 320 C263 330 262 338 259 345 C270 360 282 380 284 398 C285 405 282 411 274 413 C267 415 262 417 261 421 C267 425 268 431 263 435 C267 439 267 445 262 448 C264 454 263 460 265 466 C266 476 260 483 248 486 C220 505 185 530 158 548 C140 560 122 558 112 538 C95 500 90 430 94 370 C98 305 120 250 170 245 Z"/>
|
|
<path data-shape="2" d="M170 245 C230 240 258 270 262 320 C263 330 262 338 259 345 C270 360 282 380 284 398 C285 405 282 411 274 413 C267 415 262 417 261 421 C267 425 268 431 263 435 C267 439 267 445 262 448 C264 454 263 460 265 466 C266 476 260 483 248 486 C220 505 185 530 158 548 C140 560 122 558 112 538 C95 500 90 430 94 370 C98 305 120 250 170 245 Z"/>
|
|
</g>
|
|
</mask>
|
|
</defs>
|
|
<rect class="cam-dim-rect" x="0" y="0" width="375" height="812" mask="url(#cam-cut)"/>
|
|
<g class="cam-line">
|
|
<path data-shape="0" d="M187.5 240 C252 240 300 285 302 365 C313 367 318 381 315 397 C312 412 305 418 299 420 C294 488 246 578 187.5 586 C129 578 81 488 76 420 C70 418 63 412 60 397 C57 381 62 367 73 365 C75 285 123 240 187.5 240 Z"/>
|
|
<path data-shape="1" transform="translate(375 0) scale(-1 1)" d="M170 245 C230 240 258 270 262 320 C263 330 262 338 259 345 C270 360 282 380 284 398 C285 405 282 411 274 413 C267 415 262 417 261 421 C267 425 268 431 263 435 C267 439 267 445 262 448 C264 454 263 460 265 466 C266 476 260 483 248 486 C220 505 185 530 158 548 C140 560 122 558 112 538 C95 500 90 430 94 370 C98 305 120 250 170 245 Z"/>
|
|
<path data-shape="2" d="M170 245 C230 240 258 270 262 320 C263 330 262 338 259 345 C270 360 282 380 284 398 C285 405 282 411 274 413 C267 415 262 417 261 421 C267 425 268 431 263 435 C267 439 267 445 262 448 C264 454 263 460 265 466 C266 476 260 483 248 486 C220 505 185 530 158 548 C140 560 122 558 112 538 C95 500 90 430 94 370 C98 305 120 250 170 245 Z"/>
|
|
</g>
|
|
</svg>
|
|
<p class="cam-status" id="cam-status">检测引擎加载中…</p>
|
|
<p class="cam-hint" id="cam-hint"></p>
|
|
<div class="cam-count" id="cam-count"></div>
|
|
<p class="cam-nudge" id="cam-nudge">识别不到?可点下方快门手动拍摄</p>
|
|
<div class="cam-guide" id="cam-guide"></div>
|
|
<video id="cam-video" autoplay playsinline muted></video>
|
|
<input type="file" id="cam-file" accept="image/*" capture="user" hidden>
|
|
<div class="cam-controls">
|
|
<button class="cam-side" data-action="album" aria-label="相册">
|
|
<img src="assets/svg/图标-相册.svg" alt="相册">
|
|
</button>
|
|
<button class="cam-shutter" data-action="shutter" aria-label="拍照">
|
|
<img src="assets/svg/拍照-快门.svg" alt="快门">
|
|
</button>
|
|
<button class="cam-side cam-flip" data-action="flip" aria-label="切换">
|
|
<img src="assets/svg/图标-重拍.svg" alt="切换">
|
|
</button>
|
|
</div>
|
|
<div class="cam-fill" id="cam-fill"></div>
|
|
</section>
|
|
|
|
<!-- ═══════════ 08 报告页 ═══════════ -->
|
|
<section class="screen" id="s-report">
|
|
<header class="nav">
|
|
<button class="nav-back" data-action="back-profile" aria-label="返回"><i class="chev left"></i></button>
|
|
<span class="nav-title">1'me在线肌肤测试</span>
|
|
</header>
|
|
<div class="report-body">
|
|
<h1 class="report-title">您的肌肤状态表现</h1>
|
|
|
|
<div class="card card-stats">
|
|
<div class="stat">
|
|
<span class="stat-label">综合评分</span>
|
|
<span class="stat-value" id="stat-score">--分</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-label">肌肤年龄</span>
|
|
<span class="stat-value" id="stat-age">--岁</span>
|
|
<span class="stat-sub" id="stat-age-note"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="dims-intro">我们从6个维度观察了你的肌肤,当下优先关注:紧致、光泽、纹路、耐受、细腻、水润</p>
|
|
<p class="dims-disclaimer">*状态是动态流动的,和作息、压力、季节都有关系,仅代表此刻的状态参考。</p>
|
|
|
|
<div class="card card-dims">
|
|
<div class="dims-row">
|
|
<div class="dim"><span class="dim-name">紧塑力</span><span class="dim-score" id="dim-firm">-.-</span><span class="dim-lv" id="dim-firm-lv"></span></div>
|
|
<div class="dim"><span class="dim-name">活力感</span><span class="dim-score" id="dim-vita">-.-</span><span class="dim-lv" id="dim-vita-lv"></span></div>
|
|
<div class="dim"><span class="dim-name">稳定力</span><span class="dim-score" id="dim-stab">-.-</span><span class="dim-lv" id="dim-stab-lv"></span></div>
|
|
</div>
|
|
<p class="dims-note" id="dims-note">近期优先关注:<br>---</p>
|
|
</div>
|
|
|
|
<div class="card card-detail">
|
|
<div class="detail-tabs" id="detail-tabs">
|
|
<button class="dtab active" data-tab="0">紧塑力</button>
|
|
<button class="dtab" data-tab="1">活力感</button>
|
|
<button class="dtab" data-tab="2">稳定力</button>
|
|
</div>
|
|
<div class="detail-panels">
|
|
<div class="dpanel active" data-panel="0">
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">轮廓</h3>
|
|
<img id="ditem-img1" src="assets/img/det-1.jpg" loading="lazy" class="ditem-img" alt="轮廓检测图">
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-contour">-.-</span><i class="cat-badge" id="lv-contour"></i></p>
|
|
<p class="ditem-para" id="ditem-para1">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">皱纹</h3>
|
|
<img id="ditem-img2" src="assets/img/det-2.jpg" loading="lazy" class="ditem-img" alt="皱纹检测图">
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-wrinkles">-.-</span><i class="cat-badge" id="lv-wrinkles"></i></p>
|
|
<p class="ditem-para" id="ditem-para2">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="dpanel" data-panel="1">
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">光滑</h3>
|
|
<img id="viz-smooth" loading="lazy" class="ditem-img" alt="光滑检测标记图" hidden>
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-smooth">-.-</span><i class="cat-badge" id="lv-smooth"></i></p>
|
|
<p class="ditem-para" id="tx-smooth">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">肤色水润</h3>
|
|
<img id="viz-tone" loading="lazy" class="ditem-img" alt="肤色色沉图" hidden>
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-tone">-.-</span><i class="cat-badge" id="lv-tone"></i></p>
|
|
<p class="ditem-para" id="tx-tone">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="dpanel" data-panel="2">
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">敏感</h3>
|
|
<img id="viz-sens" loading="lazy" class="ditem-img" alt="敏感红区图" hidden>
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-sens">-.-</span><i class="cat-badge" id="lv-sens"></i></p>
|
|
<p class="ditem-para" id="tx-sens">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
<div class="ditem">
|
|
<h3 class="ditem-title">痘痘</h3>
|
|
<img id="viz-acne" loading="lazy" class="ditem-img" alt="痘痘检测标记图" hidden>
|
|
<div class="ditem-text">
|
|
<p class="cat-head"><span class="cat-s" id="s-acne">-.-</span><i class="cat-badge" id="lv-acne"></i></p>
|
|
<p class="ditem-para" id="tx-acne">检测完成后显示分析结果。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="vm-notes" id="vm-notes" style="display:none"></p>
|
|
|
|
<p class="report-disclaimer">*您的肌肤状态会随年龄、环境、作息和生活节奏动态变化。<br>*本次检测仅代表当前肌肤状态参考。</p>
|
|
|
|
<div class="card card-summary">
|
|
<h2 class="card-heading">综合分析</h2>
|
|
<p class="care-item" id="vm-summary">检测完成后显示分析结果。</p>
|
|
</div>
|
|
|
|
<div class="card card-care">
|
|
<h2 class="card-heading">生活方式建议</h2>
|
|
<p class="care-item" id="vm-lifestyle">--</p>
|
|
<p class="care-summary">好状态,是内外同调的长期节奏。</p>
|
|
</div>
|
|
|
|
<div class="card card-essence">
|
|
<h2 class="card-heading">精华推荐</h2>
|
|
<p class="essence-name" id="essence-name">--</p>
|
|
<p class="care-item" id="essence-text">--</p>
|
|
<p class="essence-triggers" id="essence-triggers"></p>
|
|
</div>
|
|
|
|
<div class="card card-product">
|
|
<h2 class="prod-title">感受高光时刻<br>从日常的好状态开始</h2>
|
|
<div class="prod-card">
|
|
<div class="prod-info">
|
|
<span class="prod-badge">CORE Ⅱ</span>
|
|
<h3 class="prod-name">以我自在有势挺括双仓精华液</h3>
|
|
<p class="prod-sub">参透皮下根源 28天校准皮肤状态</p>
|
|
</div>
|
|
<div class="prod-scroll" id="prod-scroll">
|
|
<img src="assets/img/prod-1.jpg" loading="lazy" alt="以我自在有势挺括双仓精华液">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="btn btn-primary btn-more" data-action="more">了解更多</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ═══════════ 弹窗:隐私授权 ═══════════ -->
|
|
<div class="overlay" id="p-privacy">
|
|
<div class="popup">
|
|
<button class="popup-close" data-action="close-privacy" aria-label="关闭">
|
|
<img src="assets/svg/图标-关闭.svg" alt="关闭">
|
|
</button>
|
|
<h2 class="popup-title">欢迎体验1'me<br>肌肤状态评估服务!</h2>
|
|
<p class="popup-body">我们将采集您的面部影像,为您生成专属肌肤分析与状态管理方案。所有数据严格保密,仅用于本次检测分析。</p>
|
|
<label class="privacy-row" id="privacy-row">
|
|
<span class="checkbox" id="privacy-check"></span>
|
|
<span class="privacy-text">我已阅读并同意<a class="privacy-link" id="privacy-policy-link">相关隐私协议</a></span>
|
|
</label>
|
|
<button class="btn btn-primary popup-btn" data-action="agree-continue">继续</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════ 弹窗:拍照引导 ═══════════ -->
|
|
<div class="overlay" id="p-photoguide">
|
|
<div class="popup">
|
|
<button class="popup-close" data-action="close-photoguide" aria-label="关闭">
|
|
<img src="assets/svg/图标-关闭.svg" alt="关闭">
|
|
</button>
|
|
<h2 class="popup-title popup-title-center">请拍摄正面及<br>左右侧面照片</h2>
|
|
<button class="btn btn-primary popup-btn" data-action="start-camera">开始</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════ 弹窗:隐私协议(占位) ═══════════ -->
|
|
<div class="overlay" id="p-policy">
|
|
<div class="popup popup-policy">
|
|
<button class="popup-close" data-action="close-policy" aria-label="关闭">
|
|
<img src="assets/svg/图标-关闭.svg" alt="关闭">
|
|
</button>
|
|
<h2 class="popup-title popup-title-policy">1'me 肌肤状态评估隐私协议</h2>
|
|
<p class="popup-body">(协议全文待法务提供后接入,当前为占位)</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════ 分析中过渡 ═══════════ -->
|
|
<div class="overlay overlay-dark" id="p-analyzing">
|
|
<div class="analyzing">
|
|
<div class="spinner"></div>
|
|
<p>正在生成您的肌肤报告…</p>
|
|
<p class="analyzing-tip">点击空白处取消</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════ Toast ═══════════ -->
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<script src="vendor/faceapi/face-api.js" defer></script>
|
|
<script src="js/head-pose.js" defer></script>
|
|
<script src="js/skin-score.js" defer></script>
|
|
<script>
|
|
// 仅 ?mock=1 调试:同步注入报告页可视化 fixture(生产模式不发起该请求)
|
|
if (/[?&]mock=1/.test(location.search)) document.write('<script src="assets/mock/report-fixture.js"><\/script>');
|
|
</script>
|
|
<script src="js/app.js" defer></script>
|
|
</body>
|
|
</html>
|