index.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. import Toast from 'tdesign-miniprogram/toast/index';
  2. import { fetchSettleDetail } from '../../../services/order/orderConfirm';
  3. import { commitPay, wechatPayOrder } from './pay';
  4. import { getAddressPromise } from '../../../services/address/list';
  5. const stripeImg = `https://tdesign.gtimg.com/miniprogram/template/retail/order/stripe.png`;
  6. Page({
  7. data: {
  8. placeholder: '备注信息',
  9. stripeImg,
  10. loading: false,
  11. settleDetailData: {
  12. storeGoodsList: [], //正常下单商品列表
  13. outOfStockGoodsList: [], //库存不足商品
  14. abnormalDeliveryGoodsList: [], // 不能正常配送商品
  15. inValidGoodsList: [], // 失效或者库存不足
  16. limitGoodsList: [], //限购商品
  17. couponList: [], //门店优惠券信息
  18. }, // 获取结算页详情 data
  19. orderCardList: [], // 仅用于商品卡片展示
  20. couponsShow: false, // 显示优惠券的弹框
  21. invoiceData: {
  22. email: '', // 发票发送邮箱
  23. buyerTaxNo: '', // 税号
  24. invoiceType: null, // 开票类型 1:增值税专用发票; 2:增值税普通发票; 3:增值税电子发票;4:增值税卷式发票;5:区块链电子发票。
  25. buyerPhone: '', //手机号
  26. buyerName: '', //个人或公司名称
  27. titleType: '', // 发票抬头 1-公司 2-个人
  28. contentType: '', //发票内容 1-明细 2-类别
  29. },
  30. goodsRequestList: [],
  31. userAddressReq: null,
  32. popupShow: false, // 不在配送范围 失效 库存不足 商品展示弹框
  33. notesPosition: 'center',
  34. storeInfoList: [],
  35. storeNoteIndex: 0, //当前填写备注门店index
  36. promotionGoodsList: [], //当前门店商品列表(优惠券)
  37. couponList: [], //当前门店所选优惠券
  38. submitCouponList: [], //所有门店所选优惠券
  39. currentStoreId: null, //当前优惠券storeId
  40. userAddress: null,
  41. },
  42. payLock: false,
  43. noteInfo: [],
  44. tempNoteInfo: [],
  45. onLoad(options) {
  46. this.setData({
  47. loading: true,
  48. });
  49. this.handleOptionsParams(options);
  50. },
  51. onShow() {
  52. const invoiceData = wx.getStorageSync('invoiceData');
  53. if (invoiceData) {
  54. //处理发票
  55. this.invoiceData = invoiceData;
  56. this.setData({
  57. invoiceData,
  58. });
  59. wx.removeStorageSync('invoiceData');
  60. }
  61. },
  62. init() {
  63. this.setData({
  64. loading: true,
  65. });
  66. const { goodsRequestList } = this;
  67. this.handleOptionsParams({ goodsRequestList });
  68. },
  69. // 处理不同情况下跳转到结算页时需要的参数
  70. handleOptionsParams(options, couponList) {
  71. let { goodsRequestList } = this; // 商品列表
  72. let { userAddressReq } = this; // 收货地址
  73. const storeInfoList = []; // 门店列表
  74. // 如果是从地址选择页面返回,则使用地址显选择页面新选择的地址去获取结算数据
  75. if (options.userAddressReq) {
  76. userAddressReq = options.userAddressReq;
  77. }
  78. if (options.type === 'cart') {
  79. // 从购物车跳转过来时,获取传入的商品列表数据
  80. const goodsRequestListJson = wx.getStorageSync('order.goodsRequestList');
  81. goodsRequestList = JSON.parse(goodsRequestListJson);
  82. } else if (typeof options.goodsRequestList === 'string') {
  83. goodsRequestList = JSON.parse(options.goodsRequestList);
  84. }
  85. //获取结算页请求数据列表
  86. const storeMap = {};
  87. goodsRequestList.forEach((goods) => {
  88. if (!storeMap[goods.storeId]) {
  89. storeInfoList.push({
  90. storeId: goods.storeId,
  91. storeName: goods.storeName,
  92. });
  93. storeMap[goods.storeId] = true;
  94. }
  95. });
  96. this.goodsRequestList = goodsRequestList;
  97. this.storeInfoList = storeInfoList;
  98. const params = {
  99. goodsRequestList,
  100. storeInfoList,
  101. userAddressReq,
  102. couponList,
  103. };
  104. fetchSettleDetail(params).then(
  105. (res) => {
  106. this.setData({
  107. loading: false,
  108. });
  109. this.initData(res.data);
  110. },
  111. () => {
  112. //接口异常处理
  113. this.handleError();
  114. },
  115. );
  116. },
  117. initData(resData) {
  118. // 转换商品卡片显示数据
  119. const data = this.handleResToGoodsCard(resData);
  120. this.userAddressReq = resData.userAddress;
  121. if (resData.userAddress) {
  122. this.setData({ userAddress: resData.userAddress });
  123. }
  124. this.setData({ settleDetailData: data });
  125. this.isInvalidOrder(data);
  126. },
  127. isInvalidOrder(data) {
  128. // 失效 不在配送范围 限购的商品 提示弹窗
  129. if (
  130. (data.limitGoodsList && data.limitGoodsList.length > 0) ||
  131. (data.abnormalDeliveryGoodsList && data.abnormalDeliveryGoodsList.length > 0) ||
  132. (data.inValidGoodsList && data.inValidGoodsList.length > 0)
  133. ) {
  134. this.setData({ popupShow: true });
  135. return true;
  136. }
  137. this.setData({ popupShow: false });
  138. if (data.settleType === 0) {
  139. return true;
  140. }
  141. return false;
  142. },
  143. handleError() {
  144. Toast({
  145. context: this,
  146. selector: '#t-toast',
  147. message: '结算异常, 请稍后重试',
  148. duration: 2000,
  149. icon: '',
  150. });
  151. setTimeout(() => {
  152. wx.navigateBack();
  153. }, 1500);
  154. this.setData({
  155. loading: false,
  156. });
  157. },
  158. getRequestGoodsList(storeGoodsList) {
  159. const filterStoreGoodsList = [];
  160. storeGoodsList &&
  161. storeGoodsList.forEach((store) => {
  162. const { storeName } = store;
  163. store.skuDetailVos &&
  164. store.skuDetailVos.forEach((goods) => {
  165. const data = goods;
  166. data.storeName = storeName;
  167. filterStoreGoodsList.push(data);
  168. });
  169. });
  170. return filterStoreGoodsList;
  171. },
  172. handleGoodsRequest(goods, isOutStock = false) {
  173. const { reminderStock, quantity, storeId, uid, saasId, spuId, goodsName, skuId, storeName, roomId } = goods;
  174. const resQuantity = isOutStock ? reminderStock : quantity;
  175. return {
  176. quantity: resQuantity,
  177. storeId,
  178. uid,
  179. saasId,
  180. spuId,
  181. goodsName,
  182. skuId,
  183. storeName,
  184. roomId,
  185. };
  186. },
  187. handleResToGoodsCard(data) {
  188. // 转换数据 符合 goods-card展示
  189. const orderCardList = []; // 订单卡片列表
  190. const storeInfoList = [];
  191. const submitCouponList = []; //使用优惠券列表;
  192. data.storeGoodsList &&
  193. data.storeGoodsList.forEach((ele) => {
  194. const orderCard = {
  195. id: ele.storeId,
  196. storeName: ele.storeName,
  197. status: 0,
  198. statusDesc: '',
  199. amount: ele.storeTotalPayAmount,
  200. goodsList: [],
  201. }; // 订单卡片
  202. ele.skuDetailVos.forEach((item, index) => {
  203. orderCard.goodsList.push({
  204. id: index,
  205. thumb: item.image,
  206. title: item.goodsName,
  207. specs: item.skuSpecLst.map((s) => s.specValue), // 规格列表 string[]
  208. price: item.tagPrice || item.settlePrice || '0', // 优先取限时活动价
  209. settlePrice: item.settlePrice,
  210. titlePrefixTags: item.tagText ? [{ text: item.tagText }] : [],
  211. num: item.quantity,
  212. skuId: item.skuId,
  213. spuId: item.spuId,
  214. storeId: item.storeId,
  215. });
  216. });
  217. storeInfoList.push({
  218. storeId: ele.storeId,
  219. storeName: ele.storeName,
  220. remark: '',
  221. });
  222. submitCouponList.push({
  223. storeId: ele.storeId,
  224. couponList: ele.couponList || [],
  225. });
  226. this.noteInfo.push('');
  227. this.tempNoteInfo.push('');
  228. orderCardList.push(orderCard);
  229. });
  230. this.setData({ orderCardList, storeInfoList, submitCouponList });
  231. return data;
  232. },
  233. onGotoAddress() {
  234. /** 获取一个Promise */
  235. getAddressPromise()
  236. .then((address) => {
  237. this.handleOptionsParams({
  238. userAddressReq: { ...address, checked: true },
  239. });
  240. })
  241. .catch(() => {});
  242. const { userAddressReq } = this; // 收货地址
  243. let id = '';
  244. if (userAddressReq?.id) {
  245. id = `&id=${userAddressReq.id}`;
  246. }
  247. wx.navigateTo({
  248. url: `/pages/user/address/list/index?selectMode=1&isOrderSure=1${id}`,
  249. });
  250. },
  251. onNotes(e) {
  252. const { storenoteindex: storeNoteIndex } = e.currentTarget.dataset;
  253. // 添加备注信息
  254. this.setData({
  255. dialogShow: true,
  256. storeNoteIndex,
  257. });
  258. },
  259. onInput(e) {
  260. const { storeNoteIndex } = this.data;
  261. this.noteInfo[storeNoteIndex] = e.detail.value;
  262. },
  263. onBlur() {
  264. this.setData({
  265. notesPosition: 'center',
  266. });
  267. },
  268. onFocus() {
  269. this.setData({
  270. notesPosition: 'self',
  271. });
  272. },
  273. onTap() {
  274. this.setData({
  275. placeholder: '',
  276. });
  277. },
  278. onNoteConfirm() {
  279. // 备注信息 确认按钮
  280. const { storeInfoList, storeNoteIndex } = this.data;
  281. this.tempNoteInfo[storeNoteIndex] = this.noteInfo[storeNoteIndex];
  282. storeInfoList[storeNoteIndex].remark = this.noteInfo[storeNoteIndex];
  283. this.setData({
  284. dialogShow: false,
  285. storeInfoList,
  286. });
  287. },
  288. onNoteCancel() {
  289. // 备注信息 取消按钮
  290. const { storeNoteIndex } = this.data;
  291. this.noteInfo[storeNoteIndex] = this.tempNoteInfo[storeNoteIndex];
  292. this.setData({
  293. dialogShow: false,
  294. });
  295. },
  296. onSureCommit() {
  297. // 商品库存不足继续结算
  298. const { settleDetailData } = this.data;
  299. const { outOfStockGoodsList, storeGoodsList, inValidGoodsList } = settleDetailData;
  300. if ((outOfStockGoodsList && outOfStockGoodsList.length > 0) || (inValidGoodsList && storeGoodsList)) {
  301. // 合并正常商品 和 库存 不足商品继续支付
  302. // 过滤不必要的参数
  303. const filterOutGoodsList = [];
  304. outOfStockGoodsList &&
  305. outOfStockGoodsList.forEach((outOfStockGoods) => {
  306. const { storeName } = outOfStockGoods;
  307. outOfStockGoods.unSettlementGoods.forEach((ele) => {
  308. const data = ele;
  309. data.quantity = ele.reminderStock;
  310. data.storeName = storeName;
  311. filterOutGoodsList.push(data);
  312. });
  313. });
  314. const filterStoreGoodsList = this.getRequestGoodsList(storeGoodsList);
  315. const goodsRequestList = filterOutGoodsList.concat(filterStoreGoodsList);
  316. this.handleOptionsParams({ goodsRequestList });
  317. }
  318. },
  319. // 提交订单
  320. submitOrder() {
  321. const { settleDetailData, userAddressReq, invoiceData, storeInfoList, submitCouponList } = this.data;
  322. const { goodsRequestList } = this;
  323. if (!userAddressReq && !settleDetailData.userAddress) {
  324. Toast({
  325. context: this,
  326. selector: '#t-toast',
  327. message: '请添加收货地址',
  328. duration: 2000,
  329. icon: 'help-circle',
  330. });
  331. return;
  332. }
  333. if (this.payLock || !settleDetailData.settleType || !settleDetailData.totalAmount) {
  334. return;
  335. }
  336. this.payLock = true;
  337. const resSubmitCouponList = this.handleCouponList(submitCouponList);
  338. const params = {
  339. userAddressReq: settleDetailData.userAddress || userAddressReq,
  340. goodsRequestList: goodsRequestList,
  341. userName: settleDetailData.userAddress.name || userAddressReq.name,
  342. totalAmount: settleDetailData.totalPayAmount, //取优惠后的结算金额
  343. invoiceRequest: null,
  344. storeInfoList,
  345. couponList: resSubmitCouponList,
  346. };
  347. if (invoiceData && invoiceData.email) {
  348. params.invoiceRequest = invoiceData;
  349. }
  350. commitPay(params).then(
  351. (res) => {
  352. this.payLock = false;
  353. const { data } = res;
  354. // 提交出现 失效 不在配送范围 限购的商品 提示弹窗
  355. if (this.isInvalidOrder(data)) {
  356. return;
  357. }
  358. if (res.code === 'Success') {
  359. this.handlePay(data, settleDetailData);
  360. } else {
  361. Toast({
  362. context: this,
  363. selector: '#t-toast',
  364. message: res.msg || '提交订单超时,请稍后重试',
  365. duration: 2000,
  366. icon: '',
  367. });
  368. setTimeout(() => {
  369. // 提交支付失败 返回购物车
  370. wx.navigateBack();
  371. }, 2000);
  372. }
  373. },
  374. (err) => {
  375. this.payLock = false;
  376. if (err.code === 'CONTAINS_INSUFFICIENT_GOODS' || err.code === 'TOTAL_AMOUNT_DIFFERENT') {
  377. Toast({
  378. context: this,
  379. selector: '#t-toast',
  380. message: err.msg || '支付异常',
  381. duration: 2000,
  382. icon: '',
  383. });
  384. this.init();
  385. } else if (err.code === 'ORDER_PAY_FAIL') {
  386. Toast({
  387. context: this,
  388. selector: '#t-toast',
  389. message: '支付失败',
  390. duration: 2000,
  391. icon: 'close-circle',
  392. });
  393. setTimeout(() => {
  394. wx.redirectTo({ url: '/pages/order/order-list/index' });
  395. });
  396. } else if (err.code === 'ILLEGAL_CONFIG_PARAM') {
  397. Toast({
  398. context: this,
  399. selector: '#t-toast',
  400. message: '支付失败,微信支付商户号设置有误,请商家重新检查支付设置。',
  401. duration: 2000,
  402. icon: 'close-circle',
  403. });
  404. setTimeout(() => {
  405. wx.redirectTo({ url: '/pages/order/order-list/index' });
  406. });
  407. } else {
  408. Toast({
  409. context: this,
  410. selector: '#t-toast',
  411. message: err.msg || '提交支付超时,请稍后重试',
  412. duration: 2000,
  413. icon: '',
  414. });
  415. setTimeout(() => {
  416. // 提交支付失败 返回购物车
  417. wx.navigateBack();
  418. }, 2000);
  419. }
  420. },
  421. );
  422. },
  423. // 处理支付
  424. handlePay(data, settleDetailData) {
  425. const { channel, payInfo, tradeNo, interactId, transactionId } = data;
  426. const { totalAmount, totalPayAmount } = settleDetailData;
  427. const payOrderInfo = {
  428. payInfo: payInfo,
  429. orderId: tradeNo,
  430. orderAmt: totalAmount,
  431. payAmt: totalPayAmount,
  432. interactId: interactId,
  433. tradeNo: tradeNo,
  434. transactionId: transactionId,
  435. };
  436. if (channel === 'wechat') {
  437. wechatPayOrder(payOrderInfo);
  438. }
  439. },
  440. hide() {
  441. // 隐藏 popup
  442. this.setData({
  443. 'settleDetailData.abnormalDeliveryGoodsList': [],
  444. });
  445. },
  446. onReceipt() {
  447. // 跳转 开发票
  448. const invoiceData = this.invoiceData || {};
  449. wx.navigateTo({
  450. url: `/pages/order/receipt/index?invoiceData=${JSON.stringify(invoiceData)}`,
  451. });
  452. },
  453. onCoupons(e) {
  454. const { submitCouponList, currentStoreId } = this.data;
  455. const { goodsRequestList } = this;
  456. const { selectedList } = e.detail;
  457. const tempSubmitCouponList = submitCouponList.map((storeCoupon) => {
  458. return {
  459. couponList: storeCoupon.storeId === currentStoreId ? selectedList : storeCoupon.couponList,
  460. };
  461. });
  462. const resSubmitCouponList = this.handleCouponList(tempSubmitCouponList);
  463. //确定选择优惠券
  464. this.handleOptionsParams({ goodsRequestList }, resSubmitCouponList);
  465. this.setData({ couponsShow: false });
  466. },
  467. onOpenCoupons(e) {
  468. const { storeid } = e.currentTarget.dataset;
  469. this.setData({
  470. couponsShow: true,
  471. currentStoreId: storeid,
  472. });
  473. },
  474. handleCouponList(storeCouponList) {
  475. //处理门店优惠券 转换成接口需要
  476. if (!storeCouponList) return [];
  477. const resSubmitCouponList = [];
  478. storeCouponList.forEach((ele) => {
  479. resSubmitCouponList.push(...ele.couponList);
  480. });
  481. return resSubmitCouponList;
  482. },
  483. onGoodsNumChange(e) {
  484. const {
  485. detail: { value },
  486. currentTarget: {
  487. dataset: { goods },
  488. },
  489. } = e;
  490. const index = this.goodsRequestList.findIndex(
  491. ({ storeId, spuId, skuId }) => goods.storeId === storeId && goods.spuId === spuId && goods.skuId === skuId,
  492. );
  493. if (index >= 0) {
  494. // eslint-disable-next-line no-confusing-arrow
  495. const goodsRequestList = this.goodsRequestList.map((item, i) =>
  496. i === index ? { ...item, quantity: value } : item,
  497. );
  498. this.handleOptionsParams({ goodsRequestList });
  499. }
  500. },
  501. onPopupChange() {
  502. this.setData({
  503. popupShow: !this.data.popupShow,
  504. });
  505. },
  506. });