| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- import { get, post } from '@/core/http/http-client'
- import { buildRestPath } from '@/core/config/api-paths'
- import { STORAGE } from '@/core/constants/storage-keys'
- import { decimalAdd, decimalSub, decimalMul } from '@/core/infra/runtime-bridge'
- /**
- * 从后端返回的各种数据格式中提取购物车数组
- * 后端可能返回: 数组 / { list: [] } / { data: [] } / { data: { list: [] } } 等
- */
- function pickCartRows(payload) {
- if (Array.isArray(payload)) return payload
- if (!payload || typeof payload !== 'object') return []
- if (Array.isArray(payload.list)) return payload.list
- if (Array.isArray(payload.data)) return payload.data
- if (payload.data && Array.isArray(payload.data.list)) return payload.data.list
- if (payload.data && Array.isArray(payload.data.data)) return payload.data.data
- return []
- }
- /**
- * 将扁平的購物車数组按店鋪分组
- * 输入: [{ shop_id, shop_name, goods_id, ... }, ...]
- * 输出: [{ shop_id, shop_name, products: [...], check, choose }, ...]
- * 与88live的format逻辑一致
- */
- function groupByShop(flatList) {
- let map = {}
- let dest = []
- for (let i = 0; i < flatList.length; i++) {
- let cart = flatList[i]
- cart.checked = false
- if (!map[cart.shop_id]) {
- dest.push({
- shop_id: cart.shop_id,
- shop_name: cart.shop_name,
- products: [cart],
- check: false,
- choose: 0
- })
- map[cart.shop_id] = cart
- } else {
- for (let j = 0; j < dest.length; j++) {
- let dj = dest[j]
- if (dj.shop_id == cart.shop_id) {
- dj.products.push(cart)
- break
- }
- }
- }
- }
- return dest
- }
- /**
- * 商城购物车状态管理器
- * 数据结构: list按店铺分组,每个店铺包含products商品列表
- * 选择逻辑: 商品选中 → 店铺选中 → 全选,三级联动
- */
- export default {
- namespaced: true,
- state: {
- list: [],// 购物车数据,按店铺分组 [{ shop_id, shop_name, products, check, choose }]
- cartnum: 0,//购物车商品总数量
- allchoose: 0,// 选中的店铺数量
- allsum: 0,// 选中商品合计价格
- allnum: 0,// 选中商品总数量
- status: false,// 全选状态
- operate: false// 管理模式(true=编辑模式,false=普通模式
- },
- mutations: {
- replaceList(state, list) {
- state.list = list || []
- }
- },
- actions: {
- // 切换管理模式(编辑模式/普通模式)
- async operate({ state }) {
- state.operate = !state.operate
- },
- //初始化同步购物车:已登录→拉取云端数据,未登录→读取本地缓存
- sync({ dispatch, rootState }) {
- setTimeout(() => {
- if (rootState.user.isLogin) dispatch('pullRemote')
- else {
- const local = uni.getStorageSync(STORAGE.cart)
- if (local) dispatch('normalize', local)
- }
- }, 1000)
- },
- // 登录后从云端拉取购物车数据,并与本地合并
- async pullRemote({ dispatch }) {
- const local = uni.getStorageSync(STORAGE.cart)
- try {
- const { data } = await post(buildRestPath('cart/synchro'), { cart: local || null })
- dispatch('normalize', data)
- } catch (e) {
- console.warn('[cart] pullRemote', e)
- }
- },
- // 格式化购物车数据:解析后端数据 → 更新state → 计算合计 → 保存本地
- normalize({ commit, state }, payload) {
- const flatList = pickCartRows(payload)
- const dest = groupByShop(flatList)
- commit('replaceList', dest)
- state.cartnum = 0
- state.allchoose = 0
- state.allsum = 0
- state.allnum = 0
- state.status = false
- state.operate = false
- flatList.forEach((item) => {
- if (item) state.cartnum++
- })
- uni.setStorageSync(STORAGE.cart, flatList)
- },
- // ==================== 选择操作 ====================
- // 选择/取消选择单个商品,根据当前状态分发到choosetrue或choosefalse
- async choose({ state, dispatch }, { index, keys }) {
- let cart = state.list[index]
- let goods = cart.products[keys]
- !goods.checked ? dispatch('choosetrue', { cart, goods }) : dispatch('choosefalse', { cart, goods })
- },
- // 选中商品:更新商品状态 → 检查店铺是否全选 → 检查是否全选 → 更新合计
- async choosetrue({ state }, { cart, goods }) {
- goods.checked = true
- cart.choose++
- if (cart.choose === cart.products.length) {
- cart.check = true
- }
- if (cart.check) {
- state.allchoose++
- if (state.allchoose === state.list.length) {
- state.status = true
- } else {
- state.status = false
- }
- }
- state.allsum = decimalAdd(state.allsum, goods.sum)
- state.allnum += goods.number
- },
- // 取消选中商品:更新商品状态 → 取消店铺选中 → 取消全选 → 更新合计
- async choosefalse({ state }, { cart, goods }) {
- goods.checked = false
- cart.choose--
- if (cart.check) {
- cart.check = false
- state.allchoose--
- }
- state.status = false
- state.allsum = decimalSub(state.allsum, goods.sum)
- state.allnum -= goods.number
- },
- // 选择/取消选择整个店铺,根据当前状态分发到shoptrue或shopfalse
- async shopchoose({ dispatch }, cart) {
- !cart.check ? dispatch('shoptrue', cart) : dispatch('shopfalse', cart)
- },
- // 选中店铺:遍历店铺内所有未选中商品,逐一选中
- async shoptrue({ dispatch }, cart) {
- cart.products.forEach((goods) => {
- goods.checked === false && dispatch('choosetrue', { cart, goods })
- })
- },
- // 取消选中店铺:遍历店铺内所有已选中商品,逐一取消选中
- async shopfalse({ dispatch }, cart) {
- cart.products.forEach((goods) => {
- goods.checked === true && dispatch('choosefalse', { cart, goods })
- })
- },
- // 全选/取消全选:切换全选状态,遍历所有店铺执行对应操作
- async cartchoose({ state, dispatch }) {
- state.status = !state.status
- state.status ? state.list.forEach((cart) => dispatch('shoptrue', cart)) : state.list.forEach((cart) => dispatch('shopfalse', cart))
- },
- // ==================== 数量操作 ====================
- // 增加商品数量:数量+1 → 更新小计 → 若已选中则更新合计 → 同步存储
- async bcadd({ state, dispatch }, goods) {
- goods.number++
- goods.sum = decimalAdd(goods.sum, goods.sku.price)
- if (goods.checked) {
- state.allnum++
- state.allsum = decimalAdd(state.allsum, goods.sku.price)
- }
- dispatch('storage', { type: 'bcadd', goods })
- },
- // 减少商品数量:数量-1(最少为1) → 更新小计 → 若已选中则更新合计 → 同步存储
- async bcsub({ state, dispatch }, goods) {
- if (goods.number > 1) {
- goods.number--
- goods.sum = decimalSub(goods.sum, goods.sku.price)
- if (goods.checked) {
- state.allnum--
- state.allsum = decimalSub(state.allsum, goods.sku.price)
- }
- dispatch('storage', { type: 'bcsub', goods })
- }
- },
- // ==================== 添加操作 ====================
- // 添加商品到购物车:判断店铺是否存在 → 判断商品是否已存在 → 新增或追加数量
- async add({ state, dispatch }, goods) {
- let isshop = -1
- let data = {
- goods_id: goods.goods_id,
- sku_id: goods.sku_id,
- title: goods.title,
- image: goods.image,
- sku: goods.sku,
- number: goods.number,
- sum: goods.sum,
- checked: false
- }
- state.list.find((item, index) => {
- if (item.shop_id == goods.shop_id) {
- isshop = index
- }
- })
- if (isshop == -1) {
- // 新店铺:建立新的店铺分组
- state.list.push({
- shop_id: goods.shop_id,
- shop_name: goods.shop_name,
- products: [data],
- check: false,
- choose: 0
- })
- state.cartnum++
- } else {
- // 已有店铺:检查商品是否已存在
- let products = state.list[isshop].products
- let isgoods = -1
- products.find((item, index) => {
- if (item.goods_id === goods.goods_id && item.sku_id === goods.sku_id) {
- isgoods = index
- }
- })
- if (isgoods == -1) {
- // 新商品:追加到店铺
- dispatch('shopfalse', state.list[isshop])
- products.push(data)
- state.cartnum++
- } else {
- // 已有商品:只增加数量
- let shop = products[isgoods]
- shop.number += goods.number
- shop.sum = decimalMul(goods.sku.price, shop.number)
- if (shop.checked) {
- state.allnum += goods.number
- state.allsum = decimalAdd(state.allsum, decimalMul(goods.sku.price, goods.number))
- }
- }
- }
- await dispatch('storage', { type: 'add', goods })
- },
- // ==================== 删除操作 ====================
- // 移入关注:将选中商品从购物车移到收藏夹,需登录
- async move({ state, dispatch, rootState }) {
- if (rootState.user.isLogin) {
- dispatch('storage', { type: 'follow' })
- } else {
- uni.navigateTo({ url: '/pages/passport/signin' })
- }
- },
- // 删除选中商品:全选则清空,否则删除选中项
- async del({ state, dispatch }) {
- if (state.status) {
- dispatch('empty')
- } else {
- dispatch('storage', { type: 'del' })
- }
- },
- // 清空购物车:重置所有状态
- async empty({ state, dispatch }) {
- state.list = []
- state.cartnum = 0
- state.allchoose = 0
- state.allsum = 0
- state.allnum = 0
- state.status = false
- state.operate = false
- dispatch('storage', { type: 'empty' })
- },
- // ==================== 结算操作 ====================
- // 结算:收集所有选中商品信息,跳转到确认订单页面;未登录则跳转登录页
- async settlement({ state, rootState }) {
- let data = []
- state.list.forEach((cart) => {
- cart.products.forEach((goods) => {
- if (goods.checked) {
- data.push({
- goods_id: goods.goods_id,
- number: goods.number,
- sku_id: goods.sku.id
- })
- }
- })
- })
- if (rootState.user.isLogin) {
- uni.navigateTo({ url: `/pages/order/confirm?type=cart&data=${JSON.stringify(data)}` })
- } else {
- uni.navigateTo({ url: '/pages/passport/signin' })
- }
- },
- // ==================== 数据同步 ====================
- // 统一数据同步处理:整理本地数据 → 同步到云端 → 保存本地缓存
- // type取值: empty(清空) / bcadd(加数量) / bcsub(减数量) / add(添加) / del(删除) / follow(移入关注)
- async storage({ state, dispatch, rootState }, { type, goods }) {
- let cloud = null
- let storage = []
- let select = []
- let unchecked = []
- if (type == 'empty') {
- cloud = { type }
- } else {
- // 遍历所有店铺和商品,按类型分类处理
- state.list.forEach((cart) => {
- cart.products.forEach((item) => {
- let data = {
- shop_id: cart.shop_id,
- shop_name: cart.shop_name,
- goods_id: item.goods_id,
- title: item.title,
- number: item.number,
- image: item.image,
- sku: item.sku,
- sku_id: item.sku_id,
- sum: item.sum,
- checked: item.checked
- }
- if (type == 'del' || type == 'follow') {
- // 删除或移入关注:分离选中项和未选中项
- if (item.checked) {
- select.push(data)
- } else {
- unchecked.push(data)
- }
- } else {
- // 其他操作:直接追加到存储数组
- storage.push(data)
- }
- })
- })
- state.cartnum = storage.length
- // 根据操作类型构建云端请求参数
- if (type == 'bcsub' || type == 'bcadd') {
- cloud = { type, goods_id: goods.goods_id, sku_id: goods.sku_id, number: goods.number, sum: goods.sum }
- } else if (type == 'del' || type == 'follow') {
- dispatch('normalize', unchecked)
- storage = unchecked
- cloud = { type, data: select }
- state.operate = false
- } else if (type == 'add') {
- cloud = { type, data: goods }
- state.status = false
- }
- }
- //已登录时同步到云端
- if (cloud != null && rootState.user.isLogin) {
- try {
- const { data } = await post(buildRestPath('cart/storage'), cloud)
- if (type == 'follow' && rootState.statistics && rootState.statistics.dynamic) {
- rootState.statistics.dynamic.collection = rootState.statistics.dynamic.collection + data
- }
- } catch (e) {
- console.warn('[cart] storage', e)
- }
- }
- uni.setStorageSync(STORAGE.cart, storage)
- }
- }
- }
|