You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
yanzong_qianduan/pages/checkout/index.vue

601 lines
22 KiB

<template>
<view class="container p-bottom" :style="appThemeStyle">
<view v-if="order.goodsList.length">
<!-- 实物订单:选择配送方式 -->
<block v-if="order.orderType == OrderTypeEnum.PHYSICAL.value">
<!-- 配送方式选项卡 -->
<view v-if="isShowTab" class="swiper-tab dis-flex flex-y-center flex-x-around">
<view class="swiper-tab-item" :class="{ on: curDelivery == DeliveryTypeEnum.EXPRESS.value }"
@click="handleSwichDelivery(DeliveryTypeEnum.EXPRESS.value)">
<text>快递配送</text>
</view>
<view class="swiper-tab-item" :class="{ on: curDelivery == DeliveryTypeEnum.EXTRACT.value }"
@click="handleSwichDelivery(DeliveryTypeEnum.EXTRACT.value)">
<text>上门自提</text>
</view>
</view>
<!-- 快递配送:配送地址 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" @click="onSelectAddress" class="flow-delivery">
<view class="flow-delivery__detail dis-flex flex-y-center">
<view class="detail-location dis-flex">
<text class="iconfont icon-dingwei"></text>
</view>
<view class="detail-content flex-box">
<block v-if="order.address">
<view class="detail-content__title dis-flex">
<text class="f-30">{{ order.address.name }}</text>
<text class="detail-content__title-phone f-28">{{ order.address.phone }}</text>
</view>
<view class="address detail-content__describe">
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ order.address.detail }}</text>
</view>
</block>
<block v-else>
<view class="detail-content__describe dis-flex">
<text class="col-6">请选择配送地址</text>
</view>
</block>
</view>
<view class="detail-arrow dis-flex">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 上门自提:自提门店 -->
<block v-if="curDelivery == DeliveryTypeEnum.EXTRACT.value">
<view class="flow-delivery" @click="onSelectExtractPoint()">
<view class="flow-delivery__detail dis-flex flex-y-center">
<view class="detail-location dis-flex">
<text class="iconfont icon-dingwei"></text>
</view>
<view class="detail-content flex-box">
<block v-if="order.extractShop.shop_id">
<view class="detail-content__title dis-flex">
<text class="f-30">{{ order.extractShop.shop_name }}</text>
</view>
<view class="detail-content__describe">
<text class="col-7">{{ order.extractShop.region.province }} {{ order.extractShop.region.city }}</text>
<text class="col-7">{{ order.extractShop.region.region }} {{ order.extractShop.address }}</text>
</view>
</block>
<block v-else>
<view class="detail-content__describe dis-flex">
<text class="col-6">请选择自提点</text>
</view>
</block>
</view>
<view class="detail-arrow dis-flex">
<text class="iconfont icon-arrow-right user-orderJtou"></text>
</view>
</view>
</view>
<!-- 自提联系方式 -->
<view class="flow-extract-contact b-f">
<view class="contact-item dis-flex">
<view class="item-label dis-flex flex-x-end flex-y-center">
<text>联系人:</text>
</view>
<view class="item-ipt flex-box dis-flex flex-y-center">
<input class="input flex-box" placeholder="请填写联系人姓名" v-model="linkman" />
</view>
</view>
<view class="contact-item dis-flex">
<view class="item-label dis-flex flex-x-end flex-y-center">
<text>联系电话:</text>
</view>
<view class="item-ipt flex-box dis-flex flex-y-center">
<input class="input flex-box" placeholder="请填写联系电话" v-model="phone" />
</view>
</view>
</view>
</block>
</block>
<!-- 商品列表 -->
<view class="checkout_list" v-for="(item, index) in order.goodsList" :key="index">
<view class="flow-shopList dis-flex" data-index="index" @click="onTargetGoods(item.goods_id)">
<!-- 商品图片 -->
<view class="flow-list-left">
<image mode="scaleToFill" :src="item.goods_image"></image>
</view>
<view class="flow-list-right flex-box">
<!-- 商品名称 -->
<text class="goods-name twoline-hide">{{ item.goods_name }}</text>
<!-- 商品规格 -->
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.skuInfo.goods_props" :key="idx">
<text class="group-name">{{ props.group.name }}: </text>
<text>{{ props.value.name }};</text>
</view>
</view>
<!-- 商品数量和单价 -->
<view class="flow-list-cont dis-flex flex-x-between flex-y-center">
<text class="small">×{{ item.total_num }}</text>
<text class="flow-cont" :class="[item.is_user_grade ? 'price-delete' : '']">¥{{ item.goods_price }}</text>
</view>
<!-- 会员折扣价 -->
<view v-if="item.is_user_grade" class="grade-price">
<text>会员折扣价:¥{{ item.grade_goods_price }}</text>
</view>
</view>
</view>
</view>
<view class="flow-num-box b-f">
<!-- <text>共{{ order.orderTotalNum }}件商品,合计:</text>
<text class="flow-money col-m">¥{{ order.orderTotalPrice }}</text> -->
<text>共{{ order.orderTotalNum }}件商品</text>
</view>
<!-- 商品金额 -->
<view class="flow-all-money b-f m-top20">
<view class="flow-all-list dis-flex">
<text class="flex-five">订单总金额:</text>
<view class="flex-five t-r">
<text class="col-m">¥{{ order.orderTotalPrice }}</text>
</view>
</view>
<!-- 优惠券 -->
<view v-if="$checkModule('market-coupon')" class="flow-all-list dis-flex">
<text class="flex-five">优惠券:</text>
<view class="flex-five t-r">
<view v-if="order.couponList.length > 0" @click="handleShowPopup()">
<text class="col-m" v-if="order.couponId > 0">-¥{{ order.couponMoney }}</text>
<text class="col-m" v-else>有{{ order.couponList.length }}张优惠券</text>
<text class="right-arrow iconfont icon-arrow-right"></text>
</view>
<text v-else class="">无优惠券可用</text>
</view>
</view>
<!-- 积分抵扣 -->
<view v-if="$checkModule('market-points') && order.isAllowPoints" class="points flow-all-list dis-flex flex-y-center">
<view class="block-left flex-five" @click="handleShowPoints()">
<text class="title">可用{{ setting.points_name }}抵扣:</text>
<text class="iconfont icon-help"></text>
</view>
<view class="flex-five dis-flex flex-x-end flex-y-center">
<text class="points-money col-m">-¥{{ order.pointsMoney }}</text>
<u-switch v-model="isUsePoints" size="48" active-color="#07c160" @change="getOrderData()"></u-switch>
</view>
</view>
<!-- 配送费用 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" class="dis-flex flow-all-list">
<text class="flex-five">配送费用:</text>
<view class="flex-five t-r">
<view v-if="order.address">
<text class="col-m" v-if="order.isIntraRegion">+¥{{ order.expressPrice }}</text>
<text v-else>不在配送范围</text>
</view>
<view v-else>
<text class="col-7">请先选择配送地址</text>
</view>
</view>
</view>
</view>
<!-- 买家留言 -->
<view class="flow-all-money b-f m-top20">
<view class="ipt-wrapper flow-all-list">
<input class="input" v-model="remark" placeholder="选填:买家留言(50字以内)" />
</view>
</view>
<!-- 提交订单 -->
<view class="flow-fixed-footer b-f m-top10">
<view class="dis-flex chackout-box">
<view class="chackout-left pl-12">实付款:
<text class="col-m">¥{{ order.orderPayPrice }}</text>
</view>
<view class="chackout-right" @click="onSubmitOrder()">
<view class="flow-btn f-32" :class="{ disabled }">提交订单</view>
</view>
</view>
</view>
<!-- 积分说明弹窗 -->
<u-modal v-model="showPoints" :title="`${setting.points_name}说明`">
<scroll-view class="points-content" :scroll-y="true">
<text>{{ setting.points_describe }}</text>
</scroll-view>
</u-modal>
<!-- 优惠券弹出框 -->
<u-popup v-model="showPopup" mode="bottom">
<view class="popup__coupon">
<view class="coupon__title f-30">选择优惠券</view>
<!-- 优惠券列表 -->
<view class="coupon-list">
<scroll-view :scroll-y="true" style="height: 565rpx;">
<view class="coupon-item" v-for="(item, index) in order.couponList" :key="index">
<view class="item-wrapper" :class="[item.is_apply ? 'color-' + CouponColors[index % CouponColors.length] : 'color-gray']"
@click="handleSelectCoupon(index)">
<view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
<view class="tip dis-flex flex-dir-column flex-x-center">
<view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
<text class="f-30">¥</text>
<text class="money">{{ item.reduce_price }}</text>
</view>
<text class="money" v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}折</text>
<text class="pay-line">满{{ item.min_price }}元可用</text>
</view>
<view class="split-line"></view>
<view class="content dis-flex flex-dir-column flex-x-between">
<view class="title">{{ item.name }}</view>
<view class="bottom dis-flex flex-y-center">
<view class="time flex-box">
<block v-if="item.start_time === item.end_time">{{ item.start_time }} 当天有效</block>
<block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 不使用优惠券 -->
<view class="coupon__do_not dis-flex flex-y-center flex-x-center">
<view class="control dis-flex flex-y-center flex-x-center" @click="handleNotUseCoupon()">
<text class="f-26">不使用优惠券</text>
</view>
</view>
</view>
</u-popup>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import * as Verify from '@/utils/verify'
import * as CheckoutApi from '@/api/checkout'
import * as SharpCheckoutApi from '@/api/sharp/checkout'
import * as BargainCheckoutApi from '@/api/bargain/checkout'
import * as GrouponCheckoutApi from '@/api/groupon/checkout'
import { CouponTypeEnum } from '@/common/enum/coupon'
import { OrderTypeEnum, DeliveryTypeEnum } from '@/common/enum/order'
const CouponColors = ['red', 'blue', 'violet', 'yellow']
// 根据指定mode获取对应的api类
const getCheckoutApi = (mode) => {
const apiEnum = {
buyNow: CheckoutApi,
cart: CheckoutApi,
bargain: BargainCheckoutApi,
sharp: SharpCheckoutApi,
groupon: GrouponCheckoutApi
}
return apiEnum[mode]
}
// 根据指定mode获取param
const getModeParam = (mode, options) => {
const param = {}
// 结算模式: 立即购买
if (mode === 'buyNow') {
param.goodsId = options.goodsId
param.goodsNum = options.goodsNum
param.goodsSkuId = options.goodsSkuId
}
// 结算模式: 购物车
if (mode === 'cart') {
param.cartIds = options.cartIds
}
// 结算模式: 砍价活动
if (mode === 'bargain') {
param.taskId = options.taskId
param.goodsSkuId = options.goodsSkuId
}
// 结算模式: 整点秒杀
if (mode === 'sharp') {
param.activeTimeId = options.activeTimeId
param.sharpGoodsId = options.sharpGoodsId
param.goodsSkuId = options.goodsSkuId
param.goodsNum = options.goodsNum
}
// 结算模式: 多人拼团
if (mode === 'groupon') {
param.grouponGoodsId = options.grouponGoodsId
param.taskId = options.taskId
param.goodsSkuId = options.goodsSkuId
param.goodsNum = options.goodsNum
param.stepPeople = options.stepPeople
}
return param
}
export default {
data() {
return {
// 枚举类
OrderTypeEnum,
DeliveryTypeEnum,
CouponTypeEnum,
// 当前页面参数
options: {},
// 配送方式
isShowTab: false,
DeliveryTypeEnum,
curDelivery: null,
// 自提信息
selectedShopId: 0, // 选择的门店ID
linkman: '', // 自提联系人
phone: '', // 自提联系电话
// 优惠券颜色组
CouponColors,
// 选择的优惠券
selectCouponId: 0,
// 是否使用积分抵扣
isUsePoints: false,
// 买家留言
remark: '',
// 禁用submit按钮
disabled: false,
// 是否显示积分说明
showPoints: false,
// 是否显示优惠券弹窗
showPopup: false,
// 订单信息 (从后端api中获取)
order: {
// 商品列表
goodsList: [],
// 优惠券列表
couponList: [],
// 是否存在收货地址
existAddress: false,
// 默认收货地址
address: null,
// 是否存在收货地址
existAddress: false,
// 当前用户收货城市是否存在配送规则中
isIntraRegion: true,
// 是否存在错误
hasError: false,
// 错误信息
errorMsg: '',
},
// 个人信息
personal: {},
// 商城设置
setting: {}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.options = options
// 注册全局事件订阅: 选择自提门店
uni.$on('syncSelectedId', selectedId => {
this.selectedShopId = selectedId
})
},
/**
* 生命周期函数--监听页面的卸载
*/
onUnload() {
// 卸载全局事件订阅: 选择自提门店
uni.$off('syncSelectedId')
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 获取当前订单信息
this.getOrderData()
},
methods: {
// 获取订单数据
getOrderData() {
const app = this
const { options: { mode } } = app
// 请求的参数
const params = app.getRequestParam()
// 请求api
getCheckoutApi(mode)
.order(mode, params)
.then(result => app.initData(result.data))
.catch(err => err)
},
// 初始化数据
initData({ order, setting, personal }) {
const app = this
app.order = order
app.personal = personal
app.setting = setting
// 显示错误信息
if (order.hasError) {
app.showToast(order.errorMsg, 3000)
}
// 当前选择的配送方式
app.curDelivery = order.delivery
// 如果只有一种配送方式则不显示选项卡
app.isShowTab = setting.deliveryType.length > 1
// 上门自提联系信息
if (app.linkman === '') {
app.linkman = order.lastExtract.linkman
}
if (app.phone === '') {
app.phone = order.lastExtract.phone
}
},
// 获取api请求的参数
getRequestParam() {
const app = this
const { options } = app
// 结算模式的固定参数
const modeParam = getModeParam(options.mode, options)
// 订单结算参数(用户选择)
const orderParam = {
delivery: app.curDelivery || 0,
shopId: app.selectedShopId || 0,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
}
return { ...orderParam, ...modeParam }
},
// 切换配送方式
handleSwichDelivery(key) {
this.curDelivery = key
this.getOrderData()
},
// 显示积分说明
handleShowPoints() {
this.showPoints = true
},
// 显示优惠券弹窗
handleShowPopup() {
this.showPopup = true
},
// 选择优惠券
handleSelectCoupon(index) {
const app = this
const { couponList } = app.order
// 当前选择的优惠券
const couponItem = couponList[index]
// 判断是否在适用范围
if (!couponItem.is_apply) {
app.showToast(couponItem.not_apply_info)
return
}
// 记录选中的优惠券id
app.selectCouponId = couponItem.user_coupon_id
// 重新获取订单信息
app.getOrderData()
// 隐藏优惠券弹层
app.showPopup = false
},
// 不使用优惠券
handleNotUseCoupon() {
const app = this
app.selectCouponId = 0
// 重新获取订单信息
app.getOrderData()
// 隐藏优惠券弹层
app.showPopup = false
},
// 快递配送:选择收货地址
onSelectAddress() {
this.$navTo('pages/address/index', { from: 'checkout' })
},
// 上门自提:选择自提点
onSelectExtractPoint() {
this.$navTo('pages/shop/extract', { selectedId: this.selectedShopId })
},
// 跳转到商品详情页
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
// 订单提交
onSubmitOrder() {
const app = this
if (app.disabled) {
return false
}
// 表单验证
if (!app.onVerifyFrom()) {
return false
}
// 按钮禁用
app.disabled = true
// 请求api
getCheckoutApi(app.options.mode)
.submit(app.options.mode, app.getFormData())
.then(result => {
// 订单创建成功: 跳转到订单支付页
const orderId = result.data.orderId
setTimeout(() => {
this.$navTo('pages/checkout/cashier/index', { orderId }, 'redirectTo')
}, 100)
})
.catch(res => app.showToast(res.errMsg, 3000))
.finally(() => setTimeout(() => app.disabled = false, 800))
},
// 跳转到我的订单(等待1秒)
navToMyOrder() {
setTimeout(() => {
this.$navTo('pages/order/index', {}, 'redirectTo')
}, 1000)
},
// 表单提交的数据
getFormData() {
const app = this
const { options } = app
// 表单数据
const form = {
delivery: app.curDelivery,
couponId: app.selectCouponId || 0,
shopId: app.selectedShopId || 0,
linkman: app.linkman,
phone: app.phone,
isUsePoints: app.isUsePoints ? 1 : 0,
remark: app.remark || '',
}
// 获取不同模式的参数
const modeParam = getModeParam(options.mode, options)
return { ...form, ...modeParam }
},
// 表单验证
onVerifyFrom() {
const app = this
if (app.hasError) {
app.showToast(app.errorMsg, 3000)
return false
}
// 验证自提填写的联系方式
if (app.curDelivery == DeliveryTypeEnum.EXTRACT.value) {
app.linkman = app.linkman.trim()
app.phone = app.phone.trim()
if (app.selectedShopId <= 0) {
app.showToast('请选择自提的门店')
return false
}
if (Verify.isEmpty(app.linkman)) {
app.showToast('请填写自提联系人')
return false
}
if (Verify.isEmpty(app.phone)) {
app.showToast('请填写自提联系电话')
return false
}
if (!Verify.isPhone(app.phone)) {
app.showToast('请输入正确的联系电话')
return false
}
}
return true
},
// 显示toast信息
showToast(title, duration = 2000) {
this.$refs.uToast.show({ title, duration })
}
}
}
</script>
<style lang="scss" scoped>
@import "./style.scss";
</style>