index.js 15 KB

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