SideMenu.tsx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { Button, Empty, Image, Input, Space } from 'antd';
  3. import { ReactComponent as SvgLogo } from '@/icons/logo.svg';
  4. import './styles/side-menu.less';
  5. import { SearchOutlined } from '@ant-design/icons';
  6. import ProList from '@ant-design/pro-list';
  7. import { history } from '@@/core/history';
  8. import { useLocation } from '@@/exports';
  9. import { getTeachers } from '@/services/side/SideController';
  10. import { getTeacherCourses } from '@/services/record/RecordController';
  11. import CourseImage from '@/assets/img/course_image.png';
  12. interface SideMenuProps {
  13. onCollaped: (isExpanded: boolean)=> void,
  14. initWidth: number
  15. }
  16. function SideMenu ({
  17. initWidth = 200,
  18. }: SideMenuProps) {
  19. const location = useLocation();
  20. const [isLessionPage, setIsLessionPage] = useState<boolean>(false);
  21. const [activeKey, setActiveKey] = useState<string>('');
  22. const [activeCourseKey, setActiveCourseKey] = useState<string>('');
  23. const [courseTitle, setCourseTitle] = useState<string>('');
  24. const [teacherName, setTeacherName] = useState<string>('');
  25. const [teacherData, setTeacherData] = useState<API.Teacher[]>([]);
  26. const [courseData, setCourseData] = useState<[]>([]);
  27. const containerRef = useRef<HTMLDivElement>(null);
  28. const [loading, setLoading] = useState(false);
  29. const [hasMore, setHasMore] = useState(true);
  30. const [page, setPage] = useState(1);
  31. async function getTeacherCourseList(current: number) {
  32. const searchParams = new URLSearchParams(location.search);
  33. setActiveCourseKey(searchParams.get('courseId') as string)
  34. setLoading(true);
  35. try {
  36. const data = await getTeacherCourses({
  37. teacherId: searchParams.get('teacherId') as string,
  38. courseTitle: courseTitle,
  39. current: current,
  40. size: 10,
  41. })
  42. if (data.code === 200) {
  43. if (data.data.records.length < 10) {
  44. setHasMore(false);
  45. } else {
  46. setHasMore(true);
  47. }
  48. if (current === 1) {
  49. setCourseData(data.data.records);
  50. } else {
  51. setCourseData(prev => [...prev, ...data.data.records] as []);
  52. }
  53. setPage(current + 1);
  54. }
  55. } finally {
  56. setLoading(false);
  57. }
  58. }
  59. useEffect(() => {
  60. if(location.pathname) {
  61. if(location.pathname.includes('play')) {
  62. setIsLessionPage(true);
  63. const onData = async () => {
  64. await getTeacherCourseList(1);
  65. }
  66. onData()
  67. } else {
  68. setIsLessionPage(false);
  69. }
  70. }
  71. }, [location.pathname]);
  72. async function getTeacherList() {
  73. const data: API.SideRes = await getTeachers({
  74. teacherName: teacherName,
  75. })
  76. if (data.code === 200) {
  77. setTeacherData(data.data)
  78. if (data.data.length > 0) {
  79. const searchParams = new URLSearchParams(location.search);
  80. if (searchParams.get('teacherId')) {
  81. setActiveKey(searchParams.get('teacherId') as string);
  82. } else {
  83. setActiveKey(data.data[0].teacherId as string)
  84. localStorage.setItem('teacherName', data.data[0].teacherName as string)
  85. localStorage.setItem('schoolName', data.data[0].schoolName as string)
  86. history.push('/record?teacherId=' + data.data[0].teacherId);
  87. }
  88. }
  89. }
  90. }
  91. useEffect(() => {
  92. const container = containerRef.current;
  93. if (!container) return;
  94. const handleScroll = () => {
  95. const { scrollTop, scrollHeight, clientHeight } = container;
  96. // 距离底部50px触发加载
  97. if (!loading && hasMore && scrollHeight - (scrollTop + clientHeight) < 50) {
  98. getTeacherCourseList(page);
  99. }
  100. };
  101. container.addEventListener('scroll', handleScroll);
  102. return () => container?.removeEventListener('scroll', handleScroll);
  103. }, [loading, hasMore, page]);
  104. useEffect(() => {
  105. const onData = async () => {
  106. await getTeacherList()
  107. }
  108. onData()
  109. }, []);
  110. return (
  111. <div style={{ width: initWidth, height: '100vh' }}>
  112. <div className="side-header">
  113. <SvgLogo style={{ fill: 'currentColor' }} className="header-logo" />
  114. <h3 className="header-title-text">评审管理系统</h3>
  115. </div>
  116. <div className="side-search">
  117. {
  118. isLessionPage ?
  119. (
  120. <div>
  121. <div style={{ display: 'flex', justifyContent: 'space-between', padding: '20px 10px' }}>
  122. <div style={{ color: 'black', fontSize: 16, fontWeight: 'bold', }} >
  123. {localStorage.getItem('teacherName')}
  124. </div>
  125. <div style={{ color: 'black', fontSize: 12 }}>
  126. {localStorage.getItem('schoolName')}
  127. </div>
  128. </div>
  129. <Space direction="horizontal" style={{ padding: '0 10px' }}>
  130. <Input
  131. value={courseTitle}
  132. onChange={(e) => setCourseTitle(e.target.value)}
  133. placeholder="输入课程主题搜索"
  134. allowClear
  135. />
  136. <Button
  137. icon={<SearchOutlined />}
  138. type="primary"
  139. onClick={async () => { await getTeacherCourseList(1) }}
  140. />
  141. </Space>
  142. </div>) :
  143. (
  144. <div>
  145. <div
  146. style={{
  147. color: 'black',
  148. fontSize: 16,
  149. fontWeight: 'bold',
  150. margin: '20px 0 20px 10px',
  151. textAlign: 'left',
  152. }}
  153. >
  154. 参评教师
  155. </div>
  156. <Space direction="horizontal" style={{ padding: '0 10px' }}>
  157. <Input
  158. value={teacherName}
  159. onChange={(e) => setTeacherName(e.target.value)}
  160. placeholder="输入教师姓名搜索"
  161. allowClear
  162. />
  163. <Button
  164. icon={<SearchOutlined />}
  165. type="primary"
  166. onClick={getTeacherList}
  167. />
  168. </Space>
  169. </div>
  170. )
  171. }
  172. </div>
  173. <div className="menu-area">
  174. {
  175. isLessionPage ? (
  176. <ProList<any>
  177. className={'course-list'}
  178. pagination={false}
  179. showActions="hover"
  180. tableExtraRender={(_: any, list: any) => (
  181. <div
  182. ref={containerRef}
  183. style={{ height: 'calc(100vh - 170px)', overflow: 'auto', paddingRight: 30 }}
  184. >
  185. {list.map((item: any) => (
  186. <div
  187. key={item.id}
  188. style={{
  189. backgroundColor: '#F8FAFC',
  190. cursor: 'pointer',
  191. marginBottom: '10px'
  192. }}
  193. onClick={() => {
  194. history.push(`/play?teacherId=${item.teacherId}&courseId=${item.id}`);
  195. setActiveCourseKey(item.id);
  196. }}
  197. className={item.id === activeCourseKey ? 'course-active' : ''}
  198. >
  199. <Image
  200. width={'100%'}
  201. style={{ aspectRatio: '16/9', borderRadius: '15px 15px 0 0' }}
  202. preview={false}
  203. src={CourseImage}
  204. // src={item.fullScreenOssUrl + '?x-oss-process=video/snapshot,t_0,f_jpg,w_800,h_450'}
  205. />
  206. <div className="course-info">
  207. <div style={{ fontWeight: 'bold', fontSize: '18px' }}>{item.courseTitle}</div>
  208. <div className="info-flex">
  209. <div>{item.subjectName}</div>
  210. <div>所属年级:{item.periodName}</div>
  211. </div>
  212. <div style={{ fontSize: '11px' }}>授课时间:{item.courseDate}</div>
  213. </div>
  214. </div>
  215. ))}
  216. {loading && <div style={{ textAlign: 'center', padding: '10px' }}>加载中...</div>}
  217. {!hasMore && <Empty styles={{ image: { height: 0 } }} description="没有更多数据了" image={false} />}
  218. </div>
  219. )}
  220. metas={{
  221. actions: {},
  222. }}
  223. dataSource={courseData}
  224. />
  225. ) : (
  226. <ProList<any>
  227. className={'list-box'}
  228. pagination={false}
  229. showActions="hover"
  230. onItem={(record) => {
  231. return {
  232. onClick: () => {
  233. setActiveKey(record.teacherId);
  234. localStorage.setItem('teacherName', record.teacherName)
  235. localStorage.setItem('schoolName', record.schoolName)
  236. history.push('/record?teacherId=' + record.teacherId);
  237. },
  238. };
  239. }}
  240. rowClassName={(record: { teacherId: string }) =>
  241. record.teacherId === activeKey ? 'active' : ''
  242. }
  243. metas={{
  244. title: {
  245. dataIndex: 'teacherName',
  246. },
  247. description: {
  248. dataIndex: 'schoolName',
  249. render: (text) => <div style={{ textAlign: 'left' }}>{text}</div>,
  250. },
  251. }}
  252. dataSource={teacherData}
  253. />
  254. )
  255. }
  256. </div>
  257. </div>
  258. );
  259. }
  260. export default SideMenu;