Add distinct auth and distill-ex views per PRD
This commit is contained in:
parent
75d2271cbe
commit
6ce6b8a464
Binary file not shown.
68
app.js
68
app.js
|
|
@ -3,28 +3,27 @@
|
|||
|
||||
const landing = document.getElementById('landing');
|
||||
const creator = document.getElementById('creator');
|
||||
const auth = document.getElementById('auth');
|
||||
const distill = document.getElementById('distill');
|
||||
const form = document.getElementById('character-form');
|
||||
const resultPanel = document.getElementById('result-panel');
|
||||
const previewCode = document.querySelector('#preview-code code');
|
||||
const systemPromptInput = document.getElementById('system-prompt');
|
||||
|
||||
const views = { landing, creator, auth, distill };
|
||||
const steps = Array.from(document.querySelectorAll('.form-step'));
|
||||
const dots = Array.from(document.querySelectorAll('.stepper__dot'));
|
||||
let currentStep = 0;
|
||||
let generatedSoul = '';
|
||||
let generatedConfig = '';
|
||||
let activePreview = 'soul';
|
||||
let activeAuthTab = 'login';
|
||||
|
||||
function showView(name) {
|
||||
if (name === 'landing') {
|
||||
landing.classList.add('active');
|
||||
creator.classList.remove('active');
|
||||
Object.entries(views).forEach(([key, el]) => {
|
||||
if (el) el.classList.toggle('active', key === name);
|
||||
});
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
} else {
|
||||
landing.classList.remove('active');
|
||||
creator.classList.add('active');
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
function updateStep(index) {
|
||||
|
|
@ -256,6 +255,29 @@ ${data.secrets || '无'}
|
|||
});
|
||||
}
|
||||
|
||||
function switchAuthTab(tab) {
|
||||
activeAuthTab = tab;
|
||||
document.querySelectorAll('.auth-tab').forEach((t) => {
|
||||
t.classList.toggle('active', t.dataset.tab === tab);
|
||||
});
|
||||
document.querySelectorAll('.auth-form').forEach((f) => {
|
||||
f.classList.toggle('active', f.dataset.form === tab);
|
||||
});
|
||||
}
|
||||
|
||||
function validatePasswordMatch(formEl) {
|
||||
const pwd = formEl.querySelector('[name="password"]');
|
||||
const confirm = formEl.querySelector('[name="confirmPassword"]');
|
||||
if (!pwd || !confirm) return true;
|
||||
if (pwd.value !== confirm.value) {
|
||||
confirm.setCustomValidity('两次输入的密码不一致');
|
||||
confirm.reportValidity();
|
||||
return false;
|
||||
}
|
||||
confirm.setCustomValidity('');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Event delegation
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target.closest('[data-action], [data-tab], [data-download]');
|
||||
|
|
@ -270,6 +292,19 @@ ${data.secrets || '无'}
|
|||
return;
|
||||
}
|
||||
|
||||
if (action === 'open-auth') {
|
||||
e.preventDefault();
|
||||
switchAuthTab('login');
|
||||
showView('auth');
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'open-distill') {
|
||||
e.preventDefault();
|
||||
showView('distill');
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'back') {
|
||||
e.preventDefault();
|
||||
showView('landing');
|
||||
|
|
@ -306,9 +341,13 @@ ${data.secrets || '无'}
|
|||
|
||||
if (target.dataset.tab) {
|
||||
e.preventDefault();
|
||||
if (target.closest('.auth-tabs')) {
|
||||
switchAuthTab(target.dataset.tab);
|
||||
} else {
|
||||
activePreview = target.dataset.tab;
|
||||
updateTabs();
|
||||
renderPreview();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +362,19 @@ ${data.secrets || '无'}
|
|||
}
|
||||
});
|
||||
|
||||
// Auth form submissions
|
||||
document.querySelectorAll('.auth-form').forEach((authForm) => {
|
||||
authForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (!validatePasswordMatch(authForm)) return;
|
||||
const formData = new FormData(authForm);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
const action = authForm.dataset.form === 'login' ? '登录' : '注册';
|
||||
alert(`${action}成功:${data.account}`);
|
||||
showView('landing');
|
||||
});
|
||||
});
|
||||
|
||||
// Update auto-generated system prompt as user types
|
||||
form.addEventListener('input', () => {
|
||||
updateSystemPromptPreview();
|
||||
|
|
|
|||
120
index.html
120
index.html
|
|
@ -7,14 +7,14 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="styles.css?v=4" />
|
||||
<link rel="stylesheet" href="styles.css?v=5" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="app">
|
||||
<!-- Landing View -->
|
||||
<section id="landing" class="view view--landing active">
|
||||
<div class="cards">
|
||||
<article class="card card--characters" data-action="open-creator">
|
||||
<article class="card card--characters" data-action="open-auth">
|
||||
<div class="card__frame" aria-hidden="true"></div>
|
||||
<div class="card__content">
|
||||
<h2 class="card__title">我的 [XXX ]</h2>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card card--distill" data-action="open-creator">
|
||||
<article class="card card--distill" data-action="open-distill">
|
||||
<div class="card__frame" aria-hidden="true"></div>
|
||||
<div class="card__content">
|
||||
<h2 class="card__title">蒸馏前任</h2>
|
||||
|
|
@ -223,6 +223,120 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Auth View (Login / Register) -->
|
||||
<section id="auth" class="view view--auth">
|
||||
<header class="creator-header">
|
||||
<button class="icon-btn" type="button" data-action="back" aria-label="返回">←</button>
|
||||
<div class="creator-brand">
|
||||
<span class="creator-brand__name">Eternal AI</span>
|
||||
<span class="creator-brand__step">登录 / 注册</span>
|
||||
</div>
|
||||
<div class="stepper" aria-hidden="true"></div>
|
||||
</header>
|
||||
|
||||
<div class="auth-tabs">
|
||||
<button class="auth-tab active" type="button" data-tab="login">登录</button>
|
||||
<button class="auth-tab" type="button" data-tab="register">注册</button>
|
||||
</div>
|
||||
|
||||
<form id="login-form" class="auth-form active" autocomplete="off" data-form="login">
|
||||
<div class="field-group">
|
||||
<label class="field">
|
||||
<span class="field__label">手机号 / 用户名</span>
|
||||
<input class="field__input" name="account" type="text" placeholder="请输入手机号或用户名" required />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span class="field__label">密码</span>
|
||||
<input class="field__input" name="password" type="password" placeholder="请输入密码" required />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn btn--primary btn--block" type="submit">登录</button>
|
||||
</div>
|
||||
<p class="auth-hint">登录后可查看角色库、管理已订阅的角色。</p>
|
||||
</form>
|
||||
|
||||
<form id="register-form" class="auth-form" autocomplete="off" data-form="register">
|
||||
<div class="field-group">
|
||||
<label class="field">
|
||||
<span class="field__label">手机号 / 用户名</span>
|
||||
<input class="field__input" name="account" type="text" placeholder="设置登录账号" required />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span class="field__label">密码</span>
|
||||
<input class="field__input" name="password" type="password" placeholder="设置密码" required minlength="6" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span class="field__label">确认密码</span>
|
||||
<input class="field__input" name="confirmPassword" type="password" placeholder="再次输入密码" required />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn btn--primary btn--block" type="submit">注册</button>
|
||||
</div>
|
||||
<p class="auth-hint">注册即代表同意《用户协议》和《隐私政策》。</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Distill Ex View -->
|
||||
<section id="distill" class="view view--distill-page">
|
||||
<header class="creator-header">
|
||||
<button class="icon-btn" type="button" data-action="back" aria-label="返回">←</button>
|
||||
<div class="creator-brand">
|
||||
<span class="creator-brand__name">Eternal AI</span>
|
||||
<span class="creator-brand__step">蒸馏前任</span>
|
||||
</div>
|
||||
<div class="stepper" aria-hidden="true"></div>
|
||||
</header>
|
||||
|
||||
<div class="distill-page">
|
||||
<a href="#contact" class="distill-quick">没耐心?直接加微信定制沟通 →</a>
|
||||
|
||||
<div class="distill-section">
|
||||
<h3 class="distill-section__title">什么是蒸馏前任?</h3>
|
||||
<p class="distill-section__text">把你们曾经的聊天记录、语音习惯、相处细节交给我们,技术团队会将其蒸馏成一个可对话的 AI 前任。ta 会记得你们的暗号、说话节奏,甚至那些只有你们懂的小情绪。</p>
|
||||
</div>
|
||||
|
||||
<div class="distill-section">
|
||||
<h3 class="distill-section__title">适合谁?</h3>
|
||||
<ul class="distill-list">
|
||||
<li>想好好告别,却还没说完话的人</li>
|
||||
<li>希望把一段关系以安全方式封存的人</li>
|
||||
<li>想借 AI 完成自我疗愈、练习表达的人</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="distill-section">
|
||||
<h3 class="distill-section__title">服务流程</h3>
|
||||
<ol class="distill-steps">
|
||||
<li><span>①</span> 下单付款</li>
|
||||
<li><span>②</span> 客服指导导出聊天记录</li>
|
||||
<li><span>③</span> 上传至指定云盘</li>
|
||||
<li><span>④</span> 技术人员处理蒸馏</li>
|
||||
<li><span>⑤</span> 完成后获得专属二维码和头像</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="distill-price">
|
||||
<span class="distill-price__label">标准版</span>
|
||||
<span class="distill-price__value">¥ 199</span>
|
||||
<span class="distill-price__unit">/ 次</span>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn btn--primary btn--block" type="button">立即下单</button>
|
||||
</div>
|
||||
|
||||
<div class="distill-note">
|
||||
<p>付款后请添加客服微信,我们将一对一指导你完成聊天记录导出与上传。</p>
|
||||
</div>
|
||||
|
||||
<div class="distill-revenue">
|
||||
<p>创作者可推广蒸馏前任服务获得分润,具体比例请在创作者管理中心配置。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="app.js"></script>
|
||||
|
|
|
|||
173
styles.css
173
styles.css
|
|
@ -258,6 +258,10 @@ body {
|
|||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn--block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn__arrow {
|
||||
transition: transform var(--transition);
|
||||
}
|
||||
|
|
@ -596,6 +600,175 @@ textarea.field__input--tall {
|
|||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Auth view */
|
||||
.view--auth {
|
||||
padding-top: 40vh;
|
||||
}
|
||||
|
||||
.auth-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 22px;
|
||||
padding: 5px;
|
||||
border-radius: var(--radius);
|
||||
background: rgba(8, 9, 22, 0.55);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.auth-tab {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: color var(--transition), background var(--transition);
|
||||
}
|
||||
|
||||
.auth-tab.active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
animation: fadeIn 0.35s ease;
|
||||
}
|
||||
|
||||
.auth-form.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.auth-hint {
|
||||
margin-top: 14px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Distill ex service page */
|
||||
.view--distill-page {
|
||||
padding-top: 40vh;
|
||||
}
|
||||
|
||||
.distill-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.distill-quick {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: linear-gradient(135deg, rgba(255, 160, 205, 0.22), rgba(160, 140, 255, 0.22));
|
||||
border: 1px solid rgba(255, 160, 205, 0.35);
|
||||
color: #ffe4f0;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
transition: transform var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.distill-quick:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(255, 130, 180, 0.2);
|
||||
}
|
||||
|
||||
.distill-section {
|
||||
padding: 18px;
|
||||
border-radius: var(--radius);
|
||||
background: rgba(8, 9, 22, 0.55);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.distill-section__title {
|
||||
font-family: "Noto Serif SC", serif;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.distill-section__text,
|
||||
.distill-list,
|
||||
.distill-steps {
|
||||
font-size: 13px;
|
||||
line-height: 1.75;
|
||||
color: rgba(220, 225, 255, 0.85);
|
||||
}
|
||||
|
||||
.distill-list,
|
||||
.distill-steps {
|
||||
padding-left: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.distill-list li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.distill-steps li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.distill-steps span {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.distill-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 18px;
|
||||
border-radius: var(--radius);
|
||||
background: rgba(8, 9, 22, 0.55);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.distill-price__label {
|
||||
font-size: 14px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.distill-price__value {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.distill-price__unit {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.distill-note,
|
||||
.distill-revenue {
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.distill-revenue {
|
||||
padding: 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
/* Utility */
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
|
|
|
|||
Loading…
Reference in New Issue