refactor(web): 优化侧边栏与邮件展示交互
- 关闭移动端侧边栏在路由切换后的残留打开状态。 - 补充 `SheetContent` 的可访问性描述配置,避免无效告警。 - 统一邮件收件人渲染的空值处理,防止数组为空时异常。 - 渲染纯文本邮件内容时转义 HTML,提升展示安全性。
This commit is contained in:
@@ -133,6 +133,16 @@ function ProtectedContent() {
|
||||
|
||||
function AdminSectionItems({ activeSection }: { activeSection: string }) {
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
const location = useLocation()
|
||||
const locationKey = `${location.pathname}${location.search}`
|
||||
const previousLocationKey = React.useRef(locationKey)
|
||||
|
||||
React.useEffect(() => {
|
||||
const previous = previousLocationKey.current
|
||||
previousLocationKey.current = locationKey
|
||||
if (isMobile && previous !== locationKey) setOpenMobile(false)
|
||||
}, [isMobile, locationKey, setOpenMobile])
|
||||
|
||||
function closeMobile() {
|
||||
if (isMobile) setOpenMobile(false)
|
||||
}
|
||||
|
||||
@@ -208,6 +208,7 @@ const Sidebar = React.forwardRef<
|
||||
} as React.CSSProperties
|
||||
}
|
||||
side={side}
|
||||
aria-describedby={undefined}
|
||||
>
|
||||
<SheetHeader className="sr-only">
|
||||
<SheetTitle>导航</SheetTitle>
|
||||
|
||||
@@ -472,7 +472,7 @@ function AdminMessagesSection({ mailboxes }: { mailboxes: MailboxType[] }) {
|
||||
<div className="mt-3 space-y-2 text-sm">
|
||||
<div className="truncate text-muted-foreground">邮箱:{message.mailboxAddress || message.recipientAddress || "-"}</div>
|
||||
<div className="truncate text-muted-foreground">发件人:{adminSenderDisplayName(message)}</div>
|
||||
<div className="truncate text-muted-foreground">收件人:{message.recipientAddress || message.to.join(", ")}</div>
|
||||
<div className="truncate text-muted-foreground">收件人:{message.recipientAddress || message.to?.join(", ") || ""}</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<Badge variant="secondary">{folderName(message.folder)}</Badge>
|
||||
@@ -506,7 +506,7 @@ function AdminMessagesSection({ mailboxes }: { mailboxes: MailboxType[] }) {
|
||||
{message.ownerEmail && <div className="text-xs text-muted-foreground">{message.ownerEmail}</div>}
|
||||
</TableCell>
|
||||
<TableCell className="max-w-[220px] truncate" title={adminSenderTitle(message)}>{adminSenderDisplayName(message)}</TableCell>
|
||||
<TableCell className="max-w-[220px] truncate">{message.recipientAddress || message.to.join(", ")}</TableCell>
|
||||
<TableCell className="max-w-[220px] truncate">{message.recipientAddress || message.to?.join(", ") || ""}</TableCell>
|
||||
<TableCell><Badge variant="secondary">{folderName(message.folder)}</Badge></TableCell>
|
||||
<TableCell className="text-muted-foreground">{formatDate(message.receivedAt)}</TableCell>
|
||||
<TableCell><Button variant="ghost" size="sm" onClick={() => setSelectedId(message.id)}>查看</Button></TableCell>
|
||||
@@ -848,7 +848,7 @@ function AdminMessageDialog({ message, loading, open, onOpenChange }: { message?
|
||||
<MessageMeta label="所属邮箱" value={message.mailboxAddress || message.recipientAddress || ""} />
|
||||
<MessageMeta label="所属用户" value={message.ownerEmail || ""} />
|
||||
<MessageMeta label="发件人" value={adminSenderTitle(message)} />
|
||||
<MessageMeta label="收件人" value={message.recipientAddress || message.to.join(", ")} />
|
||||
<MessageMeta label="收件人" value={message.recipientAddress || message.to?.join(", ") || ""} />
|
||||
<MessageMeta label="文件夹" value={folderName(message.folder)} />
|
||||
<MessageMeta label="时间" value={formatDate(message.receivedAt)} />
|
||||
</div>
|
||||
|
||||
@@ -737,7 +737,7 @@ export function MailPage() {
|
||||
</div>
|
||||
<ScrollArea className="min-h-0 flex-1">
|
||||
<div className="p-6">
|
||||
<div className="mail-html prose max-w-none text-sm leading-7" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(selected.bodyHtml || `<pre>${selected.bodyText || ""}</pre>`) }} />
|
||||
<div className="mail-html prose max-w-none text-sm leading-7" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(selected.bodyHtml || `<pre>${escapeHtml(selected.bodyText || "")}</pre>`) }} />
|
||||
{selected.attachments && selected.attachments.length > 0 && <div className="mt-8 rounded-lg border p-4"><div className="mb-3 font-medium">附件</div><div className="space-y-2">{selected.attachments.map((a) => <a className="flex items-center justify-between rounded-md border p-3 text-sm hover:bg-accent" href={`/api/mail/attachments/${a.id}`} key={a.id}><span className="flex items-center gap-2"><Paperclip className="h-4 w-4" />{a.filename}</span><span className="text-muted-foreground">{formatBytes(a.sizeBytes)}</span></a>)}</div></div>}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
@@ -758,7 +758,7 @@ export function MailPage() {
|
||||
<SheetTrigger asChild>
|
||||
<Button size="icon" variant="ghost" aria-label="打开导航"><PanelLeftOpen className="h-4 w-4" /></Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="w-[86vw] max-w-80 p-0 [&>button]:hidden">
|
||||
<SheetContent side="left" className="w-[86vw] max-w-80 p-0 [&>button]:hidden" aria-describedby={undefined}>
|
||||
<SheetTitle className="sr-only">邮箱导航</SheetTitle>
|
||||
<div className="h-[100dvh]">{sidebarContent}</div>
|
||||
</SheetContent>
|
||||
|
||||
@@ -234,7 +234,7 @@ export function ProfilePage() {
|
||||
<SheetTrigger asChild>
|
||||
<Button size="icon" variant="ghost" aria-label="打开导航"><PanelLeftOpen className="h-4 w-4" /></Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="w-[86vw] max-w-80 p-0 [&>button]:hidden">
|
||||
<SheetContent side="left" className="w-[86vw] max-w-80 p-0 [&>button]:hidden" aria-describedby={undefined}>
|
||||
<SheetTitle className="sr-only">个人中心导航</SheetTitle>
|
||||
<div className="h-[100dvh]">{sidebarContent}</div>
|
||||
</SheetContent>
|
||||
|
||||
Reference in New Issue
Block a user