index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. import React, { Component } from 'react';
  2. import Cropper from 'react-cropper';
  3. import 'cropperjs/dist/cropper.css';
  4. import './index.less';
  5. import FileUpload from '@src/components/FileUpload';
  6. import Assets from '@src/components/Assets';
  7. import scale from '@src/services/Scale';
  8. import { asyncSMessage } from '@src/services/AsyncTools';
  9. import { SelectInput, VerificationInput, Input } from '../Login';
  10. import { MobileArea } from '../../../Constant';
  11. import Invite from '../Invite';
  12. import Modal from '../Modal';
  13. import { Common } from '../../stores/common';
  14. import { User } from '../../stores/user';
  15. import { My } from '../../stores/my';
  16. export class BindPhone extends Component {
  17. constructor(props) {
  18. super(props);
  19. this.props.data = this.props.data || {};
  20. this.state = this.initState(this.props);
  21. this.stepProp = {
  22. 0: {
  23. title: '绑定手机',
  24. onConfirm: props.onConfirm,
  25. },
  26. 1: {
  27. title: '绑定手机',
  28. onConfirm: () => {
  29. this.submit();
  30. },
  31. onCancel: props.onCancel,
  32. confirmText: '提交',
  33. },
  34. };
  35. }
  36. initState(props) {
  37. if (this.wait) return {};
  38. const data = Object.assign({}, props.data);
  39. if (!data.area) data.area = MobileArea[0].value;
  40. return { step: props.data.mobile ? 0 : 1, data };
  41. }
  42. componentWillReceiveProps(nextProps) {
  43. this.setState(this.initState(nextProps));
  44. }
  45. onNext() {
  46. this.setState({ step: 1 });
  47. }
  48. changeData(field, value) {
  49. let { data } = this.state;
  50. data = data || {};
  51. data[field] = value;
  52. this.setState({ data, error: null });
  53. }
  54. validMobile() {
  55. const { data } = this.state;
  56. const { area, mobile } = data;
  57. if (!area || !mobile) return;
  58. this.validNumber += 1;
  59. const number = this.validNumber;
  60. User.validMobile(area, mobile)
  61. .then(result => {
  62. if (number !== this.validNumber) return Promise.resolve();
  63. return result ? Promise.resolve() : Promise.reject(new Error('该手机已绑定其他账号,请更换手机号码'));
  64. })
  65. .catch(err => {
  66. this.setState({ mobileError: err.message });
  67. });
  68. }
  69. sendValid() {
  70. const { data, error } = this.state;
  71. const { area, mobile } = data;
  72. if (!area || !mobile || error) return Promise.reject();
  73. this.wait = true;
  74. return Common.sendSms(area, mobile)
  75. .then(result => {
  76. this.wait = false;
  77. if (result) {
  78. asyncSMessage('发送成功');
  79. this.setState({ error: '', validError: '' });
  80. } else {
  81. throw new Error('发送失败');
  82. }
  83. })
  84. .catch(err => {
  85. this.wait = false;
  86. this.setState({ error: err.message });
  87. throw err;
  88. });
  89. }
  90. submit() {
  91. const { data, error } = this.state;
  92. console.log(data, error);
  93. if (!data.mobile || !data.area || error) return;
  94. const { area, mobile } = data;
  95. this.wait = true;
  96. My.bindMobile(area, mobile)
  97. .then(() => {
  98. asyncSMessage('操作成功');
  99. this.setState({ step: 0 });
  100. User.infoHandle(Object.assign(this.props.data, { area, mobile }));
  101. this.props.onConfirm();
  102. this.wait = false;
  103. })
  104. .catch(e => {
  105. this.setState({ error: e.message });
  106. this.wait = false;
  107. });
  108. }
  109. render() {
  110. const { show } = this.props;
  111. const { step } = this.state;
  112. return (
  113. <Modal className="bind-phone-modal" show={show} width={630} {...this.stepProp[step]}>
  114. <div className="bind-phone-modal-wrapper">{this[`renderStep${step}`]()}</div>
  115. </Modal>
  116. );
  117. }
  118. renderStep0() {
  119. const { data } = this.state;
  120. return (
  121. <div className="step-0-layout">
  122. 已绑定手机 {data.mobile}
  123. <a onClick={() => this.onNext()}>修改</a>
  124. </div>
  125. );
  126. }
  127. renderStep1() {
  128. return (
  129. <div className="step-1-layout">
  130. <div className="label">手机号</div>
  131. <div className="input-layout">
  132. <SelectInput
  133. placeholder="请输入手机号"
  134. selectValue={this.state.data.area}
  135. select={MobileArea}
  136. value={this.state.data.mobile}
  137. error={this.state.error}
  138. onSelect={value => {
  139. this.changeData('area', value);
  140. this.validMobile();
  141. }}
  142. onChange={e => {
  143. this.changeData('mobile', e.target.value);
  144. this.validMobile();
  145. }}
  146. />
  147. <VerificationInput
  148. placeholder="请输入验证码"
  149. value={this.state.data.mobileVerifyCode}
  150. error={this.state.validError}
  151. onSend={() => {
  152. return this.sendValid();
  153. }}
  154. onChange={e => {
  155. this.changeData('mobileVerifyCode', e.target.value);
  156. }}
  157. />
  158. </div>
  159. </div>
  160. );
  161. }
  162. }
  163. export class BindEmail extends Component {
  164. constructor(props) {
  165. super(props);
  166. this.props.data = this.props.data || {};
  167. this.state = this.initState(this.props);
  168. this.stepProp = {
  169. 0: {
  170. title: '绑定邮箱',
  171. onConfirm: props.onConfirm,
  172. },
  173. 1: {
  174. title: '绑定邮箱',
  175. onConfirm: () => {
  176. this.submit();
  177. },
  178. onCancel: props.onCancel,
  179. confirmText: '提交',
  180. },
  181. };
  182. }
  183. initState(props) {
  184. if (this.wait) return {};
  185. return { step: props.data.email ? 0 : 1, data: Object.assign({}, props.data) };
  186. }
  187. componentWillReceiveProps(nextProps) {
  188. this.setState(this.initState(nextProps));
  189. }
  190. onNext() {
  191. this.setState({ step: 1 });
  192. }
  193. changeData(field, value) {
  194. let { data } = this.state;
  195. data = data || {};
  196. data[field] = value;
  197. this.setState({ data, error: null });
  198. }
  199. submit() {
  200. const { data, error } = this.state;
  201. if (!data.email || error) return;
  202. const { email } = data;
  203. this.wait = true;
  204. My.bindEmail(email)
  205. .then(() => {
  206. asyncSMessage('操作成功');
  207. this.setState({ step: 0 });
  208. User.infoHandle(Object.assign(this.props.data, { email }));
  209. this.props.onConfirm();
  210. this.wait = false;
  211. })
  212. .catch(e => {
  213. this.setState({ error: e.message });
  214. this.wait = false;
  215. });
  216. }
  217. render() {
  218. const { show } = this.props;
  219. const { step } = this.state;
  220. return (
  221. <Modal className="bind-email-modal" show={show} width={630} {...this.stepProp[step]}>
  222. <div className="bind-email-modal-wrapper">{this[`renderStep${step}`]()}</div>
  223. </Modal>
  224. );
  225. }
  226. renderStep0() {
  227. const { data } = this.state;
  228. return (
  229. <div className="step-0-layout">
  230. 已绑定邮箱 {data.email}
  231. <a onClick={() => this.onNext()}>修改</a>
  232. </div>
  233. );
  234. }
  235. renderStep1() {
  236. return (
  237. <div className="step-1-layout">
  238. <div className="label">邮箱地址</div>
  239. <div className="input-layout">
  240. <Input
  241. placeholder="请输入邮箱"
  242. value={this.state.data.email}
  243. error={this.state.error}
  244. onChange={e => {
  245. this.changeData('email', e.target.value);
  246. }}
  247. />
  248. </div>
  249. </div>
  250. );
  251. }
  252. }
  253. export class EditInfo extends Component {
  254. constructor(props) {
  255. super(props);
  256. this.props.data = this.props.data || {};
  257. this.state = this.initState(this.props);
  258. }
  259. initState(props) {
  260. if (props.image && this.waitImage) {
  261. const { state } = this;
  262. Common.upload(props.image).then(result => {
  263. const { data } = this.state;
  264. data.avatar = result.url;
  265. this.setState({ data, uploading: false });
  266. }).catch((e) => {
  267. this.setState({ imageError: e.message });
  268. });
  269. state.uploading = true;
  270. this.waitImage = false;
  271. return state;
  272. }
  273. return { data: Object.assign({}, props.data) };
  274. }
  275. componentWillReceiveProps(nextProps) {
  276. this.setState(this.initState(nextProps));
  277. }
  278. changeData(field, value) {
  279. let { data } = this.state;
  280. data = data || {};
  281. data[field] = value;
  282. this.setState({ data, error: null });
  283. }
  284. submit() {
  285. const { data, error } = this.state;
  286. if (!data.nickname || error) return;
  287. const { nickname, avatar } = data;
  288. My.editInfo({ nickname, avatar })
  289. .then(() => {
  290. asyncSMessage('操作成功');
  291. User.infoHandle(Object.assign(this.props.data, { nickname, avatar }));
  292. this.props.onConfirm();
  293. })
  294. .catch(e => {
  295. this.setState({ error: e.message });
  296. });
  297. }
  298. render() {
  299. const { show, onCancel, onSelectImage } = this.props;
  300. return (
  301. <Modal
  302. className="edit-info-modal"
  303. show={show}
  304. width={630}
  305. title="修改资料"
  306. confirmText="保存"
  307. onCancel={onCancel}
  308. onConfirm={() => {
  309. this.submit();
  310. }}
  311. >
  312. <div className="edit-info-modal-wrapper">
  313. <div className="edit-info-modal-block">
  314. <div className="label">昵称</div>
  315. <div className="input-layout">
  316. <Input
  317. placeholder="2-20位,不可使用特殊字符。"
  318. value={this.state.data.nickname || ''}
  319. error={this.state.error}
  320. onChange={e => {
  321. this.changeData('nickname', e.target.value);
  322. }}
  323. />
  324. </div>
  325. </div>
  326. <div className="edit-info-modal-block">
  327. <div className="label">头像</div>
  328. <div className="input-layout">
  329. <FileUpload
  330. uploading={this.state.uploading}
  331. value={this.state.data.avatar}
  332. onUpload={({ file }) => {
  333. this.waitImage = true;
  334. onSelectImage(file);
  335. return Promise.reject();
  336. }} />
  337. {this.state.imageError || ''}
  338. </div>
  339. </div>
  340. </div>
  341. </Modal>
  342. );
  343. }
  344. }
  345. export class RealAuth extends Component {
  346. constructor(props) {
  347. super(props);
  348. this.state = { data: {} };
  349. }
  350. render() {
  351. const { show, onConfirm } = this.props;
  352. return (
  353. <Modal
  354. className="real-auth-modal"
  355. show={show}
  356. width={630}
  357. title="实名认证"
  358. confirmText="好的,知道了"
  359. btnAlign="center"
  360. onConfirm={onConfirm}
  361. >
  362. <div className="real-auth-modal-wrapper">
  363. <div className="real-auth-text">
  364. <div className="t1">完成实名认证即可领取:</div>
  365. <div className="t2">6个月VIP权限 和 5折机经优惠劵。</div>
  366. <div className="t3">扫码关注公众号,点击“我的-实名认证”</div>
  367. </div>
  368. <div className="real-auth-qrcode">
  369. <Assets name="qrcode" />
  370. </div>
  371. </div>
  372. </Modal>
  373. );
  374. }
  375. }
  376. export class EditAvatar extends Component {
  377. constructor(props) {
  378. super(props);
  379. this.state = this.initState(props);
  380. }
  381. initState(props) {
  382. if (props.image) this.loadImage(props.image);
  383. return { data: {} };
  384. }
  385. componentWillReceiveProps(nextProps) {
  386. this.setState(this.initState(nextProps));
  387. }
  388. loadImage(file) {
  389. this.defaultZoomValue = 1;
  390. if (window.FileReader) {
  391. const reader = new FileReader();
  392. reader.readAsDataURL(file);
  393. // 渲染文件
  394. reader.onload = (arg) => {
  395. this.setState({ image: arg.target.result, fileName: file.name, zoomValue: 1 });
  396. };
  397. } else {
  398. const img = new Image();
  399. img.onload = function () {
  400. const canvas = document.createElement('canvas');
  401. canvas.height = img.height;
  402. canvas.width = img.width;
  403. const ctx = canvas.getContext('2d');
  404. ctx.drawImage(img, 0, 0);
  405. this.setState({ image: canvas.toDataURL() });
  406. };
  407. img.src = '1.gif';
  408. }
  409. }
  410. computerZoom() {
  411. const data = this.cropRef.getImageData();
  412. this.defaultZoomValue = 200 / parseFloat(Math.max(data.naturalWidth, data.naturalHeight));
  413. }
  414. crop() {
  415. // image in dataUrl
  416. // console.log(this.cropRef.getCroppedCanvas().toDataURL())
  417. // const canvas = this.cropRef.getCroppedCanvas();
  418. // const avatar = scale(this.props.crop, canvas, 'png-src');
  419. // let scaleCanvas = this.cropRef.getCroppedCanvas(this.props.crop)
  420. // this.setState({ avatar });
  421. }
  422. select() {
  423. const { fileName } = this.state;
  424. const { onConfirm } = this.props;
  425. const canvas = this.cropRef.getCroppedCanvas();
  426. const scaleCanvas = scale(this.props.crop, canvas);
  427. // let scaleCanvas = this.cropRef.getCroppedCanvas(this.props.crop)
  428. scaleCanvas.toBlob((blob) => {
  429. const file = new File([blob], fileName);
  430. onConfirm(file);
  431. });
  432. }
  433. render() {
  434. const { show, onCancel } = this.props;
  435. const { image } = this.state;
  436. return (
  437. <Modal
  438. className="edit-avatar-modal"
  439. show={show}
  440. width={630}
  441. title="调整头像"
  442. confirmText="保存头像"
  443. onConfirm={() => {
  444. this.select();
  445. }}
  446. onCancel={onCancel}
  447. >
  448. <div className="edit-avatar-modal-wrapper">
  449. <div className="edit-avatar-o">
  450. <Cropper
  451. ref={(ref) => { this.cropRef = ref; }}
  452. src={image}
  453. ready={() => {
  454. this.computerZoom();
  455. }}
  456. style={{ height: 360, width: 360 }}
  457. // Cropper.js options
  458. aspectRatio={1}
  459. viewMode={0}
  460. // autoCropArea={0.8}
  461. preview=".img-preview"
  462. // dragMode='move'
  463. guides={false}
  464. movable={false}
  465. rotatable={false}
  466. scalable={false}
  467. // zoom={(value) => {
  468. // console.log('zoom', value);
  469. // const zoomValue = value * this.defaultZoomValue / 50;
  470. // this.cropRef.zoomTo(zoomValue);
  471. // }}
  472. cropmove={() => {
  473. this.crop();
  474. }}
  475. toggleDragModeOnDblclick={false}
  476. cropBoxResizable />
  477. </div>
  478. <div className="edit-avatar-r">
  479. <div className="text">头像预览</div>
  480. <div className="img-preview" style={{ width: 100, height: 100, overflow: 'hidden' }} />
  481. </div>
  482. </div>
  483. </Modal>
  484. );
  485. }
  486. }
  487. export class InviteModal extends Component {
  488. constructor(props) {
  489. super(props);
  490. this.state = { data: {} };
  491. }
  492. render() {
  493. const { show, onClose, data } = this.props;
  494. return (
  495. <Modal className="invite-modal" show={show} width={630} title="邀请好友" onClose={onClose}>
  496. <div className="invite-modal-wrapper">
  497. <div className="tip">每邀请一位小伙伴加入“千行GMAT”, 您的VIP权限会延长7天,可累加 无上限!赶紧行动吧~</div>
  498. <Invite data={data} />
  499. </div>
  500. </Modal>
  501. );
  502. }
  503. }