Files
hunjing/frontend/design/character-detail-preview.html
2026-08-21 16:00:13 +08:00

366 lines
20 KiB
HTML
Raw Permalink 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" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>魂镜 · 人物详情页设计预览</title>
<link rel="stylesheet" href="tokens.css" />
<link rel="stylesheet" href="_base.css" />
<link rel="stylesheet" href="_chrome.css" />
<style>
/* ============================================================
* 铜镜鉴心 v2.1 — 人物详情页
* ============================================================ */
/* ── 通用布局 ── */
.app-main { flex: 1; padding: 24px 28px 56px; max-width: 920px; width: 100%; margin: 0 auto; }
@media (max-width: 900px) { .sidebar-wrap { display: none; } }
/* ── 操作栏 ── */
.action-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 14px 0 18px; }
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 12px; border-radius: 999px; font-family: "JetBrains Mono", monospace; font-size: 11px; border: 1px solid var(--rule); background: var(--surface); color: var(--fg-dim); }
.badge.active { border-color: var(--patina-dim); color: var(--patina); background: var(--patina-wash); }
.badge.paused { border-color: var(--gilt-dim); color: var(--gilt); background: var(--gilt-faint); }
.badge.wuji { border-color: var(--rule); color: var(--fg-faint); }
/* ── 生命周期条 ── */
.lifecycle-bar { display: flex; align-items: center; gap: 3px; font-family: "JetBrains Mono", monospace; font-size: 10.5px; }
.lifecycle-step { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: 999px; color: var(--fg-faint); border: 1px solid transparent; }
.lifecycle-step.past { color: var(--patina); border-color: var(--patina-dim); background: var(--patina-wash); }
.lifecycle-step.current { color: var(--gilt); border-color: var(--gilt-dim); background: var(--gilt-faint); }
.lifecycle-arrow { color: var(--fg-faint); font-size: 12px; opacity: 0.5; }
.action-link { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; padding: 6px 14px; border-radius: 10px; font-size: 12px; color: var(--patina); border: 1px solid var(--patina-dim); background: var(--patina-wash); text-decoration: none; transition: background 0.12s; cursor: pointer; }
.action-link:hover { background: var(--patina-dim); color: var(--fg); }
/* ── 卡片 ── */
.card-banner { height: 72px; background: linear-gradient(135deg, var(--patina-wash), var(--glass), var(--gilt-faint)); }
/* ── 铜镜头像 ── */
.avatar-section { position: relative; width: 88px; height: 88px; margin-top: -48px; margin-bottom: 12px; }
.avatar-img { width: 88px; height: 88px; border-radius: 50%; border: 3px solid var(--gilt); box-shadow: 0 0 0 3px var(--surface), 0 0 16px rgba(201,162,74,0.25); overflow: hidden; display: flex; align-items: center; justify-content: center; background: conic-gradient(from 0deg, var(--ink), var(--night-bronze), var(--patina-dim), var(--gilt-dim), var(--ink)); position: relative; cursor: pointer; transition: filter 0.2s; }
.avatar-img:hover { filter: brightness(1.15); }
.avatar-img .placeholder { font-size: 28px; opacity: 0.6; }
.avatar-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.avatar-overlay { position: absolute; inset: 0; border-radius: 50%; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s; }
.avatar-section:hover .avatar-overlay { opacity: 1; }
.avatar-overlay .upload-label { display: flex; align-items: center; gap: 3px; color: #fff; font-size: 10px; cursor: pointer; }
.avatar-overlay .upload-icon { font-size: 20px; }
.avatar-name-badge { flex: 1; }
.identity-header { display: flex; align-items: flex-end; justify-content: space-between; }
.char-name { font-family: "Songti SC", serif; font-size: 20px; font-weight: 700; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.char-inner-name { font-size: 12px; color: var(--fg-dim); margin-top: 1px; }
/* ── 档案信息网格 ── */
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px; margin-top: 14px; }
.info-item .label { font-size: 12px; color: var(--muted); }
.info-item .value { font-size: 14px; font-weight: 500; color: var(--fg); }
.quote { font-style: italic; color: var(--fg-dim); font-size: 14px; margin-top: 12px; padding-left: 10px; border-left: 2px solid var(--gilt-dim); }
.bio-section { margin-top: 14px; border-top: 1px solid var(--rule); padding-top: 14px; }
.bio-section .label { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.bio-section .text { font-size: 14px; line-height: 1.7; color: var(--fg); white-space: pre-wrap; }
/* ── 双列布局 ── */
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
/* ── 表单 ── */
.form-card .card-header { display: flex; align-items: center; justify-content: space-between; padding: 14px 20px 0; }
.form-card .card-title { font-family: "Songti SC", serif; font-size: 15px; font-weight: 600; color: var(--fg); display: flex; align-items: center; gap: 6px; }
.form-card .card-title .glyph { color: var(--cinnabar); opacity: 0.7; }
.form-body { padding: 14px 20px 20px; }
.field { margin-bottom: 12px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.field-label { display: block; font-family: "JetBrains Mono", monospace; font-size: 11px; letter-spacing: 0.04em; color: var(--muted); margin-bottom: 4px; }
.field input, .field textarea, .field select { width: 100%; padding: 8px 12px; border: 1px solid var(--rule); border-radius: 10px; background: var(--glass); color: var(--fg); font-family: inherit; font-size: 13px; transition: border-color 0.15s; }
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--patina-dim); }
.field textarea { min-height: 64px; resize: vertical; }
.field-hint { font-size: 11px; color: var(--fg-faint); margin-top: 3px; }
.btn-save { display: inline-flex; align-items: center; gap: 5px; padding: 6px 16px; border-radius: 10px; font-size: 12px; font-weight: 500; border: 1px solid var(--patina-dim); background: var(--patina-wash); color: var(--patina); cursor: pointer; transition: background 0.12s; font-family: inherit; }
.btn-save:hover { background: var(--patina-dim); color: var(--fg); }
.btn-save:disabled { opacity: 0.4; cursor: default; }
/* ── Tag 输入模拟 ── */
.tag-input-mock { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 10px; border: 1px solid var(--rule); border-radius: 10px; background: var(--glass); min-height: 34px; align-items: center; }
/* ── Modal ── */
.modal-overlay { position: fixed; inset: 0; z-index: 100; background: rgba(0,0,0,0.55); backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; padding: 20px; }
.modal { background: var(--ink); border: 1px solid var(--rule); border-radius: 18px; width: 100%; max-width: 420px; box-shadow: 0 12px 60px rgba(0,0,0,0.6); }
.modal-header { display: flex; align-items: center; gap: 12px; padding: 18px 22px 10px; border-bottom: 1px solid var(--rule); }
.modal-title { font-family: "Songti SC", serif; font-size: 16px; font-weight: 600; color: var(--fg); flex: 1; }
.modal-close { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 8px; cursor: pointer; border: none; background: none; color: var(--fg-faint); font-size: 18px; transition: background 0.12s; }
.modal-close:hover { background: var(--surface-hi); color: var(--fg); }
.modal-body { padding: 18px 22px 22px; }
/* ── Toast ── */
.toast { position: fixed; bottom: 24px; right: 24px; z-index: 200; display: flex; align-items: center; gap: 8px; padding: 10px 16px; border-radius: 12px; border: 1px solid var(--patina-dim); background: var(--surface); backdrop-filter: blur(12px); font-size: 13px; color: var(--patina); box-shadow: 0 4px 24px rgba(0,0,0,0.5); }
/* ── Responsive ── */
@media (max-width: 768px) {
.app-main { padding: 16px 14px 40px; }
.two-col { grid-template-columns: 1fr; }
.info-grid { grid-template-columns: 1fr; }
.field-row { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="app-shell">
<div class="sidebar-mount" data-active="人物"></div>
<div class="main-area">
<main class="app-main">
<!-- ═══ 面包屑 ═══ -->
<a class="back-link" href="characters-preview.html">← 返回人物列表</a>
<!-- ═══ 页头 ═══ -->
<h1 class="page-title">时崎狂三</h1>
<p class="page-sub">内部名称:tokisaki_kurumi · 创建于 2026-07-09 14:32:18</p>
<!-- ═══ 操作栏 ═══ -->
<div class="action-bar">
<span class="badge active">● active</span>
<span class="lifecycle-bar">
<span class="lifecycle-step past">无极</span><span class="lifecycle-arrow">→</span>
<span class="lifecycle-step past">立名</span><span class="lifecycle-arrow">→</span>
<span class="lifecycle-step past">设定</span><span class="lifecycle-arrow">→</span>
<span class="lifecycle-step past">初识</span><span class="lifecycle-arrow">→</span>
<span class="lifecycle-step current">太极</span><span class="lifecycle-arrow">→</span>
<span class="lifecycle-step">归档</span>
</span>
<a class="action-link" href="#">资料与沉淀 →</a>
</div>
<!-- ═══ 角色身份卡 ═══ -->
<div class="card">
<div class="card-banner"></div>
<div class="card-body">
<!-- 头像:预留图片 -->
<div class="avatar-section" onclick="document.getElementById('modal-upload').style.display='flex'">
<div class="avatar-img">
<span class="placeholder">☯</span>
</div>
<div class="avatar-overlay">
<span class="upload-label">
<span class="upload-icon">📷</span>
更换头像
</span>
</div>
</div>
<div class="identity-header">
<div>
<div class="char-name">时崎狂三</div>
<div class="char-inner-name">内部名称:tokisaki_kurumi</div>
</div>
<span class="badge active">● active</span>
</div>
<div class="info-grid">
<div class="info-item"><div class="label">生日</div><div class="value">2000-12-25</div></div>
<div class="info-item"><div class="label">身高</div><div class="value">157 cm</div></div>
<div class="info-item"><div class="label">体重</div><div class="value">43 kg</div></div>
<div class="info-item"><div class="label">当前心情</div><div class="value">平静(42%)</div></div>
<div class="info-item"><div class="label">语言</div><div class="value">zh-CN</div></div>
<div class="info-item"><div class="label">创建时间</div><div class="value">2026-07-09 14:32</div></div>
</div>
<div class="quote">「优雅从容的大小姐口气,语速中等偏慢…」</div>
<div class="bio-section">
<div class="label">背景</div>
<div class="text">
拥有时间之力的精灵「Nightmare(梦魇)」。
为了某个终极目标在时间洪流中独自前行,通过吞噬他人的时间维持自己的存在。
她的过去充满了失去与痛苦,但她从不后悔自己选择的道路。
</div>
</div>
<div class="bio-section">
<div class="label">兴趣爱好</div>
<div class="tag-input-mock" style="margin-top:4px;">
<span class="tag">与猫相处</span>
<span class="tag">阅读</span>
<span class="tag">钢琴</span>
<span class="tag">收藏古董时钟</span>
<span class="tag">摄影</span>
<span class="tag">品尝红茶与甜点</span>
</div>
</div>
<div class="bio-section">
<div class="label">价值观</div>
<div class="tag-input-mock" style="margin-top:4px;">
<span class="tag">自由</span>
<span class="tag">尊严</span>
<span class="tag">独立</span>
<span class="tag">记忆</span>
</div>
</div>
</div>
</div>
<!-- ═══ 双列:档案编辑 + 模型策略 ═══ -->
<div class="two-col">
<!-- 左:人物档案编辑 -->
<div class="card form-card">
<div class="card-header">
<h2 class="card-title"><span class="glyph">📋</span> 人物档案</h2>
<button class="btn-save">✎ 保存</button>
</div>
<div class="form-body">
<div class="field">
<label class="field-label">显示名</label>
<input type="text" value="时崎狂三" />
</div>
<div class="field-row">
<div class="field">
<label class="field-label">生日</label>
<input type="date" value="2000-12-25" />
</div>
<div class="field">
<label class="field-label">身高</label>
<input type="text" value="157" />
</div>
</div>
<div class="field-row">
<div class="field">
<label class="field-label">体重</label>
<input type="text" value="43" />
</div>
<div class="field">
<label class="field-label">性情气质</label>
<input type="text" value="优雅腹黑、孤独偏执" />
</div>
</div>
<div class="field">
<label class="field-label">表达风格</label>
<input type="text" value="优雅从容的大小姐口气,语速中等偏慢" />
</div>
<div class="field">
<label class="field-label">背景</label>
<textarea>拥有时间之力的精灵「Nightmare(梦魇)」。</textarea>
</div>
</div>
</div>
<!-- 右:模型策略编辑 -->
<div class="card form-card">
<div class="card-header">
<h2 class="card-title"><span class="glyph">⚙</span> 模型策略</h2>
<button class="btn-save">✎ 保存</button>
</div>
<div class="form-body">
<div class="field">
<label class="field-label">对话模型</label>
<select>
<option>— 不设置(使用系统默认)</option>
<option selected>deepseek-v4-flash</option>
<option>deepseek-v3</option>
<option>gpt-4o</option>
</select>
<span class="field-hint">留空则使用 DEFAULT_MODEL_HINT</span>
</div>
<div class="field">
<label class="field-label">记忆提取模型</label>
<select>
<option>— 不设置(空)</option>
<option selected>memory-extraction</option>
<option>deepseek-v4-flash</option>
</select>
</div>
<div class="field">
<label class="field-label">人格分析模型</label>
<select>
<option>— 不设置(空)</option>
<option selected>personality-analysis</option>
<option>deepseek-v4-flash</option>
</select>
</div>
<div class="field">
<label class="field-label">向量嵌入模型</label>
<select>
<option>— 不设置(空)</option>
<option selected>embedding</option>
<option>text-embedding-3-small</option>
</select>
</div>
<div class="field">
<label class="field-label">私隐等级</label>
<select>
<option selected>normal</option>
<option>sensitive</option>
<option>local_only</option>
</select>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- ═══ 上传头像 Modal ═══ -->
<div class="modal-overlay" id="modal-upload" style="display:none;" onclick="if(event.target===this)this.style.display='none'">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title">📷 上传头像</h3>
<button class="modal-close" onclick="document.getElementById('modal-upload').style.display='none'">✕</button>
</div>
<div class="modal-body" style="text-align:center;">
<!-- 预览区 -->
<div style="width:120px;height:120px;border-radius:50%;border:3px dashed var(--rule);margin:0 auto 16px;display:flex;align-items:center;justify-content:center;background:var(--glass);overflow:hidden;" id="preview-area">
<span style="font-size:36px;opacity:0.25;color:var(--fg-faint);">+</span>
</div>
<!-- 上传按钮 -->
<label class="btn btn-primary" style="display:inline-flex;cursor:pointer;">
📁 选择图片
<input type="file" accept="image/*" style="display:none;" onchange="previewFile(this)" />
</label>
<p style="font-size:11px;color:var(--fg-faint);margin-top:10px;">支持 JPG / PNG / WebP,建议 512×512 以上,最大 5MB</p>
<div style="display:flex;gap:8px;justify-content:center;margin-top:16px;">
<button class="btn btn-outline" onclick="document.getElementById('modal-upload').style.display='none'">取消</button>
<button class="btn btn-primary" onclick="showToast('✔ 头像已更新');document.getElementById('modal-upload').style.display='none'">✚ 上传</button>
</div>
</div>
</div>
</div>
<div class="toast" id="toast" style="display:none;">✔ 已保存</div>
<script>
function previewFile(input) {
const file = input.files && input.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
const area = document.getElementById('preview-area');
area.innerHTML = '<img src="' + e.target.result + '" style="width:100%;height:100%;object-fit:cover;" />';
};
reader.readAsDataURL(file);
}
function showToast(msg) {
const t = document.getElementById('toast');
t.textContent = msg;
t.style.display = 'flex';
setTimeout(function() { t.style.display = 'none'; }, 2500);
}
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') document.querySelectorAll('.modal-overlay').forEach(function(m) { m.style.display = 'none'; });
});
</script>
<script src="_theme.js" defer></script>
</body>
</html>