Files
hair/static/test_interface3.html
T
2026-06-15 10:33:15 +08:00

178 lines
8.5 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 .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; }
.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: 350px; object-fit: contain; }
.preview-row .caption { font-size: 11px; color: #9ca3af; margin-top: 6px; }
.na { color: #d1d5db; font-size: 12px; padding: 40px 20px; background: #f9fafb; border-radius: 6px; }
.result-field { margin-bottom: 14px; }
.result-field .name { font-size: 12px; font-weight: 600; color: #6b7280; margin-bottom: 4px; text-transform: uppercase; letter-spacing: .5px; }
.badge { display: inline-block; padding: 2px 10px; border-radius: 10px; font-size: 12px; font-weight: 600; background: #dbeafe; color: #1e40af; }
.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; }
.tag-new { background: #059669; color: #fff; font-size: 10px; padding: 1px 6px; border-radius: 8px; margin-left: 4px; }
@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-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 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">JPG/PNG ≤1MB &nbsp;|&nbsp; 用马克笔在用户照片额头画发际线后拍照上传 &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" style="text-align:center">
<div class="thumb" id="markedThumb"><span style="color:#9ca3af;padding:40px"></span></div>
</div>
</div>
<!-- 结果 -->
<div class="card">
<div class="card-header"><span>🎯 生发效果</span></div>
<div class="card-body">
<div class="result-field">
<div class="name">发际线类型</div>
<span class="badge" id="hairlineType"></span>
</div>
<div class="result-field">
<div class="name">生发效果图(hair_growth_image<span class="tag-new">ComfyUI</span></div>
<div class="thumb" id="growthImg"><span style="color:#9ca3af;padding:40px"></span></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;
function $(id) { return document.getElementById(id); }
function setStatus(t, type) { const b=$('statusBar'); b.textContent=t; b.className='status '+type; }
async function submitTest() {
const mf = $('markedFile').files[0];
if (!mf) { setStatus('请选择划线图', 'error'); return; }
if (mf.size > 1_000_000) { setStatus('文件超过 1 MB', 'error'); return; }
// 预览划线图
$('markedThumb').innerHTML = '<img src="'+URL.createObjectURL(mf)+'" alt="划线图">';
$('submitBtn').disabled = true; $('submitBtn').textContent = '⏳ ...';
setStatus('请求中(生发需要数十秒)...', 'info'); $('resultsArea').classList.add('hidden');
const fd = new FormData();
fd.append('marked_image_file', mf);
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 = d.hairline_type || '—';
if (d.hair_growth_image_url) {
$('growthImg').innerHTML = '<img src="'+d.hair_growth_image_url+'" alt="生发效果">';
} else {
$('growthImg').innerHTML = '<div class="na">⚠ 生发图未返回<br><span style="font-size:10px;color:#9ca3af">ComfyUI 未就绪或生成失败</span></div>';
}
setStatus('✅ 成功 &nbsp;|&nbsp; 发际线类型: ' + (d.hairline_type || '—'), 'success');
} else {
setStatus('❌ code=' + json.code + ' ' + json.message, 'error');
$('growthImg').innerHTML = '<div class="na">—</div>';
}
} 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=''; $('jsonContent').textContent='';
$('markedThumb').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>