feat: 添加商品详情页及微信参数解析功能
- 新增商品详情页面,支持从首页跳转查看商品详情 - 创建currentProduct store管理当前查看的商品数据 - 实现微信小程序scene参数解析功能,创建wx-params store - 修改首页商品点击逻辑,从弹窗改为跳转详情页 - 在个人中心添加微信参数查看功能 - 优化商品详情页的借还动态加载和显示逻辑
This commit is contained in:
parent
f543a7e858
commit
918bc53059
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { onHide, onLaunch, onShow, } from '@dcloudio/uni-app'
|
||||
import { onHide, onLaunch, onShow, onLoad} from '@dcloudio/uni-app'
|
||||
import { navigateToInterceptor } from '@/router/interceptor'
|
||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
|
||||
import {onMounted, ref} from 'vue'
|
||||
|
|
|
|||
|
|
@ -76,6 +76,13 @@
|
|||
"navigationBarTitleText": "结算购物车"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/detail",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"type": "page",
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useProductStore } from '@/pinia/stores/product'
|
||||
import { useCartStore } from '@/pinia/stores/cart'
|
||||
import { useCurrentProductStore } from '@/pinia/stores/currentProduct'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import Detail from './detail.vue'
|
||||
import Cart from './cart.vue'
|
||||
import { toHttpsUrl } from '@/utils'
|
||||
|
||||
|
|
@ -24,18 +24,19 @@ const productStore = useProductStore()
|
|||
const { labels, categories } = storeToRefs(productStore)
|
||||
const cartStore = useCartStore()
|
||||
const { cartItems, totalPrice, totalQuantity } = storeToRefs(cartStore)
|
||||
const currentProductStore = useCurrentProductStore()
|
||||
|
||||
// 从props接收的数据
|
||||
const activeCategory = ref<number>(0)
|
||||
|
||||
// 商品详情弹窗控制
|
||||
const showDetailPopup = ref<boolean>(false)
|
||||
// const showDetailPopup = ref<boolean>(false)
|
||||
// 当前查看的商品ID
|
||||
const currentCellId = ref<number>()
|
||||
// 当前商品详情计算属性
|
||||
const currentProduct = computed(() =>
|
||||
categories.value.find(p => p.cellId === currentCellId.value)
|
||||
)
|
||||
// const currentProduct = computed(() =>
|
||||
// categories.value.find(p => p.cellId === currentCellId.value)
|
||||
// )
|
||||
// 购物车弹窗控制
|
||||
const showCartPopup = ref<boolean>(false)
|
||||
|
||||
|
|
@ -55,7 +56,16 @@ function handleCategoryClick(index: number) {
|
|||
*/
|
||||
function showProductDetail(cellId: number) {
|
||||
currentCellId.value = cellId
|
||||
showDetailPopup.value = true
|
||||
// 获取当前选中的商品
|
||||
const product = categories.value.find(p => p.cellId === cellId)
|
||||
if (product) {
|
||||
// 设置当前商品到store
|
||||
currentProductStore.setCurrentProduct(product)
|
||||
// 跳转到详情页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/detail'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -205,17 +215,6 @@ function handleCheckout() {
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品详情弹窗 -->
|
||||
<wd-popup
|
||||
v-model="showDetailPopup"
|
||||
position="bottom"
|
||||
:z-index="999"
|
||||
>
|
||||
<view class="detail-container" v-if="currentProduct">
|
||||
<Detail :product="currentProduct" @detail-close="showDetailPopup = false" />
|
||||
</view>
|
||||
</wd-popup>
|
||||
|
||||
<!-- 购物车弹窗 -->
|
||||
<wd-popup v-model="showCartPopup" position="bottom" :z-index="999">
|
||||
<view class="detail-container">
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, onUnmounted } from 'vue'
|
||||
import type { Product } from '@/pinia/stores/product'
|
||||
import { useCartStore } from '@/pinia/stores/cart'
|
||||
import { useCurrentProductStore } from '@/pinia/stores/currentProduct'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { toHttpsUrl } from '@/utils'
|
||||
// import { onLoad, onReachBottom, onShow } from '@dcloudio/uni-app'
|
||||
import { getBorrowReturnDynamicApi, type BorrowReturnDynamicDTO, type SearchBorrowReturnDynamicQuery } from '@/api/order'
|
||||
import { TabsInstance } from 'wot-design-uni/components/wd-tabs/types'
|
||||
|
||||
// 定义组件接收的属性
|
||||
const props = defineProps<{
|
||||
product: Product
|
||||
}>()
|
||||
|
||||
// 定义组件事件:关闭详情
|
||||
const emit = defineEmits<{
|
||||
(e: 'detailClose'): void
|
||||
}>()
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '商品详情',
|
||||
},
|
||||
})
|
||||
|
||||
// 状态管理
|
||||
const cartStore = useCartStore()
|
||||
const { cartItems } = storeToRefs(cartStore);
|
||||
const currentProductStore = useCurrentProductStore();
|
||||
|
||||
// 获取当前商品
|
||||
const product = computed(() => currentProductStore.currentProduct)
|
||||
|
||||
const tabsRef = ref<TabsInstance>();
|
||||
|
||||
|
|
@ -43,35 +44,33 @@ const loadmoreState = ref<'loading' | 'finished' | 'error'>('loading')
|
|||
|
||||
// scroll-view 高度计算
|
||||
const scrollHeight = computed(() => {
|
||||
// 计算可用高度:屏幕高度 - 顶部操作栏 - 底部操作栏 - 标签栏
|
||||
// 计算可用高度:屏幕高度 - 底部操作栏 - 标签栏
|
||||
const windowHeight = uni.getSystemInfoSync().windowHeight
|
||||
const headerHeight = 60 // 顶部操作栏高度
|
||||
const actionBarHeight = 64 // 底部操作栏高度
|
||||
const tabHeight = 44 // 标签栏高度
|
||||
return `${windowHeight - headerHeight - actionBarHeight - tabHeight}px`
|
||||
return `${windowHeight - actionBarHeight - tabHeight}px`
|
||||
})
|
||||
|
||||
const maxQuantity = computed(() => {
|
||||
const existingItem = cartItems.value.find(item => item.product.id === props.product.id)
|
||||
if (!product.value) return true
|
||||
const existingItem = cartItems.value.find(item => item.product.id === product.value.id)
|
||||
if (existingItem) {
|
||||
return props.product.stock !== -1 && existingItem.quantity + quantity.value >= props.product.stock
|
||||
return product.value.stock !== -1 && existingItem.quantity + quantity.value >= product.value.stock
|
||||
} else {
|
||||
return props.product.stock !== -1 && quantity.value >= props.product.stock
|
||||
return product.value.stock !== -1 && quantity.value >= product.value.stock
|
||||
}
|
||||
})
|
||||
|
||||
// 处理关闭按钮点击事件
|
||||
function handleClose() {
|
||||
emit('detailClose')
|
||||
}
|
||||
|
||||
|
||||
function handleAddToCart(): boolean {
|
||||
if (!product.value) return false
|
||||
if (maxQuantity.value) {
|
||||
// TODO 提示库存不足
|
||||
return false
|
||||
}
|
||||
const existingItem = cartItems.value.find(item => item.product.id === props.product.id);
|
||||
if (props.product.stock !== -1 && existingItem && existingItem.quantity + quantity.value >= props.product.stock) {
|
||||
const existingItem = cartItems.value.find(item => item.product.id === product.value.id);
|
||||
if (product.value.stock !== -1 && existingItem && existingItem.quantity + quantity.value >= product.value.stock) {
|
||||
// TODO 提示库存不足
|
||||
return false
|
||||
}
|
||||
|
|
@ -86,16 +85,20 @@ function handleRemoveFromCart() {
|
|||
}
|
||||
|
||||
function confirmAddCart() {
|
||||
cartStore.addToCart(props.product, quantity.value)
|
||||
if (product.value) {
|
||||
cartStore.addToCart(product.value, quantity.value)
|
||||
}
|
||||
// 重置参数
|
||||
quantity.value = 1
|
||||
showAddCart.value = false
|
||||
emit('detailClose')
|
||||
// 返回上一页
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
function doShowAddCart(): boolean {
|
||||
const existingItem = cartItems.value.find(item => item.product.id === props.product.id)
|
||||
if (props.product.stock !== -1 && existingItem && existingItem.quantity >= props.product.stock) {
|
||||
if (!product.value) return false
|
||||
const existingItem = cartItems.value.find(item => item.product.id === product.value.id)
|
||||
if (product.value.stock !== -1 && existingItem && existingItem.quantity >= product.value.stock) {
|
||||
// TODO 提示库存不足
|
||||
return false
|
||||
}
|
||||
|
|
@ -105,14 +108,14 @@ function doShowAddCart(): boolean {
|
|||
|
||||
// 获取借还动态数据(带分页)
|
||||
async function loadDynamicList(page: number = 1) {
|
||||
if (!props.product?.id) return
|
||||
if (!product.value?.id) return
|
||||
|
||||
loading.value = true
|
||||
loadmoreState.value = 'loading'
|
||||
try {
|
||||
const query: SearchBorrowReturnDynamicQuery = {
|
||||
goodsId: props.product.id,
|
||||
cellId: props.product.cellId,
|
||||
goodsId: product.value.id,
|
||||
cellId: product.value.cellId,
|
||||
pageNum: page,
|
||||
pageSize: pageSize.value
|
||||
}
|
||||
|
|
@ -171,10 +174,12 @@ watch(activeTab, (newVal) => {
|
|||
const refreshDynamicList = () => {
|
||||
dynamicList.value = [];
|
||||
pageNum.value = 1;
|
||||
loadDynamicList(1);
|
||||
if (product.value) {
|
||||
loadDynamicList(1);
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.product, () => {
|
||||
watch(product, () => {
|
||||
// 重置参数
|
||||
// activeTab.value = 0;
|
||||
wx.pageScrollTo({
|
||||
|
|
@ -186,17 +191,17 @@ watch(() => props.product, () => {
|
|||
showAddCart.value = false;
|
||||
refreshDynamicList();
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
// 页面卸载时清理当前商品数据
|
||||
onUnmounted(() => {
|
||||
currentProductStore.clearCurrentProduct()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 商品详情容器 -->
|
||||
<view class="detail-container">
|
||||
<!-- 顶部操作栏 -->
|
||||
<view class="header-bar">
|
||||
<view class="close-icon" @click="handleClose">
|
||||
<wd-icon name="close" size="20px" color="#969799"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 商品内容区域(可滚动) -->
|
||||
<view class="content-area">
|
||||
|
|
@ -239,7 +244,7 @@ watch(() => props.product, () => {
|
|||
</wd-tab>
|
||||
|
||||
<!-- 标签2:借还动态 -->
|
||||
<wd-tab title="借还动态">
|
||||
<wd-tab title="商品动态">
|
||||
<!-- 动态列表 -->
|
||||
<scroll-view class="dynamic-list-scroll" scroll-y :style="{ height: scrollHeight }"
|
||||
@scrolltolower="handleScrollToLower" :lower-threshold="100">
|
||||
|
|
@ -400,22 +405,7 @@ watch(() => props.product, () => {
|
|||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 10px 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1;
|
||||
|
|
@ -10,6 +10,7 @@ import { generateDynamicCode, getWxUserByOpenid, mpCodeToOpenId } from '@/api/us
|
|||
import { toHttpsUrl } from '@/utils'
|
||||
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useWxParamsStore } from '@/pinia/stores/wx-params'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
|
|
@ -108,6 +109,16 @@ function handleCheckout() {
|
|||
url: '/pages/index/checkout'
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((query) => {
|
||||
console.log('page index onLoad query: ', query);
|
||||
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
|
||||
if (query && query.scene) {
|
||||
const scene = decodeURIComponent(query.scene);
|
||||
const wxParamsStore = useWxParamsStore();
|
||||
wxParamsStore.parseScene(scene);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useWxStore } from '@/pinia/stores/wx'
|
||||
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||||
import { useWxParamsStore } from '@/pinia/stores/wx-params'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { toHttpsUrl } from '@/utils'
|
||||
import { generateDynamicCode } from '@/api/users'
|
||||
|
|
@ -16,11 +17,12 @@ definePage({
|
|||
|
||||
const wxStore = useWxStore()
|
||||
const ab98UserStore = useAb98UserStore()
|
||||
const wxParamsStore = useWxParamsStore()
|
||||
|
||||
const { balance, useBalance, balanceLimit, name: qyName, ab98User } = storeToRefs(wxStore)
|
||||
const { name: userName, face_img } = storeToRefs(ab98UserStore);
|
||||
const { wxUserDTO } = storeToRefs(wxStore);
|
||||
|
||||
const { rawScene, params, isParsed, parseError } = storeToRefs(wxParamsStore);
|
||||
|
||||
(globalThis as any).ab98UserStore = ab98UserStore;
|
||||
|
||||
|
|
@ -35,6 +37,9 @@ const dynamicCodeActionSheet = ref<boolean>(false)
|
|||
const dynamicCodeData = ref<DynamicCodeResponse | null>(null)
|
||||
const toast = useToast()
|
||||
|
||||
// wx参数展示弹窗
|
||||
const wxParamsActionSheet = ref<boolean>(false)
|
||||
|
||||
const ab98BalanceInYuan = computed(() => {
|
||||
if (ab98User.value && ab98User.value.ab98Balance !== undefined) {
|
||||
return (ab98User.value.ab98Balance / 100).toFixed(2)
|
||||
|
|
@ -42,19 +47,9 @@ const ab98BalanceInYuan = computed(() => {
|
|||
return '0.00'
|
||||
})
|
||||
|
||||
const handleLogout = () => {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出当前账号吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
ab98UserStore.clearUserInfo()
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
// 展示wx参数
|
||||
const handleShowWxParams = () => {
|
||||
wxParamsActionSheet.value = true
|
||||
}
|
||||
|
||||
// 统一的页面跳转函数
|
||||
|
|
@ -121,7 +116,7 @@ const handleGenerateDynamicCode = async () => {
|
|||
class="avatar"
|
||||
:src="wxUserDTO?.ab98FaceImg"
|
||||
mode="aspectFill"
|
||||
@click="handleLogout"
|
||||
@click="handleShowWxParams"
|
||||
/>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
|
|
@ -215,6 +210,46 @@ const handleGenerateDynamicCode = async () => {
|
|||
</view>
|
||||
</view>
|
||||
</wd-action-sheet>
|
||||
|
||||
<!-- wx参数展示弹窗 -->
|
||||
<wd-action-sheet
|
||||
v-model="wxParamsActionSheet"
|
||||
title="小程序参数信息"
|
||||
cancel-text="关闭"
|
||||
:close-on-click-modal="true"
|
||||
@close="wxParamsActionSheet = false"
|
||||
>
|
||||
<view style="padding: 20px;">
|
||||
<view style="margin-bottom: 20px;">
|
||||
<view style="font-size: 14px; font-weight: bold; color: #409EFF; margin-bottom: 8px;">原始Scene参数:</view>
|
||||
<view style="font-size: 12px; color: #666; background: #f5f5f5; padding: 8px 12px; border-radius: 4px; word-break: break-all;">
|
||||
{{ rawScene || '暂无参数' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="margin-bottom: 20px;">
|
||||
<view style="font-size: 14px; font-weight: bold; color: #409EFF; margin-bottom: 8px;">解析状态:</view>
|
||||
<view style="font-size: 12px; color: #666;">
|
||||
{{ isParsed ? '解析完成' : '未解析' }}
|
||||
<span v-if="parseError" style="color: #f56c6c;"> (错误: {{ parseError }})</span>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="isParsed && Object.keys(params).length > 0">
|
||||
<view style="font-size: 14px; font-weight: bold; color: #409EFF; margin-bottom: 8px;">解析结果:</view>
|
||||
<view style="background: #f9f9f9; border-radius: 4px; padding: 12px;">
|
||||
<view v-for="(value, key) in params" :key="key" style="display: flex; margin-bottom: 6px; font-size: 12px;">
|
||||
<view style="color: #409EFF; font-weight: 500; min-width: 80px;">{{ key }}:</view>
|
||||
<view style="color: #666; flex: 1; word-break: break-all;">{{ value }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else-if="isParsed" style="font-size: 12px; color: #999; text-align: center; padding: 20px;">
|
||||
暂无解析参数
|
||||
</view>
|
||||
</view>
|
||||
</wd-action-sheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -467,3 +502,4 @@ const handleGenerateDynamicCode = async () => {
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { pinia } from "@/pinia"
|
||||
import { defineStore } from "pinia"
|
||||
import type { Product } from './product'
|
||||
|
||||
export const useCurrentProductStore = defineStore("currentProduct", () => {
|
||||
// 当前查看的商品
|
||||
const currentProduct = ref<Product | null>(null)
|
||||
|
||||
/**
|
||||
* 设置当前查看的商品
|
||||
* @param product - 商品数据
|
||||
*/
|
||||
const setCurrentProduct = (product: Product | null) => {
|
||||
currentProduct.value = product
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前商品数据
|
||||
*/
|
||||
const clearCurrentProduct = () => {
|
||||
currentProduct.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
currentProduct,
|
||||
setCurrentProduct,
|
||||
clearCurrentProduct
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 在 SPA 应用中可用于在 pinia 实例被激活前使用 store
|
||||
* @description 在 SSR 应用中可用于在 setup 外使用 store
|
||||
*/
|
||||
export function useCurrentProductStoreOutside() {
|
||||
return useCurrentProductStore(pinia)
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
import { pinia } from "@/pinia"
|
||||
import { defineStore } from "pinia"
|
||||
|
||||
/**
|
||||
* 小程序参数store
|
||||
* 用于解析和保存从小程序码scene参数中获取的键值对
|
||||
*/
|
||||
export const useWxParamsStore = defineStore("wx-params", () => {
|
||||
// 原始scene字符串
|
||||
const rawScene = ref<string>("")
|
||||
// 解析后的参数对象
|
||||
const params = ref<Record<string, string>>({})
|
||||
// 解析是否完成
|
||||
const isParsed = ref<boolean>(false)
|
||||
// 解析错误信息
|
||||
const parseError = ref<string>("")
|
||||
|
||||
/**
|
||||
* 解析scene参数
|
||||
* @param scene - 小程序码scene参数,支持以下格式:
|
||||
* 1. key1=value1&key2=value2
|
||||
* 2. JSON字符串格式
|
||||
* 3. 其他自定义格式
|
||||
*/
|
||||
const parseScene = (scene: string) => {
|
||||
try {
|
||||
// 重置状态
|
||||
rawScene.value = scene
|
||||
params.value = {}
|
||||
isParsed.value = false
|
||||
parseError.value = ""
|
||||
|
||||
if (!scene || scene.trim() === "") {
|
||||
isParsed.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试解析为JSON格式
|
||||
if (scene.trim().startsWith("{") && scene.trim().endsWith("}")) {
|
||||
try {
|
||||
const jsonParams = JSON.parse(scene)
|
||||
if (typeof jsonParams === "object" && jsonParams !== null) {
|
||||
// 遍历JSON对象,将值转换为字符串
|
||||
Object.keys(jsonParams).forEach(key => {
|
||||
params.value[key] = String(jsonParams[key])
|
||||
})
|
||||
isParsed.value = true
|
||||
return
|
||||
}
|
||||
} catch (jsonError) {
|
||||
// JSON解析失败,继续尝试其他格式
|
||||
console.warn("Scene参数不是有效的JSON格式,尝试其他解析方式:", jsonError)
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试解析为URL查询字符串格式 (key1=value1&key2=value2)
|
||||
const urlSearchParams = new URLSearchParams(scene)
|
||||
if (urlSearchParams.toString() !== "") {
|
||||
urlSearchParams.forEach((value, key) => {
|
||||
params.value[key] = value
|
||||
})
|
||||
isParsed.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// 如果以上方法都失败,尝试简单的键值对分割
|
||||
const parts = scene.split("&")
|
||||
let hasValidPairs = false
|
||||
|
||||
parts.forEach(part => {
|
||||
const [key, ...valueParts] = part.split("=")
|
||||
if (key && key.trim() !== "") {
|
||||
const value = valueParts.join("=") // 处理值中包含等号的情况
|
||||
params.value[key.trim()] = value.trim()
|
||||
hasValidPairs = true
|
||||
}
|
||||
})
|
||||
|
||||
if (hasValidPairs) {
|
||||
isParsed.value = true
|
||||
} else {
|
||||
// 如果没有任何有效的键值对,将整个scene作为单个参数
|
||||
params.value["scene"] = scene
|
||||
isParsed.value = true
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("解析scene参数失败:", error)
|
||||
parseError.value = error instanceof Error ? error.message : "未知错误"
|
||||
isParsed.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定参数的值
|
||||
* @param key - 参数键名
|
||||
* @param defaultValue - 默认值(可选)
|
||||
* @returns 参数值或默认值
|
||||
*/
|
||||
const getParam = (key: string, defaultValue: string = ""): string => {
|
||||
return params.value[key] || defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数字类型的参数值
|
||||
* @param key - 参数键名
|
||||
* @param defaultValue - 默认值(可选)
|
||||
* @returns 数字值或默认值
|
||||
*/
|
||||
const getNumberParam = (key: string, defaultValue: number = 0): number => {
|
||||
const value = params.value[key]
|
||||
if (value === undefined || value === null) {
|
||||
return defaultValue
|
||||
}
|
||||
const num = Number(value)
|
||||
return isNaN(num) ? defaultValue : num
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取布尔类型的参数值
|
||||
* @param key - 参数键名
|
||||
* @param defaultValue - 默认值(可选)
|
||||
* @returns 布尔值或默认值
|
||||
*/
|
||||
const getBooleanParam = (key: string, defaultValue: boolean = false): boolean => {
|
||||
const value = params.value[key]
|
||||
if (value === undefined || value === null) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
const lowerValue = value.toLowerCase()
|
||||
if (lowerValue === "true" || lowerValue === "1" || lowerValue === "yes") {
|
||||
return true
|
||||
}
|
||||
if (lowerValue === "false" || lowerValue === "0" || lowerValue === "no") {
|
||||
return false
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含指定参数
|
||||
* @param key - 参数键名
|
||||
* @returns 是否包含该参数
|
||||
*/
|
||||
const hasParam = (key: string): boolean => {
|
||||
return key in params.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有参数
|
||||
* @returns 参数对象的副本
|
||||
*/
|
||||
const getAllParams = (): Record<string, string> => {
|
||||
return { ...params.value }
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有参数
|
||||
*/
|
||||
const clearParams = () => {
|
||||
rawScene.value = ""
|
||||
params.value = {}
|
||||
isParsed.value = false
|
||||
parseError.value = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置参数(用于测试或其他场景)
|
||||
* @param key - 参数键名
|
||||
* @param value - 参数值
|
||||
*/
|
||||
const setParam = (key: string, value: string) => {
|
||||
params.value[key] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置参数
|
||||
* @param newParams - 新的参数对象
|
||||
*/
|
||||
const setParams = (newParams: Record<string, string>) => {
|
||||
Object.keys(newParams).forEach(key => {
|
||||
params.value[key] = newParams[key]
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
rawScene,
|
||||
params,
|
||||
isParsed,
|
||||
parseError,
|
||||
|
||||
// 方法
|
||||
parseScene,
|
||||
getParam,
|
||||
getNumberParam,
|
||||
getBooleanParam,
|
||||
hasParam,
|
||||
getAllParams,
|
||||
clearParams,
|
||||
setParam,
|
||||
setParams
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 用于在 setup 外使用 store
|
||||
*/
|
||||
export function useWxParamsStoreOutside() {
|
||||
return useWxParamsStore(pinia)
|
||||
}
|
||||
Loading…
Reference in New Issue