refactor(store): 重构状态管理模块,迁移至pinia架构

将原有vuex状态管理迁移至pinia架构,包括用户、购物车、订单等核心模块
新增支付方式映射、验证工具函数等实用工具
优化状态持久化配置,统一使用uni-app存储API
重构企业微信登录逻辑,增加模拟登录功能
This commit is contained in:
dzq 2025-10-30 17:35:25 +08:00
parent 23041a62d2
commit 1b82b8baab
15 changed files with 700 additions and 441 deletions

13
src/pinia/index.ts Normal file
View File

@ -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,
},
}),
);

View File

@ -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<string>(storedFace ? decodeURIComponent(storedFace) : '')
// 用户性别(男/女)
const storedSex = localStorage.getItem(STORAGE_KEYS.SEX)
const sex = ref<string>(storedSex ? decodeURIComponent(storedSex) : '')
// 用户真实姓名
const storedName = localStorage.getItem(STORAGE_KEYS.NAME)
const name = ref<string>(storedName ? decodeURIComponent(storedName) : '')
// AB98系统用户唯一标识
const storedUserId = localStorage.getItem(STORAGE_KEYS.USERID)
const userid = ref<string>(storedUserId ? decodeURIComponent(storedUserId) : "")
// 是否已完成注册流程
const registered = ref<boolean>(JSON.parse(localStorage.getItem(STORAGE_KEYS.REGISTERED) || "false"))
// 用户绑定手机号
const storedTel = localStorage.getItem(STORAGE_KEYS.TEL)
const tel = ref<string>(storedTel ? decodeURIComponent(storedTel) : "")
// 用户认证令牌
const storedToken = localStorage.getItem(STORAGE_KEYS.TOKEN)
const token = ref<string>(storedToken ? decodeURIComponent(storedToken) : "");
const tokenLogin = ref<string>("");
// 登录验证码
const loginCode = '1'; // 默认验证码
// 用户登录状态
const isLogin = ref<boolean>(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)
}

View File

@ -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<ApprovalDetail | null>(null)
const setCurrentApproval = (approval: ApprovalDetail) => {
currentApproval.value = approval
}
return {
currentApproval,
setCurrentApproval
}
})
export function useApprovalStoreOutside() {
return useApprovalStore()
}

69
src/pinia/stores/cart.ts Normal file
View File

@ -0,0 +1,69 @@
import { defineStore } from 'pinia'
import type { Product } from './product'
export const useCartStore = defineStore('cart', () => {
// 购物车商品列表
const cartItems = ref<Array<{ product: Product; quantity: number }>>([])
// 添加商品到购物车
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()
}

View File

@ -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<string[]>([])
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)
}

51
src/pinia/stores/order.ts Normal file
View File

@ -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<OrderGoods>
}
export interface OrderGoods {
goodsInfo: Goods
orderGoods: ShopOrderGoodsEntity
}
export const useOrderStore = defineStore("order", () => {
const orders = ref<Order[]>([])
const orderGoods = ref<ShopOrderGoodsEntity[]>([])
const goods = ref<Goods[]>([])
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)
}

View File

@ -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<Product[]>([]);
const shopId = ref<number|null>(null);
const selectedShop = ref<ShopEntity|null>(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)
}

View File

@ -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<RentingCabinetDetailDTO[]>([])
// 租用购物车列表
const rentingCartItems = ref<Array<{ cabinetCell: CabinetCellEntity; quantity: number }>>([])
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()
}

191
src/pinia/stores/wx.ts Normal file
View File

@ -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<string>("")
// 防止 CSRF 攻击的 state 参数
const state = ref<string>("")
// 用户 openid
const openid = ref<string>("")
// 用户 userid
const userid = ref<string>("");
// 剩余借呗
const balance = ref<number>(0);
// 已用借呗
const useBalance = ref<number>(0);
// 借呗总额
const balanceLimit = ref<number>(0);
// 企业id
const corpid = ref<string>("");
// 是否企业微信登录
const corpidLogin = ref<boolean>(false);
// 是否是柜子管理员
const isCabinetAdmin = ref<boolean>(false);
// 企业微信用户姓名
const name = ref<string>("");
// 企业微信用户id
const qyUserId = ref<number>(0);
// 汇邦云用户信息
const ab98User = ref<ab98UserDTO | null>(null);
const isFakeQyLogin = ref<boolean>(false);
// handleWxCallback 是否完成
const isHandleWxCallbackComplete = ref<boolean>(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<GetBalanceResponse> | 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<boolean> => {
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)
}

View File

@ -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<CartItem[]>([]);
// 购物车详情
const itemsDetail = ref<CartItem>()
//订单详情
const orderDetail = ref<CartItem[]>()
// 查询购物车列表
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<CartItem, 'selected'>) => {
//把一下数据异步返回
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;
})

View File

@ -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'

View File

@ -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<ConfigProviderThemeVars>({
// colorTheme: 'red',
// buttonPrimaryBgColor: '#07c160',
// buttonPrimaryColor: '#07c160',
})
/** 设置主题变量 */
const setThemeVars = (partialVars: Partial<ConfigProviderThemeVars>) => {
themeVars.value = { ...themeVars.value, ...partialVars }
}
/** 切换主题 */
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
return {
/** 设置主题变量 */
setThemeVars,
/** 切换主题 */
toggleTheme,
/** 主题变量 */
themeVars,
/** 主题 */
theme,
}
},
{
persist: true,
},
)

View File

@ -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<MenuItem>;
/** 临时子路由数据, 内部属性 */
tempChildren?: Array<MenuItem>;
/** 用户位置信息 */
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)
},
})
})
}

14
src/utils/maps/payment.ts Normal file
View File

@ -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<number, number[]> = {
0: [0],
1: [0, 1],
2: [0, 1],
3: [0],
4: [2],
};

15
src/utils/validate.ts Normal file
View File

@ -0,0 +1,15 @@
/** 判断是否为数组 */
export function isArray<T>(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)
}