fix(admin): 子分组开关即时保存并立即显示分组管理

- 拨动开关即调用 PATCH allowGroups,成功后本地更新分类,无需再点保存
- 侧栏导航的分组列表同样跟随开关显示
- 失败时回滚开关状态
This commit is contained in:
2026-10-30 20:10:07 +08:00
parent 22c73c1145
commit e52db54bdc
3 changed files with 35 additions and 4 deletions
+33 -2
View File
@@ -130,6 +130,14 @@ export function AdminPage() {
const requestDelete = (card: CategorySummary) => setPendingDelete(card)
/** 只更新本地分类缓存(不改 draft),用于「子分组」开关即时生效 */
const patchCategory = (id: string, patch: Partial<CategorySummary>) =>
setCategories((current) =>
current.map((category) =>
category.id === id ? { ...category, ...patch } : category,
),
)
const confirmDelete = async () => {
if (pendingDelete === null) return
setDeleting(true)
@@ -232,6 +240,7 @@ export function AdminPage() {
categories={categories}
onReload={load}
onDelete={requestDelete}
onToggleGroups={(id, allow) => patchCategory(id, { allowGroups: allow })}
/>
) : tab === 'sidebar' ? (
<SidebarForm
@@ -505,7 +514,7 @@ function SidebarForm({
{ordered.map((category, index) => {
const visible = !value.hidden.includes(category.id)
const groups = orderedGroups(category)
const groups = category.allowGroups === true ? orderedGroups(category) : []
return (
<div className="admin-nav-block" key={category.id}>
@@ -745,10 +754,12 @@ function CategoryManager({
categories,
onReload,
onDelete,
onToggleGroups,
}: {
categories: CategorySummary[]
onReload: () => void
onDelete: (card: CategorySummary) => void
onToggleGroups: (id: string, allow: boolean) => void
}) {
return (
<Space direction="column" gap="lg" align="stretch" className="admin-form">
@@ -764,6 +775,7 @@ function CategoryManager({
category={category}
onReload={onReload}
onDelete={onDelete}
onToggleGroups={onToggleGroups}
/>
{category.allowGroups === true && (
<>
@@ -785,10 +797,12 @@ function CategoryRow({
category,
onReload,
onDelete,
onToggleGroups,
}: {
category: CategorySummary
onReload: () => void
onDelete: (card: CategorySummary) => void
onToggleGroups: (id: string, allow: boolean) => void
}) {
const [title, setTitle] = useState(category.title)
const [kind, setKind] = useState(category.kind ?? 'apps')
@@ -800,6 +814,23 @@ function CategoryRow({
const [allowGroups, setAllowGroups] = useState(category.allowGroups === true)
const [busy, setBusy] = useState(false)
/** 子分组开关即时保存:切完立刻出现 / 收起分组管理 */
const toggleGroups = async (next: boolean) => {
setAllowGroups(next)
setBusy(true)
const result = await updateCategory(category.id, { allowGroups: next })
setBusy(false)
if (!result.ok) {
setAllowGroups(!next)
toast.error(result.error)
return
}
onToggleGroups(category.id, next)
toast.success(next ? '已开启子分组' : '已关闭子分组')
}
const save = async () => {
setBusy(true)
const result = await updateCategory(category.id, {
@@ -856,7 +887,7 @@ function CategoryRow({
<div className="admin-toggle">
<Switch
checked={allowGroups}
onChange={setAllowGroups}
onChange={(next) => void toggleGroups(next)}
label={`${category.title} 允许子分组`}
/>
<Text size="sm" tone="muted">