diff --git a/src/App.vue b/src/App.vue
index 4413537..56f19d0 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,5 +1,5 @@
-
-
+
@@ -239,7 +244,7 @@ watch(() => props.product, () => {
-
+
@@ -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;
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 7e43905..d5e4d42 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -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);
+ }
+})
diff --git a/src/pages/me/index.vue b/src/pages/me/index.vue
index 7b6779e..cdfe7b9 100644
--- a/src/pages/me/index.vue
+++ b/src/pages/me/index.vue
@@ -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(false)
const dynamicCodeData = ref(null)
const toast = useToast()
+// wx参数展示弹窗
+const wxParamsActionSheet = ref(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"
/>
@@ -215,6 +210,46 @@ const handleGenerateDynamicCode = async () => {
+
+
+
+
+
+ 原始Scene参数:
+
+ {{ rawScene || '暂无参数' }}
+
+
+
+
+ 解析状态:
+
+ {{ isParsed ? '解析完成' : '未解析' }}
+ (错误: {{ parseError }})
+
+
+
+
+ 解析结果:
+
+
+ {{ key }}:
+ {{ value }}
+
+
+
+
+
+ 暂无解析参数
+
+
+
@@ -467,3 +502,4 @@ const handleGenerateDynamicCode = async () => {
}
}
+
diff --git a/src/pinia/stores/currentProduct.ts b/src/pinia/stores/currentProduct.ts
new file mode 100644
index 0000000..73e84d9
--- /dev/null
+++ b/src/pinia/stores/currentProduct.ts
@@ -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(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)
+}
\ No newline at end of file
diff --git a/src/pinia/stores/wx-params.ts b/src/pinia/stores/wx-params.ts
new file mode 100644
index 0000000..aeaa4fb
--- /dev/null
+++ b/src/pinia/stores/wx-params.ts
@@ -0,0 +1,213 @@
+import { pinia } from "@/pinia"
+import { defineStore } from "pinia"
+
+/**
+ * 小程序参数store
+ * 用于解析和保存从小程序码scene参数中获取的键值对
+ */
+export const useWxParamsStore = defineStore("wx-params", () => {
+ // 原始scene字符串
+ const rawScene = ref("")
+ // 解析后的参数对象
+ const params = ref>({})
+ // 解析是否完成
+ const isParsed = ref(false)
+ // 解析错误信息
+ const parseError = ref("")
+
+ /**
+ * 解析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 => {
+ 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) => {
+ 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)
+}
\ No newline at end of file