diff --git a/src/pinia/index.ts b/src/pinia/index.ts new file mode 100644 index 0000000..21d8fc2 --- /dev/null +++ b/src/pinia/index.ts @@ -0,0 +1,13 @@ +import { createPinia } from "pinia" +import { createPersistedState } from "pinia-plugin-persistedstate" + +export const pinia = createPinia(); + +pinia.use( + createPersistedState({ + storage: { + getItem: uni.getStorageSync, + setItem: uni.setStorageSync, + }, + }), +); \ No newline at end of file diff --git a/src/pinia/stores/ab98-user.ts b/src/pinia/stores/ab98-user.ts new file mode 100644 index 0000000..8609119 --- /dev/null +++ b/src/pinia/stores/ab98-user.ts @@ -0,0 +1,138 @@ +import { pinia } from "@/pinia" +import { LoginData } from "@/api/ab98/types" +import { defineStore } from "pinia" + +// 本地存储键名常量 +const STORAGE_KEYS = { + FACE: 'ab98_face', + SEX: 'ab98_sex', + NAME: 'ab98_name', + USERID: 'ab98_userid', + REGISTERED: 'ab98_registered', + TEL: 'ab98_tel', + TOKEN: 'ab98_token', + LOGIN_CODE: 'ab98_login_code' +} + +/** + * AB98用户信息存储 + * @description 管理AB98系统用户相关状态信息 + */ +export const useAb98UserStore = defineStore("ab98User", () => { + // 用户面部图像URL + const storedFace = localStorage.getItem(STORAGE_KEYS.FACE) + const face_img = ref(storedFace ? decodeURIComponent(storedFace) : '') + // 用户性别(男/女) + const storedSex = localStorage.getItem(STORAGE_KEYS.SEX) + const sex = ref(storedSex ? decodeURIComponent(storedSex) : '') + // 用户真实姓名 + const storedName = localStorage.getItem(STORAGE_KEYS.NAME) + const name = ref(storedName ? decodeURIComponent(storedName) : '') + // AB98系统用户唯一标识 + const storedUserId = localStorage.getItem(STORAGE_KEYS.USERID) + const userid = ref(storedUserId ? decodeURIComponent(storedUserId) : "") + // 是否已完成注册流程 + const registered = ref(JSON.parse(localStorage.getItem(STORAGE_KEYS.REGISTERED) || "false")) + // 用户绑定手机号 + const storedTel = localStorage.getItem(STORAGE_KEYS.TEL) + const tel = ref(storedTel ? decodeURIComponent(storedTel) : "") + // 用户认证令牌 + const storedToken = localStorage.getItem(STORAGE_KEYS.TOKEN) + const token = ref(storedToken ? decodeURIComponent(storedToken) : ""); + + const tokenLogin = ref(""); + // 登录验证码 + const loginCode = '1'; // 默认验证码 + // 用户登录状态 + const isLogin = ref(false); + const storedLoginCode = localStorage.getItem(STORAGE_KEYS.LOGIN_CODE); + isLogin.value = tel.value && storedLoginCode === String(loginCode) ? true : false; + + /** + * 更新用户基本信息 + * @param data - 登录接口返回的用户数据 + */ + const setUserInfo = (data: LoginData) => { + face_img.value = data.face_img + localStorage.setItem(STORAGE_KEYS.FACE, encodeURIComponent(data.face_img)) + sex.value = data.sex + localStorage.setItem(STORAGE_KEYS.SEX, encodeURIComponent(data.sex)) + name.value = data.name + localStorage.setItem(STORAGE_KEYS.NAME, encodeURIComponent(data.name)) + userid.value = data.userid + localStorage.setItem(STORAGE_KEYS.USERID, encodeURIComponent(data.userid)) + registered.value = data.registered + localStorage.setItem(STORAGE_KEYS.REGISTERED, JSON.stringify(data.registered)) + tel.value = data.tel + localStorage.setItem(STORAGE_KEYS.TEL, encodeURIComponent(data.tel)) + localStorage.setItem(STORAGE_KEYS.LOGIN_CODE, encodeURIComponent(loginCode)) + } + + /** + * 清空用户敏感信息 + * @description 用于用户登出或会话过期时 + */ + const clearUserInfo = () => { + face_img.value = "" + localStorage.removeItem(STORAGE_KEYS.FACE) + sex.value = "" + localStorage.removeItem(STORAGE_KEYS.SEX) + name.value = "" + localStorage.removeItem(STORAGE_KEYS.NAME) + userid.value = "" + localStorage.removeItem(STORAGE_KEYS.USERID) + registered.value = false + localStorage.removeItem(STORAGE_KEYS.REGISTERED) + tel.value = "" + localStorage.removeItem(STORAGE_KEYS.TEL) + localStorage.removeItem(STORAGE_KEYS.TOKEN) + localStorage.removeItem(STORAGE_KEYS.LOGIN_CODE) + } + + /** + * 设置认证令牌 + * @param value - JWT格式的认证令牌 + */ + const setToken = (value: string) => { + localStorage.setItem(STORAGE_KEYS.TOKEN, encodeURIComponent(value)) + token.value = value + } + + const setTel = (value: string) => { + localStorage.setItem(STORAGE_KEYS.TEL, btoa(value)) + tel.value = value + } + + const setIsLogin = (value: boolean) => { + isLogin.value = value; + } + + const setTokenLogin = (value: string) => { + tokenLogin.value = value; + } + + return { + face_img, + sex, + name, + userid, + registered, + tel, + token, + isLogin, + tokenLogin, + setUserInfo, + setToken, + setTel, + setIsLogin, + clearUserInfo, + setTokenLogin + } +}) + +/** + * @description 在非setup上下文或SSR场景中使用store + */ +export function useAb98UserStoreOutside() { + return useAb98UserStore(pinia) +} \ No newline at end of file diff --git a/src/pinia/stores/approval.ts b/src/pinia/stores/approval.ts new file mode 100644 index 0000000..e6efe7e --- /dev/null +++ b/src/pinia/stores/approval.ts @@ -0,0 +1,25 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import type { ReturnApprovalAssetDTO } from '@/api/approval/types' + +export interface ApprovalDetail extends ReturnApprovalAssetDTO { + goodsName: string + coverImg: string +} + +export const useApprovalStore = defineStore('approval', () => { + const currentApproval = ref(null) + + const setCurrentApproval = (approval: ApprovalDetail) => { + currentApproval.value = approval + } + + return { + currentApproval, + setCurrentApproval + } +}) + +export function useApprovalStoreOutside() { + return useApprovalStore() +} \ No newline at end of file diff --git a/src/pinia/stores/cart.ts b/src/pinia/stores/cart.ts new file mode 100644 index 0000000..d3a9654 --- /dev/null +++ b/src/pinia/stores/cart.ts @@ -0,0 +1,69 @@ +import { defineStore } from 'pinia' +import type { Product } from './product' + +export const useCartStore = defineStore('cart', () => { + // 购物车商品列表 + const cartItems = ref>([]) + + // 添加商品到购物车 + const addToCart = (product: Product, quantity: number = 1): boolean => { + if (quantity > product.stock && product.stock !== -1) { + return false; + } + const existingItem = cartItems.value.find(item => item.product.cellId === product.cellId) + if (existingItem) { + if (existingItem.quantity + quantity > product.stock && product.stock !== -1) { + return false; + } + existingItem.quantity += quantity + } else { + cartItems.value.push({ product, quantity }) + } + return true; + } + + // 移除商品 + const removeFromCart = (cellId: number, quantity: number = 1) => { + const index = cartItems.value.findIndex(item => item.product.cellId === cellId) + if (index !== -1) { + if (cartItems.value[index].quantity <= quantity) { + cartItems.value.splice(index, 1) + } else { + cartItems.value[index].quantity -= quantity + } + } + } + + // 计算总价 + const totalPrice = computed(() => { + return cartItems.value.reduce((sum, item) => { + return sum + (item.product.price || 0) * item.quantity + }, 0) + }) + + //计算总数 + const totalQuantity = computed(() => { + return cartItems.value.reduce((sum, item) => { + return sum + item.quantity + }, 0) + }) + + // 清空购物车 + const clearCart = () => { + cartItems.value = [] + } + + return { + cartItems, + addToCart, + removeFromCart, + totalPrice, + totalQuantity, + clearCart + } +}) + +// 支持在setup外使用 +export function useCartStoreOutside() { + return useCartStore() +} diff --git a/src/pinia/stores/keep-alive.ts b/src/pinia/stores/keep-alive.ts new file mode 100644 index 0000000..b99c50f --- /dev/null +++ b/src/pinia/stores/keep-alive.ts @@ -0,0 +1,30 @@ +import type { RouteLocationNormalizedGeneric } from "vue-router" +import { pinia } from "@/pinia" +import { defineStore } from "pinia" +import { isString } from "@/utils/validate" + +export const useKeepAliveStore = defineStore("keep-alive", () => { + const cachedRoutes = ref([]) + + const addCachedRoute = (route: RouteLocationNormalizedGeneric) => { + const keepAlive = route.meta.keepAlive + const name = route.name + if (keepAlive && name && isString(name) && !cachedRoutes.value.includes(name)) { + cachedRoutes.value.push(name) + } + } + + const delAllCachedRoutes = () => { + cachedRoutes.value = [] + } + + return { cachedRoutes, addCachedRoute, delAllCachedRoutes } +}) + +/** + * @description 在 SPA 应用中可用于在 pinia 实例被激活前使用 store + * @description 在 SSR 应用中可用于在 setup 外使用 store + */ +export function useKeepAliveStoreOutside() { + return useKeepAliveStore(pinia) +} diff --git a/src/pinia/stores/order.ts b/src/pinia/stores/order.ts new file mode 100644 index 0000000..8ea847b --- /dev/null +++ b/src/pinia/stores/order.ts @@ -0,0 +1,51 @@ +import { pinia } from "@/pinia" +import { getOrdersByOpenIdApi, getOrdersByQyUserIdApi } from "@/api/shop" +import type { ShopOrderEntity, ShopOrderGoodsEntity, Goods } from "@/api/shop/types" +import { defineStore } from "pinia" + +export interface Order extends ShopOrderEntity { + goodsList: Array +} + +export interface OrderGoods { + goodsInfo: Goods + orderGoods: ShopOrderGoodsEntity +} + +export const useOrderStore = defineStore("order", () => { + const orders = ref([]) + const orderGoods = ref([]) + const goods = ref([]) + + const getOrders = async (corpid: string, openid: string, qyUserId: number, hasReturn: number) => { + try { + const { data } = qyUserId ? + await getOrdersByQyUserIdApi(qyUserId, hasReturn) + : await getOrdersByOpenIdApi(corpid, openid, hasReturn); + + // 重组订单结构 + orders.value = data.orders.map(order => ({ + ...order, + goodsList: data.orderGoods + .filter(og => og.orderId === order.orderId) + .map(og => ({ + orderGoods: og, + goodsInfo: data.goods.find(g => g.goodsId === og.goodsId)! + })) + })) + .sort((a, b) => b.orderId - a.orderId) + + // 保留原始数据结构 + orderGoods.value = data.orderGoods + goods.value = data.goods + } catch (error) { + console.error("获取订单数据失败:", error) + } + } + + return { orders, orderGoods, goods, getOrders } +}) + +export function useOrderStoreOutside() { + return useOrderStore(pinia) +} \ No newline at end of file diff --git a/src/pinia/stores/product.ts b/src/pinia/stores/product.ts new file mode 100644 index 0000000..842d806 --- /dev/null +++ b/src/pinia/stores/product.ts @@ -0,0 +1,78 @@ +import { ShopEntity } from "@/api/shop/types" +import { pinia } from "@/pinia" +import { getShopGoodsApi } from "@/api/shop" +import { defineStore } from "pinia" + +export interface Product { + id: number // 商品ID + name: string // 商品名称 + price: number // 商品价格 + stock: number // 商品库存 + description: string // 商品描述 + image: string // 商品图片URL + label: number // 商品标签 + cellId: number // 商品所在的格子ID + usageInstruction: string // 商品使用说明 + belongType: number // 商品所属类型 0: 智借还 1: 固资通 +} + +export const useProductStore = defineStore("product", () => { + // 商品数据 + const labels = ref<{ id: number, name: string }[]>([]); + const categories = ref([]); + const shopId = ref(null); + const selectedShop = ref(null); + + const setSelectedShop = (shop: ShopEntity) => { + selectedShop.value = shop; + } + + const getGoods = async (shopId_?: number) => { + if (shopId_ && shopId_ > 0) { + shopId.value = shopId_; + } else { + shopId.value = null; + /* const urlParams = new URLSearchParams(window.location.search); + const shopIdParams = urlParams.get('shopId') || undefined; + if (shopIdParams) { + shopId.value = Number(shopIdParams); + } */ + } + + try { + const { data } = await getShopGoodsApi(shopId.value); + + // 转换分类标签 + labels.value = data.categoryList.map(c => ({ + id: c.categoryId, // 使用分类名称生成ID + name: c.categoryName + })) + + // 转换商品数据 + categories.value = data.goodsList.map(g => ({ + id: g.goodsId, + name: g.goodsName, + price: g.price, + stock: g.stock, + description: g.goodsDetail || "暂无描述", + image: g.coverImg, + label: g.categoryId, + cellId: g.cellId, + usageInstruction: g.usageInstruction || "", + belongType: g.belongType + })) + } catch (error) { + console.error("获取商品数据失败:", error) + } + } + return { labels, categories, shopId, selectedShop, + getGoods, setSelectedShop } +}) + +/** + * @description 在 SPA 应用中可用于在 pinia 实例被激活前使用 store + * @description 在 SSR 应用中可用于在 setup 外使用 store + */ +export function useProductStoreOutside() { + return useProductStore(pinia) +} diff --git a/src/pinia/stores/rentingCabinet.ts b/src/pinia/stores/rentingCabinet.ts new file mode 100644 index 0000000..798da5c --- /dev/null +++ b/src/pinia/stores/rentingCabinet.ts @@ -0,0 +1,76 @@ +import { defineStore } from 'pinia' +import { computed } from 'vue' +import { ref } from 'vue' +import { getRentingCabinetDetailApi } from '@/api/cabinet' +import type { CabinetCellEntity, RentingCabinetDetailDTO } from '@/api/cabinet/types' + +export const useRentingCabinetStore = defineStore('rentingCabinet', () => { + const rentingCabinets = ref([]) + + // 租用购物车列表 + const rentingCartItems = ref>([]) + + const fetchRentingCabinetDetail = async (shopId: number) => { + const res = await getRentingCabinetDetailApi(shopId); + console.log(res) + if (res.code === 0 && res.data) { + rentingCabinets.value = res.data + } + } + + // 添加到租用购物车 + const addToRentingCart = (cabinetCell: CabinetCellEntity, quantity: number = 1): boolean => { + if (quantity <= 0) return false + const existingItem = rentingCartItems.value.find(item => item.cabinetCell.cellId === cabinetCell.cellId) + if (existingItem) { + existingItem.quantity += quantity + } else { + rentingCartItems.value.push({ cabinetCell, quantity }) + } + return true + } + + // 从购物车移除 + const removeFromRentingCart = (cellId: number, quantity: number = 1) => { + const index = rentingCartItems.value.findIndex(item => item.cabinetCell.cellId === cellId) + if (index !== -1) { + if (rentingCartItems.value[index].quantity <= quantity) { + rentingCartItems.value.splice(index, 1) + } else { + rentingCartItems.value[index].quantity -= quantity + } + } + } + + // 计算租用总数 + const rentingCartTotalQuantity = computed(() => { + return rentingCartItems.value.reduce((total, item) => total + item.quantity, 0) + }) + + // 计算租用总价 + const rentingCartTotalPrice = computed(() => { + return rentingCartItems.value.reduce((sum, item) => { + return sum + (item.cabinetCell.cellPrice || 0) * item.quantity + }, 0) + }) + + // 清空租用购物车 + const clearRentingCart = () => { + rentingCartItems.value = [] + } + + return { + rentingCabinets, + rentingCartItems, + addToRentingCart, + removeFromRentingCart, + rentingCartTotalQuantity, + rentingCartTotalPrice, + clearRentingCart, + fetchRentingCabinetDetail + } +}) + +export function useRentingCabinetStoreOutside() { + return useRentingCabinetStore() +} \ No newline at end of file diff --git a/src/pinia/stores/wx.ts b/src/pinia/stores/wx.ts new file mode 100644 index 0000000..77f118b --- /dev/null +++ b/src/pinia/stores/wx.ts @@ -0,0 +1,191 @@ +import { pinia } from "@/pinia" +import { getOpenIdApi, getBalanceApi, qyLogin, getBalanceByQyUserid, fakeQyLoginApi } from "@/api/shop" +import { ab98UserDTO, GetBalanceResponse } from "@/api/shop/types" +import { useAb98UserStore } from "./ab98-user" +import { defineStore } from "pinia" + + +export const useWxStore = defineStore("wx", () => { + // 微信授权 code + const code = ref("") + // 防止 CSRF 攻击的 state 参数 + const state = ref("") + // 用户 openid + const openid = ref("") + // 用户 userid + const userid = ref(""); + // 剩余借呗 + const balance = ref(0); + // 已用借呗 + const useBalance = ref(0); + // 借呗总额 + const balanceLimit = ref(0); + // 企业id + const corpid = ref(""); + // 是否企业微信登录 + const corpidLogin = ref(false); + // 是否是柜子管理员 + const isCabinetAdmin = ref(false); + // 企业微信用户姓名 + const name = ref(""); + // 企业微信用户id + const qyUserId = ref(0); + // 汇邦云用户信息 + const ab98User = ref(null); + + const isFakeQyLogin = ref(false); + // handleWxCallback 是否完成 + const isHandleWxCallbackComplete = ref(false); + + // 设置 openid + const setOpenid = (id: string) => { + openid.value = id + } + + const setBalance = (amount: number) => { + balance.value = amount; + } + + const setIsCabinetAdmin = (isAdmin: boolean) => { + isCabinetAdmin.value = isAdmin; + } + + const setAb98User = (user: ab98UserDTO) => { + ab98User.value = user; + const ab98UserStore = useAb98UserStore(); + ab98UserStore.setUserInfo({ + face_img: ab98User.value.faceImg || "", + success: true, + sex: ab98User.value.sex || "", + name: ab98User.value.name || "", + userid: ab98User.value.userid || "", + registered: true, + tel: ab98User.value.tel || "", + }); + } + + const refreshBalance = async () => { + if (corpid.value && userid.value) { + const res = await getBalanceByQyUserid(corpid.value, userid.value); + if (res && res.code == 0) { + balance.value = res.data.balance; + useBalance.value = res.data.useBalance; + balanceLimit.value = res.data.balanceLimit; + + if (res.data.ab98User) { + setAb98User(res.data.ab98User); + } + } + } + } + + const handleWxCallback = async (params: { corpid?: string; code?: string; state?: string; }) => { + console.log('handleWxCallback:', params) + isHandleWxCallbackComplete.value = false; // 开始处理,标记为未完成 + if (params.code) { + code.value = params.code + state.value = params.state || state.value + corpid.value = params.corpid || corpid.value + corpidLogin.value = !!corpid.value; + console.log('corpid:', corpid.value) + console.log('corpidLogin:', corpidLogin.value) + + try { + if (!corpid.value) { + // 调用获取 openid 的接口 + const res = await getOpenIdApi({ code: params.code }); + if (res && res.code == 0) { + openid.value = res.data + } + } else { + // 企业微信登录 + const res = await qyLogin({ corpid: corpid.value, code: params.code, state: params.state}) + if (res && res.code == 0) { + userid.value = res.data.userid; + openid.value = res.data.openid; + isCabinetAdmin.value = res.data.isCabinetAdmin === 1; + name.value = res.data.name; + qyUserId.value = res.data.qyUserId; + setAb98User(res.data.ab98User); + } + } + + if (openid.value) { + // 获取用户余额 + let balanceRes: ApiResponseData | null = null; + + if(corpid.value) { + balanceRes = await getBalanceByQyUserid(corpid.value, userid.value); + } else { + corpid.value = "wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw"; + balanceRes = await getBalanceApi(corpid.value, openid.value) + } + console.log('获取余额成功:', balanceRes) + if (balanceRes && balanceRes.code == 0) { + balance.value = balanceRes.data.balance; + useBalance.value = balanceRes.data.useBalance; + balanceLimit.value = balanceRes.data.balanceLimit; + if (!userid.value) { + userid.value = balanceRes.data.userid; + } + if (!ab98User.value && balanceRes.data.ab98User) { + setAb98User(balanceRes.data.ab98User); + } + /* if (!corpid.value) { + corpid.value = balanceRes.data.corpid; + } */ + } + } + } catch (err) { + console.error('获取 openid 失败:', err) + } finally { + isHandleWxCallbackComplete.value = true; // 处理完成,标记为已完成 + } + + } else { + isHandleWxCallbackComplete.value = true; // 没有code参数,直接标记为已完成 + } + } + + const fakeQyLogin = async () => { + isFakeQyLogin.value = true; + corpid.value = "wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw"; + corpidLogin.value = true; + userid.value = "woZ1ZrEgAAV9AEdRt1MGQxSg-KDJrDlA"; + const res = await fakeQyLoginApi({ corpid: corpid.value, userid: userid.value}) + if (res && res.code == 0) { + userid.value = res.data.userid; + openid.value = "oMRxw6Eum0DB1IjI_pEX_yrawBHw"; + isCabinetAdmin.value = res.data.isCabinetAdmin === 1; + name.value = res.data.name; + qyUserId.value = res.data.qyUserId; + setAb98User(res.data.ab98User); + } + } + + // 检测 handleWxCallback 是否完成的异步函数 + const waitForHandleWxCallbackComplete = async (timeout = 30000): Promise => { + const startTime = Date.now(); + while (!isHandleWxCallbackComplete.value) { + if (Date.now() - startTime > timeout) { + console.warn('等待 handleWxCallback 完成超时'); + return false; + } + await new Promise(resolve => setTimeout(resolve, 100)); // 每100ms检查一次 + } + return true; + } + + + return { code, state, openid, corpid, userid, balance, useBalance, + balanceLimit, isCabinetAdmin, corpidLogin, name, ab98User, qyUserId, isFakeQyLogin, + isHandleWxCallbackComplete, setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin, + refreshBalance, setAb98User, fakeQyLogin, waitForHandleWxCallbackComplete } +}) + +/** + * @description 用于在 setup 外使用 store + */ +export function useWxStoreOutside() { + return useWxStore(pinia) +} \ No newline at end of file diff --git a/src/store/cart.ts b/src/store/cart.ts deleted file mode 100644 index ec262db..0000000 --- a/src/store/cart.ts +++ /dev/null @@ -1,160 +0,0 @@ -import {defineStore} from 'pinia'; -import {ref} from 'vue'; -import {addCart, listCart, operateAmount} from '@/api/goods/shopping-cart'; - -// 商品类型定义 -interface CartItem { - cartId: string; // 购物车唯一标识 - id: string; // 商品原始id - name: string; - image?: string; - pictures?: string[]; - price: number; - spec: string; - quantity: number; - selected: boolean; - originalPrice?: number; - listId?: string; - specs?: string; - type: string; - specification?: any; - status: number; - amount: number; -} - -export const useCartStore = defineStore('cart', () => { - // 购物车商品列表 - const items = ref([]); - // 购物车详情 - const itemsDetail = ref() - - //订单详情 - const orderDetail = ref() - - // 查询购物车列表 - const getListCart = async () => { - return listCart().then((res: any) => { - if (res.data?.products){ - items.value = res.data.products.map((item: any) => { - return { - ...item, - ...item.product, - title: item.name, - price: item.price, - type: item.type === 1 ? 'product' : 'service', - status: item.status, - specs: selectSpecs(item.product.specificationPrices), - specsIds: selectSpecsIds(item.product.specificationPrices), - id: item.id, - selected:true, - listId: `${item.productId}_${item.type === 1 ? 'product' : 'service'}`, - } - }) - } - }) - }; - - // 添加商品到购物车 type 用于区分是添加购物车还是恢复购物车 - const addItem = (product: Omit) => { - //把一下数据异步返回 - return new Promise((resolve, reject) => { - //加入购物车 - addCart({ - productId: product.id, - type: product.type === 'product' ? 1 : 0, - price:product.price, - amount:product.amount || 1, - selected:true, - specification:product.specs ? product.specification : undefined, - formId: new Date().getTime() - }).then(res => { - uni.showToast({ title: '加入购物车', icon: 'none' }) - resolve(res) - }).catch(err => { - reject(err) - }) - }) - }; - - // 从购物车移除商品 - const removeItem = (cartId: string) => { - items.value = items.value.filter(item => item.id !== cartId); - }; - - // 更新商品数量 - const updateQuantity = (cartId: string, amount: number) => { - operateAmount({ - id:cartId, - amount - }).then(res => { - if (res.data.operator === 'add') { - uni.showToast({ title: '加入购物车', icon: 'none' }) - } - }).catch(err => { - uni.showToast({ title: err, icon: 'none' }) - }) - }; - - // 全选/取消全选 - const toggleAllItems = (checked: boolean) => { - items.value = items.value.map(item => ({ ...item, selected: checked })); - }; - - // 切换商品选中状态 - const toggleItemSelection = (cartId: string, selected?: boolean, item?: CartItem) => { - items.value = items.value.map(item => - item.id === cartId ? { ...item, selected: selected ?? !item.selected } : item - ) - }; - - // 清空购物车 - const clearCart = () => { - items.value = []; - }; - - return { - items, - itemsDetail, - orderDetail, - getListCart, - addItem, - removeItem, - updateQuantity, - toggleAllItems, - toggleItemSelection, - clearCart - }; -}); -//取层级数据 -//规格名称 -const selectSpecs = ((row:any) => { - let result = ''; - if (row) { - let row1 = row[0] - result = result + row1.value; - if(row1.child){ - let row2 = row1.child[0] - result = result + ';' + row1.child[0].value; - if(row2.child){ - result = result + ';' + row2.child[0].value; - } - } - } - return result; -}); -//取规格id -const selectSpecsIds = ((row:any)=>{ - let result = ''; - if (row) { - let row1 = row[0] - result = result + row1.id; - if(row1.child){ - let row2 = row1.child[0] - result = result + ',' + row1.child[0].id; - if(row2.child){ - result = result + ',' + row2.child[0].id; - } - } - } - return result; -}) diff --git a/src/store/index.ts b/src/store/index.ts deleted file mode 100644 index 0b98dbc..0000000 --- a/src/store/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createPinia } from 'pinia' -import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化 - -const store = createPinia() -store.use( - createPersistedState({ - storage: { - getItem: uni.getStorageSync, - setItem: uni.setStorageSync, - }, - }), -) - -export default store - -export * from './theme' -// 模块统一导出 -export * from './user' diff --git a/src/store/theme.ts b/src/store/theme.ts deleted file mode 100644 index c3f1c55..0000000 --- a/src/store/theme.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { ConfigProviderThemeVars } from 'wot-design-uni' - -import { defineStore } from 'pinia' - -export const useThemeStore = defineStore( - 'theme-store', - () => { - /** 主题 */ - const theme = ref<'light' | 'dark'>('light') - - /** 主题变量 */ - const themeVars = ref({ - // colorTheme: 'red', - // buttonPrimaryBgColor: '#07c160', - // buttonPrimaryColor: '#07c160', - }) - - /** 设置主题变量 */ - const setThemeVars = (partialVars: Partial) => { - themeVars.value = { ...themeVars.value, ...partialVars } - } - - /** 切换主题 */ - const toggleTheme = () => { - theme.value = theme.value === 'light' ? 'dark' : 'light' - } - - return { - /** 设置主题变量 */ - setThemeVars, - /** 切换主题 */ - toggleTheme, - /** 主题变量 */ - themeVars, - /** 主题 */ - theme, - } - }, - { - persist: true, - }, -) diff --git a/src/store/user.ts b/src/store/user.ts deleted file mode 100644 index 30ca23f..0000000 --- a/src/store/user.ts +++ /dev/null @@ -1,221 +0,0 @@ -/** - * 登录用户状态管理 - */ -import { defineStore } from 'pinia'; -import type { User } from '@/api/system/user/model/'; -import type { Menu } from '@/api/system/menu/model'; -import { toTree,mapTree, isExternalLink } from '@/utils/dataTree'; -import { getInfoByMember,getStoreSettings,getShopList } from '@/api/layout'; -import store from "@/store/index"; -import bus, {EVENT_KEY} from "@/utils/bus"; -/** - * 菜单数据 - */ -export interface MenuItem { - /** 路由名称 */ - name?: string; - /** 菜单地址 */ - path: string; - /** 路由组件 */ - component?: string; - /** 路由重定向 */ - redirect?: string; - /** 路由元数据 */ - meta?: any; - /** 子路由 */ - children?: Array; - /** 临时子路由数据, 内部属性 */ - tempChildren?: Array; - /** 用户位置信息 */ - location?: { latitude: number; longitude: number }; - /** 是否弹出 门店选择 */ - showPosition?: boolean; - -} -/** 直接指定菜单数据 */ -const USER_MENUS: Menu[] | null = null; - -export interface UserState { - info: User | null; - menus: any[] | null; - authorities: (string | undefined)[]; - roles: (string | undefined)[]; - loginPromise: null ,// 初始化Promise为null -} -export const useUserStore = defineStore('user', { - state: (): { - settings: null; - loginPromise: null; - roles: any[]; - location: {longitude:0,latitude:0}; - menus: null; - authorities: any[]; - info: null, - showPosition: boolean, - } => ({ - /** 当前登录用户的信息 */ - info: null, - /** 当前登录用户的菜单 */ - menus: null, - /** 当前登录用户的权限 */ - authorities: [], - /** 当前登录用户的角色 */ - roles: [], - /** 登录用户信息请求Promise */ - loginPromise: null ,// - /** 用户位置信息 */ - location: null, - /** 当前登录用户的门店配置 */ - settings: null, - /** 是否弹出 门店选择 */ - showPosition:false - }), - actions: { - /** - * 请求登录用户的个人信息/权限/角色/菜单 - */ - async fetchUserInfo() { - const result = await getInfoByMember().catch((e) => console.error(e)); - //门店配置 - const storeSettings = await getStoreSettings().catch((e) => console.error(e)); - //更新门店配置 - this.setSettings(storeSettings); - const store = uni.getStorageSync('store') - if (!store) { - //门店配置为0时, 表示不选择门店, 则获取用户位置。 - if (storeSettings.choiceShopWay === 0){ - await getLocationOnce().then((res:any) => { - console.log(res,'获取到用户位置信息') - this.setLocation({ latitude: res.latitude, longitude: res.longitude }) - getShopList({longitude:res.longitude,latitude:res.latitude}).then((res:any) => { - if (res.shops.length > 0) { - //循环筛选出最近,并不是休息的门店 - const validStores = res.shops.filter((shop: any) => shop.isBusinessTime); - if (validStores.length > 0) { - uni.setStorageSync('store', validStores[0] || null) - } - bus.emit(EVENT_KEY.REFRESH_SHOPPING_CART); - }else { - uni.showToast({ - title: '未查询到门店,请稍后再试', - icon: 'none' - }) - } - }) - }).catch((e)=>{ - console.log(e,'获取用户位置失败') - this.showPosition = true - }) - }else if (storeSettings.choiceShopWay === 1){ - this.showPosition = true - } - } - if (!result) { - return {}; - } - // 用户信息 - this.setInfo(result.user); - // 用户权限 - if (result.menus) { - this.authorities = result.menus.map((d) => d.authority).filter((a) => !!a) ?? []; - } - // 用户角色 - this.roles = result.roles?.map?.((d) => d.roleCode) ?? []; - // 用户菜单, 过滤掉按钮类型并转为children形式 - const { menus, homePath } = formatMenus( - USER_MENUS ?? - toTree({ - data: result.menus?.filter?.((d) => d.menuType !== 1), - idField: 'menuId', - parentIdField: 'parentId' - }) - ); - this.setMenus(menus); - return { menus, homePath }; - }, - /** - * 更新用户信息 - */ - setInfo(value: User) { - this.info = value; - }, - /** - * 更新菜单数据 - */ - setMenus(menus: any[] | null) { - this.menus = menus; - }, - /** - * 更新用户位置信息 - */ - setLocation(value: any) { - this.location = value; - }, - /** - * 更新门店配置 - */ - setSettings(settings: any) { - this.settings = settings; - } - } -}); -/** - * 菜单数据处理为EleProLayout所需要的格式 - * @param data 菜单数据 - * @param childField 子级的字段名称 - */ -function formatMenus(data: Menu[], childField = 'children') { - let homePath: string | undefined; - let homeTitle: string | undefined; - const menus = mapTree( - data, - (item) => { - const meta: MenuItem['meta'] = typeof item.meta === 'string' ? JSON.parse(item.meta || '{}') : item.meta; - const menu: MenuItem = { - path: item.path, - component: item.component, - meta: { title: item.title, icon: item.icon, hide: !!item.hide, ...meta } - }; - const children = item[childField] ? item[childField].filter((d: any) => !(d.meta?.hide ?? d.hide)) : void 0; - if (!children?.length) { - if (!homePath && menu.path && !isExternalLink(menu.path)) { - homePath = menu.path; - homeTitle = menu.meta?.title; - } - } else { - const childPath = children[0].path; - if (childPath) { - if (!menu.redirect) { - menu.redirect = childPath; - } - if (!menu.path) { - menu.path = childPath.substring(0, childPath.lastIndexOf('/')); - } - } - } - if (!menu.path) { - console.error('菜单path不能为空且要唯一:', item); - return; - } - return menu; - }, - childField - ); - return { menus, homePath, homeTitle }; -} -// 获取一次位置 -function getLocationOnce() { - return new Promise((resolve, reject) => { - uni.getLocation({ - type: 'wgs84', - altitude: true, - success: function (res) { - resolve(res) - }, - fail: function (err) { - reject(err) - }, - - }) - }) -} diff --git a/src/utils/maps/payment.ts b/src/utils/maps/payment.ts new file mode 100644 index 0000000..ff83145 --- /dev/null +++ b/src/utils/maps/payment.ts @@ -0,0 +1,14 @@ +export const paymentMethodOptions = [ + { label: '微信支付', value: 0, type: 'primary' }, + { label: '借呗支付', value: 1, type: 'success' }, + { label: '要呗支付', value: 2, type: 'info' }, + { label: '余额支付', value: 3, type: 'warning' }, +]; + +export const modeToPaymentMethodMap: Record = { + 0: [0], + 1: [0, 1], + 2: [0, 1], + 3: [0], + 4: [2], +}; \ No newline at end of file diff --git a/src/utils/validate.ts b/src/utils/validate.ts new file mode 100644 index 0000000..47c7f5f --- /dev/null +++ b/src/utils/validate.ts @@ -0,0 +1,15 @@ +/** 判断是否为数组 */ +export function isArray(arg: T) { + return Array.isArray ? Array.isArray(arg) : Object.prototype.toString.call(arg) === "[object Array]" +} + +/** 判断是否为字符串 */ +export function isString(str: unknown) { + return typeof str === "string" || str instanceof String +} + +/** 判断是否为外链 */ +export function isExternal(path: string) { + const reg = /^(https?:|mailto:|tel:)/ + return reg.test(path) +}