feat: 购物车开发
This commit is contained in:
parent
ba07f22b6a
commit
7a63035017
@ -84,4 +84,12 @@ export const apiActionOrder = (url, data) => {
|
||||
);
|
||||
}
|
||||
|
||||
// 获取购物车列表
|
||||
export const apiGetCartList = (data) => {
|
||||
return httpRequest.post(
|
||||
`${baseUrl}/shipping/cart/list`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -45,20 +45,16 @@
|
||||
// 加入购物车事件
|
||||
async handle(goods) {
|
||||
this.goods = goods
|
||||
if (goods.spec_type == SpecTypeEnum.SINGLE.value) {
|
||||
this.singleEvent()
|
||||
}
|
||||
if (goods.spec_type == SpecTypeEnum.MULTI.value) {
|
||||
this.multiEvent()
|
||||
}
|
||||
this.singleEvent()
|
||||
// this.multiEvent()
|
||||
},
|
||||
|
||||
// 单规格商品事件
|
||||
singleEvent() {
|
||||
const { goods } = this
|
||||
this.addCart({
|
||||
goods_id: goods.goods_id,
|
||||
goods_sku_id: '0',
|
||||
goods_id: goods.id,
|
||||
goods_sku_id: goods.skuId,
|
||||
buy_num: 1
|
||||
})
|
||||
},
|
||||
@ -153,11 +149,11 @@
|
||||
// 加入购物车按钮
|
||||
addCart(selectShop) {
|
||||
const app = this
|
||||
const { goods_id, goods_sku_id, buy_num } = selectShop
|
||||
CartApi.add(goods_id, goods_sku_id, buy_num)
|
||||
const { goods_sku_id, buy_num } = selectShop
|
||||
GoodsApi.apiAddCart({ skuId: goods_sku_id, num: buy_num })
|
||||
.then(result => {
|
||||
// 显示成功
|
||||
app.$toast(result.message, 1000, false)
|
||||
app.$toast(result.msg, 1000, false)
|
||||
// 隐藏当前弹窗
|
||||
app.onChangeValue(false)
|
||||
// 购物车商品总数量
|
||||
|
||||
@ -182,7 +182,7 @@ export const setCartTabBadge = () => {
|
||||
* 验证是否已登录
|
||||
*/
|
||||
export const checkLogin = () => {
|
||||
return !!store.getters.userId
|
||||
return !!store.getters.buyerId
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
8
core/bootstrap.js
vendored
8
core/bootstrap.js
vendored
@ -2,15 +2,11 @@ import store from '@/store'
|
||||
import storage from '@/utils/storage'
|
||||
import Config from '@/core/config'
|
||||
import platform from '@/core/platform'
|
||||
import { ACCESS_TOKEN, USER_ID, APP_THEME } from '@/store/mutation-types'
|
||||
import { APP_THEME } from '@/store/mutation-types'
|
||||
|
||||
export default function YoShop2() {
|
||||
// 当前运行的终端
|
||||
store.commit('SET_PLATFORM', platform)
|
||||
// 用户认证token
|
||||
store.commit('SET_TOKEN', storage.get(ACCESS_TOKEN))
|
||||
// 当前用户ID
|
||||
store.commit('SET_USER_ID', storage.get(USER_ID))
|
||||
// 全局自定义主题
|
||||
store.commit('SET_USER_INFO', storage.get('user_info'))
|
||||
store.commit('SET_APP_THEME', storage.get(APP_THEME))
|
||||
}
|
||||
|
||||
@ -23,25 +23,25 @@
|
||||
<view class="item-radio" @click="handleCheckItem(item.id)">
|
||||
<u-checkbox :modelValue="inArray(item.id, checkedIds)" shape="circle" :activeColor="appTheme.mainBg" />
|
||||
</view>
|
||||
<view class="goods-image" @click="onTargetGoods(item.goods_id)">
|
||||
<image class="image" :src="item.goods.goods_image" mode="scaleToFill"></image>
|
||||
<view class="goods-image" @click="onTargetGoods(item.productId)">
|
||||
<image class="image" :src="item.imageUrl" mode="scaleToFill"></image>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="goods-title" @click="onTargetGoods(item.goods_id)">
|
||||
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
|
||||
<view class="goods-title" @click="onTargetGoods(item.productId)">
|
||||
<text class="twoline-hide">{{ item.productName }}</text>
|
||||
</view>
|
||||
<view class="goods-props clearfix">
|
||||
<view class="goods-props-item" v-for="(props, idx) in item.goods.skuInfo.goods_props" :key="idx">
|
||||
<text>{{ props.value.name }}</text>
|
||||
<view class="goods-props-item" v-for="(props, idx) in item.skuInfo" :key="idx">
|
||||
<text>{{ props.propertyValue }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-foot">
|
||||
<view class="goods-price">
|
||||
<text class="unit">¥</text>
|
||||
<text class="value">{{ item.goods.skuInfo.goods_price }}</text>
|
||||
<text class="value">{{ item.singlePrice }}</text>
|
||||
</view>
|
||||
<view class="stepper">
|
||||
<u-number-box :min="1" :modelValue="item.goods_num" :step="1" @change="onChangeStepper($event, item)" />
|
||||
<u-number-box :min="1" :modelValue="item.num" :step="1" @change="onChangeStepper($event, item)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -73,7 +73,7 @@
|
||||
<view class="btn-wrapper">
|
||||
<!-- dev:下面的disabled条件使用checkedIds.join方式判断 -->
|
||||
<!-- dev:通常情况下vue项目使用checkedIds.length更合理, 但是length属性在微信小程序中不起作用 -->
|
||||
<view v-if="mode == 'normal'" class="btn-item btn-main" :class="{ disabled: checkedIds.join() == '' }" @click="handleOrder()">
|
||||
<view v-if="mode == 'normal'" class="btn-item btn-main" :class="{ disabled: checkedIds.join() == '' }" @click="onSubmit()">
|
||||
<text>去结算</text>
|
||||
<!-- <text>去结算 {{ checkedIds.length > 0 ? `(${total})` : '' }}</text> -->
|
||||
</view>
|
||||
@ -91,6 +91,8 @@
|
||||
import { inArray, arrayIntersect, debounce } from '@/utils/util'
|
||||
import { checkLogin, setCartTotalNum, setCartTabBadge } from '@/core/app'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import * as OrderApi from '@/api/order'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const CartIdsIndex = 'CartIds'
|
||||
|
||||
@ -101,28 +103,19 @@
|
||||
data() {
|
||||
return {
|
||||
inArray,
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前模式: normal正常 edit编辑
|
||||
mode: 'normal',
|
||||
// 购物车商品列表
|
||||
mode: 'normal', // 当前模式: normal正常 edit编辑
|
||||
list: [],
|
||||
// 购物车商品总数量
|
||||
total: null,
|
||||
// 选中的商品ID记录
|
||||
checkedIds: [],
|
||||
// 选中的商品总金额
|
||||
totalPrice: '0.00'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听选中的商品
|
||||
checkedIds: {
|
||||
handler(val) {
|
||||
// 计算合计金额
|
||||
this.onCalcTotalPrice()
|
||||
// 记录到缓存中
|
||||
uni.setStorageSync(CartIdsIndex, val)
|
||||
this.onCalcTotalPrice() // 计算合计金额
|
||||
uni.setStorageSync(CartIdsIndex, val) // 记录到缓存中
|
||||
},
|
||||
deep: true,
|
||||
immediate: false
|
||||
@ -134,10 +127,6 @@
|
||||
setCartTabBadge()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 获取缓存中的选中记录
|
||||
this.checkedIds = uni.getStorageSync(CartIdsIndex)
|
||||
@ -146,7 +135,6 @@
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 计算合计金额 (根据选中的商品)
|
||||
onCalcTotalPrice() {
|
||||
const app = this
|
||||
@ -156,8 +144,8 @@
|
||||
let tempPrice = 0;
|
||||
checkedList.forEach(item => {
|
||||
// 商品单价, 为了方便计算先转换单位为分 (整数)
|
||||
const unitPrice = item.goods.skuInfo.goods_price * 100
|
||||
tempPrice += unitPrice * item.goods_num
|
||||
const unitPrice = item.singlePrice * 100
|
||||
tempPrice += unitPrice * item.num
|
||||
})
|
||||
app.totalPrice = (tempPrice / 100).toFixed(2)
|
||||
},
|
||||
@ -166,14 +154,13 @@
|
||||
getCartList() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
CartApi.list()
|
||||
.then(result => {
|
||||
app.list = result.data.list
|
||||
app.total = result.data.cartTotal
|
||||
OrderApi.apiGetCartList({}).then(res => {
|
||||
app.total = res.data.total
|
||||
app.list = res.data.rows.map(item => ({ ...item, skuInfo: JSON.parse(item.skuInfo) }))
|
||||
// 清除checkedIds中无效的ID
|
||||
app.onClearInvalidId()
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 清除checkedIds中无效的ID
|
||||
@ -191,19 +178,20 @@
|
||||
// 监听步进器更改事件
|
||||
onChangeStepper({ value }, item) {
|
||||
// 这里是组织首次启动时的执行
|
||||
if (item.goods_num == value) return
|
||||
if (item.num == value) return
|
||||
// 记录一个节流函数句柄
|
||||
if (!item.debounceHandle) {
|
||||
item.oldValue = item.goods_num
|
||||
item.debounceHandle = debounce(this.onUpdateCartNum, 500)
|
||||
item.oldValue = item.num
|
||||
item.num = value
|
||||
this.onCalcTotalPrice()
|
||||
// item.debounceHandle = debounce(this.onUpdateCartNum, 500)
|
||||
}
|
||||
// 更新商品数量
|
||||
item.goods_num = value
|
||||
// 提交更新购物车数量 (节流)
|
||||
item.debounceHandle(item, item.oldValue, value)
|
||||
// item.debounceHandle(item, item.oldValue, value)
|
||||
},
|
||||
|
||||
// 提交更新购物车数量
|
||||
/* // 提交更新购物车数量
|
||||
onUpdateCartNum(item, oldValue, newValue) {
|
||||
const app = this
|
||||
CartApi.update(item.goods_id, item.goods_sku_id, newValue)
|
||||
@ -220,7 +208,7 @@
|
||||
item.goods_num = oldValue
|
||||
setTimeout(() => app.$toast(err.errMsg), 10)
|
||||
})
|
||||
},
|
||||
}, */
|
||||
|
||||
// 跳转到商品详情页
|
||||
onTargetGoods(goodsId) {
|
||||
@ -246,11 +234,20 @@
|
||||
},
|
||||
|
||||
// 结算选中的商品
|
||||
handleOrder() {
|
||||
onSubmit() {
|
||||
const app = this
|
||||
if (app.checkedIds.length) {
|
||||
const cartIds = app.checkedIds.join()
|
||||
app.$navTo('pages/checkout/index', { mode: 'cart', cartIds })
|
||||
const arr = app.list.filter(item => inArray(item.id, app.checkedIds)).map(item => ({
|
||||
goodsId: item.productId,
|
||||
goodsSkuId: item.skuId,
|
||||
buy_num: item.num,
|
||||
title: item.productName,
|
||||
image: item.imageUrl,
|
||||
price: item.singlePrice,
|
||||
skuInfo: item.skuInfo
|
||||
}))
|
||||
storage.set('goods', arr)
|
||||
app.$navTo('pages/checkout/index', { mode: 'cart' })
|
||||
}
|
||||
},
|
||||
|
||||
@ -270,7 +267,6 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 确认删除商品
|
||||
onClearCart() {
|
||||
const app = this
|
||||
@ -376,11 +372,14 @@
|
||||
color: #ababab;
|
||||
font-size: 24rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
|
||||
.goods-props-item {
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #fcfcfc;
|
||||
background-color: #efefef;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,9 +54,9 @@
|
||||
<text class="goods-name twoline-hide">{{ item.title }}</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 class="goods-props-item" v-for="(props, idx) in item.skuInfo" :key="idx">
|
||||
<text class="group-name">{{ props.propertyName }}: </text>
|
||||
<text>{{ props.propertyValue }};</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品数量和单价 -->
|
||||
@ -226,6 +226,7 @@
|
||||
import * as OrderApi from '@/api/order'
|
||||
import { CouponTypeEnum } from '@/common/enum/coupon'
|
||||
import { OrderTypeEnum, DeliveryTypeEnum } from '@/common/enum/order'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const CouponColors = ['red', 'blue', 'violet', 'yellow']
|
||||
|
||||
@ -309,10 +310,10 @@
|
||||
*/
|
||||
onShow() {
|
||||
// 获取当前订单信息
|
||||
const goods = JSON.parse(decodeURIComponent(this.options.goods))
|
||||
const goods = storage.get('goods')
|
||||
console.warn('----- my data is goods: ', goods)
|
||||
goods.forEach(item => {
|
||||
item.skuInfo = { goods_props: [] }
|
||||
item.skuInfo = item.skuInfo || []
|
||||
})
|
||||
this.order.goodsList = goods
|
||||
// this.getOrderData()
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import GoodsSkuPopup from '@/components/goods-sku-popup'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -144,9 +145,7 @@
|
||||
// 立即购买
|
||||
buyNow(selectShop) {
|
||||
// 跳转到订单结算页
|
||||
this.$navTo('pages/checkout/index', {
|
||||
mode: 'buyNow',
|
||||
goods: encodeURIComponent(JSON.stringify([{
|
||||
storage.set('goods', [{
|
||||
goodsId: selectShop.goods_id,
|
||||
goodsSkuId: selectShop.goods_sku_id,
|
||||
goodsNum: selectShop.buy_num,
|
||||
@ -154,7 +153,8 @@
|
||||
image: selectShop.image,
|
||||
price: selectShop.price,
|
||||
buy_num: selectShop.buy_num
|
||||
}]))})
|
||||
}])
|
||||
this.$navTo('pages/checkout/index', { mode: 'buyNow' })
|
||||
// 隐藏当前弹窗
|
||||
this.onChangeValue(false)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const getters = {
|
||||
platform: state => state.app.platform,
|
||||
token: state => state.user.token,
|
||||
userId: state => state.user.userId,
|
||||
buyerId: state => state.user.buyerId,
|
||||
appTheme: state => state.theme.appTheme,
|
||||
pageQuery: state => state.page.query
|
||||
}
|
||||
|
||||
@ -2,51 +2,30 @@ import { ACCESS_TOKEN, USER_ID, FLAG } from '@/store/mutation-types'
|
||||
import storage from '@/utils/storage'
|
||||
import * as LoginApi from '@/api/login'
|
||||
|
||||
// 登陆成功后执行
|
||||
const loginSuccess = (commit, { token, buyerId, flag }) => {
|
||||
// 过期时间30天
|
||||
const expiryTime = 30 * 86400
|
||||
// 保存tokne和userId到缓存
|
||||
storage.set(USER_ID, buyerId, expiryTime)
|
||||
storage.set(ACCESS_TOKEN, token, expiryTime)
|
||||
storage.set(FLAG, flag, expiryTime)
|
||||
// 记录到store全局变量
|
||||
commit('SET_TOKEN', token)
|
||||
commit('SET_USER_ID', buyerId)
|
||||
commit('SET_FLAG', flag)
|
||||
}
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
// 用户认证token
|
||||
token: '',
|
||||
// 用户ID
|
||||
userId: null,
|
||||
// 用户标识
|
||||
buyerId: null,
|
||||
flag: null,
|
||||
},
|
||||
|
||||
mutations: {
|
||||
SET_TOKEN: (state, value) => {
|
||||
state.token = value
|
||||
},
|
||||
SET_USER_ID: (state, value) => {
|
||||
state.userId = value
|
||||
},
|
||||
SET_FLAG: (state, value) => {
|
||||
state.flag = value
|
||||
SET_USER_INFO: (state, value) => {
|
||||
state.token = value.token
|
||||
state.buyerId = value.buyerId
|
||||
state.flag = value.flag
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
// 用户登录 (普通登录: 输入手机号和验证码)
|
||||
Login({ commit }, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
LoginApi.login({ form: data })
|
||||
.then(response => {
|
||||
const result = response.data
|
||||
loginSuccess(commit, result)
|
||||
storage.set('user_info', result.buyer)
|
||||
commit('SET_USER_INFO', result.buyer)
|
||||
resolve(response)
|
||||
})
|
||||
.catch(reject)
|
||||
@ -60,8 +39,8 @@ const user = {
|
||||
LoginApi.quickLogin(data)
|
||||
.then(response => {
|
||||
const result = response.data
|
||||
uni.setStorageSync('user_info', result.buyer)
|
||||
loginSuccess(commit, result.buyer)
|
||||
storage.set('user_info', result.buyer)
|
||||
commit('SET_USER_INFO', result.buyer)
|
||||
resolve(response)
|
||||
})
|
||||
.catch(reject)
|
||||
@ -74,7 +53,8 @@ const user = {
|
||||
LoginApi.loginMpWxMobile({ form: data }, { isPrompt: true })
|
||||
.then(response => {
|
||||
const result = response.data
|
||||
loginSuccess(commit, result)
|
||||
uni.setStorageSync('user_info', result.buyer)
|
||||
commit('SET_USER_INFO', result.buyer)
|
||||
resolve(response)
|
||||
})
|
||||
.catch(reject)
|
||||
@ -86,12 +66,8 @@ const user = {
|
||||
const store = this
|
||||
return new Promise((resolve, reject) => {
|
||||
if (store.getters.userId > 0) {
|
||||
// 删除缓存中的tokne和userId
|
||||
storage.remove(USER_ID)
|
||||
storage.remove(ACCESS_TOKEN)
|
||||
// 记录到store全局变量
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_USER_ID', null)
|
||||
storage.remove('user_info')
|
||||
commit('SET_USER_INFO', { token: '', flag: '', buyerId: '' })
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,69 +1,20 @@
|
||||
/**
|
||||
* 缓存数据优化
|
||||
* import storage from '@/utils/storage'
|
||||
* 使用方法 【
|
||||
* 一、设置缓存
|
||||
* string storage.set('k', 'string你好啊');
|
||||
* json storage.set('k', { "b": "3" }, 2);
|
||||
* array storage.set('k', [1, 2, 3]);
|
||||
* boolean storage.set('k', true);
|
||||
* 二、读取缓存
|
||||
* 默认值 storage.get('k')
|
||||
* string storage.get('k', '你好')
|
||||
* json storage.get('k', { "a": "1" })
|
||||
* 三、移除/清理
|
||||
* 移除: storage.remove('k');
|
||||
* 清理:storage.clear();
|
||||
* 】
|
||||
* @type {String}
|
||||
*/
|
||||
|
||||
const postfix = '_expiry' // 缓存有效期后缀
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param {[type]} k [键名]
|
||||
* @param {[type]} v [键值]
|
||||
* @param {[type]} t [时间、单位秒]
|
||||
*/
|
||||
set(k, v, t) {
|
||||
set(k, v) {
|
||||
uni.setStorageSync(k, v)
|
||||
const seconds = parseInt(t)
|
||||
if (seconds > 0) {
|
||||
let timestamp = Date.parse(new Date())
|
||||
timestamp = timestamp / 1000 + seconds
|
||||
uni.setStorageSync(k + postfix, timestamp + '')
|
||||
} else {
|
||||
uni.removeStorageSync(k + postfix)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
* @param {[type]} k [键名]
|
||||
* @param {[type]} def [获取为空时默认]
|
||||
*/
|
||||
get(k, def) {
|
||||
const deadtime = parseInt(uni.getStorageSync(k + postfix))
|
||||
if (deadtime) {
|
||||
if (parseInt(deadtime) < Date.parse(new Date()) / 1000) {
|
||||
if (def) {
|
||||
return def
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
const res = uni.getStorageSync(k)
|
||||
if (res) {
|
||||
return res
|
||||
}
|
||||
if (def == undefined || def == "") {
|
||||
def = false
|
||||
}
|
||||
return def
|
||||
get(k) {
|
||||
return uni.getStorageSync(k)
|
||||
},
|
||||
|
||||
/**
|
||||
@ -72,7 +23,6 @@ export default {
|
||||
*/
|
||||
remove(k) {
|
||||
uni.removeStorageSync(k)
|
||||
uni.removeStorageSync(k + postfix)
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user