94 lines
3.5 KiB
React
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>
|
|
);
|
|
}
|