From fe9d1b2659d3af02ea7c26de11f99094e8e577c8 Mon Sep 17 00:00:00 2001 From: CNWei Date: Thu, 29 Oct 2026 20:46:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor(admin):=20=E6=8E=A5=E5=85=A5=20Passwor?= =?UTF-8?q?dInput=EF=BC=8C=E5=88=A0=E9=99=A4=E5=88=86=E7=B1=BB=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20Modal=20=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 登录弹窗与系统改密统一用 @stash/ui 的 PasswordInput(改密页补上显隐切换) - 删除分类不再用 window.confirm,改用 Modal + 危险按钮(组件库推荐的组合) - 删除页面私有的 PasswordInput 组件与 .password-field 样式 --- stash-web/src/App.css | 15 +---- stash-web/src/components/admin/AdminPage.tsx | 58 +++++++++++++++---- stash-web/src/components/auth/LoginDialog.tsx | 17 ++++-- .../src/components/auth/PasswordInput.tsx | 48 --------------- 4 files changed, 61 insertions(+), 77 deletions(-) delete mode 100644 stash-web/src/components/auth/PasswordInput.tsx diff --git a/stash-web/src/App.css b/stash-web/src/App.css index 18e65d8..d67daba 100644 --- a/stash-web/src/App.css +++ b/stash-web/src/App.css @@ -1,4 +1,4 @@ -/* 页面级样式:只保留「组件库没有」的部分(背景、外壳、封面兜底、正文版式)。 +/* 页面级样式:只保留「组件库没有」的部分(背景、外壳、封面兜底、正文版式)。 颜色 / 间距 / 字号全部取自 @stash/ui 的令牌。 */ .app { @@ -907,18 +907,7 @@ } } -/* ---------- 登录密码框 / 条目操作条 / 设置页细节 ---------- */ - -.password-field { - display: flex; - align-items: center; - gap: var(--su-space-2); -} - -.password-field > :first-child { - flex: 1; - min-width: 0; -} +/* ---------- 条目操作条 / 设置页细节 ---------- */ .item-stat { display: inline-flex; diff --git a/stash-web/src/components/admin/AdminPage.tsx b/stash-web/src/components/admin/AdminPage.tsx index 4421a78..14e2cd6 100644 --- a/stash-web/src/components/admin/AdminPage.tsx +++ b/stash-web/src/components/admin/AdminPage.tsx @@ -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([]) const [tab, setTab] = useState('header') const [saving, setSaving] = useState(false) + /** 待删除的分类;非 null 时确认弹窗打开 */ + const [pendingDelete, setPendingDelete] = useState(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() }} /> + + { + if (!deleting) setPendingDelete(null) + }} + title={pendingDelete === null ? '删除分类' : `删除「${pendingDelete.title}」?`} + size="sm" + footer={ + + + + + } + > + 删除后,该分类下的所有条目会一起删除,且不可恢复。 + ) } @@ -663,17 +701,17 @@ function SystemPanel() { return ( - setCurrent(event.target.value)} /> - setNext(event.target.value)} /> diff --git a/stash-web/src/components/auth/LoginDialog.tsx b/stash-web/src/components/auth/LoginDialog.tsx index bf17ff8..55c28dd 100644 --- a/stash-web/src/components/auth/LoginDialog.tsx +++ b/stash-web/src/components/auth/LoginDialog.tsx @@ -1,7 +1,6 @@ -import { Button, FormField, Icon, Modal, Notice, Space, Text } from '@stash/ui' +import { Button, FormField, Icon, Modal, Notice, PasswordInput, Space, Text } from '@stash/ui' import { useEffect, useState } from 'react' import { login, setup, type SessionState } from '../../auth' -import { PasswordInput } from './PasswordInput' /** * 登录弹窗:首次使用显示「设置密码 + 确认」,之后只显示密码。 @@ -88,10 +87,13 @@ export function LoginDialog({ open, hasAdmin, onClose, onSuccess }: LoginDialogP void submit()} + onChange={(event) => setPassword(event.target.value)} + onKeyDown={(event) => { + if (event.key === 'Enter') void submit() + }} /> @@ -100,9 +102,12 @@ export function LoginDialog({ open, hasAdmin, onClose, onSuccess }: LoginDialogP void submit()} + onChange={(event) => setConfirm(event.target.value)} + onKeyDown={(event) => { + if (event.key === 'Enter') void submit() + }} /> )} diff --git a/stash-web/src/components/auth/PasswordInput.tsx b/stash-web/src/components/auth/PasswordInput.tsx deleted file mode 100644 index 9d5a150..0000000 --- a/stash-web/src/components/auth/PasswordInput.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Icon, IconButton, Input } from '@stash/ui' -import { useState } from 'react' - -/** - * 语言无关的密码可见切换:右侧一个眼睛按钮,切换 input type。 - * 错误提示后再点仍然有效(状态在组件里,不会被错误重置)。 - */ -type PasswordInputProps = { - id: string - value: string - placeholder?: string - autoFocus?: boolean - onChange: (value: string) => void - onEnter?: () => void -} - -export function PasswordInput({ - id, - value, - placeholder, - autoFocus = false, - onChange, - onEnter, -}: PasswordInputProps) { - const [visible, setVisible] = useState(false) - - return ( -
- onChange(event.target.value)} - onKeyDown={(event) => { - if (event.key === 'Enter') onEnter?.() - }} - /> - } - onClick={() => setVisible((current) => !current)} - /> -
- ) -}