feat:首页调整
parent
d36beec479
commit
1ab6e37fe1
|
|
@ -3,15 +3,41 @@
|
||||||
@home-text-gray: #475569; // ui-ux-pro-max slate-600
|
@home-text-gray: #475569; // ui-ux-pro-max slate-600
|
||||||
@home-bg: #f8fafc; // ui-ux-pro-max slate-50
|
@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 {
|
.home-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 100%; // Changed from height: 100% to avoid height conflicts
|
height: 100%;
|
||||||
padding: clamp(40px, 8vh, 80px) clamp(24px, 5vw, 60px) 0; // Top shift down, ZERO bottom padding
|
min-height: 100%;
|
||||||
|
padding: clamp(40px, 8vh, 80px) clamp(24px, 5vw, 60px) 0;
|
||||||
|
border-radius: 4px;
|
||||||
background: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%);
|
background: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%);
|
||||||
color: @home-text-main;
|
color: @home-text-main;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
overflow-x: hidden;
|
overflow: hidden auto;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ import { useNavigate } from "react-router-dom";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { getRecentTasks } from "@/api/business/dashboard";
|
import { getRecentTasks } from "@/api/business/dashboard";
|
||||||
import { getMeetingCreateConfig, type MeetingCreateConfig, type MeetingVO } from "@/api/business/meeting";
|
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 "./index.less";
|
||||||
import RightVisual from "./RightVisual";
|
import RightVisual from "./RightVisual";
|
||||||
import { MeetingCreateDrawer, MeetingCreateType } from "@/components/business/MeetingCreateDrawer";
|
import { MeetingCreateDrawer, MeetingCreateType } from "@/components/business/MeetingCreateDrawer";
|
||||||
|
|
@ -193,156 +196,162 @@ export default function HomePage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="home-container">
|
<PageContainer title={null} className="home-page">
|
||||||
{/* Massive Abstract Background Sphere matching the design/img.png */}
|
<SectionCard className="home-shell-card">
|
||||||
<div className="home-bg-visual" aria-hidden="true">
|
<DataListPanel className="home-data-panel">
|
||||||
<div className="home-bg-sphere" />
|
<main className="home-container">
|
||||||
<RightVisual />
|
{/* Massive Abstract Background Sphere matching the design/img.png */}
|
||||||
</div>
|
<div className="home-bg-visual" aria-hidden="true">
|
||||||
|
<div className="home-bg-sphere" />
|
||||||
|
<RightVisual />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="home-content-wrapper">
|
<div className="home-content-wrapper">
|
||||||
<header className="home-hero">
|
<header className="home-hero">
|
||||||
<Title level={1} className="home-title">
|
<Title level={1} className="home-title">
|
||||||
每一次交流,
|
每一次交流,
|
||||||
<span className="home-title-accent-wrapper">
|
<span className="home-title-accent-wrapper">
|
||||||
<span
|
<span
|
||||||
className="home-title-accent-scroller"
|
className="home-title-accent-scroller"
|
||||||
style={{ transform: `translateY(-${wordIndex * 1.2}em)` }}
|
style={{ transform: `translateY(-${wordIndex * 1.2}em)` }}
|
||||||
>
|
>
|
||||||
{ROTATING_WORDS.map((word) => (
|
{ROTATING_WORDS.map((word) => (
|
||||||
<span key={word} className="home-title-accent">
|
<span key={word} className="home-title-accent">
|
||||||
{word}
|
{word}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
))}
|
</Title>
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</Title>
|
|
||||||
|
|
||||||
<div className="home-quick-actions">
|
<div className="home-quick-actions">
|
||||||
{configLoaded ? (
|
{configLoaded ? (
|
||||||
visibleQuickEntries.length > 0 ? visibleQuickEntries.map((entry) => (
|
visibleQuickEntries.length > 0 ? visibleQuickEntries.map((entry) => (
|
||||||
<div
|
<div
|
||||||
className={`home-action-item home-action-item--${entry.accent}`}
|
className={`home-action-item home-action-item--${entry.accent}`}
|
||||||
onClick={entry.onClick}
|
onClick={entry.onClick}
|
||||||
key={entry.title}
|
key={entry.title}
|
||||||
>
|
>
|
||||||
<div className="home-action-badge">{entry.badge}</div>
|
<div className="home-action-badge">{entry.badge}</div>
|
||||||
<div className="home-action-icon-wrapper">
|
<div className="home-action-icon-wrapper">
|
||||||
<div className="home-action-icon">{entry.icon}</div>
|
<div className="home-action-icon">{entry.icon}</div>
|
||||||
<div className="home-action-icon-glow" />
|
<div className="home-action-icon-glow" />
|
||||||
<div className="home-action-icon-circle" />
|
<div className="home-action-icon-circle" />
|
||||||
</div>
|
</div>
|
||||||
<Title level={3} className="home-action-title">{entry.title}</Title>
|
<Title level={3} className="home-action-title">{entry.title}</Title>
|
||||||
<div className="home-action-desc">
|
<div className="home-action-desc">
|
||||||
{entry.description.map((line) => (
|
{entry.description.map((line) => (
|
||||||
<Text key={line} className="home-action-line">
|
<Text key={line} className="home-action-line">
|
||||||
{line}
|
{line}
|
||||||
</Text>
|
</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>
|
) : recentCards.length ? (
|
||||||
)) : (
|
<div className="home-recent-grid">
|
||||||
<div className="home-action-item home-action-item--cyan" style={{ cursor: "default", opacity: 0.7 }}>
|
{recentCards.slice(0, 4).map((card) => (
|
||||||
<div className="home-action-badge">系统参数</div>
|
<article
|
||||||
<div className="home-action-icon-wrapper">
|
key={card.id}
|
||||||
<div className="home-action-icon"><AudioOutlined /></div>
|
className="home-recent-card"
|
||||||
<div className="home-action-icon-glow" />
|
onClick={() => handleRecentCardClick(card)}
|
||||||
<div className="home-action-icon-circle" />
|
onKeyDown={(event) => {
|
||||||
</div>
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
<Title level={3} className="home-action-title">会议创建已关闭</Title>
|
event.preventDefault();
|
||||||
<div className="home-action-desc">
|
handleRecentCardClick(card);
|
||||||
<Text className="home-action-line">当前系统参数已关闭离线上传和实时会议入口</Text>
|
}
|
||||||
</div>
|
}}
|
||||||
</div>
|
role="button"
|
||||||
)
|
tabIndex={0}
|
||||||
) : (
|
>
|
||||||
// Loading state for quick actions to prevent jump
|
{/* {!readCardIds.includes(String(card.id)) && <span className="home-recent-card-dot" />} */}
|
||||||
[...Array(2)].map((_, i) => (
|
<div className="home-recent-card-head">
|
||||||
<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)' }}>
|
<Title level={4} className="home-recent-card-title">{card.title}</Title>
|
||||||
<Skeleton active avatar paragraph={{ rows: 2 }} />
|
<div className="home-recent-card-icon">
|
||||||
</div>
|
<VideoCameraOutlined className="home-recent-card-play-icon" />
|
||||||
))
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
<div className="home-recent-card-tags">
|
||||||
</header>
|
{recentTaskMap.get(String(card.id))?.meetingSource && (
|
||||||
|
<Tag key={`${card.id}-source`} className="home-recent-card-tag" bordered={false}>
|
||||||
<section className="home-recent-section">
|
{recentTaskMap.get(String(card.id))?.meetingSource === "ANDROID" ? "安卓端" : "Web端"}
|
||||||
<div className="home-section-header">
|
</Tag>
|
||||||
<Title level={3}>最近会议</Title>
|
)}
|
||||||
<Button type="link" onClick={() => navigate("/meetings")} className="home-view-all">
|
{card.tags.slice(0, 4).map((tag) => (
|
||||||
更多 >
|
<Tag key={`${card.id}-${tag}`} className="home-recent-card-tag" bordered={false}>
|
||||||
</Button>
|
{tag}
|
||||||
</div>
|
</Tag>
|
||||||
|
))}
|
||||||
{loading ? (
|
</div>
|
||||||
<div className="home-recent-grid">
|
<div className="home-recent-card-footer">
|
||||||
{[...Array(4)].map((_, i) => (
|
<span className="home-recent-card-duration">{card.duration}</span>
|
||||||
<div key={i} className="home-recent-skeleton">
|
<span className="home-recent-card-time">{card.time}</span>
|
||||||
<Skeleton active paragraph={{ rows: 2 }} title={{ width: "60%" }} />
|
</div>
|
||||||
</div>
|
</article>
|
||||||
))}
|
|
||||||
</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>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="home-recent-card-footer">
|
) : (
|
||||||
<span className="home-recent-card-duration">{card.duration}</span>
|
<div className="home-empty-state">
|
||||||
<span className="home-recent-card-time">{card.time}</span>
|
<Empty description="暂无最近记录" />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
)}
|
||||||
))}
|
</section>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className="home-empty-state">
|
|
||||||
<Empty description="暂无最近记录" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MeetingCreateDrawer
|
<MeetingCreateDrawer
|
||||||
open={drawerOpen}
|
open={drawerOpen}
|
||||||
initialType={drawerType}
|
initialType={drawerType}
|
||||||
onCancel={() => setDrawerOpen(false)}
|
onCancel={() => setDrawerOpen(false)}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setDrawerOpen(false);
|
setDrawerOpen(false);
|
||||||
// onSuccess doesn't return meeting id in MeetingCreateDrawerProps right now
|
// onSuccess doesn't return meeting id in MeetingCreateDrawerProps right now
|
||||||
// the Drawer handles its own navigation or we can just redirect to meetings list
|
// the Drawer handles its own navigation or we can just redirect to meetings list
|
||||||
navigate('/meetings');
|
navigate('/meetings');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
|
</DataListPanel>
|
||||||
|
</SectionCard>
|
||||||
|
</PageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue