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

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

@ -0,0 +1,47 @@
<!--
@name: 空购物车
@author: kahu4
@date: 2023-11-06 15:59
@descriptionCartEmpty
@update: 2023-11-06 15:59
-->
<script setup>
import Empty from '@/components/Empty/index.vue'
import { emptyCartIcon } from "@/utils/images";
import { useRouter } from "@/hooks/useRouter";
const {pushToTab} = useRouter()
</script>
<template>
<Empty
:iconSrc="emptyCartIcon"
:padding="'220rpx 0 0 0'"
>
<template #default>
购物车里空空如也
</template>
<template #bottom>
<view
class="go-on-btn"
@click="pushToTab({url:'/root/index/index'})"
>
继续逛逛
</view>
</template>
</Empty>
</template>
<style
scoped
lang="scss"
>
.go-on-btn {
padding: 15rpx 50rpx;
margin-top: 20rpx;
background: #333333;
color: #fff;
}
</style>

View File

@ -0,0 +1,30 @@
/**
* @name: index.data
* @author: kahu4
* @date: 2023-11-06 14:15
* @descriptionindex.data
* @update: 2023-11-06 14:15
* */
// 购物车统计信息
export const settleFields = [
{
label: '商品总价',
field: 'costPrice',
prefix: '¥'
},
{
label: '优惠',
field: 'couponPrice',
prefix: '-¥'
},
{
label: '运费',
field: 'storePostage',
prefix: '¥'
},
{
label: '总计',
field: 'totalPrice',
prefix: '¥'
}
]

View File

