page.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import React from 'react';
  2. import './index.less';
  3. import Page from '@src/containers/Page';
  4. import { Checkbox } from 'antd';
  5. // import Assets from '@src/components/Assets';
  6. import { getMap, formatDate } from '@src/services/Tools';
  7. import Button from '../../../components/Button';
  8. import Money from '../../../components/Money';
  9. import Icon from '../../../components/Icon';
  10. import { Order } from '../../../stores/order';
  11. import { My } from '../../../stores/my';
  12. import { Main } from '../../../stores/main';
  13. import { ServiceParamMap, OrderInfoMap } from '../../../../Constant';
  14. import { Common } from '../../../stores/common';
  15. const ServiceParamRelation = getMap(Object.keys(ServiceParamMap).map(key => {
  16. return {
  17. map: getMap(ServiceParamMap[key].map((row, index) => {
  18. row.index = index;
  19. return row;
  20. }), 'value', 'index'),
  21. key,
  22. };
  23. }), 'key', 'map');
  24. function formatTitle(record) {
  25. if (record.productType === 'course_package') {
  26. return (record.coursePackage || {}).title;
  27. }
  28. if (record.productType === 'course') {
  29. return (record.course || {}).title;
  30. }
  31. if (record.productType === 'data') {
  32. return (record.data || {}).title;
  33. }
  34. if (record.productType === 'service') {
  35. return record.info.label || ((record.serviceInfo || {}).title);
  36. }
  37. return '';
  38. }
  39. function formatCheckout(checkouts) {
  40. checkouts.forEach(checkout => {
  41. checkout.info = OrderInfoMap[checkout.productType];
  42. if (checkout.productType === 'service') {
  43. const index = (ServiceParamRelation[checkout.service] && ServiceParamRelation[checkout.service][checkout.param]) || 0;
  44. checkout.info = Object.assign({}, checkout.info[checkout.service], checkout.serviceInfo.package[index]);
  45. }
  46. checkout.title = formatTitle(checkout);
  47. });
  48. }
  49. export default class extends Page {
  50. initState() {
  51. return { show: true, showEnd: false, checked: true };
  52. }
  53. init() {
  54. // this.wx = null;
  55. // Common.readyWechat(window.location.href, ['chooseWXPay']).then(wx => {
  56. // this.wx = wx;
  57. // });
  58. }
  59. initData() {
  60. const { id } = this.params;
  61. Order.getOrder(id).then(order => {
  62. formatCheckout(order.checkouts);
  63. const [checkout] = order.checkouts;
  64. this.setState({ order, checkout });
  65. });
  66. Main.getContract('course').then(result => {
  67. this.setState({ contract: result });
  68. });
  69. }
  70. pay() {
  71. const { id } = this.params;
  72. if (this.paying) return;
  73. this.paying = true;
  74. this.setState({ paying: true });
  75. Order.wechatJs(id).then((info) => {
  76. return Common.readyWechatBridge().then(() => {
  77. WeixinJSBridge.invoke(
  78. 'getBrandWCPayRequest', info.request,
  79. (res) => {
  80. if (res.err_msg === 'get_brand_wcpay_request:ok') {
  81. // 使用以上方式判断前端返回,微信团队郑重提示:
  82. // res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
  83. this.queryPay(true);
  84. } else if (res.err_msg === 'get_brand_wcpay_request:cancel' || res.err_msg === 'get_brand_wcpay_request:fail' || res.err_msg === 'chooseWXPay:fail') {
  85. // 支付失败
  86. this.paying = false;
  87. this.setState({ paying: false });
  88. }
  89. },
  90. );
  91. });
  92. });
  93. }
  94. queryPay(force) {
  95. const { order } = this.state;
  96. if (this.time) {
  97. clearTimeout(this.time);
  98. }
  99. if (force) {
  100. this.times = 0;
  101. } else {
  102. this.times = (this.times || 0) + 1;
  103. }
  104. this.time = setTimeout(() => {
  105. Order.query(order.id)
  106. .then(result => {
  107. if (result) {
  108. // 支付成功
  109. this.paySuccess();
  110. } else if (order) {
  111. this.queryPay();
  112. } else {
  113. this.setState({ select: null, pay: null, order: null, info: null });
  114. }
  115. });
  116. }, 1000);
  117. }
  118. paySuccess() {
  119. const { order } = this.state;
  120. const { info } = this.props.user;
  121. Order.getOrder(order.id).then(result => {
  122. // 确保开通用的是record记录id
  123. const [checkout] = result.checkouts;
  124. formatCheckout(result.checkouts);
  125. checkout.info.result = checkout.info.result.replace('{email}', info.email).replace('{useExpireDays}', checkout.useExpireDays).replace('{title}', checkout.title);
  126. if (checkout.service === 'vip') {
  127. // 查询最后有效期
  128. My.getVipInfo().then(vip => {
  129. checkout.info.result = checkout.info.result.replace('{endTime}', formatDate(vip.expireTime, 'YYYY-MM-DD'));
  130. this.setState({ show: false, showEnd: true, order: result, checkout });
  131. });
  132. } else {
  133. this.setState({ show: false, showEnd: true, order: result, checkout });
  134. }
  135. });
  136. }
  137. renderView() {
  138. const { show, showEnd, showContract } = this.state;
  139. if (showContract) {
  140. return this.renderContract();
  141. }
  142. if (show) {
  143. return this.renderPay();
  144. }
  145. if (showEnd) {
  146. return this.renderEnd();
  147. }
  148. return null;
  149. }
  150. renderPay() {
  151. const { order = {}, contract = {}, checkout = {}, checked } = this.state;
  152. const { info = {}, productType } = checkout;
  153. let content = '';
  154. switch (productType) {
  155. case 'data':
  156. content = this.renderData();
  157. break;
  158. case 'course_package':
  159. content = this.renderCoursePackage();
  160. break;
  161. case 'course':
  162. case 'service':
  163. content = this.renderSingle();
  164. break;
  165. default:
  166. }
  167. return (
  168. <div className="detail">
  169. <div className="title" style={{ marginBottom: 0 }}>
  170. 商品信息
  171. </div>
  172. {content}
  173. {info.refund_policy && [< div className="title">退款政策</div>, <div className="desc">{info.refund_policy}</div>]}
  174. {info.copyright_notes && [<div className="title">版权说明</div>, <div className="desc">{info.copyright_notes}</div>]}
  175. {order.productTypes && order.productTypes.indexOf('course') > 0 && <div className="agree">
  176. <Checkbox checked={checked} onChange={() => this.setState({ checked: !checked })} />
  177. 我已阅读并同意 <a onClick={() => this.setState({ showContract: true })}>《{contract.title}》</a>
  178. </div>}
  179. <div className="fixed">
  180. <div className="tip">*若在购买过程中遇到问题,请联系千行小助手协助解决</div>
  181. <div className="fee">
  182. 应付: <Money value={order.money} size="lager" />
  183. </div>
  184. <Button
  185. width={110}
  186. className="f-r"
  187. radius
  188. disabled={this.state.paying || !checked}
  189. onClick={() => {
  190. this.pay();
  191. }}
  192. >
  193. 立即购买
  194. </Button>
  195. </div>
  196. </div >
  197. );
  198. }
  199. renderData() {
  200. const { checkout } = this.state;
  201. return (
  202. <div className="info data">
  203. <div className="info-block">
  204. {checkout.title} <Money className="f-r" value={checkout.money} />
  205. </div>
  206. <div className="info-block">
  207. 开通有效期 <span className="f-r">{checkout.expireDays ? `${checkout.expireDays}天` : '永久'}</span>
  208. </div>
  209. </div>
  210. );
  211. }
  212. renderCoursePackage() {
  213. const { checkout, order } = this.state;
  214. return (
  215. <div className="info course-package">
  216. <div className="info-block">
  217. {checkout.title} <Money className="f-r" value={checkout.money} />
  218. </div>
  219. <div className="info-block">
  220. {order.checkouts.map(row => {
  221. if (row.parentId === 0) return null;
  222. return <div className="info-item">
  223. {row.title} <span className="f-r">开通有效期: {checkout.expireDays ? `${checkout.expireDays}天` : '付款后立即生效'} 使用有效期: {checkout.useExpireDays ? `${checkout.useExpireDays}天` : '永久'}</span>
  224. </div>;
  225. })}
  226. </div>
  227. </div>
  228. );
  229. }
  230. renderSingle() {
  231. const { checkout = {} } = this.state;
  232. return (
  233. <div className="info single">
  234. <div className="info-block">
  235. {checkout.title} <Money className="f-r" value={checkout.money} />
  236. </div>
  237. <div className="info-block">
  238. 开通有效期 <span className="f-r">{checkout.expireDays ? `${checkout.expireDays}天` : '付款后立即生效'}</span>
  239. </div>
  240. <div className="info-block">
  241. 使用有效期 <span className="f-r">{checkout.useExpireDays ? `${checkout.useExpireDays}天` : '永久'}</span>
  242. </div>
  243. </div>
  244. );
  245. }
  246. renderEnd() {
  247. const { checkout = {} } = this.state;
  248. const { info = {} } = checkout;
  249. return (
  250. <div className="finish">
  251. <div className="icon">
  252. <Icon type="check-circle" />
  253. </div>
  254. <div className="title">支付成功!</div>
  255. <div className="desc">{info.result}</div>
  256. </div>
  257. );
  258. }
  259. renderContract() {
  260. const { contract } = this.state;
  261. // 需要一个关闭x在左上角
  262. return <div dangerouslySetInnerHTML={{ __html: contract.content }} />;
  263. }
  264. }