goods.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <template>
  2. <view class="container p-bottom">
  3. <view class="flow-mode">
  4. <selectSwitch :switchList="orderModeList" checked_bj_color="#113a28" @change="switchMode"/>
  5. </view>
  6. <!-- 快递配送:配送地址 -->
  7. <view @click="onSelectAddress" v-if="orderMode == false" class="flow-delivery">
  8. <view class="flow-delivery__detail dis-flex flex-y-center">
  9. <view class="detail-location dis-flex">
  10. <text class="iconfont icon-dingwei"></text>
  11. </view>
  12. <view class="detail-content flex-box">
  13. <block v-if="address">
  14. <view class="detail-content__title dis-flex">
  15. <text class="f-30">{{ address.name }}</text>
  16. <text class="detail-content__title-phone f-28">{{ address.mobile }}</text>
  17. </view>
  18. <view class="address-info detail-content__describe">
  19. <text class="region">{{ address.provinceName }}{{ address.cityName }}{{ address.regionName }}</text>
  20. <text class="detail">{{ address.detail }}</text>
  21. <text class="icon"> » </text>
  22. </view>
  23. </block>
  24. <block v-else>
  25. <view class="detail-content__describe dis-flex select-address">
  26. <text class="col-6">请选择配送地址 <text class="icon"> > </text></text>
  27. </view>
  28. </block>
  29. </view>
  30. <view class="detail-arrow dis-flex">
  31. <text class="iconfont icon-arrow-right"></text>
  32. </view>
  33. </view>
  34. </view>
  35. <!-- 门店自提:自提地址 -->
  36. <view v-if="orderMode == true" class="flow-delivery">
  37. <view class="flow-delivery__detail dis-flex flex-y-center">
  38. <view class="detail-location dis-flex">
  39. <text class="iconfont icon-dingwei"></text>
  40. </view>
  41. <view class="detail-content flex-box">
  42. <block>
  43. <view class="store">
  44. <view class="f-30 store-name">{{ storeInfo ? storeInfo.name : '' }}</view>
  45. <view class="f-30 store-phone">{{ storeInfo ? storeInfo.phone : '' }}</view>
  46. <text class="f-30 store-address">{{ storeInfo ? storeInfo.address : '' }}</text>
  47. </view>
  48. </block>
  49. </view>
  50. <view class="detail-arrow dis-flex">
  51. <text class="iconfont icon-arrow-right"></text>
  52. </view>
  53. </view>
  54. </view>
  55. <!-- 商品列表 -->
  56. <view class="m-top20">
  57. <view v-for="(item, index) in goodsCart" :key="index" class="checkout_list">
  58. <view class="flow-shopList dis-flex">
  59. <!-- 商品图片 -->
  60. <view class="flow-list-left">
  61. <image mode="scaleToFill" :src="item.goodsInfo.logo"></image>
  62. </view>
  63. <view class="flow-list-right flex-box">
  64. <!-- 商品名称 -->
  65. <text class="goods-name twolist-hidden">{{ item.goodsInfo.name }}</text>
  66. <!-- 商品规格 -->
  67. <view class="goods-props clearfix">
  68. <view class="goods-props-item" v-for="(props, idx) in item.specList" :key="idx">
  69. <text class="group-name">{{ props.specName }}: </text>
  70. <text>{{ props.specValue }};</text>
  71. </view>
  72. </view>
  73. <!-- 商品数量和单价 -->
  74. <view class="flow-list-cont dis-flex flex-x-between flex-y-center">
  75. <text class="small"> x {{ item.num }} </text>
  76. <text class="flow-cont">¥{{ item.goodsInfo.price }} </text>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. <view class="flow-num-box b-f">
  82. <text>共{{ totalNum }}件,合计:</text>
  83. <text class="flow-money col-m">¥{{ totalPrice }}</text>
  84. </view>
  85. </view>
  86. <!-- 订单折扣 -->
  87. <view class="flow-all-money b-f m-top20">
  88. <view class="detail-title">费用明细</view>
  89. <!-- 卡券 -->
  90. <view class="flow-all-list dis-flex">
  91. <text class="flex-five">使用卡券抵扣:</text>
  92. <view class="flex-five t-r">
  93. <view v-if="couponList.length > 0" @click="handleShowPopup()">
  94. <text class="col-m" v-if="useCouponInfo">-¥{{ useCouponInfo.amount }}</text>
  95. <text class="col-m" v-else>共有卡券{{ couponList.length }}张</text>
  96. <text class="right-arrow iconfont icon-arrow-right"></text>
  97. </view>
  98. <text v-else class="">无卡券可用</text>
  99. </view>
  100. </view>
  101. <!-- 积分抵扣 -->
  102. <view class="points flow-all-list dis-flex flex-y-center" v-if="usePoint > 0">
  103. <view class="block-left flex-five" @click="handleShowPoints()">
  104. <text class="title">使用{{ usePoint }}积分抵扣:</text>
  105. <text class="iconfont icon-help"></text>
  106. </view>
  107. <view class="flex-five dis-flex flex-x-end flex-y-center">
  108. <text class="points-money col-m">-¥{{ usePointAmount }}</text>
  109. <u-switch v-model="isUsePoints" size="48" active-color="#113a28" @change="getCartList()"></u-switch>
  110. </view>
  111. </view>
  112. <!-- 会员折扣 -->
  113. <view class="points flow-all-list dis-flex flex-y-center">
  114. <view class="block-left flex-five">
  115. <text class="title">会员支付折扣:</text>
  116. </view>
  117. <view class="flex-five dis-flex flex-x-end flex-y-center">
  118. <text>{{ (memberDiscount < 10 && memberDiscount > 0) ? memberDiscount : '无折扣' }}折</text>
  119. </view>
  120. </view>
  121. <!-- 运费 -->
  122. <view class="points flow-all-list dis-flex flex-y-center" v-if="deliveryFee > 0 && orderMode == false">
  123. <view class="block-left flex-five">
  124. <text class="title">配送费用:</text>
  125. </view>
  126. <view class="flex-five dis-flex flex-x-end flex-y-center">
  127. <text class="points-money col-m">¥{{ deliveryFee }}</text>
  128. </view>
  129. </view>
  130. </view>
  131. <!-- 支付方式 -->
  132. <view class="pay-method flow-all-money b-f m-top20">
  133. <view class="flow-all-list dis-flex">
  134. <text class="flex-five">支付方式</text>
  135. </view>
  136. <!-- 微信支付 -->
  137. <view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.WECHAT.value)">
  138. <view class="item-left dis-flex flex-y-center">
  139. <view class="item-left_icon wechat">
  140. <text class="iconfont icon-weixinzhifu"></text>
  141. </view>
  142. <view class="item-left_text">
  143. <text>{{ PayTypeEnum.WECHAT.name }}</text>
  144. </view>
  145. </view>
  146. <view class="item-right col-m" v-if="curPayType == PayTypeEnum.WECHAT.value">
  147. <text class="iconfont icon-duihao"></text>
  148. </view>
  149. </view>
  150. </view>
  151. <!-- 买家留言 -->
  152. <view class="flow-all-money b-f m-top20">
  153. <view class="ipt-wrapper">
  154. <textarea v-model="remark" rows="3" maxlength=100 placeholder="买家留言 (选填,100字以内)" type="text"></textarea>
  155. </view>
  156. </view>
  157. <!-- 提交订单 -->
  158. <view class="flow-fixed-footer b-f m-top10">
  159. <view class="dis-flex chackout-box">
  160. <view class="chackout-left pl-12">
  161. <view class="col-amount-do">支付金额:
  162. <text class="pay-amount">¥{{ payPrice ? payPrice.toFixed(2) : '0.00' }}</text>
  163. </view>
  164. </view>
  165. <view class="chackout-right" @click="onSubmitOrder()">
  166. <view class="flow-btn f-32" :class="{ disabled }">提交订单</view>
  167. </view>
  168. </view>
  169. </view>
  170. <!-- 积分说明弹窗 -->
  171. <u-modal v-model="showPoints" :title="`积分说明`">
  172. <scroll-view class="points-content" :scroll-y="true">
  173. <text>积分兑换金额</text>
  174. </scroll-view>
  175. </u-modal>
  176. <!-- 卡券弹出框 -->
  177. <u-popup v-model="showPopup" mode="bottom">
  178. <view class="popup__coupon">
  179. <view class="coupon__title f-30">选择卡券</view>
  180. <!-- 卡券列表 -->
  181. <view class="coupon-list">
  182. <scroll-view :scroll-y="true" style="height: 565rpx;">
  183. <view class="coupon-item" v-for="(item, index) in couponList" :key="index">
  184. <view class="item-wrapper"
  185. :class="[item.status == 'A' ? 'color-default': 'color-gray']"
  186. @click="handleSelectCoupon(index)">
  187. <view class="coupon-type">{{ item.type }}</view>
  188. <view class="tip dis-flex flex-dir-column flex-x-center">
  189. <text class="money">¥{{ item.amount }}</text>
  190. <text class="pay-line">{{ item.description }}</text>
  191. </view>
  192. <view class="split-line"></view>
  193. <view class="content dis-flex flex-dir-column flex-x-between">
  194. <view class="title">{{ item.name }}</view>
  195. <view class="bottom dis-flex flex-y-center">
  196. <view class="time flex-box">
  197. <block>{{ item.effectiveDate }}</block>
  198. </view>
  199. </view>
  200. </view>
  201. </view>
  202. </view>
  203. </scroll-view>
  204. </view>
  205. <!-- 不使用卡券 -->
  206. <view class="coupon__do_not dis-flex flex-y-center flex-x-center">
  207. <view class="control dis-flex flex-y-center flex-x-center" @click="handleNotUseCoupon()">
  208. <text class="f-26">不使用卡券</text>
  209. </view>
  210. </view>
  211. </view>
  212. </u-popup>
  213. <!-- 支付方式弹窗 -->
  214. <u-popup v-model="showPayPopup" mode="bottom" :closeable="true">
  215. <view class="pay-type-popup">
  216. <view class="title">请选择支付方式</view>
  217. <view class="pop-content">
  218. <!-- 微信支付 -->
  219. <view class="pay-item dis-flex flex-x-between" @click="doSubmitOrder(PayTypeEnum.WECHAT.value)">
  220. <view class="item-left dis-flex flex-y-center">
  221. <view class="item-left_icon wechat">
  222. <text class="iconfont icon-weixinzhifu"></text>
  223. </view>
  224. <view class="item-left_text">
  225. <text>{{ PayTypeEnum.WECHAT.name }}</text>
  226. </view>
  227. </view>
  228. </view>
  229. <!-- 余额支付 -->
  230. <view class="pay-item dis-flex flex-x-between" @click="doSubmitOrder(PayTypeEnum.BALANCE.value)">
  231. <view class="item-left dis-flex flex-y-center">
  232. <view class="item-left_icon balance">
  233. <text class="iconfont icon-qiandai"></text>
  234. </view>
  235. <view class="item-left_text">
  236. <text>{{ PayTypeEnum.BALANCE.name }}</text>
  237. </view>
  238. </view>
  239. </view>
  240. </view>
  241. </view>
  242. </u-popup>
  243. </view>
  244. </template>
  245. <script>
  246. import * as Verify from '@/utils/verify'
  247. import * as CartApi from '@/api/cart'
  248. import * as SettlementApi from '@/api/settlement'
  249. import DeliveryTypeEnum from '@/common/enum/order/DeliveryType'
  250. import PayTypeEnum from '@/common/enum/order/PayType'
  251. import { wxPayment } from '@/utils/app'
  252. import selectSwitch from "@/components/xuan-switch/xuan-switch.vue";
  253. import * as AddressApi from '@/api/address'
  254. import * as settingApi from '@/api/setting'
  255. export default {
  256. components: {
  257. selectSwitch
  258. },
  259. data() {
  260. return {
  261. // 枚举类
  262. PayTypeEnum,
  263. // 当前页面参数
  264. options: {},
  265. // 当前选中的支付方式
  266. curPayType: PayTypeEnum.WECHAT.value,
  267. // 买家留言
  268. remark: '',
  269. // 禁用submit按钮
  270. disabled: false,
  271. // 按钮禁用
  272. disabled: false,
  273. goodsCart: [],
  274. // 优惠券列表
  275. couponList: [],
  276. totalPrice: 0,
  277. payPrice: 0,
  278. totalNum: 0,
  279. deliveryFee: 0,
  280. orderModeList: ['堂食自提', '配送到家'],
  281. orderMode: true,
  282. address: null,
  283. useCouponInfo: null,
  284. selectCouponId: 0,
  285. myPoint: 0,
  286. usePoint: 0,
  287. usePointAmount: 0.00,
  288. // 是否使用积分抵扣
  289. isUsePoints: true,
  290. // 是否显示积分说明
  291. showPoints: false,
  292. // 会员折扣
  293. memberDiscount: 0,
  294. // 是否显示卡券弹窗
  295. showPopup: false,
  296. storeInfo: null,
  297. // 支付方式弹窗
  298. showPayPopup: false,
  299. // 订单ID
  300. orderId: ""
  301. }
  302. },
  303. /**
  304. * 生命周期函数--监听页面加载
  305. */
  306. onLoad(options) {
  307. this.options = options;
  308. },
  309. /**
  310. * 生命周期函数--监听页面显示
  311. */
  312. onShow() {
  313. const app = this;
  314. if (app.orderId) {
  315. app.navToOrderResult(app.orderId);
  316. }
  317. // 获取购物车信息
  318. app.getCartList();
  319. // 获取默认收货地址
  320. app.getDefaultAddress();
  321. // 获取店铺信息
  322. app.getStoreInfo();
  323. },
  324. methods: {
  325. // 获取购物车信息
  326. getCartList() {
  327. const app = this
  328. if (!app.isUsePoints) {
  329. app.usePoint = 0;
  330. app.usePointAmount = 0;
  331. }
  332. return new Promise((resolve, reject) => {
  333. // 配送或自取
  334. let orderMode = "oneself";
  335. if (!app.orderMode) {
  336. orderMode = "express";
  337. }
  338. CartApi.list(app.options.cartIds, app.options.goodsId, app.options.skuId, app.options.buyNum, app.selectCouponId, app.isUsePoints, orderMode)
  339. .then(result => {
  340. app.goodsCart = result.data.list;
  341. app.totalNum = result.data.totalNum;
  342. app.totalPrice = result.data.totalPrice;
  343. app.payPrice = result.data.payPrice;
  344. app.couponList = result.data.couponList;
  345. app.useCouponInfo = result.data.useCouponInfo;
  346. app.usePoint = result.data.usePoint;
  347. app.myPoint = result.data.myPoint;
  348. app.deliveryFee = result.data.deliveryFee;
  349. if (app.usePoint < 1) {
  350. app.isUsePoints = false;
  351. }
  352. app.usePointAmount = result.data.usePointAmount;
  353. app.memberDiscount = result.data.memberDiscount ? result.data.memberDiscount : 0;
  354. resolve(result);
  355. })
  356. .catch(err => reject(err))
  357. })
  358. },
  359. // 获取默认收货地址
  360. getDefaultAddress() {
  361. const app = this
  362. AddressApi.detail(0)
  363. .then(result => {
  364. app.address = result.data.address ? result.data.address : null;
  365. })
  366. },
  367. // 显示积分说明
  368. handleShowPoints() {
  369. this.showPoints = true;
  370. },
  371. // 显示卡券弹窗
  372. handleShowPopup() {
  373. this.showPopup = true;
  374. },
  375. // 选择卡券
  376. handleSelectCoupon(index) {
  377. const app = this;
  378. // 当前选择的卡券
  379. const couponItem = app.couponList[index];
  380. // 记录选中的卡券id
  381. if (couponItem.status != 'A') {
  382. app.$error('该卡券不可用');
  383. return false;
  384. }
  385. app.selectCouponId = couponItem.userCouponId;
  386. // 重新获取购物车信息
  387. app.getCartList();
  388. // 隐藏卡券弹层
  389. app.showPopup = false;
  390. },
  391. // 不使用卡券
  392. handleNotUseCoupon() {
  393. const app = this;
  394. app.selectCouponId = 0;
  395. // 重新获取购物车信息
  396. app.getCartList();
  397. // 隐藏卡券弹层
  398. app.showPopup = false;
  399. },
  400. // 选择支付方式
  401. handleSelectPayType(value) {
  402. this.curPayType = value
  403. },
  404. // 快递配送:选择收货地址
  405. onSelectAddress() {
  406. this.$navTo('pages/address/index', { from: 'checkout' })
  407. },
  408. // 切换配送模式
  409. switchMode(mode) {
  410. const app = this;
  411. app.orderMode = mode;
  412. if (mode && !app.storeInfo) {
  413. app.getStoreInfo();
  414. }
  415. app.getCartList();
  416. },
  417. // 获取店铺详情
  418. getStoreInfo() {
  419. const app = this;
  420. if (!app.storeInfo) {
  421. settingApi.storeDetail()
  422. .then(result => {
  423. app.storeInfo = result.data.storeInfo;
  424. })
  425. }
  426. },
  427. // 弹出支付方式
  428. onSubmitOrder() {
  429. const app = this
  430. if (app.disabled) {
  431. return false
  432. }
  433. const tableId = uni.getStorageSync("tableId") ? uni.getStorageSync("tableId") : 0;
  434. if (tableId > 0) {
  435. return app.doSubmitOrder(PayTypeEnum.WECHAT.value);
  436. }
  437. if (app.totalPrice < 0 || app.goodsCart.length < 1) {
  438. app.disabled = true
  439. return false
  440. }
  441. // 表单验证
  442. if (!app.orderMode && app.address == undefined) {
  443. app.$toast('请先选择配送地址哦')
  444. return false
  445. }
  446. // 配送或自取
  447. let orderMode = "oneself";
  448. if (!app.orderMode) {
  449. orderMode = "express";
  450. }
  451. if (!app.payPrice) {
  452. app.doSubmitOrder(PayTypeEnum.BALANCE.value)
  453. } else {
  454. app.showPayPopup = true;
  455. }
  456. },
  457. // 订单提交
  458. doSubmitOrder(payType) {
  459. const app = this;
  460. if (app.disabled) {
  461. app.$toast('请勿重复提交订单');
  462. return false;
  463. }
  464. if (app.totalPrice < 0 || app.goodsCart.length < 1) {
  465. app.$toast('提交订单商品有误');
  466. app.disabled = true;
  467. return false;
  468. }
  469. // 表单验证
  470. if (!app.orderMode && app.address == undefined) {
  471. app.$toast('请先选择配送地址哦');
  472. return false;
  473. }
  474. // 配送或自取
  475. let orderMode = "oneself";
  476. if (!app.orderMode) {
  477. orderMode = "express";
  478. }
  479. // 按钮禁用
  480. app.disabled = true;
  481. // 请求api
  482. SettlementApi.submit(0, "", "goods", app.remark, 0, app.usePoint, app.selectCouponId, app.options.cartIds, app.options.goodsId, app.options.skuId, app.options.buyNum, orderMode, payType)
  483. .then(result => {
  484. app.onSubmitCallback(result);
  485. })
  486. .catch(err => {
  487. if (err.result) {
  488. const errData = err.result.data;
  489. if (errData.isCreated) {
  490. app.navToOrderResult(errData.orderInfo.id);
  491. return false;
  492. }
  493. }
  494. app.disabled = false;
  495. })
  496. },
  497. // 订单提交成功后回调
  498. onSubmitCallback(result) {
  499. const app = this;
  500. if (result.code != '200' && !result.data) {
  501. if (result.message) {
  502. app.$error(result.message);
  503. } else {
  504. app.$error('订单提交失败');
  505. }
  506. app.disabled = false;
  507. return false;
  508. }
  509. const tableId = uni.getStorageSync("tableId") ? uni.getStorageSync("tableId") : 0;
  510. if (tableId > 0) {
  511. app.navToOrderResult(result.data.orderInfo.id, '订单提交成功');
  512. return false;
  513. }
  514. // 发起微信支付
  515. if (result.data.payType == PayTypeEnum.WECHAT.value) {
  516. // #ifdef H5
  517. app.orderId = result.data.orderInfo.id;
  518. // #endif
  519. wxPayment(result.data.payment).then(() => {
  520. app.$success('支付成功');
  521. }).catch(err => {
  522. app.$error('支付失败');
  523. }).finally(() => {
  524. app.disabled = false;
  525. app.navToOrderResult(result.data.orderInfo.id, '');
  526. })
  527. }
  528. // 余额支付
  529. if (result.data.payType == PayTypeEnum.BALANCE.value) {
  530. app.disabled = false;
  531. app.navToOrderResult(result.data.orderInfo.id, result.message);
  532. }
  533. },
  534. // 跳转到订单结果页
  535. navToOrderResult(orderId, message) {
  536. if (!message || message == undefined) {
  537. message = "";
  538. }
  539. this.$navTo('pages/order/result?orderId='+orderId+'&message=' + message);
  540. },
  541. /**
  542. * 下拉刷新
  543. */
  544. onPullDownRefresh() {
  545. const app = this;
  546. setTimeout(() => {
  547. // 获取购物车信息
  548. app.getCartList();
  549. // 获取默认收货地址
  550. app.getDefaultAddress();
  551. // 获取店铺信息
  552. app.getStoreInfo();
  553. uni.stopPullDownRefresh();
  554. }, 1000)
  555. }
  556. }
  557. }
  558. </script>
  559. <style lang="scss" scoped>
  560. @import "./style.scss";
  561. </style>