feat:首页调整
parent
d36beec479
commit
1ab6e37fe1
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
更多 >
|
||||
</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">
|
||||
更多 >
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue