Files
rainblogweb/frontend/src/components/LockBlock.jsx
T

94 lines
3.5 KiB
React

import React, { useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
/**
* markdown 锁定块占位(未解锁时渲染,MD3 风格):
* - login → 「登录后查看」+ 去登录按钮
* - reply → 「评论后可见」+ 去评论按钮(onGoComment 滚动到评论区)
* - password → 密码输入 + 解锁按钮(onUnlock(index, password),失败显示错误态)
* 键盘:密码框 Enter 提交、Esc 清空;Tab 自然可达。
*/
export default function LockBlock({ index, type = 'password', onUnlock, onGoComment }) {
const [pwd, setPwd] = useState('');
const [err, setErr] = useState('');
const [busy, setBusy] = useState(false);
const pwdRef = useRef(null);
// 弹窗/焦点管理不适用这里(块内内联控件),但复用 useDialog 的 Esc 习惯:直接监听
useEffect(() => {
if (type === 'password') {
const h = (e) => {
if (e.key === 'Escape') { setPwd(''); setErr(''); }
};
window.addEventListener('keydown', h);
return () => window.removeEventListener('keydown', h);
}
return undefined;
}, [type]);
const tryUnlock = async () => {
if (busy) return;
if (!pwd) { setErr('请输入密码'); if (pwdRef.current) pwdRef.current.focus(); return; }
setBusy(true);
setErr('');
try {
await onUnlock(index, pwd);
// 成功后父级重渲染(unlocked/lockContent 更新),本组件被内容替换
} catch (e) {
setErr((e && e.message) || '密码错误');
}
setBusy(false);
};
if (type === 'login') {
return (
<div className="lock-placeholder">
<span className="material-icons lock-icon" aria-hidden="true">lock</span>
<div className="lock-title">登录后查看</div>
<div className="lock-desc">登录后可查看此内容</div>
<Link to="/login.html" className="btn btn-filled btn-sm">去登录</Link>
</div>
);
}
if (type === 'reply') {
return (
<div className="lock-placeholder">
<span className="material-icons lock-icon" aria-hidden="true">lock</span>
<div className="lock-title">评论后可见</div>
<div className="lock-desc">发表评论后即可查看此内容</div>
<button type="button" className="btn btn-filled btn-sm" onClick={onGoComment}>去评论</button>
</div>
);
}
// password
return (
<div className="lock-placeholder">
<span className="material-icons lock-icon" aria-hidden="true">lock</span>
<div className="lock-title">密码可见</div>
<div className="lock-desc">输入访问密码解锁内容</div>
<div className="lock-form">
<input
ref={pwdRef}
type="password"
className={'lock-pwd-input' + (err ? ' error' : '')}
value={pwd}
placeholder="访问密码"
aria-label="访问密码"
autoComplete="off"
onChange={(e) => { setPwd(e.target.value); if (err) setErr(''); }}
onKeyDown={(e) => {
if (e.key === 'Enter') tryUnlock();
else if (e.key === 'Escape') { setPwd(''); setErr(''); }
}}
/>
<button type="button" className="btn btn-filled btn-sm" onClick={tryUnlock} disabled={busy}>
{busy ? '解锁中…' : '解锁'}
</button>
</div>
{err && <div className="lock-error" role="alert"><span className="material-icons" style={{ fontSize: 14 }}>error_outline</span> {err}</div>}
</div>
);
}