@ -0,0 +1,307 @@
/**
* @name: 购物车相关操作方法
* @author: kahu4
* @date: 2023-11-06 15:03
* @descriptionindex.utils
* @update: 2023-11-06 15:03
* */
import { computed, nextTick, ref, unref } from "vue";
import { changeCartSku, computeSelectInfo, deleteCartByIds, getCartList, updateCartNumber } from "@/api/cart";
import { onShow } from '@dcloudio/uni-app'
import _ from "loadsh";
import { useInterface } from "@/hooks/useInterface";
import { useRouter } from "@/hooks/useRouter";
import { getProductDetail } from "@/api/product";
/**
* 购物车数据
*/
export function useCartData() {
const cartListLoading = ref(false)
const cartList = ref([])
const showEmpty = computed(() => cartList.value.length <= 0)
/**
* 获取购物车列表
* @returns {Promise<void>}
*/
async function doGetCartList() {
try {
cartListLoading.value = true
const res = await getCartList()
cartList.value = res?.valid ?? []
} finally {
cartListLoading.value = false
}
}
onShow(async () => {
await doGetCartList()
})
return {
showEmpty,
cartListLoading,
cartList,
doGetCartList
}
}
/**
* 用户操作相关
*/
export function useCartOption(options) {
const {cartList, doGetCartList} = options
const {toast} = useInterface()
const {push} = useRouter()
const manage = ref(false)
const manageStr = computed(() => {
return manage.value ? '取消' : '管理'
})
const shoppingSelect = ref([]) // 选中数据
const shoppingSelectAll = ref(false) // 是否全选
/**
* 用户单选
* @param value
*/
async function handleSingleSelect(value) {
shoppingSelectAll.value = value.length === cartList.value.length
setTimeout(async () => {
await computeSelectInfoByShoppingSelect()
}, 100)
}
/**
* 用户全选
* @param e
* @returns {Promise<void>}
*/
async function handleSelectAll(e) {
shoppingSelect.value = !!e ? cartList.value.map(item => item.id) : []
shoppingSelectAll.value = e
await computeSelectInfoByShoppingSelect()
}
const statisticsInfo = ref(undefined) // 统计信息
/**
* 根据shoppingSelect去计算选中数据
* @returns {Promise<void>}
*/
async function computeSelectInfoByShoppingSelect() {
if (unref(shoppingSelect).length <= 0) return statisticsInfo.value = void (0)
const res = await computeSelectInfo({
cartId: unref(shoppingSelect).join(','), orderType: 1,
useIntegral: false
});
statisticsInfo.value = res.priceGroup
}
/**
* 重新设置选中信息
* @returns {Promise<void>}
*/
async function resetUserSelect() {
shoppingSelect.value = []
shoppingSelectAll.value = false
await computeSelectInfoByShoppingSelect()
}
/**
* 打开删除弹窗
* @returns {*}
*/
function openDelModal(modalRef) {
if (unref(shoppingSelect).length <= 0) return toast({title: '请勾选需要删除的商品'})
unref(modalRef).show()
}
/**
* 删除数据
* @returns {Promise<void>}
*/
async function doDelete() {
await deleteCartByIds({
ids: shoppingSelect.value
})
await doGetCartList()
await resetUserSelect()
}
/**
* 提交订单
* @returns {*}
*/
function submitOrder() {
if (unref(shoppingSelect).length <= 0) return toast({title: '请勾选需要购买的商品'})
push({url: '/pages/submitOrder/submitOrder'}, {
data: {cartId: shoppingSelect.value.toString()}
})
}
return {
manage,
manageStr,
shoppingSelect,
shoppingSelectAll,
statisticsInfo,
handleSingleSelect,
handleSelectAll,
computeSelectInfoByShoppingSelect,
openDelModal,
doDelete,
submitOrder
}
}
/**
* 更改sku
*/
export function useSku() {
const {toast} = useInterface()
const openSkuProductId = ref(undefined) // 当前选中的sku商品id
const openSkuSkuId = ref(undefined) // 当前选中sku的sku id
const openSkuCartId = ref(undefined)
const openProductItem = ref(undefined)
/**
* 获取商品详情
* @param data
* @param data.skuId skuId
* @param data.productId productId
* @return {Promise<void>}
*/
const handleGetDetail = async (data) => {
openProductItem.value = await getProductDetail(data);
};
/**
* 打开sku选择器
* @param item
* @param modalRef
*/
async function handleOpenSkuSelect(item, modalRef) {
await handleGetDetail({productId: item.productId})
openSkuSkuId.value = item.productInfo.attrInfo.id
openSkuProductId.value = item.productId
openSkuCartId.value = item.id
await nextTick(() => {
modalRef.open(item.cartNum)
})
}
/**
* 关闭sku选择器
*/
function handleCloseSkuSelect() {
openSkuProductId.value = openSkuCartId.value = void (0)
}
async function handleSubmitSkuSelect(e, modalRef, cartList, func, doGetCartList) {
if (!openSkuProductId.value) return
const {store, num} = e
await changeCartSku({
id: openSkuCartId.value,
productId: openSkuProductId.value,
productAttrUnique: store.unique
})
if (typeof func === 'function') {
const find = cartList.find(item => item.id === openSkuCartId.value);
await func({detail: {value: num}}, find)
}
setTimeout(async () => {
await doGetCartList()
toast({title: '修改成功', icon: 'success'})
handleCloseSkuSelect()
}, 400)
}
return {
openProductItem,
openSkuSkuId,
openSkuProductId,
handleOpenSkuSelect,
handleCloseSkuSelect,
handleSubmitSkuSelect
}
}
/**
* 更改购物车数量
*/
export function useCartNumber(options) {
const {toast} = useInterface()
/**
* 用户手动输入改变数量
* @param e
* @param item
* @returns {*}
*/
function handleCartNumberInputChange(e, item) {
const value = parseInt(e.detail.value)
if (value <= 0) {
item.cartNum = 1
toast({title: '至少选一件哦~'})
return
}
if (value > item.trueStock) {
item.cartNum = item.productInfo.stock
toast({title: '超出库存啦~'})
return
}
nextTick(()=>{
item.cartNum = value.toString().replace(/^0+/, '')
doCartNumberChangeRequest(item)
})
}
/**
* 用户点击购物车+-改变数量
* @param item
* @param type
* @returns {*}
*/
function handleCartNumberChange(item, type = 'plus') {
if (type === 'plus') {
if (item.cartNum + 1 > item.trueStock) {
item.cartNum = item.trueStock
} else {
item.cartNum += 1
}
} else {
if (item.cartNum <= 1) {
item.cartNum = 1
} else {
item.cartNum -= 1
}
}
doCartNumberChangeRequest(item)
}
/**
* 请求改变后台用户购物车数据
* @param item
* @returns {Promise<void>}
*/
const doCartNumberChangeRequest = _.debounce(async (item) => {
await updateCartNumber({
id: item.id,
number: item.cartNum
})
options && options.afterChange && await options.afterChange()
}, 300)
return {
handleCartNumberInputChange,
handleCartNumberChange
}
}

