Files
pure-note/web/src/pages/AdminTrash.tsx
T
wangairnan b94e9b1721 fix(web): 修复图片上传、自动保存竞态、登录竞态并统一失效公共缓存
FormData 不强设 Content-Type(P1-2),补 api 客户端回归测试; 自动保存成功仅失效列表与公共键,防详情 refetch 回滚输入(P1-4); login/logout 以请求序号丢弃迟到 /api/me 响应,401 统一回调跳登录(P2-15); 首页不硬编码 page_size,写操作后统一 invalidatePublic,TagPage 补 error 分支(P2-15)。
2026-09-08 17:32:57 +08:00

60 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { RotateCcw } from 'lucide-react'
import { api } from '../lib/api'
import type { NoteItem } from '../lib/api'
import { invalidatePublic } from '../lib/invalidate'
import { formatDateTime } from '../lib/utils'
export default function AdminTrash() {
const qc = useQueryClient()
const { data, isLoading } = useQuery({
queryKey: ['admin', 'trash'],
queryFn: () => api<{ items: NoteItem[]; total: number }>('/api/admin/trash'),
})
const restore = useMutation({
mutationFn: (id: number) => api(`/api/admin/trash/${id}/restore`, { method: 'POST' }),
onSuccess: () => {
void qc.invalidateQueries({ queryKey: ['admin'] })
invalidatePublic(qc)
},
})
return (
<div>
<h1 className="mb-4 text-lg font-semibold">回收站({data?.total ?? 0})</h1>
<p className="mb-4 text-sm text-zinc-400">软删除的笔记保留 30 天,到期由每日 gc 物理清除;恢复后图片引用天然保全。</p>
{isLoading ? (
<p className="py-12 text-center text-zinc-400">加载中…</p>
) : !data || data.items.length === 0 ? (
<p className="py-12 text-center text-zinc-400">回收站是空的</p>
) : (
<ul className="divide-y divide-zinc-100 rounded-lg border border-zinc-200 dark:divide-zinc-900 dark:border-zinc-800">
{data.items.map((n) => (
<li key={n.id} className="flex items-center gap-3 px-4 py-3">
<span className="rounded bg-zinc-100 px-1.5 py-0.5 text-xs text-zinc-500 dark:bg-zinc-800">
{n.status === 'public' ? '公开' : '私有'}
</span>
<div className="min-w-0 flex-1">
<p className="truncate font-medium">{n.title}</p>
<p className="truncate text-xs text-zinc-400">
/{n.slug} · 删除于 {formatDateTime(n.deleted_at ?? 0)}
</p>
</div>
<button
type="button"
onClick={() => restore.mutate(n.id)}
disabled={restore.isPending}
className="inline-flex items-center gap-1 rounded px-2 py-1 text-xs text-zinc-600 hover:bg-zinc-100 disabled:opacity-50 dark:text-zinc-400 dark:hover:bg-zinc-800"
>
<RotateCcw size={13} /> 恢复
</button>
</li>
))}
</ul>
)}
{restore.isError && <p className="mt-3 text-sm text-red-600">恢复失败,请重试</p>}
</div>
)
}