refactor(ui): merge showcase settings into settings page tab, fix profile showcase GET route, fix layout

This commit is contained in:
2026-07-05 23:56:28 +08:00
parent 148cfdf2a4
commit c550289527
4 changed files with 151 additions and 21 deletions

View File

@@ -77,8 +77,10 @@ def login_submit(
def settings_page( def settings_page(
request: Request, request: Request,
user: User = Depends(get_current_user_web), user: User = Depends(get_current_user_web),
db: Session = Depends(get_db),
): ):
return templates.TemplateResponse("settings.html", {"request": request, "user": user}) profiles = db.query(Profile).filter(Profile.user_id == user.id).all()
return templates.TemplateResponse("settings.html", {"request": request, "user": user, "profiles": profiles})
@router.post("/settings/change-username") @router.post("/settings/change-username")
@@ -458,16 +460,9 @@ async def upload_image(
# ---------- Showcase ---------- # ---------- Showcase ----------
@router.get("/showcase/manage", response_class=HTMLResponse) @router.get("/showcase/manage")
def showcase_manage(request: Request, user: User = Depends(get_current_user_web), db: Session = Depends(get_db)): def showcase_manage_redirect(user: User = Depends(get_current_user_web)):
profiles = db.query(Profile).filter(Profile.user_id == user.id).all() return _redirect("/settings?tab=showcase")
profile_cards = {}
for p in profiles:
cards = db.query(Postcard).filter(Postcard.profile_id == p.id).all()
profile_cards[p.id] = cards
return templates.TemplateResponse("showcase_manage.html", {
"request": request, "user": user, "profiles": profiles, "profile_cards": profile_cards,
})
@router.post("/showcase/manage") @router.post("/showcase/manage")
@@ -475,7 +470,23 @@ def showcase_manage_save(request: Request, showcase_mode: str = Form("profile"),
if showcase_mode in ("profile", "flat"): if showcase_mode in ("profile", "flat"):
user.showcase_mode = showcase_mode user.showcase_mode = showcase_mode
db.commit() db.commit()
return _redirect("/showcase/manage") return _redirect("/settings?tab=showcase")
@router.get("/profiles/{profile_id}/showcase", response_class=HTMLResponse)
def profile_showcase_page(
request: Request,
profile_id: int,
user: User = Depends(get_current_user_web),
db: Session = Depends(get_db),
):
p = db.query(Profile).filter(Profile.id == profile_id, Profile.user_id == user.id).first()
if not p:
return _redirect("/settings?tab=showcase")
cards = db.query(Postcard).filter(Postcard.profile_id == profile_id).all()
return templates.TemplateResponse("profile_showcase.html", {
"request": request, "user": user, "profile": p, "cards": cards,
})
@router.post("/profiles/{profile_id}/showcase") @router.post("/profiles/{profile_id}/showcase")
@@ -492,7 +503,7 @@ def profile_showcase_save(
if showcase_visible in ("sent", "received", "both"): if showcase_visible in ("sent", "received", "both"):
p.showcase_visible = showcase_visible p.showcase_visible = showcase_visible
db.commit() db.commit()
return _redirect("/showcase/manage") return _redirect("/settings?tab=showcase")
@router.post("/postcards/{postcard_id}/showcase-toggle") @router.post("/postcards/{postcard_id}/showcase-toggle")
@@ -503,7 +514,7 @@ def postcard_showcase_toggle(postcard_id: int, showcase_hidden: str = Form(""),
if profile and profile.showcase_enabled: if profile and profile.showcase_enabled:
pc.showcase_hidden = showcase_hidden == "1" pc.showcase_hidden = showcase_hidden == "1"
db.commit() db.commit()
return _redirect("/showcase/manage") return _redirect("/settings?tab=showcase")
@router.get("/u/{username}", response_class=HTMLResponse) @router.get("/u/{username}", response_class=HTMLResponse)

View File

@@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block title %}{{ profile.nickname }} 展示设置 - 明信片管理器{% endblock %}
{% block content %}
<div class="section-header">
<div class="detail-title-row">
<a href="/settings?tab=showcase" class="btn btn-back"></a>
<h1>{{ profile.nickname }} 展示设置</h1>
</div>
</div>
<div class="form-card">
<form method="post" action="/profiles/{{ profile.id }}/showcase" style="display:flex;flex-wrap:wrap;gap:1rem;align-items:center">
<div style="display:flex;align-items:center;gap:.5rem">
<label style="margin:0;font-size:.85rem">开启展示</label>
<label class="toggle-switch">
<input type="checkbox" name="showcase_enabled" value="1" {% if profile.showcase_enabled %}checked{% endif %} onchange="this.form.submit()">
<span class="toggle-slider"></span>
</label>
</div>
<div style="display:flex;align-items:center;gap:.5rem">
<label style="margin:0;font-size:.85rem">展示内容</label>
<select name="showcase_visible" onchange="this.form.submit()" style="width:auto">
<option value="both" {% if profile.showcase_visible == 'both' %}selected{% endif %}>发出 + 收到</option>
<option value="sent" {% if profile.showcase_visible == 'sent' %}selected{% endif %}>仅发出</option>
<option value="received" {% if profile.showcase_visible == 'received' %}selected{% endif %}>仅收到</option>
</select>
</div>
</form>
</div>
{% if profile.showcase_enabled and cards %}
<div style="margin-top:1.5rem">
<h2 style="font-size:1rem;margin-bottom:1rem">单张明信片展示控制</h2>
<div style="background:var(--card-bg);border:1px solid var(--border);border-radius:10px;padding:1rem">
{% for pc in cards %}
<form method="post" action="/postcards/{{ pc.id }}/showcase-toggle" style="display:inline-flex;align-items:center;gap:.4rem;margin-right:1rem;margin-bottom:.4rem;font-size:.85rem">
<span>{{ pc.card_number }}</span>
<span class="badge" style="font-size:.7rem;{% if pc.status == 'sent' %}background:var(--sent){% elif pc.status == 'delivered' %}background:var(--delivered){% else %}background:var(--received){% endif %}">
{{ {'sent':'寄出','delivered':'送达','received':'收到'}.get(pc.status, pc.status) }}
</span>
<label class="toggle-switch toggle-sm">
<input type="checkbox" name="showcase_hidden" value="1" {% if pc.showcase_hidden %}checked{% endif %} onchange="this.form.submit()">
<span class="toggle-slider"></span>
</label>
{% if pc.showcase_hidden %}<span style="color:var(--text-muted);font-size:.75rem">隐藏</span>{% endif %}
</form>
{% endfor %}
</div>
</div>
{% endif %}
{% if not profile.showcase_enabled %}
<div class="empty-state" style="margin-top:2rem">
<p>开启展示后可控制单张明信片的可见性</p>
</div>
{% endif %}
{% endblock %}

View File

@@ -51,8 +51,7 @@
<div class="profile-card-footer"> <div class="profile-card-footer">
<span class="profile-card-date">创建于 {{ p.created_at.strftime('%Y-%m-%d') }}</span> <span class="profile-card-date">创建于 {{ p.created_at.strftime('%Y-%m-%d') }}</span>
<div class="profile-card-actions"> <div class="profile-card-actions">
<a href="/profiles/{{ p.id }}/postcards" class="btn btn-primary btn-sm">查看明信片</a> <a href="/profiles/{{ p.id }}/postcards" class="btn btn-primary btn-sm">查看</a>
<a href="/profiles/{{ p.id }}/showcase" class="btn btn-sm">展示管理</a>
<form method="post" action="/profiles/{{ p.id }}/delete" class="inline"> <form method="post" action="/profiles/{{ p.id }}/delete" class="inline">
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('确定删除「{{ p.nickname }}」及其所有明信片?')">删除</button> <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('确定删除「{{ p.nickname }}」及其所有明信片?')">删除</button>
</form> </form>

View File

@@ -5,11 +5,9 @@
<h1>用户设置</h1> <h1>用户设置</h1>
</div> </div>
<div class="form-card" style="margin-bottom:1.5rem"> <div class="tabs" id="tabs">
<div style="display:flex;align-items:center;justify-content:space-between"> <button class="tab active" onclick="switchTab('account')">账号</button>
<h3>展示设置</h3> <button class="tab" onclick="switchTab('showcase')">展示</button>
<a href="/showcase/manage" class="btn btn-primary btn-sm">全局展示设置</a>
</div>
</div> </div>
{% if success %} {% if success %}
@@ -19,6 +17,7 @@
<div class="alert alert-error">{{ error }}</div> <div class="alert alert-error">{{ error }}</div>
{% endif %} {% endif %}
<div id="tab-account" class="tab-panel">
<div class="form-card"> <div class="form-card">
<h3 style="margin-bottom:.75rem">修改用户名</h3> <h3 style="margin-bottom:.75rem">修改用户名</h3>
<form method="post" action="/settings/change-username"> <form method="post" action="/settings/change-username">
@@ -46,4 +45,68 @@
</div> </div>
</form> </form>
</div> </div>
</div>
<div id="tab-showcase" class="tab-panel" style="display:none">
<div class="form-card">
<h3 style="margin-bottom:.75rem">全局展示设置</h3>
<form method="post" action="/showcase/manage">
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
<label style="margin:0;white-space:nowrap">展示页面模式</label>
<select name="showcase_mode" style="width:auto">
<option value="profile" {% if user.showcase_mode == 'profile' %}selected{% endif %}>按 Profile 分类</option>
<option value="flat" {% if user.showcase_mode == 'flat' %}selected{% endif %}>全部合并</option>
</select>
<button type="submit" class="btn btn-primary btn-sm">保存</button>
</div>
</form>
<p style="margin-top:.75rem;font-size:.8rem;color:var(--text-muted)">
公开展示页:<a href="/u/{{ user.username }}" target="_blank">/u/{{ user.username }}</a>
</p>
</div>
<div style="margin-top:1.5rem">
<h2 style="font-size:1rem;margin-bottom:1rem">各 Profile 展示设置</h2>
{% if profiles %}
{% for p in profiles %}
<div style="background:var(--card-bg);border:1px solid var(--border);border-radius:10px;padding:1rem;margin-bottom:.75rem">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem">
<strong>{{ p.nickname }}</strong>
<span class="badge" style="{% if p.showcase_enabled %}background:var(--sent){% else %}background:var(--text-muted){% endif %}">{% if p.showcase_enabled %}展示中{% else %}未开启{% endif %}</span>
</div>
<form method="post" action="/profiles/{{ p.id }}/showcase" style="display:flex;flex-wrap:wrap;gap:1rem;align-items:center">
<div style="display:flex;align-items:center;gap:.5rem">
<label style="margin:0;font-size:.8rem">开启展示</label>
<label class="toggle-switch">
<input type="checkbox" name="showcase_enabled" value="1" {% if p.showcase_enabled %}checked{% endif %} onchange="this.form.submit()">
<span class="toggle-slider"></span>
</label>
</div>
<div style="display:flex;align-items:center;gap:.5rem">
<label style="margin:0;font-size:.8rem">展示内容</label>
<select name="showcase_visible" onchange="this.form.submit()" style="width:auto">
<option value="both" {% if p.showcase_visible == 'both' %}selected{% endif %}>发出 + 收到</option>
<option value="sent" {% if p.showcase_visible == 'sent' %}selected{% endif %}>仅发出</option>
<option value="received" {% if p.showcase_visible == 'received' %}selected{% endif %}>仅收到</option>
</select>
</div>
</form>
</div>
{% endfor %}
{% else %}
<div class="empty-state">
<p>暂无 Profile请先创建</p>
</div>
{% endif %}
</div>
</div>
<script>
function switchTab(name) {
document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });
document.querySelectorAll('.tab-panel').forEach(function(p) { p.style.display = 'none'; });
event.currentTarget.classList.add('active');
document.getElementById('tab-' + name).style.display = '';
}
</script>
{% endblock %} {% endblock %}