146 lines
4.1 KiB
Plaintext
146 lines
4.1 KiB
Plaintext
---
|
|
import { resolvePublicApiBaseUrl } from '../lib/api/client';
|
|
|
|
interface Props {
|
|
requestUrl?: string | URL;
|
|
}
|
|
|
|
const { requestUrl } = Astro.props as Props;
|
|
const subscribeApiUrl = `${resolvePublicApiBaseUrl(requestUrl)}/subscriptions`;
|
|
---
|
|
|
|
<section class="terminal-subscribe-card" data-subscribe-root data-api-url={subscribeApiUrl}>
|
|
<div class="terminal-subscribe-head">
|
|
<p class="terminal-subscribe-kicker">newsletter / notifications</p>
|
|
<h3>订阅更新</h3>
|
|
<p>输入邮箱后,可以收到新文章通知;提交后需要先去邮箱点击确认链接才会正式生效。</p>
|
|
</div>
|
|
|
|
<form class="terminal-subscribe-form" data-subscribe-form>
|
|
<input type="text" name="displayName" placeholder="称呼(可选)" autocomplete="name" />
|
|
<input type="email" name="email" placeholder="name@example.com" autocomplete="email" required />
|
|
<button type="submit">订阅</button>
|
|
</form>
|
|
|
|
<p class="terminal-subscribe-status" data-subscribe-status>支持确认订阅、退订链接和偏好管理页。</p>
|
|
</section>
|
|
|
|
<script>
|
|
document.querySelectorAll('[data-subscribe-root]').forEach((root) => {
|
|
const form = root.querySelector('[data-subscribe-form]');
|
|
const status = root.querySelector('[data-subscribe-status]');
|
|
const apiUrl = root.getAttribute('data-api-url');
|
|
|
|
if (!(form instanceof HTMLFormElement) || !(status instanceof HTMLElement) || !apiUrl) {
|
|
return;
|
|
}
|
|
|
|
form.addEventListener('submit', async (event) => {
|
|
event.preventDefault();
|
|
const formData = new FormData(form);
|
|
const email = String(formData.get('email') || '').trim();
|
|
const displayName = String(formData.get('displayName') || '').trim();
|
|
|
|
if (!email) {
|
|
status.textContent = '请输入邮箱地址。';
|
|
return;
|
|
}
|
|
|
|
status.textContent = '提交中...';
|
|
|
|
try {
|
|
const response = await fetch(apiUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
email,
|
|
displayName,
|
|
source: 'frontend-home',
|
|
}),
|
|
});
|
|
|
|
const payload = await response.json().catch(() => ({}));
|
|
if (!response.ok) {
|
|
throw new Error(payload?.message || payload?.description || '订阅失败,请稍后再试。');
|
|
}
|
|
|
|
form.reset();
|
|
status.textContent =
|
|
payload?.message || '订阅申请已提交,请前往邮箱确认后生效。';
|
|
} catch (error) {
|
|
status.textContent = error instanceof Error ? error.message : '订阅失败,请稍后重试。';
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.terminal-subscribe-card {
|
|
margin-top: 1.5rem;
|
|
border: 1px solid rgba(94, 234, 212, 0.16);
|
|
background: linear-gradient(135deg, rgba(15, 23, 42, 0.86), rgba(15, 23, 42, 0.72));
|
|
border-radius: 1rem;
|
|
padding: 1.1rem;
|
|
}
|
|
|
|
.terminal-subscribe-kicker {
|
|
margin: 0 0 0.35rem;
|
|
color: var(--primary);
|
|
font-size: 0.72rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.22em;
|
|
}
|
|
|
|
.terminal-subscribe-head h3 {
|
|
margin: 0;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.terminal-subscribe-head p:last-child {
|
|
margin: 0.45rem 0 0;
|
|
color: var(--text-secondary);
|
|
font-size: 0.92rem;
|
|
line-height: 1.7;
|
|
}
|
|
|
|
.terminal-subscribe-form {
|
|
display: grid;
|
|
gap: 0.75rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.terminal-subscribe-form input {
|
|
width: 100%;
|
|
border-radius: 0.8rem;
|
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
background: rgba(15, 23, 42, 0.45);
|
|
color: var(--text-primary);
|
|
padding: 0.85rem 0.95rem;
|
|
}
|
|
|
|
.terminal-subscribe-form button {
|
|
border: 0;
|
|
border-radius: 0.8rem;
|
|
padding: 0.9rem 1rem;
|
|
font-weight: 600;
|
|
color: #08111f;
|
|
background: linear-gradient(135deg, var(--primary), #8b5cf6);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.terminal-subscribe-status {
|
|
margin: 0.75rem 0 0;
|
|
color: var(--text-secondary);
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
@media (min-width: 768px) {
|
|
.terminal-subscribe-form {
|
|
grid-template-columns: minmax(180px, 0.8fr) minmax(220px, 1.2fr) auto;
|
|
align-items: center;
|
|
}
|
|
}
|
|
</style>
|