web-backset.cn/apps/web/src/view/Course/index.tsx
2023-03-22 10:09:21 +08:00

139 lines
4.0 KiB
TypeScript

import "./index.less";
import Timeline, { IOnScrollParam } from "./components/Timeline";
import Tab20Regular from "@ricons/fluent/Tab20Regular";
import Table20Regular from "@ricons/fluent/Table20Regular";
import { useRef, useState } from "react";
import { Icon } from "@ricons/utils";
import { useNavigate } from "react-router-dom";
import { useMount } from "../../hook";
import { getCourseList } from "../../api";
import { processTime } from "./util";
import { Footer } from "../../components/Footer";
import Card from "./components/Card";
export default function Index() {
const navigate = useNavigate();
const thumbnailRef = useRef<HTMLElement | null>(null);
const scale = useRef<number>(1); // thumbnail / timeline 高度比例
const [timeline, setTimeline] = useState({
top: -4,
});
const [actions, setActions] = useState([
{
key: "table",
icon: <Table20Regular />,
active: true,
tip: "列出更多项目",
gridClass: "table",
},
{
key: "tab",
icon: <Tab20Regular />,
active: false,
tip: "使用大缩略图显示单个项目",
gridClass: "tab",
},
]);
useMount(() => {
getCourseList().then((res) => {
setCourseTimeList(processTime(res.data));
});
});
const [courseTimeList, setCourseTimeList] = useState<any>([]);
const onClickActionItem = (action: any) => {
setActions((p) => p.map((a) => ({ ...a, active: a.key === action.key })));
};
/**
* 右侧时间线滚动
*/
const onTimelineScroll = (p: IOnScrollParam) => {
const { top, height } = p;
//左侧区域高度
const { scrollHeight, clientHeight } = thumbnailRef.current!;
scale.current = (scrollHeight - clientHeight) / height;
thumbnailRef.current!.scrollTop = top * scale.current;
};
/**
* 左侧缩略图预览区域滚动
*/
const onThumbnailScroll = () => {
const { scrollHeight, scrollTop } = thumbnailRef.current!;
const isTop = scrollTop === 0;
const isBottom = scrollTop === scrollHeight;
const top = isTop ? -4 : scrollTop / scale.current; // 修正顶部
setTimeline({ top });
};
/**
* 点击课程
*/
const onClickCourseItem = (d: any) => {
navigate(`/course/detail/${d.course_id}`);
};
return (
<div className="container course">
<div className="action-bar">
{actions.map((action) => (
<span
key={action.key}
style={{ cursor: "pointer" }}
onClick={() => onClickActionItem(action)}
className="bs tip ml6"
data-tip={action.tip}
>
<Icon
size={20}
color={
action.active ? "var(--color-text-2)" : "var(--color-text-4)"
}
>
{action.icon}
</Icon>
</span>
))}
</div>
<div className="thumbnail-timeline">
<div className="thumbnail-container">
<article ref={thumbnailRef} onScroll={onThumbnailScroll}>
{courseTimeList.map((item: any, index: number) => (
<section key={index}>
<div className="time">
{item.year}{item.month}
</div>
<div className="statistic">{item.data.length} </div>
<div
className={`grid ${actions.find((a) => a.active)?.gridClass}`}
>
{item.data.map((d: any) => (
<Card
onClick={() => onClickCourseItem(d)}
key={d.course_id}
imgUrl={d.course_cover_url}
title={d.course_title}
summary={d.course_summary}
/>
))}
</div>
</section>
))}
</article>
</div>
<Timeline
className="timeline-container"
data={courseTimeList}
onScroll={onTimelineScroll}
model={{ top: timeline.top }}
/>
</div>
<Footer />
</div>
);
}