59 lines
1.7 KiB
React
59 lines
1.7 KiB
React
|
|
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>
|
||
|
|
);
|
||
|
|
}
|