page.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. import React, { Component } from 'react';
  2. import { Link } from 'react-router-dom';
  3. import './index.less';
  4. import { Icon } from 'antd';
  5. import FileUpload from '@src/components/FileUpload';
  6. import Page from '@src/containers/Page';
  7. import Assets from '@src/components/Assets';
  8. import UserLayout from '../../../layouts/User';
  9. import Button from '../../../components/Button';
  10. import ProgressText from '../../../components/ProgressText';
  11. import { Icon as GIcon } from '../../../components/Icon';
  12. import menu from '../index';
  13. import Tabs from '../../../components/Tabs';
  14. import UserTable from '../../../components/UserTable';
  15. import More from '../../../components/More';
  16. import Modal from '../../../components/Modal';
  17. import Date from '../../../components/Date';
  18. import Note from '../../../components/Note';
  19. import { My } from '../../../stores/my';
  20. export default class extends Page {
  21. initState() {
  22. return {
  23. tab: 'online',
  24. status: 'all',
  25. data: [
  26. {
  27. status: 'ing',
  28. history: [{}],
  29. list: [
  30. { status: 'ing', type: '1' },
  31. { status: 'not', type: '2' },
  32. { status: 'end', type: '3' },
  33. { status: 'not', type: '4' },
  34. { status: 'end', type: '5' },
  35. { status: 'not', type: '6' },
  36. { status: 'end', type: '7' },
  37. ],
  38. },
  39. {
  40. status: 'not',
  41. history: [{}],
  42. list: [
  43. { status: 'ing', type: '1' },
  44. { status: 'not', type: '2' },
  45. { status: 'end', type: '3' },
  46. { status: 'not', type: '4' },
  47. { status: 'end', type: '5' },
  48. { status: 'not', type: '6' },
  49. { status: 'end', type: '7' },
  50. ],
  51. },
  52. { status: 'end', history: [{}], list: [] },
  53. ],
  54. };
  55. }
  56. initData() {
  57. const data = Object.assign(this.state, this.state.search);
  58. data.filterMap = this.state.search;
  59. if (data.order) {
  60. data.sortMap = { [data.order]: data.direction };
  61. }
  62. this.setState(data);
  63. let isUsed = null;
  64. let isEnd = null;
  65. switch (data.status) {
  66. case 'learning':
  67. isUsed = true;
  68. isEnd = false;
  69. break;
  70. case 'end':
  71. isUsed = true;
  72. isEnd = true;
  73. break;
  74. case 'all':
  75. default:
  76. }
  77. My.listCourse(Object.assign({ courseModule: data.tab }, this.state.search, { isUsed, isEnd })).then(result => {
  78. this.setState({ list: result.list, total: result.total, page: data.page });
  79. });
  80. }
  81. onAction() {}
  82. onTabChange(tab) {
  83. const data = { tab };
  84. this.refreshQuery(data);
  85. }
  86. onStatusChange(status) {
  87. this.search({ status });
  88. }
  89. refresh(tab1, tab2) {
  90. this.setState({ tab1, tab2 });
  91. }
  92. renderView() {
  93. const { config } = this.props;
  94. return <UserLayout active={config.key} menu={menu} center={this.renderDetail()} />;
  95. }
  96. renderDetail() {
  97. const { tab, status, showTime, showRestore, showUploadNote = true, showNote } = this.state;
  98. return (
  99. <div className="detail-layout">
  100. <div className="tip">
  101. <div className="text">理清备考思路,避开常见误区,让学习的每一分钟发光发热!</div>
  102. <Link to="">去填写 ></Link>
  103. <Icon type="close-circle" theme="filled" />
  104. </div>
  105. <Tabs
  106. type="division"
  107. theme="theme"
  108. size="small"
  109. space={2.5}
  110. width={100}
  111. active={tab}
  112. tabs={[{ key: 'online', title: '在线课程' }, { key: 'vs', title: '1V1私教' }]}
  113. onChange={key => this.onTabChange(key)}
  114. />
  115. <Tabs
  116. type="tag"
  117. theme="white"
  118. size="small"
  119. space={5}
  120. width={54}
  121. active={status}
  122. tabs={[
  123. { key: 'all', title: '全部' },
  124. { key: 'nouse', title: '未开通' },
  125. { key: 'learning', title: '学习中' },
  126. { key: 'end', title: '已结束' },
  127. ]}
  128. onChange={key => this.onStatusChange(key)}
  129. />
  130. {this[`renderTab${tab}`]()}
  131. <Modal show={showTime} className="clock-modal" title="打卡表" width={460} onClose={() => {}}>
  132. <div>
  133. <div className="d-i-b w-3">
  134. <div className="t-2 t-s-14">听课频率</div>
  135. <div className="t-4 t-s-18">2天/课时</div>
  136. </div>
  137. <div className="d-i-b w-3">
  138. <div className="t-2 t-s-14">作业完成度</div>
  139. <div className="t-4 t-s-18">50%</div>
  140. </div>
  141. </div>
  142. <div className="b-b m-t-1 m-b-1" />
  143. <div className="m-b-1">
  144. <div className="tag d-i-b t-s-16">
  145. <i style={{ background: '#6865FD' }} />
  146. 听课
  147. </div>
  148. <div className="tag d-i-b t-s-16">
  149. <i style={{ background: '#4299FF' }} />
  150. 预习
  151. </div>
  152. <div className="tag d-i-b t-s-16">
  153. <i style={{ background: '#E7E7E7' }} />
  154. 停课
  155. </div>
  156. </div>
  157. <Date
  158. hideInput
  159. disabledDate={() => false}
  160. dateRender={current => {
  161. return (
  162. <div className="ant-calendar-date">
  163. {current.get('date')}
  164. <i className="s1" style={{ background: '#6865FD' }} />
  165. <i className="s2" style={{ background: '#4299FF' }} />
  166. </div>
  167. );
  168. }}
  169. checkRefresh={(date, refresh) => {
  170. console.log(date);
  171. setTimeout(() => {
  172. refresh();
  173. }, 2000);
  174. return true;
  175. }}
  176. />
  177. <div className="t-2 t-s-12">*听课频率≤2天/课时,作业完成度≥90%,课程有效期可延长7-10天。</div>
  178. </Modal>
  179. <Modal show={showRestore} title="恢复学习" onConfirm={() => {}} onCancel={() => {}}>
  180. <div className="t-2 t-s-18">恢复学习后,本课程的停课功能将无法使用。是否恢复学习?</div>
  181. </Modal>
  182. <Modal
  183. show={showUploadNote}
  184. title="上传笔记"
  185. width={630}
  186. confirmText="提交"
  187. onConfirm={() => {}}
  188. onCancel={() => {}}
  189. >
  190. <textarea
  191. className="b-c-1 w-10 p-10 m-b-1"
  192. rows={6}
  193. placeholder="请输入留言内容,也可将文件拖动到这里上传。"
  194. />
  195. <div className={`t-c drag-upload ${this.state.draging ? 'draging' : ''}`}>
  196. <Button theme="file">上传文件</Button>
  197. <span className="m-l-1 t-3 t-s-14">支持 docx, xls, PDF, rar, zip, PNG, JPG 等类型的文件</span>
  198. <div className="fixed">
  199. <span className="f-w-b t-s-18">放开文件立刻上传</span>
  200. </div>
  201. <FileUpload
  202. type="none"
  203. onDragEnter={() => this.setState({ draging: true })}
  204. onDragLeave={() => this.setState({ draging: false })}
  205. />
  206. </div>
  207. <div className="b-b m-t-2" />
  208. </Modal>
  209. <Modal show={showNote} title="课后笔记" width={500} height={500} onClose={() => {}}>
  210. <Note />
  211. <Note />
  212. <Note />
  213. <Note />
  214. </Modal>
  215. </div>
  216. );
  217. }
  218. renderTabonline() {
  219. const { list = [] } = this.state;
  220. return list.map(item => {
  221. return <Course data={item} />;
  222. });
  223. }
  224. renderTabvs() {
  225. const { list = [] } = this.state;
  226. return list.map(item => {
  227. return <Education data={item} />;
  228. });
  229. }
  230. }
  231. class Course extends Component {
  232. constructor(props) {
  233. super(props);
  234. this.columns = [
  235. { title: '学习内容' },
  236. { title: '预习作业' },
  237. { title: '进度' },
  238. { title: '最近学习' },
  239. { title: '笔记' },
  240. { title: '问答' },
  241. ];
  242. this.state = { open: false };
  243. }
  244. render() {
  245. const { data = {} } = this.props;
  246. switch (data.status) {
  247. case 'ing':
  248. return this.renderIng();
  249. case 'not':
  250. return this.renderNot();
  251. case 'end':
  252. return this.renderEnd();
  253. default:
  254. return <div />;
  255. }
  256. }
  257. renderIng() {
  258. const { data } = this.props;
  259. const { history = [] } = data;
  260. return (
  261. <div className="course-item ing">
  262. <div className="title">
  263. <div className="tag">学习中</div>
  264. <div className="text">OG20整合刷题-语法SC</div>
  265. </div>
  266. <div className="continue">
  267. <Assets name="notice" />
  268. 继续学习:课时 13:解读句子属性
  269. </div>
  270. <div className="detail">
  271. <div className="left">
  272. <Assets name="sun_blue" />
  273. <div className="info">
  274. <div className="t1">授课老师</div>
  275. <div className="t2">李小小</div>
  276. <div className="t1">有效期</div>
  277. <div className="t2">88Day</div>
  278. </div>
  279. </div>
  280. <div className="right">
  281. <div className="item">
  282. <GIcon name="speed-block" noHover />
  283. <div className="text">
  284. <span>12</span>/20
  285. </div>
  286. </div>
  287. <div className="item">
  288. <GIcon name="question-block" active noHover />
  289. <div className="text">
  290. <span>12</span>/20
  291. </div>
  292. </div>
  293. <div className="item">
  294. <GIcon name="clockin-block" noHover />
  295. <div className="text">
  296. <span>12</span>/20
  297. </div>
  298. </div>
  299. <div className="item">
  300. <GIcon name="note-block" noHover />
  301. <div className="text">
  302. <span>12</span>/20
  303. </div>
  304. </div>
  305. </div>
  306. </div>
  307. <UserTable size="small" columns={this.columns} data={history} />
  308. </div>
  309. );
  310. }
  311. renderNot() {
  312. return (
  313. <div className="course-item not">
  314. <div className="title">
  315. <div className="tag">未开通</div>
  316. <div className="text">OG20整合刷题-语法SC</div>
  317. </div>
  318. <div className="detail">
  319. <div className="left">
  320. <Assets name="sun_blue" />
  321. <div className="info">
  322. <div className="t1">授课老师</div>
  323. <div className="t2">李小小</div>
  324. <div className="t1">有效期</div>
  325. <div className="t2">88Day</div>
  326. </div>
  327. </div>
  328. <div className="right t-c">
  329. <div className="text">请于 2020-06-26 前开通</div>
  330. <Button size="lager" radius>
  331. 立即开通
  332. </Button>
  333. </div>
  334. </div>
  335. </div>
  336. );
  337. }
  338. renderEnd() {
  339. return (
  340. <div className="course-item end">
  341. <div className="title">
  342. <div className="tag">已结束</div>
  343. <div className="text">OG20整合刷题-语法SC</div>
  344. </div>
  345. <div className="detail">
  346. <div className="left">
  347. <Assets name="sun_blue" />
  348. <div className="info">
  349. <div className="t1">授课老师</div>
  350. <div className="t2">李小小</div>
  351. <div className="t1">有效期</div>
  352. <div className="t2">88Day</div>
  353. </div>
  354. </div>
  355. <div className="right">
  356. <div className="item">
  357. <GIcon name="gift-block" active />
  358. <div className="text">赠送7天</div>
  359. </div>
  360. </div>
  361. </div>
  362. </div>
  363. );
  364. }
  365. }
  366. class Education extends Component {
  367. constructor(props) {
  368. super(props);
  369. this.columns = [
  370. { title: '学习内容' },
  371. { title: '预习作业' },
  372. { title: '授课时间' },
  373. { title: '课后笔记' },
  374. { title: '课后补充' },
  375. ];
  376. this.state = { open: true, tab: '1' };
  377. }
  378. render() {
  379. const { data = {} } = this.props;
  380. switch (data.status) {
  381. case 'ing':
  382. return this.renderIng();
  383. case 'not':
  384. return this.renderNot();
  385. case 'end':
  386. return this.renderEnd();
  387. default:
  388. return <div />;
  389. }
  390. }
  391. renderIng() {
  392. const { tab } = this.state;
  393. return (
  394. <div className="education-item ing">
  395. <div className="title">
  396. <div className="tag">学习中</div>
  397. <div className="text">OG20整合刷题-语法SC</div>
  398. <div className="right">
  399. <More menu={[{ label: '评价', key: '1' }, { label: '停课', key: '2' }]} />
  400. </div>
  401. </div>
  402. <div className="continue">
  403. <Assets name="notice" />
  404. 继续学习:课时 13:解读句子属性
  405. </div>
  406. <div className="detail">
  407. <div className="left">
  408. <Assets name="sun_blue" />
  409. <div className="info">
  410. <div className="t1">授课老师</div>
  411. <div className="t2">李小小</div>
  412. <div className="t1">有效期</div>
  413. <div className="t2">88Day</div>
  414. </div>
  415. </div>
  416. <div className="right">
  417. <div className="item">
  418. <GIcon name="speed-block" noHover />
  419. <div className="text">
  420. <span>12</span>/20
  421. </div>
  422. </div>
  423. <div className="item">
  424. <GIcon name="question-block" active noHover />
  425. <div className="text">
  426. <span>12</span>/20
  427. </div>
  428. </div>
  429. </div>
  430. </div>
  431. <Tabs
  432. className="t-l"
  433. type="line"
  434. theme="theme"
  435. size="small"
  436. width={80}
  437. active={tab}
  438. tabs={[{ key: '1', title: '在线课程' }, { key: '2', title: '1V1私教' }]}
  439. onChange={key => this.setState({ tab: key })}
  440. />
  441. <div className="class-hour">
  442. <div className="text">课时 5:解读句子属性</div>
  443. <div className="right">
  444. <GIcon name="prev" onClick={() => {}} />
  445. <span>上一课时</span>
  446. <span>下一课时</span>
  447. <GIcon name="next" onClick={() => {}} />
  448. </div>
  449. </div>
  450. {tab === '1' ? this.renderTimeLine() : this.renderTable()}
  451. </div>
  452. );
  453. }
  454. renderNot() {
  455. return (
  456. <div className="education-item not">
  457. <div className="title">
  458. <div className="tag">未开通</div>
  459. <div className="text">OG20整合刷题-语法SC</div>
  460. </div>
  461. <div className="detail">
  462. <div className="left">
  463. <Assets name="sun_blue" />
  464. <div className="info">
  465. <div className="t1">授课老师</div>
  466. <div className="t2">李小小</div>
  467. <div className="t1">有效期</div>
  468. <div className="t2">88Day</div>
  469. </div>
  470. </div>
  471. <div className="right">
  472. <div className="qr-code">
  473. <Assets name="qrcode" />
  474. <div className="i">
  475. <div className="t1">请尽快与老师预约上课时间</div>
  476. <div className="t2">请于 2019-07-25 前开通课程</div>
  477. </div>
  478. </div>
  479. </div>
  480. </div>
  481. {this.renderTimeLine()}
  482. </div>
  483. );
  484. }
  485. renderEnd() {
  486. const { open } = this.state;
  487. return (
  488. <div className="education-item end">
  489. <div className="title">
  490. <div className="tag">已结课</div>
  491. <div className="text">OG20整合刷题-语法SC</div>
  492. </div>
  493. <div className="detail">
  494. <div className="left">
  495. <Assets name="sun_blue" />
  496. <div className="info">
  497. <div className="t1">授课老师</div>
  498. <div className="t2">李小小</div>
  499. <div className="t1">有效期</div>
  500. <div className="t2">88Day</div>
  501. </div>
  502. </div>
  503. <div className="right">
  504. <div className="item">
  505. <GIcon name="speed-block" noHover />
  506. <div className="text">
  507. <span>12</span>/20
  508. </div>
  509. </div>
  510. <div className="item">
  511. <GIcon name="question-block" active noHover />
  512. <div className="text">
  513. <span>12</span>/20
  514. </div>
  515. </div>
  516. </div>
  517. <div className="open">
  518. <GIcon name={open ? 'up' : 'down'} onClick={() => this.setState({ open: !open })} />
  519. </div>
  520. </div>
  521. {open && this.renderTable()}
  522. </div>
  523. );
  524. }
  525. renderTimeLine() {
  526. const { data = {} } = this.props;
  527. const { list = [] } = data;
  528. return (
  529. <div className="time-line">
  530. {list.map(item => {
  531. return <TimeLineItem data={item} />;
  532. })}
  533. </div>
  534. );
  535. }
  536. renderTable() {
  537. const { data = {} } = this.props;
  538. const { history = [] } = data;
  539. return <UserTable size="small" columns={this.columns} data={history} />;
  540. }
  541. }
  542. class TimeLineItem extends Component {
  543. constructor(props) {
  544. super(props);
  545. this.titleMap = {
  546. 1: '预约时间',
  547. 2: '答疑文档',
  548. 3: '上课',
  549. 4: '课后笔记',
  550. 5: '课后补充',
  551. 6: '备考信息',
  552. 7: '完成作业',
  553. };
  554. this.iconMap = {
  555. 1: 'time-icon',
  556. 2: 'QA-icon',
  557. 3: 'class-icon',
  558. 4: 'note-icon',
  559. 5: 'supplement-icon',
  560. 6: 'information-icon',
  561. 7: 'homework-icon',
  562. };
  563. }
  564. onClick(key) {
  565. const { data = {}, onClick } = this.props;
  566. const { status } = data;
  567. if (status === 'not') return;
  568. if (onClick) onClick(key);
  569. }
  570. render() {
  571. const { data = {} } = this.props;
  572. const { status, type } = data;
  573. return (
  574. <div className={`time-line-item ${status}`}>
  575. <div className="icon-title">
  576. <GIcon name={this.iconMap[type]} active={status !== 'not'} noHover />
  577. <div className="title">{this.titleMap[type]}</div>
  578. </div>
  579. <div className="time-line-detail">{this.renderDetail()}</div>
  580. </div>
  581. );
  582. }
  583. renderDetail() {
  584. const { data = {} } = this.props;
  585. const { type, status } = data;
  586. switch (type) {
  587. case '1':
  588. return status === 'not' ? (
  589. <span>请尽快与老师预约上课时间,扫码加微信</span>
  590. ) : (
  591. <span>2019-07-24 09:00 ~ 10:00</span>
  592. );
  593. case '2':
  594. return <span className="link">点此上传</span>;
  595. case '3':
  596. return status === 'end' ? (
  597. <span>
  598. CCtalk 频道号 :1234567 <span className="link">CC talk使用手册</span>
  599. </span>
  600. ) : (
  601. <input placeholder="请输入CCtalk用户名查看授课频道" />
  602. );
  603. case '4':
  604. return (
  605. <div>
  606. <div>
  607. <span className="link">点此上传</span>
  608. </div>
  609. <div className="note-list">
  610. <Note onAction={() => {}} actionList={[{ key: '123', label: '123' }]} />
  611. <Note />
  612. <Note />
  613. <Note />
  614. </div>
  615. </div>
  616. );
  617. case '5':
  618. return <span className="link">写留言</span>;
  619. case '6':
  620. return [
  621. <div>
  622. <span>基本情况梳理</span>
  623. <span>2019-08-20 更新</span>
  624. <span className="link">修改</span>
  625. </div>,
  626. <div>
  627. <span>备考细节梳理</span>
  628. <span className="link">填写</span>
  629. </div>,
  630. ];
  631. case '7':
  632. return <ProgressText progress={10} size="small" />;
  633. default:
  634. return <div />;
  635. }
  636. }
  637. }