新增营销系统、分销系统、会员功能、门店、提现功能

This commit is contained in:
Shaw
2024-02-08 21:01:37 +08:00
parent 68b3f2dcc3
commit 17c043348a
1398 changed files with 81279 additions and 56269 deletions

View File

@ -1,8 +1,7 @@
<script setup>
import UvNavbar from "@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue";
import { useRouter } from "@/hooks/useRouter";
import { onLoad, onPageScroll, onShow } from "@dcloudio/uni-app";
import { ref, unref } from "vue";
import {onLoad, onPageScroll, onShow} from "@dcloudio/uni-app";
import { nextTick, onBeforeUnmount, ref, unref } from "vue";
import { useMainStore } from '@/store/store';
import { storeToRefs } from "pinia";
import { orderConfirm, orderCreate } from "@/api/order";
@ -15,42 +14,71 @@ import { doPayment, PayType } from "@/utils/paymentUtils";
import CouponSelect from "@/pages/submitOrder/components/coupon-select.vue";
import Header from "@/components/Header/index.vue"
import { useScroll } from "@/hooks/useScroll";
import { nextIcon } from "@/utils/images";
import { nextIcon, shopIcon } from "@/utils/images";
import { emitter } from "@/utils/emitter";
import {h5InWeChat} from "@/utils";
import Login from "@/pages/login/login.vue";
// ==================================== hooks =====================================
const {scrollTop} = useScroll()
onPageScroll(()=>{})
const {getParams, push, goBack} = useRouter()
const {toast, loading, hideLoading} = useInterface();
const mainStore = useMainStore();
const {selectAddress, defaultAddress, address} = storeToRefs(mainStore);
const routerParams = ref({})
const selectCouponRef = ref(false)
const couponList = ref([])
const selectCoupon = ref({})
const currentCouponId = ref(0)
const cartIds = ref('')
const flag = ref(false)
const selectCouponFn = async (coupon) => {
currentCouponId.value = coupon.couponId
const campaignType = ref(0)
const zeroPrice = ref(false) // 本单支付金额是否为0
/**
* 重新计算价格
* @return {Promise<void>}
*/
async function calculateOrder() {
orderDetail.value = await orderConfirm({
cartId: unref(routerParams).cartId,
orderType: unref(routerParams).orderType,
addressId: unref(selectAddress)?.id || undefined,
shippingType: addressTabSelect.value, // 默认快递 1快递2门店
storeId: shopSelect.value?.id,
useIntegral: useIntegral.value.length > 0,
couponId: currentCouponId.value
})
console.log(orderDetail.value)
// 余额为0 自动走余额支付
if (orderDetail.value.priceGroup.totalPrice <= 0) {
payType.value = PayType["1"]
zeroPrice.value = true
toast({title: '支付金额为0.00,自动选择余额支付', icon: 'none', duration: 3000})
} else {
zeroPrice.value = false
}
}
// ==================================== 优惠券相关 ===========================================
const selectCouponRef = ref(false)
const currentCouponId = ref(undefined)
const selectCouponFn = async (coupon) => {
currentCouponId.value = coupon.couponId
await calculateOrder()
selectCouponRef.value.close()
}
const addressTabSelect = ref(0) // 提货方式
const showCouponSelect = (index) => {
if (index === 0) {
selectCouponRef.value.open()
}
}
// =================================== 配送方式相关 ================================================
const addressTabSelect = ref(1) // 提货方式
/**
* 选择提货方式
* @param tab
*/
function changeAddressTab(tab) {
if (tab.value === 1) {
toast({title: '敬请期待'})
} else {
addressTabSelect.value = tab.value
}
addressTabSelect.value = tab.value
shopSelect.value = undefined
doGetInitConfirmOrder()
}
/**
@ -62,7 +90,6 @@ function gotoSelectAddress() {
/**
* 获取默认选择的地址
* @returns {Promise<void>}
*/
async function doGetSelectAddress() {
// store内部没有地址就先去获取地址
@ -76,58 +103,104 @@ async function doGetSelectAddress() {
// 没有默认地址设置默认地址
if (!selectAddress.value || !selectAddress.value?.id) {
defaultAddress.value && defaultAddress.value.id ? mainStore.setSelectAddress(defaultAddress.value.id) : mainStore.setSelectAddress(unref(address)[0].id)
doGetInitConfirmOrder()
}
}
const shopSelect = ref(null) // 当前选择的店铺
// 注册事件监听
emitter.on('selectShop', (res) => {
// 处理门店选择逻辑
shopSelect.value = res
doGetInitConfirmOrder()
})
emitter.on('selectAddress', async () => {
// 处理门店选择逻辑
await doGetSelectAddress()
await doGetInitConfirmOrder()
})
/**
* 选择店铺
*/
function selectShop() {
push({url: '/pages/submitOrder/shopSelect'}, {data: {shopSelect: shopSelect.value}})
}
// =============================== 订单信息相关 ======================================================
const flag = ref(false)
const payType = ref(PayType["0"]) // 支付方式
// 初始订单信息
const useIntegral = ref([1]) // 是否使用积分 length>0使用积分
// 订单信息
const orderDetail = ref({
cartInfo: [],
priceGroup: {
costPrice: 0, payIntegral: 0, storeFreePostage: 0, storePostage: 0, totalPrice: 0, vipPrice: 0
costPrice: 0,
payIntegral: 0,
storeFreePostage: 0,
storePostage: 0,
totalPrice: 0,
vipPrice: 0
}
})
/**
* 获取初始订单信息
* todo 如果没有地址,选择地址后需要重新调用
* @returns {Promise<void>}
* 调用此方法可以重新生成订单信息(包括价格信息)
*/
async function doGetInitConfirmOrder() {
orderDetail.value = await orderConfirm({
cartId: unref(routerParams).cartId,
addressId: unref(selectAddress)?.id || undefined
})
await calculateOrder()
cartIds.value = unref(routerParams).cartId
currentCouponId.value = orderDetail.value.priceGroup.couponId
flag.value = true
}
const subLoading = ref(false) // 加载中
const showCouponSelect = (index) => {
if (index === 0) {
selectCouponRef.value.open()
}
/**
* 改变是否使用积分
*/
function handleUseIntegralChange() {
nextTick(() => {
doGetInitConfirmOrder()
})
}
const subLoading = ref(false) // 加载中
/**
* 提交订单
* @return {Promise<void>}
*/
async function handleConfirm() {
if (!unref(selectAddress) || !unref(selectAddress).id) {
if (addressTabSelect.value === 1 && (!unref(selectAddress) || !unref(selectAddress).id)) {
toast({title: '请先选择地址'})
return
}
if (addressTabSelect.value === 2 && !shopSelect.value) {
toast({title: '请先选择门店'})
return
}
subLoading.value = true
try {
const payInfo = await doCreateServiceOrder()
// 去拉取支付
await doPayment({type: payType.value, payInfo})
await doPayment({type: payType.value, payInfo,isGroup:routerParams.value.campaignType===1})
// #ifndef H5
push({url: '/pages/payStatus/index'}, {type: 'redirectTo'})
push({url: '/pages/payStatus/index'}, { data: {campaignType: campaignType.value},type: 'redirectTo'})
// #endif
// 处理微信内h5
// #ifdef H5
if(h5InWeChat()){
push({url: '/pages/payStatus/index'}, { data: {campaignType: campaignType.value},type: 'redirectTo'})
}
// #endif
} catch (e) {
console.error(e)
toast({title: '支付失败'})
push({url: '/pages/payStatus/index'}, {type: 'redirectTo'})
push({url: '/pages/payStatus/index'}, {data: {campaignType: campaignType.value} , type: 'redirectTo'})
} finally {
subLoading.value = false
mainStore.cartId = null
@ -140,9 +213,10 @@ async function handleConfirm() {
async function doCreateServiceOrder() {
try {
loading({title: '订单创建中...'})
const res = await orderCreate({
const data = {
key: unref(orderDetail).orderKey,
addressId: unref(selectAddress).id,
addressId: unref(selectAddress) && unref(selectAddress).id,
storeId: unref(shopSelect) && unref(shopSelect).id,
bargainId: 0,
combinationId: 0,
couponId: currentCouponId.value,
@ -151,16 +225,41 @@ async function doCreateServiceOrder() {
pinkId: 0,
seckillId: 0,
shippingType: addressTabSelect.value,
useIntegral: 0,
isChannel: 1,
});
distributorId: unref(routerParams)?.distributorId, // 分销商ID
useIntegral: useIntegral.value.length > 0,
orderType: unref(routerParams)?.orderType,
}
setActivityData(data)
const res = await orderCreate(data);
return res.result
} finally {
hideLoading()
}
}
const {scrollTop} = useScroll()
/**
* 设置活动参数
* @param data
*/
function setActivityData(data) {
// 处理活动数据 路由参数 orderType 1普通下单 2活动下单
if (routerParams.value.orderType !== 2) return
// 活动商品
data.campaignType = routerParams.value.campaignType // 1拼团 2秒杀 3砍价
campaignType.value = routerParams.value.campaignType
data.campaignDetailId = routerParams.value.campaignDetailId // 活动营销ID
// 拼团 路由参数 campaignType 1拼团活动
if (routerParams.value.campaignType !== 1) return;
data.teamworkType = routerParams.value.teamworkType // 1发起拼团 2加入
data.teamworkType === 2 ? data.teamworkId = routerParams.value.teamworkId : void (0) // 加入拼团的id
}
// =============================== 生命周期 ========================================
const routerParams = ref({})
const cartIds = ref('')
/**
* 检查路由参数
@ -173,16 +272,22 @@ function checkRouterParam(params) {
routerParams.value = params
}
onLoad(async options => {
try {
const params = getParams(options)
await checkRouterParam(params)
await doGetInitConfirmOrder()
} catch (e) {
console.error(e)
}
})
onShow(async ()=>{
onBeforeUnmount(() => {
emitter.clear('selectShop')
emitter.clear('selectAddress')
})
onShow(async () => {
await doGetInitConfirmOrder()
await doGetSelectAddress()
})
</script>
@ -195,10 +300,7 @@ onShow(async ()=>{
</Header>
<!-- 地址 -->
<view class="address-box">
<view
class="address-box__inner"
>
<view class="address-box__inner">
<view class="tab-box">
<!-- tab top -->
<view class="title-row flex flex-ai__center flex-jc__sb">
@ -213,47 +315,69 @@ onShow(async ()=>{
</view>
</view>
<!-- address info -->
<view
class="address-row flex flex-ai__center flex-jc__sb"
v-if="address.length>0"
@click="gotoSelectAddress"
>
<template v-if="addressTabSelect === 1">
<view
class="flex flex-ai__center"
v-if="selectAddress"
class="address-row flex flex-ai__center flex-jc__sb"
v-if="address.length>0"
@click="gotoSelectAddress"
>
<uv-icon
name="map"
size="22"
/>
<view class="info">
<view>
{{ selectAddress.realName }} {{ selectAddress.phone }}
</view>
<view class="address-info">
{{ selectAddress.province }} - {{ selectAddress.city }}- {{ selectAddress.district }} -
{{ selectAddress.detail }}
<view
class="flex flex-ai__center"
v-if="selectAddress"
>
<uv-icon
name="map"
size="22"
/>
<view class="info">
<view>
{{ selectAddress.realName }} {{ selectAddress.phone }}
</view>
<view class="address-info">
{{ selectAddress.province }} - {{ selectAddress.city }}- {{ selectAddress.district }} -
{{ selectAddress.detail }}
</view>
</view>
</view>
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
<view
class="address-row flex flex-ai__center flex-jc__sb"
v-else
@click="gotoSelectAddress"
>
点击添加地址
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
<view
class="address-row flex flex-ai__center flex-jc__sb"
v-else
@click="gotoSelectAddress"
>
点击添加地址
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
</template>
<!-- 门店INFO -->
<template v-else>
<view
class="shop-select"
@click="selectShop">
<image :src="shopIcon" />
<view class="right flex flex-jc__sb flex-ai__center">
<view
v-if="shopSelect">
<view>{{ shopSelect.storeIntro }}</view>
<view style="font-size: 23rpx;font-weight: normal">{{ shopSelect.address }}</view>
</view>
<view
v-else>
点击选择门店
</view>
<u-icon name="arrow-right" />
</view>
</view>
</template>
</view>
</view>
</view>
@ -278,11 +402,11 @@ onShow(async ()=>{
imgWidth="200rpx"
>
<template #options="{goods}">
<view class="goods-detail">
<view class="goods-detail" style="width: 100%">
<view class="sku-row">
{{ goods.attrInfo.sku }}
</view>
<view class="price-row flex flex-ai__center flex-jc__sb">
<view class="price-row flex flex-ai__center flex-jc__sb" style="width:100%;">
<view class="price flex flex-ai__end">
{{ sku.truePrice }}
<span class="old-price">
@ -314,7 +438,7 @@ onShow(async ()=>{
<view class="label">{{ row.label }}</view>
<view class="value">
{{ row.prefix }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) || '0.00' }}
<image
v-if="row.field=== 'couponPrice'"
class="arrow-icon"
@ -350,7 +474,8 @@ onShow(async ()=>{
<view class="pay-item">
<uv-radio
:name="payItem.type"
:disabled="payItem.disabled"
:disabled="
payItem.disabled || (payItem.type===PayType['0']&&zeroPrice) || (mainStore.user.nowMoney===0 &&!zeroPrice&&payItem.type===PayType['1'])"
>
<view class="flex flex-ai__center flex-jc__sb">
<view
@ -362,8 +487,11 @@ onShow(async ()=>{
/>
</view>
<view class="text">
<view>
<view class="flex flex-nowrap flex-ai__end" style="white-space: nowrap">
{{ payItem.label }}
<span v-if="payItem.type===PayType['1']" style="font-size: 16rpx;white-space: nowrap;">
{{mainStore.user.nowMoney}}
</span>
</view>
<view class="e-text">
{{ payItem.eLabel }}
@ -386,10 +514,42 @@ onShow(async ()=>{
<view class="label">{{ row.label }}</view>
<view class="value">
{{ row.prefix }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) || '0.00' }}
</view>
</view>
</template>
<!--会员 -->
<view
class="row flex flex-ai__center flex-jc__sb"
v-if="orderDetail.priceGroup.isVip">
<view class="label">会员优惠</view>
<view class="value">
-{{ orderDetail.priceGroup.vipDeductionAmount.toFixed(2) }}
</view>
</view>
<view
class="row flex flex-ai__center flex-jc__sb"
v-if="orderDetail.priceGroup.enableIntegral">
<view class="label">积分抵扣</view>
<view class="value flex flex-ai__center">
当前积分:
<text class="primary-color">{{ orderDetail.priceGroup.integral }}</text>
<u-checkbox-group
style="margin-left: 10rpx;margin-top: 3rpx;"
shape="circle"
activeColor="#ee6d46"
v-model="useIntegral"
@change="handleUseIntegralChange">
<u-checkbox
label=" "
:name="1" />
</u-checkbox-group>
<template v-if="useIntegral.length>0">
-{{ orderDetail.priceGroup.deductionPrice.toFixed(2) }}
</template>
</view>
</view>
</view>
<!-- 底部操作条 -->
@ -477,13 +637,26 @@ $bottomHeight: 100rpx;
.address-info {
color: #333333;
margin: 10rpx 0;
//max-width: 60vw;
//overflow: hidden;
//white-space: nowrap;
//text-overflow: ellipsis;
}
}
}
.shop-select {
@include usePadding(42, 42);
@include useFlex(flex-start, center);
.right {
flex-grow: 1;
font-size: 28rpx;
font-weight: bold;
}
image {
margin-right: 20rpx;
width: 35rpx;
height: 35rpx;
}
}
}
}
}
@ -554,6 +727,7 @@ $bottomHeight: 100rpx;
}
}
.pay-box {
:deep(.uv-radio-group ) {
@ -590,7 +764,7 @@ $bottomHeight: 100rpx;
position: fixed;
width: 100%;
height: $bottomHeight;
bottom: 0;
bottom: env(safe-area-inset-bottom);
left: 0;
background: $white-color;
@ -617,4 +791,5 @@ $bottomHeight: 100rpx;
max-height: 1000rpx;
overflow-y: auto;
}
</style>