Преглед изворни кода

feat: 课程列表加载更多

NeeDaye пре 6 месеци
родитељ
комит
b34b20256e

+ 122 - 71
src/layouts/SideMenu.tsx

@@ -1,5 +1,5 @@
-import React, { useEffect, useState } from 'react';
-import { Button, Image, Input, Space } from 'antd';
+import React, { useEffect, useRef, useState } from 'react';
+import { Button, Empty, Image, Input, Space } from 'antd';
 import { ReactComponent as SvgLogo } from '@/icons/logo.svg';
 
 
@@ -7,9 +7,10 @@ import './styles/side-menu.less';
 import { SearchOutlined } from '@ant-design/icons';
 import ProList from '@ant-design/pro-list';
 import { history } from '@@/core/history';
-import CourseImage from '@/assets/img/course_image.png';
 import { useLocation } from '@@/exports';
 import { getTeachers } from '@/services/side/SideController';
+import { getTeacherCourses } from '@/services/record/RecordController';
+import CourseImage from '@/assets/img/course_image.png';
 
 
 interface SideMenuProps {
@@ -23,44 +24,53 @@ function SideMenu  ({
   const location = useLocation();
   const [isLessionPage, setIsLessionPage] = useState<boolean>(false);
   const [activeKey, setActiveKey] = useState<string>('');
+  const [activeCourseKey, setActiveCourseKey] = useState<string>('');
+  const [courseTitle, setCourseTitle] = useState<string>('');
   const [teacherName, setTeacherName] = useState<string>('');
   const [teacherData, setTeacherData] = useState<API.Teacher[]>([]);
+  const [courseData, setCourseData] = useState<[]>([]);
+  const containerRef = useRef<HTMLDivElement>(null);
+  const [loading, setLoading] = useState(false);
+  const [hasMore, setHasMore] = useState(true);
+  const [page, setPage] = useState(1);
 
-  const lessonData = [
-    {
-      lessonId: '1',
-      lessonName: '第一课第一节',
-      subjectName: '数学',
-      grade: '初一',
-      teachTime: '2025-05-01 10:00:00',
-    },
-    {
-      lessonId: '2',
-      lessonName: '第一课第一节',
-      subjectName: '数学',
-      grade: '初一',
-      teachTime: '2025-05-01 10:00:00',
-    },
-    {
-      lessonId: '3',
-      lessonName: '第一课第一节',
-      subjectName: '数学',
-      grade: '初一',
-      teachTime: '2025-05-01 10:00:00',
-    },
-    {
-      lessonId: '4',
-      lessonName: '第一课第一节',
-      subjectName: '数学',
-      grade: '初一',
-      teachTime: '2025-05-01 10:00:00',
-    },
-  ];
+  async function getTeacherCourseList(current: number) {
+    const searchParams = new URLSearchParams(location.search);
+    setActiveCourseKey(searchParams.get('courseId') as string)
+    setLoading(true);
+    try {
+      const data = await getTeacherCourses({
+        teacherId: searchParams.get('teacherId') as string,
+        courseTitle: courseTitle,
+        current: current,
+        size: 10,
+      })
+      if (data.code === 200) {
+        if (data.data.records.length < 10) {
+          setHasMore(false);
+        } else {
+          setHasMore(true);
+        }
+        if (current === 1) {
+          setCourseData(data.data.records);
+        } else {
+          setCourseData(prev => [...prev, ...data.data.records] as []);
+        }
+        setPage(current + 1);
+      }
+    } finally {
+      setLoading(false);
+    }
+  }
 
   useEffect(() => {
     if(location.pathname) {
       if(location.pathname.includes('play')) {
         setIsLessionPage(true);
+        const onData = async () => {
+          await getTeacherCourseList(1);
+        }
+        onData()
       } else {
         setIsLessionPage(false);
       }
@@ -74,15 +84,36 @@ function SideMenu  ({
     if (data.code === 200) {
       setTeacherData(data.data)
       if (data.data.length > 0) {
-        setActiveKey(data.data[0].teacherName as string)
-        localStorage.setItem('teacherName', data.data[0].teacherName as string)
-        localStorage.setItem('schoolName', data.data[0].schoolName as string)
-        history.push('/record?teacherId=' + data.data[0].teacherId);
+        const searchParams = new URLSearchParams(location.search);
+        if (searchParams.get('teacherId')) {
+          setActiveKey(searchParams.get('teacherId') as string);
+        } else {
+          setActiveKey(data.data[0].teacherId as string)
+          localStorage.setItem('teacherName', data.data[0].teacherName as string)
+          localStorage.setItem('schoolName', data.data[0].schoolName as string)
+          history.push('/record?teacherId=' + data.data[0].teacherId);
+        }
       }
     }
   }
 
   useEffect(() => {
+    const container = containerRef.current;
+    if (!container) return;
+
+    const handleScroll = () => {
+      const { scrollTop, scrollHeight, clientHeight } = container;
+      // 距离底部50px触发加载
+      if (!loading && hasMore && scrollHeight - (scrollTop + clientHeight) < 50) {
+        getTeacherCourseList(page);
+      }
+    };
+
+    container.addEventListener('scroll', handleScroll);
+    return () => container?.removeEventListener('scroll', handleScroll);
+  }, [loading, hasMore, page]);
+
+  useEffect(() => {
     const onData = async () => {
       await getTeacherList()
     }
@@ -103,15 +134,24 @@ function SideMenu  ({
               <div>
                 <div style={{  display: 'flex', justifyContent: 'space-between', padding: '20px 10px' }}>
                   <div style={{ color: 'black', fontSize: 16, fontWeight: 'bold', }} >
-                    张三
+                    {localStorage.getItem('teacherName')}
                   </div>
                   <div style={{ color: 'black', fontSize: 12 }}>
-                    杭州市滨江区市一中
+                    {localStorage.getItem('schoolName')}
                   </div>
                 </div>
                 <Space direction="horizontal" style={{ padding: '0 10px' }}>
-                  <Input placeholder="输入课时名称搜索" />
-                  <Button icon={<SearchOutlined />} type="primary" />
+                  <Input
+                    value={courseTitle}
+                    onChange={(e) => setCourseTitle(e.target.value)}
+                    placeholder="输入课程主题搜索"
+                    allowClear
+                  />
+                  <Button
+                    icon={<SearchOutlined />}
+                    type="primary"
+                    onClick={async () => { await getTeacherCourseList(1) }}
+                  />
                 </Space>
               </div>) :
               (
@@ -132,6 +172,7 @@ function SideMenu  ({
                       value={teacherName}
                       onChange={(e) => setTeacherName(e.target.value)}
                       placeholder="输入教师姓名搜索"
+                      allowClear
                     />
                     <Button
                       icon={<SearchOutlined />}
@@ -147,43 +188,53 @@ function SideMenu  ({
         {
           isLessionPage ? (
             <ProList<any>
-              className={'list-box'}
+              className={'course-list'}
               pagination={false}
               showActions="hover"
-              onItem={(record) => {
-                return {
-                  onClick: () => {
-                    console.log(record);
-                  },
-                };
-              }}
-              renderItem={(item) => (
+              tableExtraRender={(_: any, list: any) => (
                 <div
-                  style={{ backgroundColor: '#F8FAFC', cursor: 'pointer' }}
-                  onClick={() => {
-                    history.push('/play?courseId=1&lessonId=' + item.lessonId);
-                  }}
+                  ref={containerRef}
+                  style={{ height: 'calc(100vh - 170px)', overflow: 'auto', paddingRight: 30 }}
                 >
-                  <Image
-                    width={'100%'}
-                    style={{ aspectRatio: '16/9', borderRadius: '15px 15px 0 0' }}
-                    preview={false}
-                    src={CourseImage}
-                  />
-                  <div className="course-info">
-                    <div style={{ fontWeight: 'bold', fontSize: '18px' }}>{item.lessonName}</div>
-                    <div className="info-flex">
-                      <div>{item.subjectName}</div>
-                      <div>所属年级:{item.grade}</div>
+                  {list.map((item: any) => (
+                    <div
+                      key={item.id}
+                      style={{
+                        backgroundColor: '#F8FAFC',
+                        cursor: 'pointer',
+                        marginBottom: '10px'
+                      }}
+                      onClick={() => {
+                        history.push(`/play?teacherId=${item.teacherId}&courseId=${item.id}`);
+                        setActiveCourseKey(item.id);
+                      }}
+                      className={item.id === activeCourseKey ? 'course-active' : ''}
+                    >
+                      <Image
+                        width={'100%'}
+                        style={{ aspectRatio: '16/9', borderRadius: '15px 15px 0 0' }}
+                        preview={false}
+                        src={CourseImage}
+                        // src={item.fullScreenOssUrl + '?x-oss-process=video/snapshot,t_0,f_jpg,w_800,h_450'}
+                      />
+                      <div className="course-info">
+                        <div style={{ fontWeight: 'bold', fontSize: '18px' }}>{item.courseTitle}</div>
+                        <div className="info-flex">
+                          <div>{item.subjectName}</div>
+                          <div>所属年级:{item.periodName}</div>
+                        </div>
+                        <div style={{ fontSize: '11px' }}>授课时间:{item.courseDate}</div>
+                      </div>
                     </div>
-                    <div style={{ fontSize: '11px' }}>授课时间:{item.teachTime}</div>
-                  </div>
+                  ))}
+                  {loading && <div style={{ textAlign: 'center', padding: '10px' }}>加载中...</div>}
+                  {!hasMore && <Empty styles={{ image: { height: 0 } }} description="没有更多数据了" image={false} />}
                 </div>
               )}
               metas={{
                 actions: {},
               }}
-              dataSource={lessonData}
+              dataSource={courseData}
             />
             ) : (
             <ProList<any>
@@ -193,15 +244,15 @@ function SideMenu  ({
               onItem={(record) => {
                 return {
                   onClick: () => {
-                    setActiveKey(record.teacherName);
+                    setActiveKey(record.teacherId);
                     localStorage.setItem('teacherName', record.teacherName)
                     localStorage.setItem('schoolName', record.schoolName)
                     history.push('/record?teacherId=' + record.teacherId);
                   },
                 };
               }}
-              rowClassName={(record: { teacherName: string }) =>
-                record.teacherName === activeKey ? 'active' : ''
+              rowClassName={(record: { teacherId: string }) =>
+                record.teacherId === activeKey ? 'active' : ''
               }
               metas={{
                 title: {

+ 9 - 1
src/layouts/styles/side-menu.less

@@ -35,7 +35,7 @@
 .menu-area {
     margin-top: 10px;
     height: calc(100vh - 65px);
-    overflow-y: auto;
+    overflow-y: hidden;
     .ant-list-item {
         cursor: pointer;
     }
@@ -44,6 +44,10 @@
             color: #3C82F6 !important;
         }
     }
+    .course-active {
+        border: 2px solid #3C82F6;
+        border-radius: 18px;
+    }
     .course-info {
         padding: 10px;
         text-align: left;
@@ -62,6 +66,10 @@
             }
         }
     }
+    .course-list {
+        margin: 0 5px;
+        color: black;
+    }
 }
 .ant-menu-item-selected::before {
     content: ' ';

+ 1 - 1
src/pages/Record/index.tsx

@@ -90,7 +90,7 @@ const TableList: React.FC<unknown> = () => {
           <div
             style={{ backgroundColor: '#F8FAFC', cursor: 'pointer' }}
             onClick={() => {
-              history.push('/play?courseId=' + item.courseId);
+              history.push('/play?teacherId=' + teacherId + '&courseId=' + item.id);
             }}
           >
             <Image

+ 1 - 0
src/services/record/typings.d.ts

@@ -8,6 +8,7 @@ declare namespace API {
     courseStart?: string;
     courseEnd?: string;
     size?: number,
+    current?: number,
   }
 
   // 响应接口