Files
weibo_signin/frontend/templates/topics.html

208 lines
8.7 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.
{% extends "base.html" %}
{% block title %}超话签到 - {{ account.remark or account.weibo_user_id }}{% endblock %}
{% block extra_css %}
<style>
.topics-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 10px; }
.topics-header h1 { font-size: 22px; font-weight: 700; color: #1e293b; }
.topic-list { display: flex; flex-direction: column; gap: 0; }
.topic-item {
display: flex; align-items: center; gap: 14px; padding: 14px 16px;
border-bottom: 1px solid #f1f5f9; cursor: pointer; transition: background 0.15s;
}
.topic-item:hover { background: #f8fafc; }
.topic-item:last-child { border-bottom: none; }
.topic-cb { width: 20px; height: 20px; accent-color: #6366f1; cursor: pointer; }
.topic-name { font-weight: 500; color: #1e293b; font-size: 15px; }
.topic-id { font-size: 12px; color: #94a3b8; font-family: monospace; }
.select-bar {
display: flex; justify-content: space-between; align-items: center;
padding: 16px 0; border-bottom: 1px solid #e2e8f0; margin-bottom: 8px;
flex-wrap: wrap; gap: 8px;
}
.select-bar label { font-weight: 600; color: #475569; font-size: 14px; cursor: pointer; }
.action-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.signin-btn, .save-btn {
padding: 10px 20px; border-radius: 12px; border: none; font-size: 14px;
font-weight: 600; cursor: pointer; color: white; transition: all 0.2s;
}
.signin-btn { background: linear-gradient(135deg, #6366f1, #818cf8); box-shadow: 0 2px 8px rgba(99,102,241,0.25); }
.save-btn { background: linear-gradient(135deg, #10b981, #059669); box-shadow: 0 2px 8px rgba(16,185,129,0.25); }
.signin-btn:disabled, .save-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.result-box { margin-top: 16px; padding: 14px; border-radius: 12px; display: none; font-size: 13px; font-weight: 500; }
.result-success { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.result-error { background: #fef2f2; color: #991b1b; border: 1px solid #fecaca; }
.tip-box { background: #eff6ff; color: #1e40af; border: 1px solid #bfdbfe; border-radius: 12px; padding: 12px 16px; margin-bottom: 16px; font-size: 13px; }
@media (max-width: 768px) {
.action-btns { width: 100%; }
.signin-btn, .save-btn { flex: 1; text-align: center; }
}
</style>
{% endblock %}
{% block content %}
<div style="max-width: 720px; margin: 0 auto;">
<div class="topics-header">
<div>
<h1>🔥 超话签到管理</h1>
<div style="color:#94a3b8; font-size:13px; margin-top:4px;">
{{ account.remark or account.weibo_user_id }} · 共 {{ topics|length }} 个超话
</div>
</div>
<a href="{{ url_for('account_detail', account_id=account.id) }}" class="btn btn-secondary">← 返回</a>
</div>
<div class="tip-box">
💡 勾选要参与定时签到的超话,点击「保存选择」后,定时任务和手动签到都只签选中的超话。不选则签到全部。
</div>
<div class="card">
{% if topics %}
<div class="select-bar">
<label><input type="checkbox" id="selectAll" class="topic-cb" onchange="toggleAll()"> 全选 (<span id="selectedCount">0</span>/{{ topics|length }})</label>
<div class="action-btns">
<button class="save-btn" id="saveBtn" onclick="saveSelection()">💾 保存选择</button>
<button class="signin-btn" id="signinBtn" onclick="doSignin()">🚀 立即签到选中</button>
</div>
</div>
<div class="topic-list" id="topicList">
{% for topic in topics %}
<label class="topic-item">
<input type="checkbox" class="topic-cb topic-check"
data-index="{{ loop.index0 }}"
data-title="{{ topic.title }}"
data-cid="{{ topic.containerid }}"
onchange="updateCount()">
<div>
<div class="topic-name">{{ topic.title }}</div>
<div class="topic-id">{{ topic.containerid }}</div>
</div>
</label>
{% endfor %}
</div>
{% else %}
<p style="color:#94a3b8; text-align:center; padding:40px; font-size:15px;">
未找到关注的超话,请确认 Cookie 有效且已关注超话
</p>
{% endif %}
</div>
<div id="resultBox" class="result-box"></div>
</div>
<script>
// 已保存的选中超话
const savedTopics = {{ (selected_topics or [])|tojson }};
const savedCids = new Set(savedTopics.map(t => t.containerid));
// 页面加载时恢复选中状态
document.addEventListener('DOMContentLoaded', function() {
const checks = document.querySelectorAll('.topic-check');
if (savedCids.size > 0) {
checks.forEach(cb => {
cb.checked = savedCids.has(cb.dataset.cid);
});
} else {
// 没有保存过 = 全部选中
checks.forEach(cb => cb.checked = true);
}
updateCount();
});
function toggleAll() {
const checked = document.getElementById('selectAll').checked;
document.querySelectorAll('.topic-check').forEach(cb => cb.checked = checked);
updateCount();
}
function updateCount() {
const total = document.querySelectorAll('.topic-check').length;
const checked = document.querySelectorAll('.topic-check:checked').length;
document.getElementById('selectedCount').textContent = checked;
document.getElementById('selectAll').checked = (checked === total);
}
function getSelectedTopics() {
const selected = [];
document.querySelectorAll('.topic-check:checked').forEach(cb => {
selected.push({ title: cb.dataset.title, containerid: cb.dataset.cid });
});
return selected;
}
async function saveSelection() {
const btn = document.getElementById('saveBtn');
const resultBox = document.getElementById('resultBox');
const selected = getSelectedTopics();
const total = document.querySelectorAll('.topic-check').length;
btn.disabled = true; btn.textContent = '⏳ 保存中...';
try {
// 全选时传 null签到全部
const body = (selected.length === total)
? { selected_topics: null }
: { selected_topics: selected };
const resp = await fetch('/accounts/{{ account.id }}/topics/save', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body),
});
const data = await resp.json();
resultBox.style.display = 'block';
if (data.success) {
resultBox.className = 'result-box result-success';
resultBox.textContent = '✅ ' + data.message;
} else {
resultBox.className = 'result-box result-error';
resultBox.textContent = data.message || '保存失败';
}
} catch(e) {
resultBox.className = 'result-box result-error';
resultBox.style.display = 'block';
resultBox.textContent = '请求失败: ' + e.message;
}
btn.disabled = false; btn.textContent = '💾 保存选择';
}
async function doSignin() {
const btn = document.getElementById('signinBtn');
const resultBox = document.getElementById('resultBox');
const indices = [];
document.querySelectorAll('.topic-check:checked').forEach(cb => {
indices.push(parseInt(cb.dataset.index));
});
if (indices.length === 0) {
resultBox.className = 'result-box result-error';
resultBox.style.display = 'block';
resultBox.textContent = '请至少选择一个超话';
return;
}
btn.disabled = true; btn.textContent = '⏳ 签到中...';
resultBox.style.display = 'none';
try {
const resp = await fetch('{{ url_for("signin_selected", account_id=account.id) }}', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({topic_indices: indices}),
});
const data = await resp.json();
resultBox.style.display = 'block';
if (data.success) {
resultBox.className = 'result-box result-success';
resultBox.textContent = data.message;
} else {
resultBox.className = 'result-box result-error';
resultBox.textContent = data.message || '签到失败';
}
} catch(e) {
resultBox.className = 'result-box result-error';
resultBox.style.display = 'block';
resultBox.textContent = '请求失败: ' + e.message;
}
btn.disabled = false; btn.textContent = '🚀 立即签到选中';
}
</script>
{% endblock %}