index.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584
  1. import React, { Component } from 'react';
  2. import Cropper from 'react-cropper';
  3. import 'cropperjs/dist/cropper.css';
  4. import './index.less';
  5. import { Checkbox, Icon } from 'antd';
  6. import FileUpload from '@src/components/FileUpload';
  7. import Assets from '@src/components/Assets';
  8. import scale from '@src/services/Scale';
  9. import { asyncSMessage } from '@src/services/AsyncTools';
  10. import AnswerButton from '../AnswerButton';
  11. import { SelectInput, VerificationInput, DefaultInput, Input, Textarea } from '../Input';
  12. import { MobileArea, TextbookFeedbackTarget, TextbookSubject, AskTarget, QrCode } from '../../../Constant';
  13. import Invite from '../Invite';
  14. import Modal from '../Modal';
  15. import { Common } from '../../stores/common';
  16. import { User } from '../../stores/user';
  17. import { My } from '../../stores/my';
  18. import Select from '../Select';
  19. import { formatDate, getMap, checkMobile, checkEmail } from '../../../../src/services/Tools';
  20. const TextbookFeedbackTargetMap = getMap(TextbookFeedbackTarget, 'value', 'tip');
  21. export class BindPhone extends Component {
  22. constructor(props) {
  23. super(props);
  24. this.validNumber = 0;
  25. this.props.data = this.props.data || {};
  26. this.state = Object.assign({ step: 0, data: {}, empty: {} }, this.initState(this.props));
  27. this.stepProp = {
  28. 0: {
  29. title: '绑定手机',
  30. onConfirm: props.onConfirm,
  31. },
  32. 1: {
  33. title: '绑定手机',
  34. onConfirm: () => {
  35. this.submit();
  36. },
  37. onCancel: props.onCancel,
  38. confirmText: '提交',
  39. },
  40. };
  41. }
  42. initState(props) {
  43. if (!props.show || this.props.show) return {};
  44. const data = Object.assign({}, props.data);
  45. if (!data.area) data.area = MobileArea[0].value;
  46. return { step: props.data.mobile ? 0 : 1, data, validError: '', empty: {} };
  47. }
  48. componentWillReceiveProps(nextProps) {
  49. this.setState(this.initState(nextProps));
  50. }
  51. onNext() {
  52. this.setState({ step: 1, validError: '', mobileError: '', empty: {} });
  53. }
  54. changeData(field, value) {
  55. let { data, empty } = this.state;
  56. data = data || {};
  57. empty = empty || {};
  58. data[field] = value;
  59. if (value) empty[field] = !value;
  60. this.setState({ data, empty, mobileError: null });
  61. }
  62. validMobile() {
  63. const { data } = this.state;
  64. const { area, mobile } = data;
  65. if (!area || !mobile) return;
  66. if (!checkMobile(mobile)) {
  67. this.setState({ mobileError: '请输入正确手机号' });
  68. return;
  69. }
  70. this.validNumber += 1;
  71. const number = this.validNumber;
  72. if (mobile === this.props.data.mobile) {
  73. this.setState({ mobileError: '' });
  74. return;
  75. }
  76. User.validMobile(area, mobile)
  77. .then(result => {
  78. if (number !== this.validNumber) return Promise.resolve();
  79. if (result) {
  80. this.setState({ mobileError: '' });
  81. return Promise.resolve();
  82. }
  83. return Promise.reject(new Error('该手机已绑定其他账号,请更换手机号码'));
  84. })
  85. .catch(err => {
  86. this.setState({ mobileError: err.message });
  87. });
  88. }
  89. sendValid() {
  90. const { data, mobileError } = this.state;
  91. const { area, mobile } = data;
  92. if (mobileError) return Promise.reject();
  93. if (!area || !mobile) {
  94. this.setState({ empty: { area: !data.area, mobile: !data.mobile } });
  95. return Promise.reject();
  96. }
  97. return Common.sendSms(area, mobile)
  98. .then(result => {
  99. if (result) {
  100. asyncSMessage('发送成功');
  101. this.setState({ mobileError: '', validError: '' });
  102. } else {
  103. throw new Error('发送失败');
  104. }
  105. })
  106. .catch(err => {
  107. this.setState({ mobileError: err.message });
  108. throw err;
  109. });
  110. }
  111. submit() {
  112. const { data, validError, mobileError } = this.state;
  113. if (validError || mobileError) return;
  114. const { area, mobile, mobileVerifyCode } = data;
  115. if (!area || !mobile || !mobileVerifyCode) {
  116. this.setState({ empty: { area: !data.area, mobile: !data.mobile, mobileVerifyCode: !mobileVerifyCode } });
  117. return;
  118. }
  119. My.bindMobile(area, mobile, mobileVerifyCode)
  120. .then(() => {
  121. asyncSMessage('操作成功');
  122. this.setState({ step: 0, validError: '', mobileError: '' });
  123. User.infoHandle(Object.assign(this.props.data, { area, mobile }));
  124. this.props.onConfirm();
  125. })
  126. .catch(err => {
  127. if (err.message.indexOf('验证码') >= 0) {
  128. this.setState({ validError: err.message });
  129. } else {
  130. this.setState({ mobileError: err.message });
  131. }
  132. });
  133. }
  134. render() {
  135. const { show } = this.props;
  136. const { step = 0 } = this.state;
  137. return (
  138. <Modal className="bind-phone-modal" show={show} width={630} {...this.stepProp[step]}>
  139. <div className="bind-phone-modal-wrapper">{this[`renderStep${step}`]()}</div>
  140. </Modal>
  141. );
  142. }
  143. renderStep0() {
  144. const { data } = this.state;
  145. return (
  146. <div className="step-0-layout t-2">
  147. 已绑定手机 {data.mobile}
  148. <a onClick={() => this.onNext()}>修改</a>
  149. </div>
  150. );
  151. }
  152. renderStep1() {
  153. return (
  154. <div className="step-1-layout">
  155. <div className="label">手机号</div>
  156. <div className="input-layout">
  157. <SelectInput
  158. className="w-10"
  159. placeholder="请输入手机号"
  160. selectValue={this.state.data.area}
  161. select={MobileArea}
  162. value={this.state.data.mobile}
  163. error={this.state.mobileError}
  164. empty={this.state.empty.mobile}
  165. onSelect={value => {
  166. this.changeData('area', value);
  167. this.validMobile();
  168. }}
  169. onChange={e => {
  170. this.changeData('mobile', e.target.value);
  171. this.validMobile();
  172. }}
  173. />
  174. <VerificationInput
  175. className="w-10"
  176. placeholder="请输入验证码"
  177. value={this.state.data.mobileVerifyCode}
  178. error={this.state.validError}
  179. empty={this.state.empty.mobileVerifyCode}
  180. onSend={() => {
  181. return this.sendValid();
  182. }}
  183. onChange={e => {
  184. this.changeData('mobileVerifyCode', e.target.value);
  185. this.setState({ validError: '' });
  186. }}
  187. />
  188. </div>
  189. </div>
  190. );
  191. }
  192. }
  193. export class BindEmail extends Component {
  194. constructor(props) {
  195. super(props);
  196. this.validNumber = 0;
  197. this.props.data = this.props.data || {};
  198. this.state = Object.assign({ step: 0, data: {}, empty: {} }, this.initState(this.props));
  199. this.stepProp = {
  200. 0: {
  201. title: '绑定邮箱',
  202. onConfirm: props.onConfirm,
  203. },
  204. 1: {
  205. title: '绑定邮箱',
  206. onConfirm: () => {
  207. this.submit();
  208. },
  209. onCancel: props.onCancel,
  210. confirmText: '提交',
  211. },
  212. };
  213. }
  214. initState(props) {
  215. if (!props.show || this.props.show) return {};
  216. return { step: props.data.email ? 0 : 1, data: Object.assign({}, props.data) };
  217. }
  218. componentWillReceiveProps(nextProps) {
  219. this.setState(this.initState(nextProps));
  220. }
  221. onNext() {
  222. this.setState({ step: 1, error: '', empty: {} });
  223. }
  224. changeData(field, value) {
  225. let { data, empty } = this.state;
  226. data = data || {};
  227. empty = empty || {};
  228. data[field] = value;
  229. if (value) empty[field] = !value;
  230. this.setState({ data, empty, error: null });
  231. }
  232. validEmail() {
  233. const { data } = this.state;
  234. const { email } = data;
  235. if (!email) return;
  236. if (!checkEmail(email)) {
  237. this.setState({ error: '请输入正确邮箱' });
  238. return;
  239. }
  240. this.validNumber += 1;
  241. const number = this.validNumber;
  242. if (email === this.props.data.email) {
  243. this.setState({ error: '' });
  244. return;
  245. }
  246. User.validEmail(email)
  247. .then(result => {
  248. if (number !== this.validNumber) return Promise.resolve();
  249. if (result) {
  250. this.setState({ error: '' });
  251. return Promise.resolve();
  252. }
  253. return Promise.reject(new Error('该邮箱已绑定其他账号,请更换邮箱地址'));
  254. })
  255. .catch(err => {
  256. this.setState({ error: err.message });
  257. });
  258. }
  259. submit() {
  260. const { data, error } = this.state;
  261. if (error) return;
  262. if (!data.email) {
  263. this.setState({ empty: { email: !data.email } });
  264. return;
  265. }
  266. const { email } = data;
  267. My.bindEmail(email)
  268. .then(() => {
  269. asyncSMessage('操作成功');
  270. this.setState({ step: 0, error: '' });
  271. User.infoHandle(Object.assign(this.props.data, { email }));
  272. this.props.onConfirm();
  273. })
  274. .catch(e => {
  275. this.setState({ error: e.message });
  276. });
  277. }
  278. render() {
  279. const { show } = this.props;
  280. const { step = 0 } = this.state;
  281. return (
  282. <Modal className="bind-email-modal" show={show} width={630} {...this.stepProp[step]}>
  283. <div className="bind-email-modal-wrapper">{this[`renderStep${step}`]()}</div>
  284. </Modal>
  285. );
  286. }
  287. renderStep0() {
  288. const { data } = this.state;
  289. return (
  290. <div className="step-0-layout t-2">
  291. 已绑定邮箱 {data.email}
  292. <a onClick={() => this.onNext()}>修改</a>
  293. </div>
  294. );
  295. }
  296. renderStep1() {
  297. return (
  298. <div className="step-1-layout">
  299. <div className="label">邮箱地址</div>
  300. <div className="input-layout">
  301. <DefaultInput
  302. className="w-10"
  303. placeholder="请输入邮箱"
  304. value={this.state.data.email}
  305. error={this.state.error}
  306. empty={this.state.empty.email}
  307. onChange={e => {
  308. this.changeData('email', e.target.value);
  309. this.validEmail();
  310. }}
  311. />
  312. </div>
  313. </div>
  314. );
  315. }
  316. }
  317. export class EditInfo extends Component {
  318. constructor(props) {
  319. super(props);
  320. this.props.data = this.props.data || {};
  321. this.state = Object.assign({ data: {}, empty: {} }, this.initState(this.props));
  322. }
  323. initState(props) {
  324. if (props.image && this.waitImage) {
  325. const { state } = this;
  326. Common.upload(props.image)
  327. .then(result => {
  328. const { data } = this.state;
  329. data.avatar = result.url;
  330. this.setState({ data, uploading: false });
  331. })
  332. .catch(e => {
  333. this.setState({ imageError: e.message });
  334. });
  335. state.uploading = true;
  336. this.waitImage = false;
  337. return state;
  338. }
  339. if (!props.show || this.props.show) return {};
  340. return { data: Object.assign({}, props.data) };
  341. }
  342. componentWillReceiveProps(nextProps) {
  343. this.setState(this.initState(nextProps));
  344. }
  345. changeData(field, value) {
  346. let { data, empty } = this.state;
  347. data = data || {};
  348. empty = empty || {};
  349. data[field] = value;
  350. if (value) empty[field] = !value;
  351. this.setState({ data, empty, nicknameError: null });
  352. }
  353. strLen(str) {
  354. let len = 0;
  355. for (let i = 0; i < str.length; i += 1) {
  356. if (str.charCodeAt(i) > 255 || str.charCodeAt(i) < 0) len += 2; else len += 1;
  357. }
  358. return len;
  359. }
  360. // 将字符串拆成字符,并存到数组中
  361. strToChars(str) {
  362. const chars = [];
  363. for (let i = 0; i < str.length; i += 1) {
  364. chars[i] = [str.substr(i, 1), this.isCHS(str, i)];
  365. }
  366. return chars;
  367. }
  368. // 判断某个字符是否是汉字
  369. isCHS(str, i) {
  370. if (str.charCodeAt(i) > 255 || str.charCodeAt(i) < 0) {
  371. return true;
  372. }
  373. return false;
  374. }
  375. // 截取字符串(从start字节到end字节)
  376. subCHString(str, start, end) {
  377. let len = 0;
  378. let s = '';
  379. const chars = this.strToChars(str);
  380. for (let i = 0; i < str.length; i += 1) {
  381. if (chars[i][1]) {
  382. len += 2;
  383. } else {
  384. len += 1;
  385. }
  386. if (end < len) {
  387. return s;
  388. }
  389. if (start < len) {
  390. s += chars[i][0];
  391. }
  392. }
  393. return s;
  394. }
  395. // 截取字符串(从start字节截取length个字节)
  396. subCHStr(str, start, length) {
  397. return this.subCHString(str, start, start + length);
  398. }
  399. submit() {
  400. const { data, nicknameError } = this.state;
  401. if (nicknameError) return;
  402. if (!data.nickname) {
  403. this.setState({ empty: { nickname: !data.nickname } });
  404. return;
  405. }
  406. const len = this.strLen(data.nickname);
  407. if (len < 2 || len > 14) {
  408. this.setState({ nicknameError: '用户名长度不足2个字符或超过14个字符' });
  409. return;
  410. }
  411. const { nickname, avatar } = data;
  412. My.editInfo({ nickname, avatar })
  413. .then(() => {
  414. asyncSMessage('操作成功');
  415. User.infoHandle(Object.assign(this.props.data, { nickname, avatar }));
  416. this.props.onConfirm();
  417. this.setState({ nicknameError: '' });
  418. })
  419. .catch(e => {
  420. this.setState({ nicknameError: e.message });
  421. });
  422. }
  423. render() {
  424. const { show, onCancel, onSelectImage } = this.props;
  425. return (
  426. <Modal
  427. className="edit-info-modal"
  428. show={show}
  429. width={630}
  430. title="修改资料"
  431. confirmText="保存"
  432. onCancel={onCancel}
  433. onConfirm={() => {
  434. this.submit();
  435. }}
  436. >
  437. <div className="edit-info-modal-wrapper">
  438. <div className="edit-info-modal-block">
  439. <div className="label">用户名</div>
  440. <div className="input-layout">
  441. <DefaultInput
  442. className="w-10"
  443. placeholder="2-14个字符。"
  444. value={this.state.data.nickname || ''}
  445. error={this.state.nicknameError}
  446. empty={this.state.empty.nickname}
  447. onChange={e => {
  448. this.changeData('nickname', e.target.value);
  449. }}
  450. />
  451. </div>
  452. </div>
  453. <div className="edit-info-modal-block">
  454. <div className="label">头像</div>
  455. <div className="input-layout">
  456. <FileUpload
  457. uploading={this.state.uploading}
  458. value={this.state.data.avatar}
  459. onUpload={({ file }) => {
  460. this.waitImage = true;
  461. onSelectImage(file);
  462. return Promise.reject();
  463. }}
  464. />
  465. {this.state.imageError || ''}
  466. </div>
  467. </div>
  468. </div>
  469. </Modal>
  470. );
  471. }
  472. }
  473. export class RealAuth extends Component {
  474. constructor(props) {
  475. super(props);
  476. this.state = { data: {} };
  477. }
  478. render() {
  479. const { show, onConfirm } = this.props;
  480. return (
  481. <Modal
  482. className="real-auth-modal"
  483. show={show}
  484. width={630}
  485. title="实名认证"
  486. confirmText="好的,知道了"
  487. btnAlign="center"
  488. onConfirm={onConfirm}
  489. >
  490. <div className="real-auth-modal-wrapper">
  491. <div className="real-auth-text">
  492. <div className="t1">完成实名认证即可领取:</div>
  493. <div className="t2">30天VIP权限。</div>
  494. <div className="t3">扫码关注公众号,点击“我的-实名认证”</div>
  495. </div>
  496. <div className="real-auth-qrcode">
  497. <Assets name={QrCode} width={150} height={150} />
  498. </div>
  499. </div>
  500. </Modal>
  501. );
  502. }
  503. }
  504. export class EditAvatar extends Component {
  505. constructor(props) {
  506. super(props);
  507. this.state = Object.assign({ data: {} }, this.initState(this.props));
  508. }
  509. initState(props) {
  510. if (props.image) this.loadImage(props.image);
  511. if (!props.show || this.props.show) return {};
  512. return { data: {} };
  513. }
  514. componentWillReceiveProps(nextProps) {
  515. this.setState(this.initState(nextProps));
  516. }
  517. loadImage(file) {
  518. this.defaultZoomValue = 1;
  519. if (window.FileReader) {
  520. const reader = new FileReader();
  521. reader.readAsDataURL(file);
  522. // 渲染文件
  523. reader.onload = arg => {
  524. this.setState({ image: arg.target.result, fileName: file.name, zoomValue: 1 });
  525. };
  526. } else {
  527. const img = new Image();
  528. img.onload = function () {
  529. const canvas = document.createElement('canvas');
  530. canvas.height = img.height;
  531. canvas.width = img.width;
  532. const ctx = canvas.getContext('2d');
  533. ctx.drawImage(img, 0, 0);
  534. this.setState({ image: canvas.toDataURL() });
  535. };
  536. img.src = '1.gif';
  537. }
  538. }
  539. computerZoom() {
  540. const data = this.cropRef.getImageData();
  541. this.defaultZoomValue = 200 / parseFloat(Math.max(data.naturalWidth, data.naturalHeight));
  542. }
  543. crop() {
  544. // image in dataUrl
  545. // console.log(this.cropRef.getCroppedCanvas().toDataURL())
  546. // const canvas = this.cropRef.getCroppedCanvas();
  547. // const avatar = scale(this.props.crop, canvas, 'png-src');
  548. // let scaleCanvas = this.cropRef.getCroppedCanvas(this.props.crop)
  549. // this.setState({ avatar });
  550. }
  551. select() {
  552. const { fileName } = this.state;
  553. const { onConfirm } = this.props;
  554. const canvas = this.cropRef.getCroppedCanvas();
  555. const scaleCanvas = scale(this.props.crop, canvas);
  556. // let scaleCanvas = this.cropRef.getCroppedCanvas(this.props.crop)
  557. scaleCanvas.toBlob(blob => {
  558. const file = new File([blob], fileName);
  559. onConfirm(file);
  560. });
  561. }
  562. render() {
  563. const { show, onCancel } = this.props;
  564. const { image } = this.state;
  565. return (
  566. <Modal
  567. className="edit-avatar-modal"
  568. show={show}
  569. width={630}
  570. title="调整头像"
  571. confirmText="保存头像"
  572. onConfirm={() => {
  573. this.select();
  574. }}
  575. onCancel={onCancel}
  576. >
  577. <div className="edit-avatar-modal-wrapper">
  578. <div className="edit-avatar-o">
  579. <Cropper
  580. ref={ref => {
  581. this.cropRef = ref;
  582. }}
  583. src={image}
  584. ready={() => {
  585. this.computerZoom();
  586. }}
  587. style={{ height: 360, width: 360 }}
  588. // Cropper.js options
  589. aspectRatio={1}
  590. viewMode={0}
  591. // autoCropArea={0.8}
  592. preview=".img-preview"
  593. // dragMode='move'
  594. guides={false}
  595. movable={false}
  596. rotatable={false}
  597. scalable={false}
  598. // zoom={(value) => {
  599. // console.log('zoom', value);
  600. // const zoomValue = value * this.defaultZoomValue / 50;
  601. // this.cropRef.zoomTo(zoomValue);
  602. // }}
  603. cropmove={() => {
  604. this.crop();
  605. }}
  606. toggleDragModeOnDblclick={false}
  607. cropBoxResizable
  608. />
  609. </div>
  610. <div className="edit-avatar-r">
  611. <div className="text">头像预览</div>
  612. <div className="img-preview" style={{ width: 100, height: 100, overflow: 'hidden' }} />
  613. </div>
  614. </div>
  615. </Modal>
  616. );
  617. }
  618. }
  619. export class InviteModal extends Component {
  620. constructor(props) {
  621. super(props);
  622. this.state = { data: {} };
  623. }
  624. render() {
  625. const { show, onClose, data } = this.props;
  626. return (
  627. <Modal className="invite-modal" show={show} width={630} title="邀请好友" onClose={onClose}>
  628. <div className="invite-modal-wrapper">
  629. <div className="tip">每邀请一位小伙伴加入“千行GMAT”, 您的VIP权限会延长7天,可累加 无上限!赶紧行动吧~</div>
  630. <Invite data={data} />
  631. </div>
  632. </Modal>
  633. );
  634. }
  635. }
  636. // 模考选择下载
  637. export class DownloadModal extends Component {
  638. constructor(props) {
  639. super(props);
  640. this.state = { checkMap: {} };
  641. }
  642. onConfirm() {
  643. const { onConfirm, data } = this.props;
  644. if (onConfirm) onConfirm();
  645. const { checkMap } = this.state;
  646. Object.keys(checkMap).forEach(key => {
  647. if (!checkMap[key]) return;
  648. const link = data[`${key}`];
  649. if (link) {
  650. openLink(`${link}&download=true`);
  651. }
  652. });
  653. this.setState({ checkList: [] });
  654. }
  655. render() {
  656. const { show, data = {}, onCancel } = this.props;
  657. const { checkMap = {} } = this.state;
  658. const quantVersion = data.quantVersion || 0;
  659. const irVersion = data.irVersion || 0;
  660. const rcVersion = data.rcVersion || 0;
  661. return (
  662. <Modal
  663. className="download-modal"
  664. show={show}
  665. width={570}
  666. title="下载"
  667. confirmText="下载"
  668. onConfirm={() => this.onConfirm()}
  669. onCancel={onCancel}
  670. >
  671. <div className="download-modal-wrapper">
  672. <div className="t-2 t-s-18 m-b-1">请选择下载科目</div>
  673. <div className="m-b-1">
  674. {quantVersion > 0 && (
  675. <div className="t-2 t-s-16">
  676. <Checkbox
  677. checked={checkMap.quant}
  678. onChange={() => {
  679. checkMap.quant = !checkMap.quant;
  680. this.setState({ checkMap });
  681. }}
  682. />
  683. <span className="m-l-5">数学</span>
  684. <span className="t-8">
  685. (版本{quantVersion} 最后更新:{formatDate(data.quantTime, 'YYYY-MM-DD HH:mm:ss')})
  686. </span>
  687. </div>
  688. )}
  689. {irVersion > 0 && (
  690. <div className="t-2 t-s-16">
  691. <Checkbox
  692. checked={checkMap.ir}
  693. onChange={() => {
  694. checkMap.ir = !checkMap.ir;
  695. this.setState({ checkMap });
  696. }}
  697. />
  698. <span className="m-l-5">逻辑</span>
  699. <span className="t-8">
  700. (版本{irVersion} 最后更新:{formatDate(data.irTime, 'YYYY-MM-DD HH:mm:ss')})
  701. </span>
  702. </div>
  703. )}
  704. {rcVersion > 0 && (
  705. <div className="t-2 t-s-16">
  706. <Checkbox
  707. checked={checkMap.rc}
  708. onChange={() => {
  709. checkMap.rc = !checkMap.rc;
  710. this.setState({ checkMap });
  711. }}
  712. />
  713. <span className="m-l-5">阅读</span>
  714. <span className="t-8">
  715. (版本{rcVersion} 最后更新:{formatDate(data.rcTime, 'YYYY-MM-DD HH:mm:ss')})
  716. </span>
  717. </div>
  718. )}
  719. </div>
  720. </div>
  721. </Modal>
  722. );
  723. }
  724. }
  725. // 模考开通确认
  726. export class OpenConfirmModal extends Component {
  727. render() {
  728. const { show, onConfirm, onCancel, data = {} } = this.props;
  729. return (
  730. <Modal
  731. className="open-confirm-modal"
  732. show={show}
  733. width={570}
  734. title="开通确认"
  735. confirmText="开通"
  736. cancelText="暂不开通"
  737. onConfirm={onConfirm}
  738. onCancel={onCancel}
  739. >
  740. <div className="open-confirm-modal-wrapper m-b-2">
  741. <div className="t-2 t-s-18">您正在开通「{data.title}」。</div>
  742. <div className="t-2 t-s-18">模考有效期至:{data.endTime && formatDate(data.endTime, 'YYYY-MM-DD')}</div>
  743. </div>
  744. </Modal>
  745. );
  746. }
  747. }
  748. // 模考重置确认
  749. export class RestartConfirmModal extends Component {
  750. render() {
  751. const { show, onConfirm, onCancel } = this.props;
  752. return (
  753. <Modal
  754. className="restart-confirm-modal"
  755. show={show}
  756. width={570}
  757. title="重置确认"
  758. confirmText="立即重置"
  759. cancelText="暂不重置"
  760. onConfirm={onConfirm}
  761. onCancel={onCancel}
  762. >
  763. <div className="restart-confirm-modal-wrapper m-b-2">
  764. <div className="t-2 t-s-18">重置后,可进行新一轮的模考。 </div>
  765. <div className="t-2 t-s-18 m-b-2">本轮模考的成绩和报告可在本页面或「我的报告」查看。 </div>
  766. <div className="t-2 t-s-18 m-b-1">只有 1 次重置机会。</div>
  767. </div>
  768. </Modal>
  769. );
  770. }
  771. }
  772. export class FeedbackErrorDataModal extends Component {
  773. constructor(props) {
  774. super(props);
  775. this.state = { data: { position: ['', '', ''], content: '', originContent: '' } };
  776. }
  777. componentWillReceiveProps(nextProps) {
  778. if (nextProps.show && nextProps.defaultData) {
  779. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  780. }
  781. }
  782. changeData(field, value) {
  783. let { data, empty } = this.state;
  784. data = data || {};
  785. empty = empty || {};
  786. data[field] = value;
  787. if (value) empty[field] = !value;
  788. this.setState({ data, empty });
  789. }
  790. changeDataIndex(field, index, value) {
  791. let { data, empty } = this.state;
  792. data = data || {};
  793. empty = empty || {};
  794. data[field][index] = value;
  795. if (value) empty[`${field}[${index}]`] = !value;
  796. this.setState({ data, empty });
  797. }
  798. onConfirm() {
  799. const { onConfirm } = this.props;
  800. const { data } = this.state;
  801. if (!data.position[0] || !data.position[1] || !data.position[2] || !data.content || !data.originContent) {
  802. this.setState({
  803. empty: {
  804. 'position[0]': !data.position[0],
  805. 'position[1]': !data.position[1],
  806. 'position[2]': !data.position[2],
  807. content: !data.content,
  808. originContent: !data.originContent,
  809. },
  810. });
  811. return Promise.reject();
  812. }
  813. return My.addFeedbackErrorData(
  814. data.dataId,
  815. data.title,
  816. data.position.join(','),
  817. data.originContent,
  818. data.content,
  819. ).then(() => {
  820. this.setState({ data: { position: ['', '', ''], content: '', originContent: '' } });
  821. if (onConfirm) onConfirm();
  822. });
  823. }
  824. onCancel() {
  825. const { onCancel } = this.props;
  826. this.setState({ data: { position: ['', '', ''], content: '', originContent: '' } });
  827. if (onCancel) onCancel();
  828. }
  829. render() {
  830. const { show } = this.props;
  831. const { data, empty = {} } = this.state;
  832. return (
  833. <Modal
  834. show={show}
  835. title="纠错"
  836. btnType="link"
  837. width={630}
  838. onConfirm={() => this.onConfirm()}
  839. onCancel={() => this.onCancel()}
  840. >
  841. <div className="t-2 m-b-1 t-s-16">
  842. 定位:
  843. <Input
  844. value={data.position[0]}
  845. className="t-c b-c-1 m-r-5"
  846. style={{ width: 56 }}
  847. empty={empty['position[0]']}
  848. onChange={e => {
  849. this.changeDataIndex('position', 0, e.target.value);
  850. }}
  851. />
  852. <span className="require">页</span>
  853. <Input
  854. value={data.position[1]}
  855. className="t-c b-c-1 m-r-5"
  856. style={{ width: 56 }}
  857. empty={empty['position[1]']}
  858. onChange={e => {
  859. this.changeDataIndex('position', 1, e.target.value);
  860. }}
  861. />
  862. <span className="require">行</span> , 题号
  863. <Input
  864. value={data.position[2]}
  865. className="t-c b-c-1"
  866. style={{ width: 56 }}
  867. empty={empty['position[2]']}
  868. onChange={e => {
  869. this.changeDataIndex('position', 2, e.target.value);
  870. }}
  871. />
  872. </div>
  873. <div className="t-2 t-s-16">错误内容是:</div>
  874. <Textarea
  875. value={data.originContent}
  876. className="b-c-1 w-10 p-10"
  877. rows={10}
  878. placeholder={'可简单描述您发现的问题'}
  879. empty={empty.originContent}
  880. onChange={e => {
  881. this.changeData('originContent', e.target.value);
  882. }}
  883. />
  884. <div className="t-2 t-s-16">应该更改为:</div>
  885. <Textarea
  886. value={data.content}
  887. className="b-c-1 w-10 p-10"
  888. rows={10}
  889. placeholder={'提供您认为正确的内容即可'}
  890. empty={empty.content}
  891. onChange={e => {
  892. this.changeData('content', e.target.value);
  893. }}
  894. />
  895. <div className="b-b m-t-2" />
  896. </Modal>
  897. );
  898. }
  899. }
  900. export class AskCourseModal extends Component {
  901. constructor(props) {
  902. super(props);
  903. this.state = { data: { position: [], content: '', originContent: '' } };
  904. }
  905. componentWillReceiveProps(nextProps) {
  906. if (nextProps.show && nextProps.defaultData) {
  907. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  908. }
  909. }
  910. changeData(field, value) {
  911. let { data, empty } = this.state;
  912. data = data || {};
  913. empty = empty || {};
  914. data[field] = value;
  915. if (value) empty[field] = !value;
  916. this.setState({ data, empty });
  917. }
  918. onConfirm() {
  919. const { course, courseNo, onConfirm } = this.props;
  920. const { data } = this.state;
  921. if (!data.content || !data.originContent) {
  922. this.setState({ empty: { content: !data.content, originContent: !data.originContent } });
  923. return Promise.reject();
  924. }
  925. return My.addCourseAsk(course.id, courseNo.id, data.position.join(','), data.originContent, data.content).then(
  926. () => {
  927. this.setState({ data: { position: [], content: '', originContent: '' } });
  928. if (onConfirm) onConfirm();
  929. },
  930. );
  931. }
  932. onCancel() {
  933. const { onCancel } = this.props;
  934. this.setState({ data: { position: [], content: '', originContent: '' } });
  935. if (onCancel) onCancel();
  936. }
  937. render() {
  938. const { show, selectList, courseNo, getContainer } = this.props;
  939. const { data, empty = {} } = this.state;
  940. return (
  941. <Modal
  942. show={show}
  943. title="提问"
  944. btnType="link"
  945. width={630}
  946. getContainer={getContainer}
  947. confirmText="提交"
  948. onConfirm={() => this.onConfirm()}
  949. onCancel={() => this.onCancel()}
  950. >
  951. <div className="t-2 m-b-1 t-s-16">
  952. 针对<span className="t-4">课时{courseNo.no}</span>的{' '}
  953. <Select
  954. value={data.position}
  955. theme="white"
  956. list={selectList}
  957. onChange={item => {
  958. this.changeData('position', item.key);
  959. }}
  960. />
  961. 进行提问.
  962. </div>
  963. <div className="t-2 t-s-16">老师讲解的内容是:</div>
  964. <Textarea
  965. value={data.originContent}
  966. className="b-c-1 w-10 p-10"
  967. rows={4}
  968. placeholder={'请简单描述,以便老师准确定位。'}
  969. empty={empty.originContent}
  970. onChange={e => {
  971. this.changeData('originContent', e.target.value);
  972. }}
  973. />
  974. <div className="t-2 t-s-16">您的问题是:</div>
  975. <Textarea
  976. value={data.content}
  977. className="b-c-1 w-10 p-10"
  978. rows={4}
  979. placeholder={'老师会在n小时内回答你的问题。'}
  980. onChange={e => {
  981. this.changeData('content', e.target.value);
  982. }}
  983. />
  984. <div className="b-b m-t-2" />
  985. </Modal>
  986. );
  987. }
  988. }
  989. export class CourseNoteModal extends Component {
  990. constructor(props) {
  991. super(props);
  992. this.state = { data: { content: '' } };
  993. }
  994. componentWillReceiveProps(nextProps) {
  995. if (nextProps.show && nextProps.defaultData) {
  996. this.setState({ data: Object.assign({}, nextProps.defaultData) });
  997. }
  998. }
  999. changeData(field, value) {
  1000. let { data, empty } = this.state;
  1001. data = data || {};
  1002. empty = empty || {};
  1003. data[field] = value;
  1004. if (value) empty[field] = !value;
  1005. this.setState({ data, empty });
  1006. }
  1007. onConfirm() {
  1008. const { course, onConfirm } = this.props;
  1009. const { data } = this.state;
  1010. if (!data.content || !data.originContent) {
  1011. this.setState({ empty: { content: !data.content } });
  1012. return Promise.reject();
  1013. }
  1014. return My.updateCourseNote(course.id, data.courseNoId, data.content).then(() => {
  1015. this.setState({ data: { content: '' } });
  1016. if (onConfirm) onConfirm();
  1017. });
  1018. }
  1019. onCancel() {
  1020. const { onCancel } = this.props;
  1021. this.setState({ data: { content: '' } });
  1022. if (onCancel) onCancel();
  1023. }
  1024. render() {
  1025. const { show, course = {}, courseNos = [], noteMap = {}, getContainer } = this.props;
  1026. const { data, empty = {} } = this.state;
  1027. return (
  1028. <Modal
  1029. show={show}
  1030. title="笔记"
  1031. width={630}
  1032. getContainer={getContainer}
  1033. confirmText="提交"
  1034. onConfirm={() => this.onConfirm()}
  1035. onCancel={() => this.onCancel()}
  1036. >
  1037. <div className="t-2 m-b-1 t-s-16">
  1038. {course.title}
  1039. <Select
  1040. theme="white"
  1041. value={data.courseNoId}
  1042. list={courseNos.map(row => {
  1043. return {
  1044. title: `课时${row.no}`,
  1045. key: row.id,
  1046. };
  1047. })}
  1048. onChange={item => {
  1049. if (data.courseNoId !== item.key) {
  1050. data.courseNoId = item.key;
  1051. data.content = noteMap[item.key] ? noteMap[item.key].content : '';
  1052. }
  1053. this.setState({ data });
  1054. }}
  1055. />
  1056. </div>
  1057. <Textarea
  1058. value={data.content}
  1059. className="b-c-1 w-10 p-10"
  1060. rows={10}
  1061. placeholder={'写下笔记,方便以后复习。'}
  1062. empty={empty.content}
  1063. onChange={e => {
  1064. this.changeData('content', e.target.value);
  1065. }}
  1066. />
  1067. <div className="b-b m-t-2" />
  1068. </Modal>
  1069. );
  1070. }
  1071. }
  1072. export class TextbookFeedbackModal extends Component {
  1073. constructor(props) {
  1074. super(props);
  1075. this.state = {
  1076. data: { content: '' },
  1077. targetList: TextbookFeedbackTarget.map(row => {
  1078. return {
  1079. title: row.label,
  1080. key: row.value,
  1081. };
  1082. }),
  1083. textbookSubject: TextbookSubject.map(row => {
  1084. return {
  1085. title: row.label,
  1086. key: row.value,
  1087. };
  1088. }),
  1089. };
  1090. }
  1091. componentWillReceiveProps(nextProps) {
  1092. if (nextProps.show && nextProps.defaultData) {
  1093. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  1094. }
  1095. }
  1096. changeData(field, value) {
  1097. let { data, empty } = this.state;
  1098. data = data || {};
  1099. empty = empty || {};
  1100. data[field] = value;
  1101. if (value) empty[field] = !value;
  1102. this.setState({ data, empty });
  1103. }
  1104. onConfirm() {
  1105. const { onConfirm } = this.props;
  1106. const { data } = this.state;
  1107. if (data.target !== 'new' && (!data.no || !data.content)) {
  1108. this.setState({ empty: { content: !data.content, no: !data.no } });
  1109. return Promise.reject();
  1110. }
  1111. if (!data.content) {
  1112. this.setState({ empty: { content: !data.content } });
  1113. return Promise.reject();
  1114. }
  1115. return My.addTextbookFeedback(data.textbookSubject, data.target, data.no, data.content)
  1116. .then(() => {
  1117. this.setState({ data: { content: '' } });
  1118. if (onConfirm) onConfirm();
  1119. })
  1120. .catch(e => {
  1121. asyncSMessage(e.message, 'error');
  1122. });
  1123. }
  1124. onCancel() {
  1125. const { onCancel } = this.props;
  1126. this.setState({ data: { content: '' } });
  1127. if (onCancel) onCancel();
  1128. }
  1129. render() {
  1130. const { show } = this.props;
  1131. const { data, targetList, textbookSubject, empty = {} } = this.state;
  1132. return (
  1133. <Modal show={show} title="反馈" width={630} onConfirm={() => this.onConfirm()} onCancel={() => this.onCancel()}>
  1134. <div className="t-2 t-s-16 m-b-1">
  1135. 机经类别:{' '}
  1136. <Select
  1137. value={data.textbookSubject}
  1138. theme="white"
  1139. list={textbookSubject}
  1140. onChange={value => {
  1141. data.textbookSubject = value;
  1142. this.setState({ data });
  1143. }}
  1144. />
  1145. 反馈类型:{' '}
  1146. <Select
  1147. value={data.target}
  1148. theme="white"
  1149. list={targetList}
  1150. onChange={({ key }) => {
  1151. data.target = key;
  1152. this.setState({ data });
  1153. }}
  1154. />
  1155. <span hidden={data.target === 'new'}>
  1156. {' '}
  1157. 题号是{' '}
  1158. <Input
  1159. value={data.no}
  1160. style={{ width: 80 }}
  1161. className="m-l-1 b-c-1 t-c"
  1162. empty={empty.no}
  1163. onChange={e => {
  1164. this.changeData('no', e.target.value);
  1165. }}
  1166. />
  1167. </span>
  1168. </div>
  1169. <div className="t-2 t-s-16">{TextbookFeedbackTargetMap[data.target]}:</div>
  1170. <Textarea
  1171. value={data.content}
  1172. className="b-c-1 w-10 p-10"
  1173. rows={10}
  1174. placeholder={TextbookFeedbackTargetMap[data.target]}
  1175. empty={empty.content}
  1176. onChange={e => {
  1177. this.changeData('content', e.target.value);
  1178. }}
  1179. />
  1180. <div className="b-b m-t-2" />
  1181. </Modal>
  1182. );
  1183. }
  1184. }
  1185. export class FaqModal extends Component {
  1186. constructor(props) {
  1187. super(props);
  1188. this.state = { data: { content: '' } };
  1189. }
  1190. componentWillReceiveProps(nextProps) {
  1191. if (nextProps.defaultData && nextProps.show) {
  1192. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  1193. }
  1194. }
  1195. changeData(field, value) {
  1196. let { data, empty } = this.state;
  1197. data = data || {};
  1198. empty = empty || {};
  1199. data[field] = value;
  1200. if (value) empty[field] = !value;
  1201. this.setState({ data, empty });
  1202. }
  1203. onConfirm() {
  1204. const { onConfirm } = this.props;
  1205. const { data } = this.state;
  1206. if (!data.content) {
  1207. this.setState({ empty: { content: !data.content } });
  1208. return Promise.reject();
  1209. }
  1210. return My.addFaq(data.channel, data.position, data.content).then(() => {
  1211. this.setState({ data: { content: '' } });
  1212. if (onConfirm) onConfirm();
  1213. });
  1214. }
  1215. onCancel() {
  1216. const { onCancel } = this.props;
  1217. this.setState({ data: { content: '' } });
  1218. if (onCancel) onCancel();
  1219. }
  1220. render() {
  1221. const { show } = this.props;
  1222. const { data, empty = {} } = this.state;
  1223. return (
  1224. <Modal show={show} title="咨询" onConfirm={() => this.onConfirm()} onCancel={() => this.onCancel()}>
  1225. <Textarea
  1226. className="b-c-1 w-10 p-10"
  1227. value={data.content}
  1228. rows={6}
  1229. placeholder="请输入您的问题!"
  1230. empty={empty.content}
  1231. onChange={e => {
  1232. this.changeData('content', e.target.value);
  1233. }}
  1234. />
  1235. <div className="b-b m-t-2" />
  1236. </Modal>
  1237. );
  1238. }
  1239. }
  1240. export class CommentModal extends Component {
  1241. constructor(props) {
  1242. super(props);
  1243. this.state = { data: { content: '' } };
  1244. }
  1245. componentWillReceiveProps(nextProps) {
  1246. if (nextProps.show && nextProps.defaultData) {
  1247. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  1248. }
  1249. }
  1250. changeData(field, value) {
  1251. let { data, empty } = this.state;
  1252. data = data || {};
  1253. empty = empty || {};
  1254. data[field] = value;
  1255. if (value) empty[field] = !value;
  1256. this.setState({ data, empty });
  1257. }
  1258. onConfirm() {
  1259. const { onConfirm } = this.props;
  1260. const { data } = this.state;
  1261. if (!data.content) {
  1262. this.setState({ empty: { content: !data.content } });
  1263. return Promise.reject();
  1264. }
  1265. return My.addComment(data.channel, data.position, data.content).then(() => {
  1266. this.setState({ data: { content: '' } });
  1267. if (onConfirm) onConfirm();
  1268. });
  1269. }
  1270. onCancel() {
  1271. const { onCancel } = this.props;
  1272. this.setState({ data: { content: '' } });
  1273. if (onCancel) onCancel();
  1274. }
  1275. render() {
  1276. const { show } = this.props;
  1277. const { data, empty = {} } = this.state;
  1278. return (
  1279. <Modal show={show} title="评价" onConfirm={() => this.onConfirm()} onCancel={() => this.onCancel()}>
  1280. <Textarea
  1281. value={data.content}
  1282. className="b-c-1 w-10 p-10"
  1283. rows={6}
  1284. placeholder="您的看法对我们来说很重要!"
  1285. empty={empty.content}
  1286. onChange={e => {
  1287. this.changeData('content', e.target.value);
  1288. }}
  1289. />
  1290. <div className="b-b m-t-2" />
  1291. </Modal>
  1292. );
  1293. }
  1294. }
  1295. export class FinishModal extends Component {
  1296. render() {
  1297. const { show, onConfirm } = this.props;
  1298. return (
  1299. <Modal show={show} title="提交成功" confirmText="好的,知道了" btnAlign="center" onConfirm={() => onConfirm()}>
  1300. <div className="t-2 t-s-18">
  1301. <Icon type="check" className="t-5 m-r-5" />
  1302. 您的每一次反馈都是千行进步的动力。
  1303. </div>
  1304. </Modal>
  1305. );
  1306. }
  1307. }
  1308. export class SuppleModal extends Component {
  1309. constructor(props) {
  1310. super(props);
  1311. this.state = { data: { content: '' } };
  1312. }
  1313. componentWillReceiveProps(nextProps) {
  1314. if (nextProps.show && nextProps.defaultData) {
  1315. this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
  1316. }
  1317. }
  1318. changeData(field, value) {
  1319. let { data, empty } = this.state;
  1320. data = data || {};
  1321. empty = empty || {};
  1322. data[field] = value;
  1323. if (value) empty[field] = !value;
  1324. this.setState({ data, empty });
  1325. }
  1326. onConfirm() {
  1327. const { onConfirm } = this.props;
  1328. const { data } = this.state;
  1329. if (!data.content) {
  1330. this.setState({ empty: { content: !data.content } });
  1331. return Promise.reject();
  1332. }
  1333. return My.addRoomFeedback(data.roomId, data.content).then(() => {
  1334. this.setState({ data: { content: '' } });
  1335. if (onConfirm) onConfirm();
  1336. });
  1337. }
  1338. onCancel() {
  1339. const { onCancel } = this.props;
  1340. this.setState({ data: { content: '' } });
  1341. if (onCancel) onCancel();
  1342. }
  1343. render() {
  1344. const { show, info = {} } = this.props;
  1345. const { data, empty = {} } = this.state;
  1346. return (
  1347. <Modal show={show} title="考场信息" onConfirm={() => this.onConfirm()} onCancel={() => this.onCancel()}>
  1348. <div className="t-2 t-s-16">
  1349. 考场位置: {info.isOverseas ? '海外' : '中国'}
  1350. {info.area ? ` ${info.area}` : ''} {info.title}
  1351. </div>
  1352. <div className="t-2 t-s-16">补充内容:</div>
  1353. <Textarea
  1354. value={data.content}
  1355. className="b-c-1 w-10 p-10"
  1356. empty={empty.content}
  1357. rows={6}
  1358. onChange={e => {
  1359. this.changeData('content', e.target.value);
  1360. }}
  1361. />
  1362. <div className="b-b m-t-2" />
  1363. </Modal>
  1364. );
  1365. }
  1366. }
  1367. export class SuppleFinishModal extends Component {
  1368. render() {
  1369. const { show, onConfirm } = this.props;
  1370. return (
  1371. <Modal show={show} title="提交成功" confirmText="好的,知道了" btnAlign="center" onConfirm={() => onConfirm()}>
  1372. <div className="t-2 t-s-18">
  1373. <Icon type="check" className="t-5 m-r-5" />
  1374. 内容将在审核通过后发布,感谢您的参与。
  1375. </div>
  1376. </Modal>
  1377. );
  1378. }
  1379. }
  1380. export class QuestionNoteModal extends Component {
  1381. constructor(props) {
  1382. super(props);
  1383. this.state = { data: {}, noteField: AskTarget[0].value };
  1384. this.target = [{ label: '题目', value: 'question', title: '题目', key: 'question' }];
  1385. }
  1386. componentWillReceiveProps(nextProps) {
  1387. if (nextProps.show && nextProps.defaultData) {
  1388. this.setState({ data: Object.assign({}, nextProps.defaultData) });
  1389. }
  1390. }
  1391. changeData(field, value) {
  1392. let { data, empty } = this.state;
  1393. data = data || {};
  1394. empty = empty || {};
  1395. data[field] = value;
  1396. if (value) empty[field] = !value;
  1397. this.setState({ data, empty });
  1398. }
  1399. onConfirm(close) {
  1400. const { questionNo, onConfirm } = this.props;
  1401. const { data } = this.state;
  1402. return My.updateQuestionNote(questionNo.id, data).then(() => {
  1403. if (close) {
  1404. this.setState({ noteField: AskTarget[0].value });
  1405. if (onConfirm) onConfirm(data);
  1406. }
  1407. });
  1408. }
  1409. onCancel() {
  1410. const { onCancel } = this.props;
  1411. this.setState({ data: { content: '' } });
  1412. if (onCancel) onCancel();
  1413. }
  1414. render() {
  1415. const { show } = this.props;
  1416. const { data, noteField } = this.state;
  1417. return (
  1418. <div hidden={!show} className="question-modal question-note-modal">
  1419. <div className="mask" />
  1420. <div className="modal-body">
  1421. <div className="modal-title">笔记</div>
  1422. <div className="modal-content">
  1423. <div className="tabs">
  1424. {this.target.map(item => {
  1425. return (
  1426. <div
  1427. className={`tab ${noteField === item.key ? 'active' : ''}`}
  1428. onClick={() => {
  1429. this.setState({ noteField: item.key });
  1430. }}
  1431. >
  1432. <div className="text">{item.label}</div>
  1433. <div className="date">{data[`${item.key}Time`] ? formatDate(data[`${item.key}Time`]) : ''}</div>
  1434. </div>
  1435. );
  1436. })}
  1437. </div>
  1438. <div className="input">
  1439. <Textarea
  1440. className="textarea"
  1441. value={data[`${noteField}Content`] || ''}
  1442. placeholder="记下笔记,方便以后复习"
  1443. onChange={e => {
  1444. data[`${noteField}Time`] = new Date();
  1445. data[`${noteField}Content`] = e.target.value;
  1446. this.setState({ data });
  1447. }}
  1448. />
  1449. <div className="bottom">
  1450. <AnswerButton
  1451. theme="cancel"
  1452. size="lager"
  1453. onClick={() => {
  1454. this.onCancel();
  1455. }}
  1456. >
  1457. 取消
  1458. </AnswerButton>
  1459. {/* <AnswerButton
  1460. size="lager"
  1461. onClick={() => {
  1462. this.onConfirm();
  1463. }}
  1464. >
  1465. 编辑
  1466. </AnswerButton> */}
  1467. <AnswerButton
  1468. size="lager"
  1469. onClick={() => {
  1470. this.onConfirm(true);
  1471. }}
  1472. >
  1473. 保存
  1474. </AnswerButton>
  1475. </div>
  1476. </div>
  1477. </div>
  1478. </div>
  1479. </div>
  1480. );
  1481. }
  1482. }