From f15a3d32220bc1b3945c1b04f7d379379062b5db Mon Sep 17 00:00:00 2001 From: zxyszx <299979470+zxyszx@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:42:27 +0800 Subject: [PATCH] release: prepare v1.2.42 --- .github/release-notes/v1.2.42.md | 4 ++ VERSION | 2 +- apps/web/src/components/ui/sidebar.tsx | 8 +-- apps/web/src/index.css | 11 ++++ apps/web/src/pages/admin.tsx | 76 ++++++++++++-------------- apps/web/src/pages/mail.tsx | 6 +- apps/web/src/pages/profile.tsx | 6 +- 7 files changed, 60 insertions(+), 53 deletions(-) create mode 100644 .github/release-notes/v1.2.42.md diff --git a/.github/release-notes/v1.2.42.md b/.github/release-notes/v1.2.42.md new file mode 100644 index 0000000..58f9dcb --- /dev/null +++ b/.github/release-notes/v1.2.42.md @@ -0,0 +1,4 @@ +- 将邮箱、个人设置和后台管理的当前选中项统一为清晰的淡蓝色,并补齐悬停、二级标签和邮件列表选中状态。 +- 优化后台数据总览:合并重复指标,改为四项核心数据、紧凑首次配置和统一系统状态布局,并确保公网地址完整显示。 + +**完整更新日志**:[v1.2.41...v1.2.42](https://github.com/zxyszx/NewSzxcn-Email/compare/v1.2.41...v1.2.42) diff --git a/VERSION b/VERSION index 50e400b..8d4450b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.41 +1.2.42 diff --git a/apps/web/src/components/ui/sidebar.tsx b/apps/web/src/components/ui/sidebar.tsx index b3ebdca..c15cc36 100644 --- a/apps/web/src/components/ui/sidebar.tsx +++ b/apps/web/src/components/ui/sidebar.tsx @@ -519,7 +519,7 @@ const SidebarMenuItem = React.forwardRef< SidebarMenuItem.displayName = "SidebarMenuItem" const sidebarMenuButtonVariants = cva( - "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", + "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-[hsl(var(--sidebar-active))] data-[active=true]:font-medium data-[active=true]:text-[hsl(var(--sidebar-active-foreground))] data-[active=true]:hover:bg-[hsl(var(--sidebar-active))] data-[active=true]:hover:text-[hsl(var(--sidebar-active-foreground))] data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", { variants: { variant: { @@ -621,7 +621,7 @@ const SidebarMenuAction = React.forwardRef< "peer-data-[size=lg]/menu-button:top-2.5", "group-data-[collapsible=icon]:hidden", showOnHover && - "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", + "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-[hsl(var(--sidebar-active-foreground))] md:opacity-0", className )} {...props} @@ -639,7 +639,7 @@ const SidebarMenuBadge = React.forwardRef< data-sidebar="menu-badge" className={cn( "pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground", - "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", + "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-[hsl(var(--sidebar-active-foreground))]", "peer-data-[size=sm]/menu-button:top-1", "peer-data-[size=default]/menu-button:top-1.5", "peer-data-[size=lg]/menu-button:top-2.5", @@ -730,7 +730,7 @@ const SidebarMenuSubButton = React.forwardRef< data-active={isActive} className={cn( "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", - "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", + "data-[active=true]:bg-[hsl(var(--sidebar-active))] data-[active=true]:text-[hsl(var(--sidebar-active-foreground))]", size === "sm" && "text-xs", size === "md" && "text-sm", "group-data-[collapsible=icon]:hidden", diff --git a/apps/web/src/index.css b/apps/web/src/index.css index c88f72a..034ae06 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -35,6 +35,9 @@ --sidebar-accent-foreground: 0 0% 12%; --sidebar-border: 0 0% 89.8%; --sidebar-ring: 0 0% 42%; + --sidebar-active: 207 100% 92%; + --sidebar-active-foreground: 202 100% 30%; + --mail-selected: 210 100% 96%; --compose-send: 217 89% 43%; --compose-send-hover: 216 94% 32%; } @@ -86,6 +89,9 @@ --sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-border: 240 3.7% 15.9%; --sidebar-ring: 217.2 91.2% 59.8%; + --sidebar-active: 210 48% 24%; + --sidebar-active-foreground: 210 100% 88%; + --mail-selected: 210 38% 20%; --compose-send: 217 89% 52%; --compose-send-hover: 214 94% 60%; } @@ -124,6 +130,11 @@ max-width: 384px; } + .mail-selected-row { + background-color: hsl(var(--mail-selected)); + } + + @media (max-width: 767px) { .mail-content-grid { grid-template-columns: minmax(0, 1fr); diff --git a/apps/web/src/pages/admin.tsx b/apps/web/src/pages/admin.tsx index 0ffe3cc..c75bd15 100644 --- a/apps/web/src/pages/admin.tsx +++ b/apps/web/src/pages/admin.tsx @@ -2,7 +2,7 @@ import * as React from "react" import DOMPurify from "dompurify" import { useSearchParams } from "react-router-dom" import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query" -import { ArrowRight, BookOpen, CheckCircle2, ChevronDown, Circle, ClipboardList, Cloud, Copy, Download, ExternalLink, Eye, EyeOff, Github, Globe2, HardDrive, KeyRound, Loader2, Mail, Mailbox, MoreHorizontal, RefreshCcw, Scale, Search, Send, ShieldCheck, Star, Trash2, Users } from "lucide-react" +import { BookOpen, CheckCircle2, ChevronDown, Circle, ClipboardList, Cloud, Copy, Download, ExternalLink, Eye, EyeOff, Github, Globe2, HardDrive, KeyRound, Loader2, Mail, Mailbox, MoreHorizontal, RefreshCcw, Scale, Search, Send, ShieldCheck, Star, Trash2, Users } from "lucide-react" import { api, AdminUser, Alias, DNSRecord, Domain, Mailbox as MailboxType, MailMessage, MailTemplate, MaildirSyncHealth, PermissionGroup, PermissionInfo, PermissionLimits, SystemSettings } from "@/lib/api" import { cn, decodeMimeHeader, formatBytes, formatDate } from "@/lib/utils" import { Button } from "@/components/ui/button" @@ -135,18 +135,18 @@ export function AdminPage() { return ( -
+
{sectionQuery?.isError && { void sectionQuery.refetch() }} />} {section === "overview" && canOverview && ( -
- } label="账号" value={overview.data?.users || 0} /> - } label="邮件域名" value={overview.data?.domains || 0} /> - } label="邮箱" value={overview.data?.mailboxes || 0} /> - } label="存储用量" value={formatBytes(overview.data?.storageBytes || 0)} /> -
+
+ } label="账号" value={overview.data?.users || 0} detail={`${overview.data?.activeUsers || 0} 个活跃`} /> + } label="邮件域名" value={overview.data?.domains || 0} detail={`${domainItems.filter((domain) => domain.dnsStatus === "ok").length} 个 DNS 正常`} /> + } label="邮箱" value={overview.data?.mailboxes || 0} detail={`${overview.data?.activeMailboxes || 0} 个活跃`} /> + } label="存储用量" value={formatBytes(overview.data?.storageBytes || 0)} detail={`${overview.data?.unreadMessages || 0} 封未读 · ${overview.data?.aliases || 0} 个转发`} /> +
)} {section === "overview" && setParams(next === "overview" ? {} : { section: next })} />} @@ -191,51 +191,43 @@ function AdminPageHeader({ section, refreshing, onRefresh }: { section: Section; function OverviewSection({ overview, domains, settings, visibleSections, onSectionChange }: { overview?: { activeUsers: number; activeMailboxes: number; aliases: number; messages: number; unreadMessages: number }; domains: Domain[]; settings?: SystemSettings; visibleSections: Section[]; onSectionChange: (section: Section) => void }) { const checklist = setupChecklist(overview, domains, settings).filter((item) => visibleSections.includes(item.section)) return ( -
-
- - 系统状态 - - - - - - - - - 首次配置 - +
+ + 首次配置 + {checklist.map((item) => ( - ))} - - -
-
- - DNS 状态 - + + + + 系统状态 + +
+

DNS 状态

+
{domains.map((domain) => )} {domains.length === 0 && } - - - - 运行提示 - +
+
+ +
+

运行信息

+
- - -
+
+ +
+
) } @@ -255,7 +247,7 @@ function setupChecklist(overview: { activeUsers: number; activeMailboxes: number } function InfoLine({ label, value }: { label: string; value: React.ReactNode }) { - return
{label}{value}
+ return
{label}{value}
} function generateBackupPassword(length = 24) { @@ -2223,8 +2215,8 @@ function sendAuditBadgeVariant(event?: string) { return "secondary" } -function Stat({ icon, label, value }: { icon: React.ReactNode; label: string; value: React.ReactNode }) { - return
{icon}
{value}
{label}
+function Stat({ icon, label, value, detail }: { icon: React.ReactNode; label: string; value: React.ReactNode; detail: string }) { + return
{icon}
{value}
{label}
{detail}
} function InfoBox({ label, value }: { label: string; value: React.ReactNode }) { return
{value}
{label}
} function Empty({ text }: { text: string }) { return
{text}
} diff --git a/apps/web/src/pages/mail.tsx b/apps/web/src/pages/mail.tsx index 18264f5..d8acc41 100644 --- a/apps/web/src/pages/mail.tsx +++ b/apps/web/src/pages/mail.tsx @@ -3393,7 +3393,7 @@ function CompactMessageRow({ message, active, checked, scheduled, onCheckedChang
onCheckedChange(value === true)} onClick={(event) => event.stopPropagation()} className="mt-0.5 shrink-0 sm:mt-0" /> @@ -3830,8 +3830,8 @@ function MessageRow({ return
setTab(key)} > - svg]:h-4 [&>svg]:w-4 [&>svg]:stroke-[1.8]", tab === key && "text-foreground/70")}>{tabs[key].icon} + svg]:h-4 [&>svg]:w-4 [&>svg]:stroke-[1.8]", tab === key && "text-[hsl(var(--sidebar-active-foreground))]")}>{tabs[key].icon} {tabs[key].label} ))} @@ -563,7 +563,7 @@ function SettingsPageHeader({ title, subtitle, action, activeTab, onAccountTabCh type="button" className={cn( "h-[38px] shrink-0 border-b-2 px-4 text-sm font-medium transition-colors", - activeTab === item.key ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground", + activeTab === item.key ? "border-[hsl(var(--sidebar-active-foreground))] bg-[hsl(var(--sidebar-active))] text-[hsl(var(--sidebar-active-foreground))]" : "border-transparent text-muted-foreground hover:text-foreground", )} onClick={() => onAccountTabChange(item.key)} >