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)} - /> -
- ) -}