Update overview layout
This commit is contained in:
@@ -13,8 +13,12 @@ export const DashboardPage: React.FC = () => {
|
|||||||
const [selectedAssistantId, setSelectedAssistantId] = useState<string>('all');
|
const [selectedAssistantId, setSelectedAssistantId] = useState<string>('all');
|
||||||
const [assistants, setAssistants] = useState<Assistant[]>([]);
|
const [assistants, setAssistants] = useState<Assistant[]>([]);
|
||||||
const [topRailHeight, setTopRailHeight] = useState(0);
|
const [topRailHeight, setTopRailHeight] = useState(0);
|
||||||
|
const [isOverviewVisible, setIsOverviewVisible] = useState(true);
|
||||||
|
const [scrollRootCenterX, setScrollRootCenterX] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const scrollRootRef = useRef<HTMLDivElement>(null);
|
||||||
const topRailRef = useRef<HTMLDivElement>(null);
|
const topRailRef = useRef<HTMLDivElement>(null);
|
||||||
|
const overviewRef = useRef<HTMLElement>(null);
|
||||||
const workflowRef = useRef<HTMLDivElement>(null);
|
const workflowRef = useRef<HTMLDivElement>(null);
|
||||||
const aboutRef = useRef<HTMLDivElement>(null);
|
const aboutRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -56,12 +60,53 @@ export const DashboardPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!scrollRootRef.current || !overviewRef.current) return;
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
setIsOverviewVisible(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: scrollRootRef.current,
|
||||||
|
threshold: 0.35,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
observer.observe(overviewRef.current);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateScrollRootCenter = () => {
|
||||||
|
if (!scrollRootRef.current) return;
|
||||||
|
const rect = scrollRootRef.current.getBoundingClientRect();
|
||||||
|
setScrollRootCenterX(rect.left + rect.width / 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
updateScrollRootCenter();
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
updateScrollRootCenter();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scrollRootRef.current) {
|
||||||
|
observer.observe(scrollRootRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', updateScrollRootCenter);
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
window.removeEventListener('resize', updateScrollRootCenter);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const scrollToNext = (ref: React.RefObject<HTMLDivElement>) => {
|
const scrollToNext = (ref: React.RefObject<HTMLDivElement>) => {
|
||||||
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-[calc(100vh-5rem)] overflow-y-auto snap-y snap-mandatory custom-scrollbar relative scroll-smooth bg-background">
|
<div ref={scrollRootRef} className="h-[calc(100vh-5rem)] overflow-y-auto snap-y snap-mandatory custom-scrollbar relative scroll-smooth bg-background">
|
||||||
<div ref={topRailRef} className="sticky top-0 z-40 bg-gradient-to-b from-background via-background/95 to-transparent pb-3">
|
<div ref={topRailRef} className="sticky top-0 z-40 bg-gradient-to-b from-background via-background/95 to-transparent pb-3">
|
||||||
<div className="mx-auto w-full max-w-[1600px] px-6 lg:px-12 pt-3">
|
<div className="mx-auto w-full max-w-[1600px] px-6 lg:px-12 pt-3">
|
||||||
<div className="flex justify-end items-center gap-3 rounded-2xl border border-white/10 bg-background/75 px-3 py-2 backdrop-blur-xl shadow-[0_8px_30px_rgba(0,0,0,0.25)]">
|
<div className="flex justify-end items-center gap-3 rounded-2xl border border-white/10 bg-background/75 px-3 py-2 backdrop-blur-xl shadow-[0_8px_30px_rgba(0,0,0,0.25)]">
|
||||||
@@ -77,6 +122,7 @@ export const DashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* SECTION 1: METRICS & CHARTS */}
|
{/* SECTION 1: METRICS & CHARTS */}
|
||||||
<section
|
<section
|
||||||
|
ref={overviewRef}
|
||||||
className="min-h-full snap-start flex flex-col pb-4 relative"
|
className="min-h-full snap-start flex flex-col pb-4 relative"
|
||||||
style={{ paddingTop: `${topRailHeight + 8}px` }}
|
style={{ paddingTop: `${topRailHeight + 8}px` }}
|
||||||
>
|
>
|
||||||
@@ -212,17 +258,25 @@ export const DashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Scroll Indicator */}
|
</section>
|
||||||
<div
|
|
||||||
onClick={() => scrollToNext(workflowRef)}
|
{/* Scroll Indicator (Overview, fixed to viewport bottom) */}
|
||||||
className="mt-3 mb-1 self-center flex flex-col items-center gap-2 cursor-pointer group animate-bounce-slow opacity-35 hover:opacity-100 transition-all duration-500 hover:scale-110"
|
{isOverviewVisible && (
|
||||||
|
<div
|
||||||
|
className="pointer-events-none fixed bottom-4 z-30 -translate-x-1/2"
|
||||||
|
style={{ left: scrollRootCenterX ? `${scrollRootCenterX}px` : '50%' }}
|
||||||
>
|
>
|
||||||
<span className="text-[10px] font-bold text-primary tracking-[0.3em] uppercase group-hover:tracking-[0.5em] transition-all">核心流程</span>
|
<div
|
||||||
<div className="p-2 rounded-full bg-primary/10 border border-primary/20 backdrop-blur-sm group-hover:bg-primary group-hover:text-black transition-colors">
|
onClick={() => scrollToNext(workflowRef)}
|
||||||
<ArrowDown className="w-5 h-5" />
|
className="pointer-events-auto flex flex-col items-center gap-2 cursor-pointer group animate-bounce-slow opacity-45 hover:opacity-100 transition-all duration-500 hover:scale-110"
|
||||||
|
>
|
||||||
|
<span className="text-[10px] font-bold text-primary tracking-[0.3em] uppercase group-hover:tracking-[0.5em] transition-all">核心流程</span>
|
||||||
|
<div className="p-2 rounded-full bg-primary/10 border border-primary/20 backdrop-blur-sm group-hover:bg-primary group-hover:text-black transition-colors">
|
||||||
|
<ArrowDown className="w-5 h-5" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
)}
|
||||||
|
|
||||||
{/* SECTION 2: WORKFLOW LOGIC */}
|
{/* SECTION 2: WORKFLOW LOGIC */}
|
||||||
<section
|
<section
|
||||||
@@ -282,10 +336,12 @@ export const DashboardPage: React.FC = () => {
|
|||||||
{/* Scroll Indicator */}
|
{/* Scroll Indicator */}
|
||||||
<div
|
<div
|
||||||
onClick={() => scrollToNext(aboutRef)}
|
onClick={() => scrollToNext(aboutRef)}
|
||||||
className="absolute bottom-10 left-1/2 -translate-x-1/2 cursor-pointer opacity-30 hover:opacity-100 transition-opacity p-4 flex flex-col items-center gap-2 hover:scale-110 duration-300"
|
className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 cursor-pointer group animate-bounce-slow opacity-45 hover:opacity-100 transition-all duration-500 hover:scale-110"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] text-muted-foreground tracking-widest font-bold">了解更多</span>
|
<span className="text-[10px] font-bold text-primary tracking-[0.3em] uppercase group-hover:tracking-[0.5em] transition-all">了解更多</span>
|
||||||
<ArrowDown className="w-6 h-6 text-white animate-bounce" />
|
<div className="p-2 rounded-full bg-primary/10 border border-primary/20 backdrop-blur-sm group-hover:bg-primary group-hover:text-black transition-colors">
|
||||||
|
<ArrowDown className="w-5 h-5" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user