View File

@ -126,7 +126,7 @@
</card>
</uv-checkbox-group>
<!-- bottom action row -->
<view class="screen action-bar ">
<view class="screen action-bar " :style="{paddingBottom:`${safeArea}px`}">
<view class="action-info">
<view class="action-checkbox">
<uv-checkbox-group
@ -170,7 +170,7 @@
<!-- null data -->
<CartEmpty v-else />
<!-- 商品推荐 -->
<Recommend />
<Recommend />
<view class="action-height"></view>
<view class="h5-tabbar-height"></view>
<!-- sku select -->
@ -178,8 +178,9 @@
style="z-index: 999"
:id="openSkuProductId"
ref="goodsAttrSelectRef"
@close="handleCloseSkuSelect"
@submit="(e)=>handleSubmitSkuSelect(e, goodsAttrSelectRef,cartList,handleCartNumberInputChange,doGetCartList)
:goods-detail="openProductItem"
:sku-id="openSkuSkuId"
@select="(e)=>handleSubmitSkuSelect(e, goodsAttrSelectRef,cartList,handleCartNumberInputChange,doGetCartList)
"
/>
<!-- delete modal -->
@ -196,11 +197,11 @@ import { ref } from 'vue'
import Goods from "@/components/goodsComponents/Goods.vue";
import GoodAttrSelect from "@/components/good-attr-select/good-attr-select.vue";
import Modal from "@/components/Modal/index.vue"
import { settleFields } from "@/pages/shoppingCart/index.data";
import { useCartData, useCartNumber, useCartOption, useSku } from "@/pages/shoppingCart/index.utils";
import CartEmpty from "@/pages/shoppingCart/components/CartEmpty.vue";
import { settleFields } from "./index.data";
import { useCartData, useCartNumber, useCartOption, useSku } from "./index.utils";
import CartEmpty from "./components/CartEmpty.vue";
import Header from "@/components/Header/index.vue"
import { onHide, onPageScroll, onReachBottom } from "@dcloudio/uni-app";
import { onHide } from "@dcloudio/uni-app";
import Recommend from "@/components/Recommend/index.vue";
import { useScroll } from "@/hooks/useScroll";
import ReturnTop from "@/components/ReturnTop/index.vue"
@ -230,6 +231,8 @@ const {
} = useCartOption({cartList, doGetCartList})
const {
openProductItem,
openSkuSkuId,
openSkuProductId,
handleOpenSkuSelect,
handleCloseSkuSelect,
@ -243,13 +246,16 @@ const {
} = useCartNumber({afterChange: computeSelectInfoByShoppingSelect})
onHide(() => {
modalRef.value?.close()
goodsAttrSelectRef.value?.close()
})
const safeArea=ref(0)
uni.getSystemInfo({
success: res => {
safeArea.value = res.safeAreaInsets.bottom;
}
})
const {scrollTop} = useScroll()
</script>
@ -416,4 +422,7 @@ const {scrollTop} = useScroll()
}
}
}
.action-bar{
//padding-bottom: env(safe-area-inset-bottom);
}
</style>