refactor(admin): 接入 PasswordInput,删除分类改用 Modal 确认

- 登录弹窗与系统改密统一用 @stash/ui 的 PasswordInput(改密页补上显隐切换)
- 删除分类不再用 window.confirm,改用 Modal + 危险按钮(组件库推荐的组合)
- 删除页面私有的 PasswordInput 组件与 .password-field 样式
This commit is contained in:
2026-10-29 20:46:11 +08:00
parent 5de1382262
commit fe9d1b2659
4 changed files with 61 additions and 77 deletions
+48 -10
View File
@@ -4,7 +4,9 @@ import {
FormField,
Icon,
Input,
Modal,
Notice,
PasswordInput,
Select,
Space,
Switch,
@@ -55,6 +57,9 @@ export function AdminPage() {
const [categories, setCategories] = useState<CategorySummary[]>([])
const [tab, setTab] = useState('header')
const [saving, setSaving] = useState(false)
/** 待删除的分类;非 null 时确认弹窗打开 */
const [pendingDelete, setPendingDelete] = useState<CategorySummary | null>(null)
const [deleting, setDeleting] = useState(false)
const load = async () => {
const [nextSession, nextDocument] = await Promise.all([fetchSession(), fetchSettings()])
@@ -94,14 +99,21 @@ export function AdminPage() {
toast.success('已保存')
}
const removeCategory = async (card: CategorySummary) => {
if (!window.confirm(`删除「${card.title}」会同时删除它下面的所有条目,确定继续?`)) return
const result = await deleteCategory(card.id)
const requestDelete = (card: CategorySummary) => setPendingDelete(card)
const confirmDelete = async () => {
if (pendingDelete === null) return
setDeleting(true)
const result = await deleteCategory(pendingDelete.id)
setDeleting(false)
if (!result.ok) {
toast.error(result.error)
return
}
toast.success(`已删除「${card.title}」`)
toast.success(`已删除「${pendingDelete.title}」`)
setPendingDelete(null)
await load()
}
@@ -179,7 +191,7 @@ export function AdminPage() {
categories={categories}
value={draft.sections}
onChange={(sections) => setDraft({ ...draft, sections })}
onDelete={removeCategory}
onDelete={requestDelete}
onReload={load}
/>
) : tab === 'sidebar' ? (
@@ -187,7 +199,7 @@ export function AdminPage() {
categories={categories}
value={draft.sidebar}
onChange={(sidebar) => setDraft({ ...draft, sidebar })}
onDelete={removeCategory}
onDelete={requestDelete}
onReload={load}
/>
) : tab === 'profile' ? (
@@ -228,6 +240,32 @@ export function AdminPage() {
void load()
}}
/>
<Modal
open={pendingDelete !== null}
onClose={() => {
if (!deleting) setPendingDelete(null)
}}
title={pendingDelete === null ? '删除分类' : `删除「${pendingDelete.title}」?`}
size="sm"
footer={
<Space gap="sm" justify="end">
<Button variant="secondary" disabled={deleting} onClick={() => setPendingDelete(null)}>
取消
</Button>
<Button
variant="danger"
loading={deleting}
icon={<Icon name="delete_2_line" />}
onClick={() => void confirmDelete()}
>
删除
</Button>
</Space>
}
>
<Text>删除后,该分类下的所有条目会一起删除,且不可恢复。</Text>
</Modal>
</div>
)
}
@@ -663,17 +701,17 @@ function SystemPanel() {
return (
<Space direction="column" gap="lg" align="stretch" className="admin-form">
<FormField label="当前密码" htmlFor="current-password">
<Input
<PasswordInput
id="current-password"
type="password"
autoComplete="current-password"
value={current}
onChange={(event) => setCurrent(event.target.value)}
/>
</FormField>
<FormField label="新密码" htmlFor="next-password" hint="至少 8 位">
<Input
<PasswordInput
id="next-password"
type="password"
autoComplete="new-password"
value={next}
onChange={(event) => setNext(event.target.value)}
/>