105 lines
2.8 KiB
TypeScript
Executable File
105 lines
2.8 KiB
TypeScript
Executable File
'use client';
|
|
|
|
import { Separator } from '@nice/ui/components/separator';
|
|
import { SidebarTrigger } from '@nice/ui/components/sidebar';
|
|
import { IconCalendar } from '@tabler/icons-react';
|
|
import { cn } from '@nice/ui/lib/utils';
|
|
import { useState, useEffect } from 'react';
|
|
import { useDashboard } from './providers/dashboard-provider';
|
|
|
|
// 页面信息接口
|
|
export interface PageInfo {
|
|
title: string;
|
|
subtitle?: string;
|
|
rightContent?: React.ReactNode;
|
|
}
|
|
|
|
// 组件属性接口
|
|
export interface SiteHeaderProps {
|
|
pageInfo?: PageInfo;
|
|
showDate?: boolean;
|
|
rightContent?: React.ReactNode;
|
|
className?: string;
|
|
useContext?: boolean; // 新增:是否使用 context 中的页面信息
|
|
}
|
|
|
|
|
|
// 格式化日期
|
|
const formatDate = (date: Date) => {
|
|
return date.toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
});
|
|
};
|
|
|
|
// 默认时间显示组件
|
|
const DefaultTimeDisplay = () => {
|
|
const [currentDate, setCurrentDate] = useState<Date | null>(null);
|
|
|
|
useEffect(() => {
|
|
// 只在客户端设置当前时间
|
|
setCurrentDate(new Date());
|
|
}, []);
|
|
|
|
// 在水合完成之前显示占位符
|
|
if (!currentDate) {
|
|
return (
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<IconCalendar className="h-4 w-4" />
|
|
<time>加载中...</time>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<IconCalendar className="h-4 w-4" />
|
|
<time dateTime={currentDate.toISOString()}>{formatDate(currentDate)}</time>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// 页面标题区域组件
|
|
const PageTitleSection = ({ pageInfo }: { pageInfo: PageInfo }) => {
|
|
return (
|
|
<div className="flex items-baseline gap-2 flex-1 min-w-0">
|
|
<h1 className="text-base font-semibold text-foreground truncate">{pageInfo.title}</h1>
|
|
{pageInfo.subtitle && (
|
|
<>
|
|
<span className="text-muted-foreground">·</span>
|
|
<p className="text-sm text-muted-foreground truncate">{pageInfo.subtitle}</p>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// 主组件
|
|
export function SiteHeader() {
|
|
const {Dashboard}=useDashboard()
|
|
|
|
return (
|
|
<header
|
|
className={cn(
|
|
'flex h-[--header-height] shrink-0 items-center gap-2 border-b border-border',
|
|
'transition-[width,height] ease-linear',
|
|
'group-has-data-[collapsible=icon]/sidebar-wrapper:h-[--header-height]',
|
|
|
|
)}
|
|
>
|
|
<div className="flex w-full items-center gap-2 px-4 py-2 lg:gap-3 lg:px-6">
|
|
{/* 侧边栏触发器 */}
|
|
<SidebarTrigger className="-ml-1" />
|
|
<Separator orientation="vertical" className="h-4" />
|
|
|
|
{/* 页面标题区域 */}
|
|
<PageTitleSection pageInfo={Dashboard.pageInfo} />
|
|
|
|
{/* 右侧内容区域 */}
|
|
<div className="flex items-center gap-3 ml-auto">{Dashboard.pageInfo?.rightContent || <DefaultTimeDisplay />}</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|