Преглед изворни кода

feat(router): 支持扫码参数 q 为编码 URL 并解析参数

- 新增 parseUrlParams 工具函数解析编码或非编码的 URL 查询参数
- 在 appendParamsToPath 函数中处理 q 参数,支持提取并合并编码 URL 查询参数
- 在商品页组件中使用 parseUrlParams 解析 q 参数,获取 id、storeId、channelId
- 优化获取商品详情逻辑,传入获取到的店铺和渠道 ID
- 在类型声明文件中添加 parseUrlParams 类型定义支持自动导入
zhangtao пре 1 недеља
родитељ
комит
40e52acbb1
4 измењених фајлова са 145 додато и 8 уклоњено
  1. 2 0
      src/auto-imports.d.ts
  2. 74 2
      src/router/index.ts
  3. 9 6
      src/subPack-xsb/goods/index.vue
  4. 60 0
      src/utils/index.ts

+ 2 - 0
src/auto-imports.d.ts

@@ -111,6 +111,7 @@ declare global {
   const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
   const onUnmounted: typeof import('vue')['onUnmounted']
   const onUpdated: typeof import('vue')['onUpdated']
+  const parseUrlParams: typeof import('./utils/index')['parseUrlParams']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const persistPlugin: typeof import('./store/persist')['persistPlugin']
   const phoneFormat: typeof import('./utils/index')['phoneFormat']
@@ -475,6 +476,7 @@ declare module 'vue' {
     readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
     readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
+    readonly parseUrlParams: UnwrapRef<typeof import('./utils/index')['parseUrlParams']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly persistPlugin: UnwrapRef<typeof import('./store/persist')['persistPlugin']>
     readonly phoneFormat: UnwrapRef<typeof import('./utils/index')['phoneFormat']>

+ 74 - 2
src/router/index.ts

@@ -86,6 +86,7 @@ router.afterEach((to, from) => {
 export default router
 /**
  * 将 params 参数拼接到 path 路径后面
+ * 兼容扫码拉起小程序时 q 参数为编码后的 URL 的情况
  * @param path 基础路径
  * @param params 参数对象
  * @returns 拼接后的完整路径
@@ -95,11 +96,26 @@ function appendParamsToPath(path: string, params: Record<string, any>): string {
     return path
   }
   const queryParams: string[] = []
+
   Object.entries(params).forEach(([key, value]) => {
     if (value !== undefined && value !== null) {
-      // 对参数进行 URL 编码
+      const strValue = String(value)
+
+      // 处理扫码参数 q,可能是编码后的 URL
+      if (key === 'q') {
+        const extractedParams = extractParamsFromEncodedUrl(strValue)
+        if (extractedParams) {
+          // 将提取的参数合并到 queryParams
+          Object.entries(extractedParams).forEach(([k, v]) => {
+            queryParams.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
+          })
+          return
+        }
+      }
+
+      // 普通参数直接编码
       const encodedKey = encodeURIComponent(key)
-      const encodedValue = encodeURIComponent(String(value))
+      const encodedValue = encodeURIComponent(strValue)
       queryParams.push(`${encodedKey}=${encodedValue}`)
     }
   })
@@ -107,3 +123,59 @@ function appendParamsToPath(path: string, params: Record<string, any>): string {
   const queryString = queryParams.join('&')
   return queryString ? `${path}?${queryString}` : path
 }
+
+/**
+ * 从编码后的 URL 中提取查询参数
+ * @param encodedUrl 可能被编码的 URL 字符串
+ * @returns 解析后的查询参数对象,如果不是有效的 URL 则返回 null
+ */
+function extractParamsFromEncodedUrl(encodedUrl: string): Record<string, string> | null {
+  try {
+    // 尝试解码 URL
+    let decodedUrl = encodedUrl
+    // 可能需要多次解码(处理多次编码的情况)
+    while (decodedUrl.includes('%')) {
+      const newDecoded = decodeURIComponent(decodedUrl)
+      if (newDecoded === decodedUrl)
+        break
+      decodedUrl = newDecoded
+    }
+
+    // 检查是否是有效的 URL 格式
+    if (!decodedUrl.includes('http://') && !decodedUrl.includes('https://')) {
+      return null
+    }
+
+    // 提取 ? 后面的查询参数
+    const questionIndex = decodedUrl.indexOf('?')
+    if (questionIndex === -1) {
+      return null
+    }
+
+    const queryString = decodedUrl.substring(questionIndex + 1)
+    if (!queryString) {
+      return null
+    }
+
+    // 解析查询参数
+    const params: Record<string, string> = {}
+    const pairs = queryString.split('&')
+
+    pairs.forEach((pair) => {
+      const equalIndex = pair.indexOf('=')
+      if (equalIndex > 0) {
+        const key = decodeURIComponent(pair.substring(0, equalIndex))
+        const val = decodeURIComponent(pair.substring(equalIndex + 1))
+        params[key] = val
+      }
+      else if (pair) {
+        params[decodeURIComponent(pair)] = ''
+      }
+    })
+
+    return Object.keys(params).length > 0 ? params : null
+  }
+  catch {
+    return null
+  }
+}

+ 9 - 6
src/subPack-xsb/goods/index.vue

@@ -37,13 +37,16 @@ const isShowTab = ref(false)
 const goodsId = ref()
 onLoad((option: any) => {
   goodsId.value = option.id
+  console.log(option, 'option====================')
+
   if (option.q) {
-    const urls = decodeURIComponent(option.q)
-    const id = urls.split('?id=')[1]
+    const { id, storeId, channelId } = parseUrlParams(option.q)
     goodsId.value = id
+    getGoodsDetaile(Number(storeId), Number(channelId))
+  }
+  else {
+    getGoodsDetaile(option.storeId, option.channelId)
   }
-
-  getGoodsDetaile()
 })
 
 const goodsInfo = ref<Api.xsbProductDetail>()
@@ -93,8 +96,8 @@ function handleGoCurren(id: number) {
   uni.pageScrollTo({ selector: `.view-${id}` })
 }
 
-async function getGoodsDetaile() {
-  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || 2, channelId: userInfo.value.channelId || 1 } })
+async function getGoodsDetaile(shopId: number, channelId: number) {
+  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || shopId || 2, channelId: userInfo.value.channelId || channelId || 1 } })
   console.log(res, '请求')
   if (!res.data) {
     useGlobalToast().show('暂无该商品查看权限!')

+ 60 - 0
src/utils/index.ts

@@ -157,3 +157,63 @@ export class InputFormatUtil {
 export function phoneFormat(phone: string) {
   return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
 }
+
+/**
+ * 从 URL 中解析查询参数
+ * 支持已编码和未编码的 URL
+ * @param url URL 字符串,可以是完整 URL 或仅查询字符串部分
+ * @returns 解析后的参数对象
+ * @example
+ */
+export function parseUrlParams(url: string): Record<string, string> {
+  if (!url)
+    return {}
+
+  try {
+    // 尝试解码(处理多次编码的情况)
+    let decodedUrl = url
+    while (decodedUrl.includes('%')) {
+      const newDecoded = decodeURIComponent(decodedUrl)
+      if (newDecoded === decodedUrl)
+        break
+      decodedUrl = newDecoded
+    }
+
+    // 提取查询字符串部分
+    let queryString = decodedUrl
+    const questionIndex = decodedUrl.indexOf('?')
+    if (questionIndex !== -1) {
+      queryString = decodedUrl.substring(questionIndex + 1)
+    }
+
+    // 移除可能存在的 hash 部分
+    const hashIndex = queryString.indexOf('#')
+    if (hashIndex !== -1) {
+      queryString = queryString.substring(0, hashIndex)
+    }
+
+    if (!queryString)
+      return {}
+
+    // 解析参数
+    const params: Record<string, string> = {}
+    const pairs = queryString.split('&')
+
+    pairs.forEach((pair) => {
+      const equalIndex = pair.indexOf('=')
+      if (equalIndex > 0) {
+        const key = decodeURIComponent(pair.substring(0, equalIndex))
+        const val = decodeURIComponent(pair.substring(equalIndex + 1))
+        params[key] = val
+      }
+      else if (pair) {
+        params[decodeURIComponent(pair)] = ''
+      }
+    })
+
+    return params
+  }
+  catch {
+    return {}
+  }
+}