Files
hair/static/test_interface3.html
T

206 lines
9.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>接口3 — B端生发 测试页</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f5f5f5; color: #333; }
.container { max-width: 1200px; margin: 0 auto; padding: 24px; }
h1 { font-size: 22px; margin-bottom: 6px; }
.subtitle { color: #888; font-size: 13px; margin-bottom: 24px; }
.card { background: #fff; border-radius: 12px; box-shadow: 0 1px 4px rgba(0,0,0,.06); margin-bottom: 20px; }
.card-header { font-weight: 700; font-size: 14px; padding: 14px 18px; border-bottom: 1px solid #f0f0f0; background: #fafafa; display: flex; justify-content: space-between; align-items: center; }
.card-body { padding: 18px; }
.upload-group { margin-bottom: 14px; }
.upload-group:last-child { margin-bottom: 0; }
.upload-group .label { font-weight: 700; font-size: 13px; color: #374151; margin-bottom: 6px; }
.upload-group .label .tag { font-size: 11px; padding: 2px 8px; border-radius: 10px; margin-left: 6px; }
.upload-group .label .tag.required { background: #fee2e2; color: #991b1b; }
.upload-group input[type=file] { width: 100%; padding: 8px; border: 2px dashed #ddd; border-radius: 8px; cursor: pointer; }
.upload-row { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.upload-row .upload-group { flex: 1; min-width: 250px; }
.btn { padding: 10px 28px; border: none; border-radius: 8px; font-size: 15px; cursor: pointer; font-weight: 600; transition: .2s; }
.btn-primary { background: #2563eb; color: #fff; }
.btn-primary:hover { background: #1d4ed8; }
.btn-primary:disabled { background: #93c5fd; cursor: not-allowed; }
.btn-sm { padding: 6px 14px; font-size: 13px; }
.btn-outline { background: #fff; border: 1px solid #d1d5db; color: #374151; }
.btn-outline:hover { background: #f9fafb; }
.hint { font-size: 12px; color: #9ca3af; margin-top: 8px; }
.status { padding: 10px 16px; border-radius: 8px; font-size: 14px; margin-bottom: 16px; display: none; }
.status.info { background: #dbeafe; color: #1e40af; display: block; }
.status.error { background: #fee2e2; color: #991b1b; display: block; }
.status.success { background: #d1fae5; color: #065f46; display: block; }
.results-layout { display: flex; gap: 24px; }
.col-main { flex: 1.5; min-width: 0; }
.col-side { flex: 1; min-width: 0; }
/* 结果卡片 */
.outcome { margin-top: 14px; }
.outcome .field { margin-bottom: 10px; }
.outcome .field .name { font-size: 12px; font-weight: 600; color: #6b7280; margin-bottom: 4px; text-transform: uppercase; letter-spacing: .5px; }
.outcome img { max-width: 100%; max-height: 350px; border-radius: 8px; display: block; }
/* 标记图 + 原图预览 */
.preview-row { display: flex; gap: 16px; }
.preview-row > div { flex: 1; text-align: center; }
.preview-row .thumb { background: #222; border-radius: 8px; overflow: hidden; display: inline-block; line-height: 0; }
.preview-row .thumb img { max-width: 100%; max-height: 300px; object-fit: contain; }
.preview-row .caption { font-size: 11px; color: #9ca3af; margin-top: 6px; }
.json-panel { max-height: 550px; overflow: auto; }
.json-content { padding: 14px 16px; font-family: "SF Mono", "Fira Code", monospace; font-size: 12px; line-height: 1.6; white-space: pre-wrap; word-break: break-all; }
.hidden { display: none !important; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; background: #dbeafe; color: #1e40af; }
.mt-8 { margin-top: 8px; }
@media (max-width: 800px) { .results-layout, .preview-row { flex-direction: column; } }
</style>
</head>
<body>
<div class="container">
<h1>🏥 接口3 — B端生发 测试</h1>
<p class="subtitle">POST /api/v1/hair/grow-b &nbsp;|&nbsp; 医生划线图 + 原图 → 最佳发际线 + 生发效果</p>
<div class="card">
<div class="card-body">
<div class="upload-row">
<div class="upload-group">
<div class="label">✏️ 划线图(marked_image<span class="tag required">必填</span></div>
<input type="file" id="markedFile" accept="image/jpeg,image/png,.jpg,.jpeg,.png">
</div>
<div class="upload-group">
<div class="label">📷 原始照片(original_image<span class="tag required">必填</span></div>
<input type="file" id="origFile" accept="image/jpeg,image/png,.jpg,.jpeg,.png">
</div>
</div>
<div style="margin-top:14px;display:flex;gap:12px;align-items:center">
<button class="btn btn-primary" id="submitBtn" onclick="submitTest()">🚀 提交</button>
<button class="btn btn-outline btn-sm" onclick="clearResults()">清除</button>
</div>
<div class="hint">两张图都必填,各 ≤1MB &nbsp;|&nbsp; 划线图 = 医生在原图上标了目标发际线的图片</div>
<div id="statusBar" class="status hidden"></div>
</div>
</div>
<div class="results-layout hidden" id="resultsArea">
<div class="col-main">
<!-- 输入图预览 -->
<div class="card">
<div class="card-header"><span>📸 输入预览</span></div>
<div class="card-body">
<div class="preview-row">
<div><div class="thumb" id="markedThumb"><span style="color:#9ca3af;padding:40px">划线图</span></div><div class="caption">marked_image(划线图)</div></div>
<div><div class="thumb" id="origThumb"><span style="color:#9ca3af;padding:40px">原始照片</span></div><div class="caption">original_image(原图)</div></div>
</div>
</div>
</div>
<!-- 结果 -->
<div class="card">
<div class="card-header"><span>🎯 返回结果</span></div>
<div class="card-body">
<div class="outcome">
<div class="field">
<div class="name">发际线类型 <span class="badge" id="hairlineType"></span></div>
</div>
<div class="field">
<div class="name">最合适发际线图(best_hairline_image</div>
<div class="thumb" id="bestHairlineImg"><span style="color:#9ca3af;padding:40px"></span></div>
</div>
<div class="field">
<div class="name">生发效果图(hair_growth_image</div>
<div class="thumb" id="growthImg"><span style="color:#9ca3af;padding:40px"></span></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-side">
<div class="card">
<div class="card-header"><span>📋 JSON</span><button class="btn btn-outline btn-sm" onclick="copyJson()">📋 复制</button></div>
<div class="json-panel"><pre class="json-content" id="jsonContent"></pre></div>
</div>
</div>
</div>
</div>
<script>
const API_BASE = window.location.origin;
const TYPE_LABELS = {
ellipse:'椭圆形', flower:'花瓣形', heart:'心形', straight:'直线形', wave:'波浪形', m:'M形', inverse_arc:'倒弧形'
};
function $(id) { return document.getElementById(id); }
function setStatus(t, type) { const b=$('statusBar'); b.textContent=t; b.className='status '+type; }
function showThumb(id, file) {
if (!file) return;
const url = URL.createObjectURL(file);
$(id).innerHTML = '<img src="'+url+'" alt="preview">';
}
async function submitTest() {
const mf = $('markedFile').files[0], of = $('origFile').files[0];
if (!mf) { setStatus('请选择划线图', 'error'); return; }
if (!of) { setStatus('请选择原始照片', 'error'); return; }
if (mf.size > 1_000_000 || of.size > 1_000_000) { setStatus('文件超过 1 MB', 'error'); return; }
showThumb('markedThumb', mf); showThumb('origThumb', of);
$('submitBtn').disabled = true; $('submitBtn').textContent = '⏳ ...';
setStatus('请求中...', 'info'); $('resultsArea').classList.add('hidden');
const fd = new FormData();
fd.append('marked_image_file', mf);
fd.append('original_image_file', of);
try {
const r = await fetch(API_BASE + '/api/v1/hair/grow-b', { method:'POST', body:fd });
const json = await r.json();
$('jsonContent').textContent = JSON.stringify(json, null, 2);
$('resultsArea').classList.remove('hidden');
if (json.code === 0) {
const d = json.data;
$('hairlineType').textContent = TYPE_LABELS[d.hairline_type] || d.hairline_type || '—';
if (d.best_hairline_image_url) {
$('bestHairlineImg').innerHTML = '<img src="'+d.best_hairline_image_url+'" alt="best hairline">';
}
if (d.hair_growth_image_url) {
$('growthImg').innerHTML = '<img src="'+d.hair_growth_image_url+'" alt="hair growth">';
}
setStatus('✅ 成功 &nbsp;|&nbsp; 发际线类型: ' + (TYPE_LABELS[d.hairline_type] || d.hairline_type), 'success');
} else {
setStatus('❌ code=' + json.code + ' ' + json.message, 'error');
}
} catch(e) { setStatus('❌ ' + e.message, 'error'); $('resultsArea').classList.remove('hidden'); }
finally { $('submitBtn').disabled = false; $('submitBtn').textContent = '🚀 提交'; }
}
function clearResults() {
$('resultsArea').classList.add('hidden'); $('statusBar').className='status hidden';
$('markedFile').value=''; $('origFile').value=''; $('jsonContent').textContent='';
$('markedThumb').innerHTML='<span style="color:#9ca3af;padding:40px">划线图</span>';
$('origThumb').innerHTML='<span style="color:#9ca3af;padding:40px">原始照片</span>';
$('bestHairlineImg').innerHTML='<span style="color:#9ca3af;padding:40px">—</span>';
$('growthImg').innerHTML='<span style="color:#9ca3af;padding:40px">—</span>';
$('hairlineType').textContent='—';
}
function copyJson() {
navigator.clipboard.writeText($('jsonContent').textContent).then(() => {
const b = event.target, o = b.textContent; b.textContent = '✅ 已复制'; setTimeout(() => b.textContent = o, 1500);
});
}
</script>
</body>
</html>