page.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import React from 'react';
  2. import moment from 'moment';
  3. import { DatePicker, Checkbox, Modal, Form, Input, Row, Col, Button, Upload, List, Comment } from 'antd';
  4. import './index.less';
  5. import Page from '@src/containers/Page';
  6. import Block from '@src/components/Block';
  7. // import FilterLayout from '@src/layouts/FilterLayout';
  8. import ActionLayout from '@src/layouts/ActionLayout';
  9. import TableLayout from '@src/layouts/TableLayout';
  10. import { formatDate, formatSecond, formatPercent } from '@src/services/Tools';
  11. import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
  12. import { PcUrl } from '../../../../Constant';
  13. import { Course } from '../../../stores/course';
  14. import { User } from '../../../stores/user';
  15. import { System } from '../../../stores/system';
  16. export default class extends Page {
  17. init() {
  18. this.videoColumns = [{
  19. title: '课时',
  20. dataIndex: 'courseNo.no',
  21. }, {
  22. title: '课时名称',
  23. dataIndex: 'courseNo.title',
  24. }, {
  25. title: '学习记录',
  26. dataIndex: 'records',
  27. render: (text) => {
  28. return (text || []).map(row => {
  29. return <p>{formatDate(row.createTime, 'YYYY-MM-DD HH:mm:ss')} {formatSecond(row.userTime)}</p>;
  30. });
  31. },
  32. }, {
  33. title: '预习作业进度',
  34. dataIndex: 'userPaper',
  35. render: (text, record) => {
  36. return `${text.times}遍+${formatPercent(record.userReport.userNumber, record.userReport.questionNumber)}`;
  37. },
  38. }, {
  39. title: '操作',
  40. dataIndex: 'handler',
  41. render: (text, record) => {
  42. return <div className="table-button">
  43. {<a onClick={() => {
  44. User.locationUser(record.userId, `${PcUrl}/my/report`);
  45. }}>查看</a>}
  46. </div>;
  47. },
  48. }];
  49. this.vsAction = [{
  50. key: 'addAppointment',
  51. name: '添加预约',
  52. }];
  53. this.vsList = [{
  54. key: 'id',
  55. type: 'hidden',
  56. }, {
  57. key: 'title',
  58. type: 'input',
  59. name: '课程名称',
  60. }, {
  61. key: 'timerange',
  62. type: 'daterange',
  63. showTime: true,
  64. name: '上课时间',
  65. }, {
  66. key: 'cctalkChannel',
  67. type: 'input',
  68. name: '频道号',
  69. }];
  70. this.vsColumns = [{
  71. title: '课时序号',
  72. dataIndex: 'no',
  73. render: (text) => {
  74. const { data } = this.state;
  75. return `${text}/${data.number}`;
  76. },
  77. }, {
  78. title: '上课时间',
  79. dataIndex: 'time',
  80. render: (text, record) => {
  81. return <DatePicker.RangePicker showTime value={[record.startTime, record.endTime]} onChange={(value) => {
  82. this.changeAppointment(record.id, { startTime: value[0], endTime: value[1] });
  83. }} />;
  84. },
  85. }, {
  86. title: '课程名称',
  87. dataIndex: 'title',
  88. }, {
  89. title: '频道号',
  90. dataIndex: 'cctalkChannel',
  91. }, {
  92. title: '学习进度',
  93. dataIndex: 'isFinish',
  94. render: (text, record) => {
  95. return <Checkbox checked={text > 0} onChange={(e) => {
  96. this.changeAppointment(record.id, { isFinish: e.target.checked ? 1 : 0 });
  97. }} />;
  98. },
  99. }, {
  100. title: '预习作业',
  101. dataIndex: 'userPaper',
  102. render: (text, record) => {
  103. return text ? <a onClick={() => {
  104. User.locationUser(record.userId, `${PcUrl}/paper/report/${record.reportId}`);
  105. }}>查看{text.times > 0 ? '(已完成)' : ''}</a> : '';
  106. },
  107. }, {
  108. title: '笔记批阅',
  109. dataIndex: 'noteList',
  110. render: (text, record) => {
  111. return <a onClick={() => {
  112. this.noteAction(record);
  113. }}>查看</a>;
  114. },
  115. }, {
  116. title: '课后补充',
  117. dataIndex: 'supplyList',
  118. render: (text, record) => {
  119. return <a onClick={() => {
  120. this.supplyAction(record);
  121. }}>查看</a>;
  122. },
  123. }];
  124. }
  125. initData() {
  126. const { id } = this.params;
  127. let handler;
  128. if (id) {
  129. handler = Course.listStudy({ ids: [id] });
  130. }
  131. handler
  132. .then(result => {
  133. const [row] = result.list;
  134. if (!row) {
  135. asyncSMessage('记录不存在');
  136. return;
  137. }
  138. const { course } = row;
  139. this.setState({ data: row, module: course.courseModule });
  140. this.refresh();
  141. });
  142. }
  143. refresh() {
  144. // const { id } = this.params;
  145. const { module } = this.state;
  146. switch (module) {
  147. case 'video':
  148. this.refreshVideo();
  149. break;
  150. case 'online':
  151. break;
  152. case 'vs':
  153. this.refreshVs();
  154. break;
  155. default:
  156. }
  157. }
  158. refreshVideo() {
  159. const { id } = this.params;
  160. User.allCourseRecord(Object.assign({ recordId: id }, this.state.search)).then(result => {
  161. this.setTableData(result, result ? result.length : 0);
  162. });
  163. }
  164. refreshVs() {
  165. const { id } = this.params;
  166. User.listCourseAppointment(Object.assign({ recordId: id }, this.state.search)).then(result => {
  167. result.list = result.list.map(row => {
  168. row.startTime = moment(row.startTime);
  169. row.endTime = moment(row.endTime);
  170. return row;
  171. });
  172. this.setTableData(result.list, result.total);
  173. });
  174. }
  175. refreshComment(appointmentId, type) {
  176. this.setState({ comment: {} });
  177. User.allCourseAppointmentComment({ appointmentId, type })
  178. .then(result => {
  179. const commentMap = {};
  180. result.forEach((row) => {
  181. commentMap[row.id] = row;
  182. });
  183. this.setState({ commentList: result, commentMap });
  184. });
  185. }
  186. changeAppointment(id, data) {
  187. data.id = id;
  188. return User.editCourseAppointment(data).then(() => {
  189. this.refreshVs();
  190. this.cleanInfo();
  191. });
  192. }
  193. addAppointmentAction() {
  194. const { id } = this.params;
  195. asyncForm('添加', this.vsList, {}, info => {
  196. const { data } = this.state;
  197. ([info.startTime, info.endTime] = info.timerange);
  198. return User.addCourseAppointment(Object.assign({ recordId: id, userId: data.userId, courseId: data.courseId, noteList: [], supplyList: [] }, info)).then(() => {
  199. asyncSMessage('添加成功!');
  200. this.refreshVs();
  201. });
  202. }).catch(err => {
  203. console.log(err);
  204. });
  205. }
  206. noteAction(record) {
  207. this.open(record, 'note');
  208. this.refreshComment(record.id, 'note');
  209. }
  210. supplyAction(record) {
  211. this.open(record, 'supply');
  212. this.refreshComment(record.id, 'supply');
  213. }
  214. submitComment(comment) {
  215. if (comment.id) {
  216. User.editCourseAppointmentComment(comment)
  217. .then(() => {
  218. this.refreshComment(comment.appointmentId, comment.type);
  219. this.setState({ comment: {} });
  220. });
  221. } else {
  222. User.addCourseAppointmentComment(comment)
  223. .then(() => {
  224. this.refreshComment(comment.appointmentId, comment.type);
  225. this.setState({ comment: {} });
  226. });
  227. }
  228. }
  229. deleteComment(comment) {
  230. User.delCourseAppointmentComment({ id: comment }).then(() => {
  231. this.refreshComment(comment.appointmentId, comment.type);
  232. });
  233. }
  234. deleteAppointment(row) {
  235. User.delCourseAppointment({ id: row.id }).then(() => {
  236. asyncSMessage('删除成功!');
  237. this.refresh();
  238. });
  239. }
  240. changeInfo(key, data) {
  241. const info = this.state[key] || {};
  242. this.setState({ [key]: Object.assign(info, data) });
  243. }
  244. renderVs() {
  245. const { data, page, commentMap } = this.state;
  246. return <div flex>
  247. {data.number > page.total && <ActionLayout
  248. itemList={this.vsAction}
  249. selectedKeys={this.state.selectedKeys}
  250. onAction={key => this.onAction(key)}
  251. />}
  252. <TableLayout
  253. columns={this.vsColumns}
  254. list={this.state.list}
  255. pagination={this.state.page}
  256. loading={this.props.core.loading}
  257. onChange={(pagination, filters, sorter) => this.tableChange(pagination, filters, sorter)}
  258. onSelect={(keys, rows) => this.tableSelect(keys, rows)}
  259. selectedKeys={this.state.selectedKeys}
  260. />
  261. {this.state.note && <Modal visible closable title='课后笔记' footer={null} onCancel={() => {
  262. this.close(false, 'note');
  263. }}>
  264. <List
  265. className="comment-list"
  266. itemLayout="horizontal"
  267. dataSource={this.state.commentList}
  268. renderItem={item => (
  269. <Comment
  270. actions={[!!item.userId && <span onClick={() => this.changeInfo('comment', { id: null, parentId: item.id, file: null, content: null })}>回复</span>, !item.userId && <span onClick={() => this.changeInfo('comment', item)}>编辑</span>, !item.userId && <span onClick={() => this.deleteComment(item)}>删除</span>]}
  271. author={item.userId ? '学生' : '老师'}
  272. // avatar={item.userId ? '学' : '师'}
  273. content={<p>{item.reply && <span>{item.reply}</span>}{item.content}<br /> {item.file && <a href={item.file} target="_blank">{item.name || '文件'}</a>}</p>}
  274. datetime={formatDate(item.createTime, 'YYYY-MM-DD')}
  275. />
  276. )}
  277. />
  278. <Form>
  279. <Row>
  280. {this.state.comment.parentId && <Col span={24}><span>回复:{(commentMap[this.state.comment.parentId] || {}).content}</span></Col>}
  281. <Col span={12}>
  282. <Input value={(this.state.comment || {}).content || ''} onChange={e => {
  283. this.changeInfo('comment', { content: e.target.value });
  284. }} />
  285. </Col>
  286. <Col span={1}><Upload
  287. showUploadList={false}
  288. beforeUpload={(file) => System.uploadImage(file).then((result) => {
  289. this.changeInfo('comment', { file: result.url, name: file.name });
  290. return Promise.reject();
  291. })}
  292. >
  293. <Button>上传文档{(this.state.comment || {}).file ? '(已上传)' : ''}</Button>
  294. </Upload></Col>
  295. <Col span={1} offset={8}><Button type='primary' onClick={() => {
  296. if (!this.state.comment || !this.state.comment.content) return;
  297. this.state.comment.appointmentId = this.state.note.id;
  298. this.state.comment.type = 'note';
  299. this.state.comment.recordId = this.state.note.recordId;
  300. this.submitComment(this.state.comment);
  301. }}>{(this.state.comment || {}).id ? '修改' : '发布'}</Button></Col>
  302. </Row>
  303. </Form>
  304. </Modal>}
  305. {this.state.supply && <Modal visible closable title='课后补充' footer={null} onCancel={() => {
  306. this.close(false, 'supply');
  307. }}>
  308. <List
  309. className="comment-list"
  310. itemLayout="horizontal"
  311. dataSource={this.state.commentList}
  312. renderItem={item => (
  313. <Comment
  314. actions={[!!item.userId && <span onClick={() => this.changeInfo('comment', { id: null, parentId: item.id, file: null, content: null })}>回复</span>, !item.userId && <span onClick={() => this.changeInfo('comment', item)}>编辑</span>, !item.userId && <span onClick={() => this.deleteComment(item)}>删除</span>]}
  315. author={item.userId ? '学生' : '老师'}
  316. // avatar={item.userId ? '学' : '师'}
  317. content={<p>{item.reply && <span>{item.reply}</span>}{item.content}<br /> {item.file && <a href={item.file} target="_blank">{item.name || '文件'}</a>}</p>}
  318. datetime={formatDate(item.createTime, 'YYYY-MM-DD')}
  319. />
  320. )}
  321. />
  322. <Form>
  323. <Row>
  324. {this.state.comment.parentId && <Col span={24}><span>回复:{(commentMap[this.state.comment.parentId] || {}).content}</span></Col>}
  325. <Col span={12}>
  326. <Input value={(this.state.comment || {}).content || ''} onChange={e => {
  327. this.changeInfo('comment', { content: e.target.value });
  328. }} />
  329. </Col>
  330. <Col span={1}><Upload
  331. showUploadList={false}
  332. beforeUpload={(file) => System.uploadImage(file).then((result) => {
  333. this.changeInfo('comment', { file: result.url, name: file.name });
  334. return Promise.reject();
  335. })}
  336. >
  337. <Button>上传文档{(this.state.comment || {}).file ? '(已上传)' : ''}</Button>
  338. </Upload></Col>
  339. <Col span={1} offset={8}><Button type='primary' onClick={() => {
  340. if (!this.state.comment || !this.state.comment.content) return;
  341. this.state.comment.appointmentId = this.state.supply.id;
  342. this.state.comment.type = 'supply';
  343. this.state.comment.recordId = this.state.supply.recordId;
  344. this.submitComment(this.state.comment);
  345. }}>{(this.state.comment || {}).id ? '修改' : '发布'}</Button></Col>
  346. </Row>
  347. </Form>
  348. </Modal>}
  349. </div>;
  350. }
  351. renderVideo() {
  352. return <div flex>
  353. <TableLayout
  354. columns={this.videoColumns}
  355. list={this.state.list}
  356. pagination={this.state.page}
  357. loading={this.props.core.loading}
  358. onChange={(pagination, filters, sorter) => this.tableChange(pagination, filters, sorter)}
  359. onSelect={(keys, rows) => this.tableSelect(keys, rows)}
  360. selectedKeys={this.state.selectedKeys}
  361. />
  362. </div>;
  363. }
  364. renderDetail() {
  365. switch (this.state.module) {
  366. case 'online':
  367. return [];
  368. case 'vs':
  369. return [this.renderVs()];
  370. case 'video':
  371. return [this.renderVideo()];
  372. default:
  373. return <div />;
  374. }
  375. }
  376. renderView() {
  377. const { data = {} } = this.state;
  378. const { course = {} } = data;
  379. return <Block flex>
  380. <h1>{course.title}{data.vsNo > 0 ? `V${data.vsNo}` : ''}{data.number > 0 ? `(${data.number}课时)` : ''}</h1>
  381. {this.renderDetail()}
  382. </Block>;
  383. }
  384. }