index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <template>
  2. <view class="container p-bottom" v-if="order.goodsList.length">
  3. <!-- 快递配送:配送地址 -->
  4. <view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" @click="onSelectAddress" class="flow-delivery">
  5. <view class="flow-delivery__detail dis-flex flex-y-center">
  6. <view class="detail-location dis-flex">
  7. <text class="iconfont icon-dingwei"></text>
  8. </view>
  9. <view class="detail-content flex-box">
  10. <block v-if="order.address">
  11. <view class="detail-content__title dis-flex">
  12. <text class="f-30">{{ order.address.name }}</text>
  13. <text class="detail-content__title-phone f-28">{{ order.address.phone }}</text>
  14. </view>
  15. <view class="address detail-content__describe">
  16. <text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
  17. <text class="detail">{{ order.address.detail }}</text>
  18. </view>
  19. </block>
  20. <block v-else>
  21. <view class="detail-content__describe dis-flex">
  22. <text class="col-6">请选择配送地址</text>
  23. </view>
  24. </block>
  25. </view>
  26. <view class="detail-arrow dis-flex">
  27. <text class="iconfont icon-arrow-right"></text>
  28. </view>
  29. </view>
  30. </view>
  31. <!-- 商品列表 -->
  32. <view class="m-top20">
  33. <view class="checkout_list" v-for="(item, index) in order.goodsList" :key="index">
  34. <view class="flow-shopList dis-flex" data-index="index" @click="onTargetGoods(item.goods_id)">
  35. <!-- 商品图片 -->
  36. <view class="flow-list-left">
  37. <image mode="scaleToFill" :src="item.goods_image"></image>
  38. </view>
  39. <view class="flow-list-right flex-box">
  40. <!-- 商品名称 -->
  41. <text class="goods-name twoline-hide">{{ item.goods_name }}</text>
  42. <!-- 商品规格 -->
  43. <view class="goods-props clearfix">
  44. <view class="goods-props-item" v-for="(props, idx) in item.skuInfo.goods_props" :key="idx">
  45. <text class="group-name">{{ props.group.name }}: </text>
  46. <text>{{ props.value.name }};</text>
  47. </view>
  48. </view>
  49. <!-- 商品数量和单价 -->
  50. <view class="flow-list-cont dis-flex flex-x-between flex-y-center">
  51. <text class="small">×{{ item.total_num }}</text>
  52. <text class="flow-cont" :class="[item.is_user_grade ? 'price-delete' : '']">¥{{ item.goods_price }}</text>
  53. </view>
  54. <!-- 会员折扣价 -->
  55. <view v-if="item.is_user_grade" class="grade-price">
  56. <text>会员折扣价:¥{{ item.grade_goods_price }}</text>
  57. </view>
  58. </view>
  59. </view>
  60. </view>
  61. <view class="flow-num-box b-f">
  62. <text>共{{ order.orderTotalNum }}件商品,合计:</text>
  63. <text class="flow-money col-m">¥{{ order.orderTotalPrice }}</text>
  64. </view>
  65. </view>
  66. <!-- 商品金额 -->
  67. <view class="flow-all-money b-f m-top20">
  68. <view class="flow-all-list dis-flex">
  69. <text class="flex-five">订单总金额:</text>
  70. <view class="flex-five t-r">
  71. <text class="col-m">¥{{ order.orderTotalPrice }}</text>
  72. </view>
  73. </view>
  74. <!-- 优惠券 -->
  75. <view class="flow-all-list dis-flex">
  76. <text class="flex-five">优惠券:</text>
  77. <view class="flex-five t-r">
  78. <view v-if="order.couponList.length > 0" @click="handleShowPopup()">
  79. <text class="col-m" v-if="order.couponId > 0">-¥{{ order.couponMoney }}</text>
  80. <text class="col-m" v-else>有{{ order.couponList.length }}张优惠券</text>
  81. <text class="right-arrow iconfont icon-arrow-right"></text>
  82. </view>
  83. <text v-else class="">无优惠券可用</text>
  84. </view>
  85. </view>
  86. <!-- 积分抵扣 -->
  87. <view v-if="order.isAllowPoints" class="points flow-all-list dis-flex flex-y-center">
  88. <view class="block-left flex-five" @click="handleShowPoints()">
  89. <text class="title">可用{{ setting.points_name }}抵扣:</text>
  90. <text class="iconfont icon-help"></text>
  91. </view>
  92. <view class="flex-five dis-flex flex-x-end flex-y-center">
  93. <text class="points-money col-m">-¥{{ order.pointsMoney }}</text>
  94. <u-switch v-model="isUsePoints" size="48" active-color="#07c160" @change="getOrderData()"></u-switch>
  95. </view>
  96. </view>
  97. <!-- 配送费用 -->
  98. <view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" class="dis-flex flow-all-list">
  99. <text class="flex-five">配送费用:</text>
  100. <view class="flex-five t-r">
  101. <view v-if="order.address">
  102. <text class="col-m" v-if="order.isIntraRegion">+¥{{ order.expressPrice }}</text>
  103. <text v-else>不在配送范围</text>
  104. </view>
  105. <view v-else>
  106. <text class="col-7">请先选择配送地址</text>
  107. </view>
  108. </view>
  109. </view>
  110. </view>
  111. <!-- 支付方式 -->
  112. <view class="pay-method flow-all-money b-f m-top20">
  113. <view class="flow-all-list dis-flex">
  114. <text class="flex-five">支付方式</text>
  115. </view>
  116. <!-- 微信支付 -->
  117. <!-- #ifdef MP-WEIXIN -->
  118. <view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.WECHAT.value)">
  119. <view class="item-left dis-flex flex-y-center">
  120. <view class="item-left_icon wechat">
  121. <text class="iconfont icon-wechat-pay"></text>
  122. </view>
  123. <view class="item-left_text">
  124. <text>{{ PayTypeEnum.WECHAT.name }}</text>
  125. </view>
  126. </view>
  127. <view class="item-right col-m" v-if="curPayType == PayTypeEnum.WECHAT.value">
  128. <text class="iconfont icon-check"></text>
  129. </view>
  130. </view>
  131. <!-- #endif -->
  132. <!-- 余额支付 -->
  133. <view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.BALANCE.value)">
  134. <view class="item-left dis-flex flex-y-center">
  135. <view class="item-left_icon balance">
  136. <text class="iconfont icon-balance-pay"></text>
  137. </view>
  138. <view class="item-left_text">
  139. <text>{{ PayTypeEnum.BALANCE.name }}</text>
  140. </view>
  141. <view class="user-balance">
  142. <text>(可用¥{{ personal.balance }}元)</text>
  143. </view>
  144. </view>
  145. <view class="item-right col-m" v-if="curPayType == PayTypeEnum.BALANCE.value">
  146. <text class="iconfont icon-check"></text>
  147. </view>
  148. </view>
  149. </view>
  150. <!-- 买家留言 -->
  151. <view class="flow-all-money b-f m-top20">
  152. <view class="ipt-wrapper dis-flex flow-all-list">
  153. <input v-model="remark" placeholder="选填:买家留言(50字以内)"></input>
  154. </view>
  155. </view>
  156. <!-- 提交订单 -->
  157. <view class="flow-fixed-footer b-f m-top10">
  158. <view class="dis-flex chackout-box">
  159. <view class="chackout-left pl-12">实付款:
  160. <text class="col-m">¥{{ order.orderPayPrice }}</text>
  161. </view>
  162. <view class="chackout-right" @click="onSubmitOrder()">
  163. <view class="flow-btn f-32" :class="{ disabled }">提交订单</view>
  164. </view>
  165. </view>
  166. </view>
  167. <!-- 积分说明弹窗 -->
  168. <u-modal v-model="showPoints" :title="`${setting.points_name}说明`">
  169. <scroll-view class="points-content" :scroll-y="true">
  170. <text>{{ setting.points_describe }}</text>
  171. </scroll-view>
  172. </u-modal>
  173. <!-- 优惠券弹出框 -->
  174. <u-popup v-model="showPopup" mode="bottom">
  175. <view class="popup__coupon">
  176. <view class="coupon__title f-30">选择优惠券</view>
  177. <!-- 优惠券列表 -->
  178. <view class="coupon-list">
  179. <scroll-view :scroll-y="true" style="height: 565rpx;">
  180. <view class="coupon-item" v-for="(item, index) in order.couponList" :key="index">
  181. <view class="item-wrapper"
  182. :class="[item.is_apply ? 'color-' + CouponColors[index % CouponColors.length] : 'color-gray']"
  183. @click="handleSelectCoupon(index)">
  184. <view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
  185. <view class="tip dis-flex flex-dir-column flex-x-center">
  186. <view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
  187. <text class="f-30">¥</text>
  188. <text class="money">{{ item.reduce_price }}</text>
  189. </view>
  190. <text class="money"
  191. v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}折</text>
  192. <text class="pay-line">满{{ item.min_price }}元可用</text>
  193. </view>
  194. <view class="split-line"></view>
  195. <view class="content dis-flex flex-dir-column flex-x-between">
  196. <view class="title">{{ item.name }}</view>
  197. <view class="bottom dis-flex flex-y-center">
  198. <view class="time flex-box">
  199. <block v-if="item.start_time === item.end_time">{{ item.start_time }}</block>
  200. <block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
  201. </view>
  202. </view>
  203. </view>
  204. </view>
  205. </view>
  206. </scroll-view>
  207. </view>
  208. <!-- 不使用优惠券 -->
  209. <view class="coupon__do_not dis-flex flex-y-center flex-x-center">
  210. <view class="control dis-flex flex-y-center flex-x-center" @click="handleNotUseCoupon()">
  211. <text class="f-26">不使用优惠券</text>
  212. </view>
  213. </view>
  214. </view>
  215. </u-popup>
  216. </view>
  217. </template>
  218. <script>
  219. import * as Verify from '@/utils/verify'
  220. import * as CheckoutApi from '@/api/checkout'
  221. import { CouponTypeEnum } from '@/common/enum/coupon'
  222. import DeliveryTypeEnum from '@/common/enum/order/DeliveryType'
  223. import PayTypeEnum from '@/common/enum/order/PayType'
  224. import { wxPayment } from '@/core/app'
  225. const CouponColors = ['red', 'blue', 'violet', 'yellow']
  226. export default {
  227. data() {
  228. return {
  229. // 枚举类
  230. DeliveryTypeEnum,
  231. PayTypeEnum,
  232. CouponTypeEnum,
  233. // 当前页面参数
  234. options: {},
  235. // 优惠券颜色组
  236. CouponColors,
  237. // 当前选中的配送方式
  238. curDelivery: null,
  239. // 当前选中的支付方式
  240. curPayType: PayTypeEnum.BALANCE.value,
  241. // 选择的优惠券
  242. selectCouponId: 0,
  243. // 是否使用积分抵扣
  244. isUsePoints: false,
  245. // 买家留言
  246. remark: '',
  247. // 禁用submit按钮
  248. disabled: false,
  249. // 是否显示积分说明
  250. showPoints: false,
  251. // 是否显示优惠券弹窗
  252. showPopup: false,
  253. // 按钮禁用
  254. disabled: false,
  255. // 订单信息 (从后端api中获取)
  256. order: {
  257. // 商品列表
  258. goodsList: [],
  259. // 优惠券列表
  260. couponList: [],
  261. // 是否存在收货地址
  262. existAddress: false,
  263. // 默认收货地址
  264. address: null,
  265. // 是否存在收货地址
  266. existAddress: false,
  267. // 当前用户收货城市是否存在配送规则中
  268. isIntraRegion: true,
  269. // 是否存在错误
  270. hasError: false,
  271. // 错误信息
  272. errorMsg: ''
  273. },
  274. // 个人信息
  275. personal: {},
  276. // 商城设置
  277. setting: {}
  278. }
  279. },
  280. /**
  281. * 生命周期函数--监听页面加载
  282. */
  283. onLoad(options) {
  284. this.options = options
  285. },
  286. /**
  287. * 生命周期函数--监听页面显示
  288. */
  289. onShow() {
  290. // 获取当前订单信息
  291. this.getOrderData()
  292. },
  293. methods: {
  294. // 获取订单数据
  295. getOrderData() {
  296. const app = this
  297. // 请求的参数
  298. const params = {
  299. delivery: app.curDelivery || 0,
  300. couponId: app.selectCouponId || 0,
  301. isUsePoints: app.isUsePoints ? 1 : 0,
  302. }
  303. // 请求api
  304. CheckoutApi.order(app.options.mode, app.getRequestParam())
  305. .then(result => {
  306. app.initData(result.data)
  307. })
  308. .catch(err => err)
  309. },
  310. // 初始化数据
  311. initData({ order, setting, personal }) {
  312. const app = this
  313. app.order = order
  314. app.personal = personal
  315. app.setting = setting
  316. app.order.goodsList.forEach((item) =>{
  317. item.goods_image = item.goods_image.replace('http://','https://')
  318. })
  319. // 显示错误信息
  320. if (order.hasError) {
  321. app.$toast(order.errorMsg)
  322. }
  323. // 当前选择的配送方式
  324. app.curDelivery = order.delivery
  325. // 如果只有一种配送方式则不显示选项卡
  326. app.isShowTab = setting.deliveryType.length > 1
  327. // 当前选择支付方式 (如果是微信小程序默认使用微信支付)
  328. if (app.$platform === 'MP-WEIXIN') {
  329. app.curPayType = PayTypeEnum.WECHAT.value
  330. }
  331. },
  332. // 获取api请求的参数
  333. getRequestParam() {
  334. const app = this
  335. const { options } = app
  336. // 结算模式的固定参数
  337. const modeParam = {}
  338. // 结算模式: 立即购买
  339. if (options.mode === 'buyNow') {
  340. modeParam.goodsId = options.goodsId
  341. modeParam.goodsNum = options.goodsNum
  342. modeParam.goodsSkuId = options.goodsSkuId
  343. }
  344. // 结算模式: 购物车
  345. if (options.mode === 'cart') {
  346. modeParam.cartIds = options.cartIds
  347. }
  348. // 订单结算参数(用户选择)
  349. const orderParam = {
  350. delivery: app.curDelivery || 0,
  351. couponId: app.selectCouponId || 0,
  352. isUsePoints: app.isUsePoints ? 1 : 0,
  353. }
  354. return { ...orderParam, ...modeParam }
  355. },
  356. // 显示积分说明
  357. handleShowPoints() {
  358. this.showPoints = true
  359. },
  360. // 显示优惠券弹窗
  361. handleShowPopup() {
  362. this.showPopup = true
  363. },
  364. // 选择优惠券
  365. handleSelectCoupon(index) {
  366. const app = this
  367. const { couponList } = app.order
  368. // 当前选择的优惠券
  369. const couponItem = couponList[index]
  370. // 判断是否在适用范围
  371. if (!couponItem.is_apply) {
  372. app.$toast(couponItem.not_apply_info)
  373. return
  374. }
  375. // 记录选中的优惠券id
  376. app.selectCouponId = couponItem.user_coupon_id
  377. // 重新获取订单信息
  378. app.getOrderData()
  379. // 隐藏优惠券弹层
  380. app.showPopup = false
  381. },
  382. // 不使用优惠券
  383. handleNotUseCoupon() {
  384. const app = this
  385. app.selectCouponId = 0
  386. // 重新获取订单信息
  387. app.getOrderData()
  388. // 隐藏优惠券弹层
  389. app.showPopup = false
  390. },
  391. // 选择支付方式
  392. handleSelectPayType(value) {
  393. this.curPayType = value
  394. },
  395. // 快递配送:选择收货地址
  396. onSelectAddress() {
  397. this.$navTo('pages/address/index', { from: 'checkout' })
  398. },
  399. // 跳转到商品详情页
  400. onTargetGoods(goodsId) {
  401. this.$navTo('pages/goods/detail', { goodsId })
  402. },
  403. // 订单提交
  404. onSubmitOrder() {
  405. const app = this
  406. if (app.disabled) {
  407. return false
  408. }
  409. // 表单验证
  410. if (!app.onVerifyFrom()) {
  411. return false
  412. }
  413. // 按钮禁用
  414. app.disabled = true
  415. // 请求api
  416. CheckoutApi.submit(app.options.mode, app.getFormData())
  417. .then(result => app.onSubmitCallback(result))
  418. .catch(err => {
  419. if (err.result) {
  420. const errData = err.result.data
  421. if (errData.is_created) {
  422. app.navToMyOrder()
  423. return false
  424. }
  425. }
  426. app.disabled = false
  427. })
  428. },
  429. // 订单提交成功后回调
  430. onSubmitCallback(result) {
  431. const app = this
  432. // 发起微信支付
  433. if (result.data.payType == PayTypeEnum.WECHAT.value) {
  434. wxPayment(result.data.payment)
  435. .then(() => app.$success('支付成功'))
  436. .catch(err => app.$error('订单未支付'))
  437. .finally(() => {
  438. app.disabled = false
  439. app.navToMyOrder()
  440. })
  441. }
  442. // 余额支付
  443. if (result.data.payType == PayTypeEnum.BALANCE.value) {
  444. app.$success('支付成功')
  445. app.disabled = false
  446. app.navToMyOrder()
  447. }
  448. },
  449. // 跳转到我的订单(等待1秒)
  450. navToMyOrder() {
  451. setTimeout(() => {
  452. this.$navTo('pages/order/index')
  453. }, 1000)
  454. },
  455. // 表单提交的数据
  456. getFormData() {
  457. const app = this
  458. const { options } = app
  459. // 表单数据
  460. const form = {
  461. delivery: app.curDelivery,
  462. payType: app.curPayType,
  463. couponId: app.selectCouponId || 0,
  464. isUsePoints: app.isUsePoints ? 1 : 0,
  465. remark: app.remark || '',
  466. }
  467. // 创建订单-立即购买
  468. if (options.mode === 'buyNow') {
  469. form.goodsId = options.goodsId
  470. form.goodsNum = options.goodsNum
  471. form.goodsSkuId = options.goodsSkuId
  472. }
  473. // 创建订单-购物车结算
  474. if (options.mode === 'cart') {
  475. form.cartIds = options.cartIds || null
  476. }
  477. return form
  478. },
  479. // 表单验证
  480. onVerifyFrom() {
  481. const app = this
  482. if (app.hasError) {
  483. app.$toast(app.errorMsg)
  484. return false
  485. }
  486. return true
  487. },
  488. }
  489. }
  490. </script>
  491. <style lang="scss" scoped>
  492. @import "./style.scss";
  493. </style>