1
0

page.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. import React from 'react';
  2. import { Link } from 'react-router-dom';
  3. import moment from 'moment';
  4. import { Form, Input, Button, Row, Col, InputNumber, Upload, DatePicker } from 'antd';
  5. import './index.less';
  6. import Editor from '@src/components/Editor';
  7. import Page from '@src/containers/Page';
  8. import Block from '@src/components/Block';
  9. import TreeSelect from '@src/components/TreeSelect';
  10. import EditTableCell from '@src/components/EditTableCell';
  11. import Radio from '@src/components/Radio';
  12. import ActionLayout from '@src/layouts/ActionLayout';
  13. import TableLayout from '@src/layouts/TableLayout';
  14. // import FileUpload from '@src/components/FileUpload';
  15. import { formatFormError, formatSeconds, formatTreeData, getMap } from '@src/services/Tools';
  16. import { asyncSMessage } from '@src/services/AsyncTools';
  17. import { CrowdList, CourseStatus } from '../../../../Constant';
  18. import { Course } from '../../../stores/course';
  19. import { System } from '../../../stores/system';
  20. import { Exercise } from '../../../stores/exercise';
  21. const CourseStatusMap = getMap(CourseStatus, 'value', 'label');
  22. export default class extends Page {
  23. init() {
  24. this.exerciseMap = {};
  25. this.actionList = [{
  26. key: 'add',
  27. type: 'primary',
  28. name: '上传视频',
  29. render: (item) => {
  30. return <Upload
  31. showUploadList={false}
  32. beforeUpload={(file) => System.uploadVideo(file).then((result) => {
  33. return Course.addNo({ courseId: this.params.id, resource: result.url, time: result.time });
  34. }).then(() => {
  35. this.refreshNo();
  36. })}
  37. >
  38. <Button>{item.name}</Button>
  39. </Upload>;
  40. },
  41. }];
  42. this.noColumns = [{
  43. title: '课程序号',
  44. dataIndex: 'no',
  45. }, {
  46. title: '名称',
  47. dataIndex: 'title',
  48. render: (text, record) => {
  49. return <EditTableCell value={text} onChange={(v) => {
  50. this.changeNo('title', record.id, v);
  51. }} />;
  52. },
  53. }, {
  54. title: '时长',
  55. dataIndex: 'time',
  56. render: (text) => {
  57. return formatSeconds(text);
  58. },
  59. }, {
  60. title: '视频地址',
  61. dataIndex: 'resource',
  62. render: (text) => {
  63. return <a href={text} target='_blank'>访问</a>;
  64. },
  65. }, {
  66. title: '试用区间',
  67. dataIndex: 'originContent',
  68. render: (text, record) => {
  69. return <EditTableCell value={text} onChange={(v) => {
  70. this.changeNo('originContent', record.id, v);
  71. }} />;
  72. },
  73. }, {
  74. title: '操作',
  75. dataIndex: 'handler',
  76. render: (text, record) => {
  77. const { total } = this.state;
  78. return <div className="table-button">
  79. {record.no > 1 && (
  80. <a onClick={() => {
  81. this.changeNo('no', record.id, record.no - 1);
  82. }}>上移</a>
  83. )}
  84. {record.no <= total && (
  85. <a onClick={() => {
  86. this.changeNo('no', record.id, record.no + 1);
  87. }}>下移</a>
  88. )}
  89. {(
  90. <a onClick={() => {
  91. this.delNo(record.id);
  92. }}>删除</a>
  93. )}
  94. </div>;
  95. },
  96. }];
  97. this.timeColumns = [{
  98. title: '时间段',
  99. dataIndex: 'time',
  100. render: (text, record) => {
  101. return <DatePicker.RangePicker value={[record.startTime, record.endTime]} onChange={(value) => {
  102. this.changeTime(record.id, { startTime: value[0], endTime: value[1] });
  103. }} />;
  104. },
  105. }, {
  106. title: '学员数量',
  107. dataIndex: 'studentNumber',
  108. render: (text, record) => {
  109. return <Link to={`/course/student/${this.params.id}?timeId=${record.id}`}>{text || 0}</Link>;
  110. },
  111. }, {
  112. title: '状态',
  113. dataIndex: 'status',
  114. render: (text) => {
  115. return CourseStatusMap[text] || text;
  116. },
  117. }];
  118. Exercise.courseStruct().then((result) => {
  119. const list = result.map(row => { row.title = `${row.titleZh}`; row.value = row.id; return row; });
  120. const tree = formatTreeData(list, 'id', 'title', 'parentId');
  121. this.exerciseMap = getMap(result.map(row => {
  122. row.title = `${row.titleZh}`;
  123. row.value = row.id;
  124. return row;
  125. }), 'id');
  126. this.setState({ exercise: tree });
  127. });
  128. }
  129. initData() {
  130. const { id } = this.params;
  131. const { module } = this.state.search;
  132. let handler;
  133. if (id) {
  134. handler = Course.get({ id });
  135. } else {
  136. handler = Promise.resolve({ structId: 0, courseModule: module });
  137. }
  138. handler
  139. .then(result => {
  140. this.setState({ module: result.courseModule });
  141. const { form } = this.props;
  142. result.structId = `${result.structId}`;
  143. form.setFieldsValue(result);
  144. if (id) {
  145. switch (result.courseModule) {
  146. case 'video':
  147. this.refreshNo();
  148. break;
  149. case 'online':
  150. this.refreshTime();
  151. break;
  152. default:
  153. }
  154. }
  155. this.setState({ data: result });
  156. });
  157. }
  158. refreshNo() {
  159. const { id } = this.params;
  160. Course.allNo({ courseId: id }).then(result => {
  161. this.setState({ list: result, total: result.length, no: true });
  162. });
  163. }
  164. changeNo(field, id, value) {
  165. Course.editNo({ id, [field]: value }).then(() => {
  166. this.refreshNo();
  167. });
  168. }
  169. delNo(id) {
  170. Course.delNo({ id }).then(() => {
  171. this.refreshNo();
  172. });
  173. }
  174. refreshTime() {
  175. const { id } = this.params;
  176. Course.listTime({ courseId: id }).then(result => {
  177. result.list = result.list.map(row => {
  178. row.startTime = moment(row.startTime);
  179. row.endTime = moment(row.endTime);
  180. return row;
  181. });
  182. this.setState({ list: result.list, total: result.total, time: true });
  183. });
  184. }
  185. changeTime(id, data) {
  186. data.id = id;
  187. Course.editTime(data).then(() => {
  188. this.refreshTime();
  189. });
  190. }
  191. submit() {
  192. const { form } = this.props;
  193. const { module } = this.state;
  194. form.validateFields((err) => {
  195. if (!err) {
  196. const data = form.getFieldsValue();
  197. data.parentStructId = this.exerciseMap[data.structId] ? this.exerciseMap[data.structId].parentId : 0;
  198. data.extend = this.exerciseMap[data.structId] ? this.exerciseMap[data.structId].extend : '';
  199. let handler;
  200. if (data.id) {
  201. handler = Course.edit(data);
  202. } else {
  203. data.courseModule = module;
  204. handler = Course.add(data);
  205. }
  206. handler.then((result) => {
  207. asyncSMessage('保存成功');
  208. if (data.id) {
  209. linkTo(`/course/detail/${data.id}`);
  210. } else {
  211. linkTo(`/course/detail/${result.id}`);
  212. }
  213. }).catch((e) => {
  214. if (e.result) form.setFields(formatFormError(data, e.result));
  215. });
  216. }
  217. });
  218. }
  219. renderVideo() {
  220. const { exercise, data } = this.state;
  221. const { getFieldDecorator } = this.props.form;
  222. return <Block>
  223. <Form>
  224. {getFieldDecorator('id')(<input hidden />)}
  225. {exercise && data.structId && <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='学科'>
  226. {getFieldDecorator('structId', {
  227. rules: [{
  228. required: true, message: '请选择学科',
  229. }],
  230. initialValue: data.structId,
  231. })(
  232. <TreeSelect treeData={exercise} />,
  233. )}
  234. </Form.Item>}
  235. <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='适合人群'>
  236. {getFieldDecorator('crowd', {
  237. rules: [
  238. { required: true, message: '请选择' },
  239. ],
  240. })(
  241. <Radio select={CrowdList} />,
  242. )}
  243. </Form.Item>
  244. <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='定价'>
  245. {getFieldDecorator('price', {
  246. rules: [
  247. { required: true, message: '请输入价格' },
  248. ],
  249. })(
  250. <InputNumber placeholder='请输入' />,
  251. )}
  252. </Form.Item>
  253. <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='课程名称'>
  254. {getFieldDecorator('title', {
  255. rules: [
  256. { required: true, message: '请输入课程名称' },
  257. ],
  258. })(
  259. <Input placeholder='请输入课程名称' />,
  260. )}
  261. </Form.Item>
  262. <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='教师'>
  263. {getFieldDecorator('teacher', {
  264. rules: [
  265. { required: true, message: '请输入教师' },
  266. ],
  267. })(
  268. <Input placeholder='请输入教师' />,
  269. )}
  270. </Form.Item>
  271. </Form>
  272. </Block>;
  273. }
  274. renderNo() {
  275. const { no } = this.state;
  276. if (!no) return null;
  277. return <Block>
  278. <h1>上传正式视频</h1>
  279. <ActionLayout
  280. itemList={this.actionList}
  281. selectedKeys={this.state.selectedKeys}
  282. onAction={key => this.onAction(key)}
  283. />
  284. <TableLayout
  285. columns={this.noColumns}
  286. list={this.state.list}
  287. pagination={false}
  288. loading={this.props.core.loading}
  289. onChange={(pagination, filters, sorter) => this.tableChange(pagination, filters, sorter)}
  290. onSelect={(keys, rows) => this.tableSelect(keys, rows)}
  291. selectedKeys={this.state.selectedKeys}
  292. />
  293. </Block>;
  294. }
  295. renderInfoVideo() {
  296. const { getFieldDecorator } = this.props.form;
  297. return <Block flex>
  298. <h1>课程介绍</h1>
  299. <Form>
  300. <Form.Item label='老师资历'>
  301. {getFieldDecorator('teacherContent', {
  302. })(
  303. <Editor placeholder='输入内容' />,
  304. )}
  305. </Form.Item>
  306. <Form.Item label='基本参数'>
  307. {getFieldDecorator('baseContent', {
  308. })(
  309. <Editor placeholder='输入内容' />,
  310. )}
  311. </Form.Item>
  312. <Form.Item label='授课内容'>
  313. {getFieldDecorator('courseContent', {
  314. })(
  315. <Editor placeholder='输入内容' />,
  316. )}
  317. </Form.Item>
  318. <Form.Item label='授课重点'>
  319. {getFieldDecorator('pointContent', {
  320. })(
  321. <Editor placeholder='输入内容' />,
  322. )}
  323. </Form.Item>
  324. <Form.Item label='适合人群'>
  325. {getFieldDecorator('crowdContent', {
  326. })(
  327. <Editor placeholder='输入内容' />,
  328. )}
  329. </Form.Item>
  330. </Form>
  331. </Block>;
  332. }
  333. renderSyllabus() {
  334. const { getFieldDecorator } = this.props.form;
  335. return <Block flex>
  336. <Form>
  337. <Form.Item label='授课大纲'>
  338. {getFieldDecorator('syllabusContent', {
  339. })(
  340. <Editor placeholder='输入内容' />,
  341. )}
  342. </Form.Item>
  343. </Form>
  344. </Block>;
  345. }
  346. renderPromote() {
  347. const { getFieldDecorator } = this.props.form;
  348. return <Block flex>
  349. <Form>
  350. <Form.Item label='优惠信息'>
  351. {getFieldDecorator('promoteContent', {
  352. })(
  353. <Editor placeholder='输入内容' />,
  354. )}
  355. </Form.Item>
  356. </Form>
  357. </Block>;
  358. }
  359. renderOnline() {
  360. const { exercise, data } = this.state;
  361. const { getFieldDecorator } = this.props.form;
  362. return <Block>
  363. <Form>
  364. {getFieldDecorator('id')(<input hidden />)}
  365. {exercise && data.structId && <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='学科'>
  366. {getFieldDecorator('structId', {
  367. rules: [{
  368. required: true, message: '请选择学科',
  369. }],
  370. initialValue: data.structId,
  371. })(
  372. <TreeSelect treeData={exercise} />,
  373. )}
  374. </Form.Item>}
  375. <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='课程名称'>
  376. {getFieldDecorator('title', {
  377. rules: [
  378. { required: true, message: '请输入课程名称' },
  379. ],
  380. })(
  381. <Input placeholder='请输入课程名称' />,
  382. )}
  383. </Form.Item>
  384. </Form>
  385. </Block>;
  386. }
  387. renderTime() {
  388. const { time, timerange } = this.state;
  389. if (!time) return null;
  390. return <Block>
  391. <h1>课时管理</h1>
  392. <TableLayout
  393. columns={this.timeColumns}
  394. list={this.state.list}
  395. pagination={false}
  396. loading={this.props.core.loading}
  397. onChange={(pagination, filters, sorter) => this.tableChange(pagination, filters, sorter)}
  398. onSelect={(keys, rows) => this.tableSelect(keys, rows)}
  399. selectedKeys={this.state.selectedKeys}
  400. />
  401. <Row>
  402. <Col span={8}>
  403. <Form.Item>
  404. <DatePicker.RangePicker value={timerange} onChange={(value) => {
  405. this.setState({ timerange: value });
  406. }} />
  407. </Form.Item>
  408. </Col>
  409. <Col span={1}>
  410. <Form.Item>
  411. <Button onClick={() => {
  412. Course.addTime({ courseId: this.params.id, startTime: timerange[0], endTime: timerange[1] }).then(() => {
  413. this.refreshTime();
  414. this.setState({ timerange: null });
  415. });
  416. }}>添加</Button>
  417. </Form.Item>
  418. </Col>
  419. </Row>
  420. </Block>;
  421. }
  422. renderContent() {
  423. switch (this.state.module) {
  424. case 'online':
  425. return [this.renderOnline(), this.renderTime()];
  426. case 'video':
  427. return [this.renderVideo(), this.renderNo(), this.renderInfoVideo(), this.renderSyllabus(), this.renderPromote()];
  428. default:
  429. return <div />;
  430. }
  431. }
  432. renderView() {
  433. return <div flex>
  434. {this.renderContent()}
  435. <Row type="flex" justify="center">
  436. <Col>
  437. <Button type="primary" onClick={() => {
  438. this.submit();
  439. }}>保存</Button>
  440. </Col>
  441. </Row>
  442. </div>;
  443. }
  444. }