feat: 全站评论开关,并统一后台表单控件

关闭评论后前台隐藏评论能力;外观/设置/公告改用 AdminField 与 Stepper。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 01:22:00 +08:00
parent 442af625f6
commit d93635c961
27 changed files with 989 additions and 479 deletions

View File

@@ -0,0 +1,94 @@
"use client";
import { Minus, Plus } from "lucide-react";
import { useEffect, useState, type KeyboardEvent } from "react";
/** 后台数字步进器:0 是合法值;加减与手输都会提交数字 */
export default function AdminStepper({
id,
value,
onChange,
min = 0,
max = 999,
step = 1,
unit,
ariaLabel,
disabled,
}: {
id?: string;
value: number;
onChange: (n: number) => void;
min?: number;
max?: number;
step?: number;
unit?: string;
ariaLabel: string;
disabled?: boolean;
}) {
const clamp = (n: number) => Math.min(max, Math.max(min, Math.round(n)));
const [text, setText] = useState(() => String(value));
useEffect(() => {
setText(String(value));
}, [value]);
const emit = (n: number) => {
const next = clamp(Number.isFinite(n) ? n : min);
onChange(next);
setText(String(next));
};
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "ArrowUp") {
e.preventDefault();
emit(value + step);
} else if (e.key === "ArrowDown") {
e.preventDefault();
emit(value - step);
}
};
return (
<div className={`admin-stepper${disabled ? " is-disabled" : ""}`}>
<button
type="button"
className="admin-stepper-btn"
onClick={() => emit(value - step)}
disabled={disabled || value <= min}
aria-label="减少"
tabIndex={-1}
>
<Minus size={14} />
</button>
<input
id={id}
className="admin-stepper-input"
type="text"
inputMode="numeric"
value={text}
aria-label={ariaLabel}
disabled={disabled}
onFocus={(e) => e.target.select()}
onChange={(e) => {
const raw = e.target.value.replace(/[^\d]/g, "");
setText(raw);
if (raw === "") return;
onChange(clamp(Number(raw)));
}}
onBlur={() => emit(text === "" ? min : Number(text))}
onKeyDown={onKeyDown}
/>
{unit ? <span className="admin-stepper-unit">{unit}</span> : null}
<button
type="button"
className="admin-stepper-btn"
onClick={() => emit(value + step)}
disabled={disabled || value >= max}
aria-label="增加"
tabIndex={-1}
>
<Plus size={14} />
</button>
</div>
);
}