feat:首页调整

dev_na
puz 2026-06-30 15:25:51 +08:00
parent d36beec479
commit 1ab6e37fe1
2 changed files with 180 additions and 145 deletions

View File

@ -3,15 +3,41 @@
@home-text-gray: #475569; // ui-ux-pro-max slate-600
@home-bg: #f8fafc; // ui-ux-pro-max slate-50
.home-page {
padding: 8px;
min-width: 0;
background: #f5f6fa;
}
.home-page > .page-container__body {
padding: 0;
overflow: hidden;
border: none;
border-radius: 0;
background: transparent;
}
.home-shell-card .section-card__content {
padding: 8px;
}
.home-data-panel .data-list-panel__table-area,
.home-data-panel .app-page__table-wrap {
height: 100%;
min-height: 0;
}
.home-container {
position: relative;
flex: 1;
min-height: 100%; // Changed from height: 100% to avoid height conflicts
padding: clamp(40px, 8vh, 80px) clamp(24px, 5vw, 60px) 0; // Top shift down, ZERO bottom padding
height: 100%;
min-height: 100%;
padding: clamp(40px, 8vh, 80px) clamp(24px, 5vw, 60px) 0;
border-radius: 4px;
background: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%);
color: @home-text-main;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
overflow-x: hidden;
overflow: hidden auto;
z-index: 1;
display: flex;
flex-direction: column;

View File

