Files
blweb/src/components/admin/AdminGate.jsx
Cho-P4 c5994759fb Initial commit: anime-minimalist personal blog
- React 18 + Vite + React Router v6
- Glass morphism cards with rounded corners
- Dark/light theme toggle
- Article CRUD with localStorage persistence
- Search, admin panel (PIN: 2501), sakura petal canvas animation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 18:10:59 +08:00

59 lines
1.7 KiB
JavaScript

import { useState } from 'react';
import styles from './AdminGate.module.css';
const CORRECT_PIN = '2501';
export default function AdminGate({ children }) {
const authed = sessionStorage.getItem('blog_admin_auth') === 'true';
const [pin, setPin] = useState('');
const [shake, setShake] = useState(false);
const [passed, setPassed] = useState(authed);
const handleDigit = (d) => {
if (pin.length >= 4) return;
const next = pin + d;
setPin(next);
if (next.length === 4) {
if (next === CORRECT_PIN) {
sessionStorage.setItem('blog_admin_auth', 'true');
setPassed(true);
} else {
setShake(true);
setTimeout(() => { setPin(''); setShake(false); }, 600);
}
}
};
const handleDelete = () => setPin(p => p.slice(0, -1));
if (passed) return children;
return (
<div className={styles.gate}>
<div className={`${styles.panel} glass-card`}>
<h1 className={styles.title}>管理员入口</h1>
<p className={styles.sub}>请输入 PIN </p>
<div className={`${styles.dots} ${shake ? styles.shake : ''}`}>
{[0,1,2,3].map(i => (
<div key={i} className={`${styles.dot} ${i < pin.length ? styles.filled : ''}`} />
))}
</div>
<div className={styles.keypad}>
{['1','2','3','4','5','6','7','8','9','','0','⌫'].map((k, i) => (
<button
key={i}
className={`${styles.key} ${k === '' ? styles.empty : ''} ${k === '⌫' ? styles.del : ''}`}
onClick={() => k === '⌫' ? handleDelete() : k !== '' ? handleDigit(k) : null}
disabled={k === ''}
>
{k}
</button>
))}
</div>
</div>
</div>
);
}