@ -9,6 +9,9 @@ import { useNavigate } from "react-router-dom";
import dayjs from "dayjs";
import { getRecentTasks } from "@/api/business/dashboard";
import { getMeetingCreateConfig, type MeetingCreateConfig, type MeetingVO } from "@/api/business/meeting";
import DataListPanel from "@/components/shared/DataListPanel";
import PageContainer from "@/components/shared/PageContainer";
import SectionCard from "@/components/shared/SectionCard";
import "./index.less";
import RightVisual from "./RightVisual";
import { MeetingCreateDrawer, MeetingCreateType } from "@/components/business/MeetingCreateDrawer";
@ -193,156 +196,162 @@ export default function HomePage() {
};
return (
<main className="home-container">
{/* Massive Abstract Background Sphere matching the design/img.png */}
<div className="home-bg-visual" aria-hidden="true">
<div className="home-bg-sphere" />
<RightVisual />
</div>
<PageContainer title={null} className="home-page">
<SectionCard className="home-shell-card">
<DataListPanel className="home-data-panel">
<main className="home-container">
{/* Massive Abstract Background Sphere matching the design/img.png */}
<div className="home-bg-visual" aria-hidden="true">
<div className="home-bg-sphere" />
<RightVisual />
</div>
<div className="home-content-wrapper">
<header className="home-hero">
<Title level={1} className="home-title">
<span className="home-title-accent-wrapper">
<span
className="home-title-accent-scroller"
style={{ transform: `translateY(-${wordIndex * 1.2}em)` }}
>
{ROTATING_WORDS.map((word) => (
<span key={word} className="home-title-accent">
{word}
<div className="home-content-wrapper">
<header className="home-hero">
<Title level={1} className="home-title">
<span className="home-title-accent-wrapper">
<span
className="home-title-accent-scroller"
style={{ transform: `translateY(-${wordIndex * 1.2}em)` }}
>
{ROTATING_WORDS.map((word) => (
<span key={word} className="home-title-accent">
{word}
</span>
))}
</span>
</span>
))}
</span>
</span>
</Title>
<div className="home-quick-actions">
{configLoaded ? (
visibleQuickEntries.length > 0 ? visibleQuickEntries.map((entry) => (
<div
className={`home-action-item home-action-item--${entry.accent}`}
onClick={entry.onClick}
key={entry.title}
>
<div className="home-action-badge">{entry.badge}</div>
<div className="home-action-icon-wrapper">
<div className="home-action-icon">{entry.icon}</div>
<div className="home-action-icon-glow" />
<div className="home-action-icon-circle" />
</div>
<Title level={3} className="home-action-title">{entry.title}</Title>
<div className="home-action-desc">
{entry.description.map((line) => (
<Text key={line} className="home-action-line">
{line}
</Text>
</Title>
<div className="home-quick-actions">
{configLoaded ? (
visibleQuickEntries.length > 0 ? visibleQuickEntries.map((entry) => (
<div
className={`home-action-item home-action-item--${entry.accent}`}
onClick={entry.onClick}
key={entry.title}
>
<div className="home-action-badge">{entry.badge}</div>
<div className="home-action-icon-wrapper">
<div className="home-action-icon">{entry.icon}</div>
<div className="home-action-icon-glow" />
<div className="home-action-icon-circle" />
</div>
<Title level={3} className="home-action-title">{entry.title}</Title>
<div className="home-action-desc">
{entry.description.map((line) => (
<Text key={line} className="home-action-line">
{line}
</Text>
))}
</div>
</div>
)) : (
<div className="home-action-item home-action-item--cyan" style={{ cursor: "default", opacity: 0.7 }}>
<div className="home-action-badge"></div>
<div className="home-action-icon-wrapper">
<div className="home-action-icon"><AudioOutlined /></div>
<div className="home-action-icon-glow" />
<div className="home-action-icon-circle" />
</div>
<Title level={3} className="home-action-title"></Title>
<div className="home-action-desc">
<Text className="home-action-line">线</Text>
</div>
</div>
)
) : (
// Loading state for quick actions to prevent jump
[...Array(2)].map((_, i) => (
<div key={i} className="home-action-item home-action-item--loading" style={{ background: 'rgba(255,255,255,0.4)', border: '1px dashed rgba(0,0,0,0.05)' }}>
<Skeleton active avatar paragraph={{ rows: 2 }} />
</div>
))
)}
</div>
</header>
<section className="home-recent-section">
<div className="home-section-header">
<Title level={3}></Title>
<Button type="link" onClick={() => navigate("/meetings")} className="home-view-all">
&gt;
</Button>
</div>
{loading ? (
<div className="home-recent-grid">
{[...Array(4)].map((_, i) => (
<div key={i} className="home-recent-skeleton">
<Skeleton active paragraph={{ rows: 2 }} title={{ width: "60%" }} />
</div>
))}
</div>
</div>
)) : (
<div className="home-action-item home-action-item--cyan" style={{ cursor: "default", opacity: 0.7 }}>
<div className="home-action-badge"></div>
<div className="home-action-icon-wrapper">
<div className="home-action-icon"><AudioOutlined /></div>
<div className="home-action-icon-glow" />
<div className="home-action-icon-circle" />
</div>
<Title level={3} className="home-action-title"></Title>
<div className="home-action-desc">
<Text className="home-action-line">线</Text>
</div>
</div>
)
) : (
// Loading state for quick actions to prevent jump
[...Array(2)].map((_, i) => (
<div key={i} className="home-action-item home-action-item--loading" style={{ background: 'rgba(255,255,255,0.4)', border: '1px dashed rgba(0,0,0,0.05)' }}>
<Skeleton active avatar paragraph={{ rows: 2 }} />
</div>
))
)}
</div>
</header>
<section className="home-recent-section">
<div className="home-section-header">
<Title level={3}></Title>
<Button type="link" onClick={() => navigate("/meetings")} className="home-view-all">
&gt;
</Button>
</div>
{loading ? (
<div className="home-recent-grid">
{[...Array(4)].map((_, i) => (
<div key={i} className="home-recent-skeleton">
<Skeleton active paragraph={{ rows: 2 }} title={{ width: "60%" }} />
</div>
))}
</div>
) : recentCards.length ? (
<div className="home-recent-grid">
{recentCards.slice(0, 4).map((card) => (
<article
key={card.id}
className="home-recent-card"
onClick={() => handleRecentCardClick(card)}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
handleRecentCardClick(card);
}
}}
role="button"
tabIndex={0}
>
{/* {!readCardIds.includes(String(card.id)) && <span className="home-recent-card-dot" />} */}
<div className="home-recent-card-head">
<Title level={4} className="home-recent-card-title">{card.title}</Title>
<div className="home-recent-card-icon">
<VideoCameraOutlined className="home-recent-card-play-icon" />
</div>
</div>
<div className="home-recent-card-tags">
{recentTaskMap.get(String(card.id))?.meetingSource && (
<Tag key={`${card.id}-source`} className="home-recent-card-tag" bordered={false}>
{recentTaskMap.get(String(card.id))?.meetingSource === "ANDROID" ? "安卓端" : "Web端"}
</Tag>
)}
{card.tags.slice(0, 4).map((tag) => (
<Tag key={`${card.id}-${tag}`} className="home-recent-card-tag" bordered={false}>
{tag}
</Tag>
) : recentCards.length ? (
<div className="home-recent-grid">
{recentCards.slice(0, 4).map((card) => (
<article
key={card.id}
className="home-recent-card"
onClick={() => handleRecentCardClick(card)}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
handleRecentCardClick(card);
}
}}
role="button"
tabIndex={0}
>
{/* {!readCardIds.includes(String(card.id)) && <span className="home-recent-card-dot" />} */}
<div className="home-recent-card-head">
<Title level={4} className="home-recent-card-title">{card.title}</Title>
<div className="home-recent-card-icon">
<VideoCameraOutlined className="home-recent-card-play-icon" />
</div>
</div>
<div className="home-recent-card-tags">
{recentTaskMap.get(String(card.id))?.meetingSource && (
<Tag key={`${card.id}-source`} className="home-recent-card-tag" bordered={false}>
{recentTaskMap.get(String(card.id))?.meetingSource === "ANDROID" ? "安卓端" : "Web端"}
</Tag>
)}
{card.tags.slice(0, 4).map((tag) => (
<Tag key={`${card.id}-${tag}`} className="home-recent-card-tag" bordered={false}>
{tag}
</Tag>
))}
</div>
<div className="home-recent-card-footer">
<span className="home-recent-card-duration">{card.duration}</span>
<span className="home-recent-card-time">{card.time}</span>
</div>
</article>
))}
</div>
<div className="home-recent-card-footer">
<span className="home-recent-card-duration">{card.duration}</span>
<span className="home-recent-card-time">{card.time}</span>
) : (
<div className="home-empty-state">
<Empty description="暂无最近记录" />
</div>
</article>
))}
)}
</section>
</div>
) : (
<div className="home-empty-state">
<Empty description="暂无最近记录" />
</div>
)}
</section>
</div>
<MeetingCreateDrawer
open={drawerOpen}
initialType={drawerType}
onCancel={() => setDrawerOpen(false)}
onSuccess={() => {
setDrawerOpen(false);
// onSuccess doesn't return meeting id in MeetingCreateDrawerProps right now
// the Drawer handles its own navigation or we can just redirect to meetings list
navigate('/meetings');
}}
/>
</main>
<MeetingCreateDrawer
open={drawerOpen}
initialType={drawerType}
onCancel={() => setDrawerOpen(false)}
onSuccess={() => {
setDrawerOpen(false);
// onSuccess doesn't return meeting id in MeetingCreateDrawerProps right now
// the Drawer handles its own navigation or we can just redirect to meetings list
navigate('/meetings');
}}
/>
</main>
</DataListPanel>
</SectionCard>
</PageContainer>
);
}