Przeglądaj źródła

fix(group-buy): 完善拼团分享登录与门店上下文

- 团购详情页接入手机号登录弹层,开团和参团前校验登录态,登录成功后刷新详情并回填 hdId

- 分享进入保留接口回填的 hdId,支付和单独购买实时解析门店上下文,避免被缺省参数覆盖为 0

- POST 请求仅在 URL 携带 account/hdId,业务参数保留在 body,账号登录失败时展示后端错误提示
shizhongqi 3 dni temu
rodzic
commit
5e2f39c4ad

+ 71 - 9
mallApp/src/pages/groupBuy/detail.vue

@@ -4,6 +4,7 @@
   展示商品信息、当前拼团卡片、开团玩法、底部开团/参团/分享操作
 -->
 <template>
+  <view class="group-buy-detail-page">
   <view class="group-buy-detail" v-if="loaded">
     <!-- 商品图 -->
     <view class="hero">
@@ -103,16 +104,22 @@
       </view>
     </view>
   </view>
+
+  <!-- 未登录时弹出手机号登录/注册(与花材页一致;置于 loaded 外,加载中也能弹出) -->
+  <wangCg :loginStyle.sync="globalLoginStyle" @loginSuccess="loginSuccess()"></wangCg>
+  </view>
 </template>
 
 <script>
 /**
  * 团购详情页
  * 入参:goodsId(首页进入)或 id(分享进入的 groupBuyId)
- * 开团/参团成功后跳转待付款页
+ * 开团/参团成功后跳转待付款页;未登录时通过 wangCg 引导登录/注册
  */
+import { mapGetters } from 'vuex'
 import CountDown from '@/components/CountDown.vue'
 import GroupAvatarStack from '@/components/GroupAvatarStack.vue'
+import wangCg from '@/components/wangCg'
 import share from '@/mixins/share'
 import {
   getGoodsLanding,
@@ -124,7 +131,7 @@ import { GROUP_BUY_HD_NOT_BOUND } from '@/constant/errCode'
 
 export default {
   name: 'GroupBuyDetail',
-  components: { CountDown, GroupAvatarStack },
+  components: { CountDown, GroupAvatarStack, wangCg },
   mixins: [share],
   data() {
     return {
@@ -147,12 +154,10 @@ export default {
     }
   },
   computed: {
+    ...mapGetters({ loginInfo: 'getLoginInfo' }),
     account() {
       return (this.option && this.option.account) || uni.getStorageSync('account') || ''
     },
-    hdId() {
-      return (this.option && this.option.hdId) || uni.getStorageSync('hdId') || ''
-    },
     swiperList() {
       const cover = this.goods.coverUrl || this.goods.cover
       const list = []
@@ -168,6 +173,20 @@ export default {
       return 0
     }
   },
+  // 监听登录态变化,驱动 wangCg 显隐(0 未登录弹层 / 1 已登录关闭)
+  watch: {
+    loginInfo: {
+      deep: true,
+      handler: function (newVal) {
+        if (!this.$util.isEmpty(newVal)) {
+          this.globalLoginStyle = 1
+        } else {
+          this.globalLoginStyle = 0
+        }
+      },
+      immediate: true
+    }
+  },
   onLoad(option) {
     this.option = option || {}
     this.goodsId = Number(option.goodsId || 0)
@@ -180,6 +199,14 @@ export default {
     }
     this.init()
   },
+  // 每次回到页面时同步登录态,避免从其他页登录回来仍显示登录弹层
+  onShow() {
+    if (!this.$util.isEmpty(this.loginInfo)) {
+      this.globalLoginStyle = 1
+    } else {
+      this.globalLoginStyle = 0
+    }
+  },
   onUnload() {
     this.clearRemainTimer()
   },
@@ -192,6 +219,14 @@ export default {
       if (isNaN(n)) return '0'
       return n % 1 === 0 ? String(n) : n.toFixed(2)
     },
+    /**
+     * wangCg 登录/注册成功回调
+     * 刷新详情数据(分享进页会回填 hdId,供开团/参团/支付使用)
+     */
+    loginSuccess() {
+      this.globalLoginStyle = 1
+      this.init()
+    },
     init() {
       uni.showLoading({ title: '加载中', mask: true })
       const tasks = []
@@ -216,10 +251,11 @@ export default {
               return
             }
             if (res.code === 1 && res.data) {
-              // TODO: 临时处理,后续需要增加判断登录(或注册情况)
+              // 分享进页无 hdId,接口回填后需 $set 保证 Vue2 响应式,供 goPay/跳转使用
               if (res.data.hdId) {
                 uni.setStorageSync('hdId', res.data.hdId)
-                this.option.hdId = res.data.hdId
+                if (!this.option) this.option = {}
+                this.$set(this.option, 'hdId', res.data.hdId)
               }
               this.applyGroupDetail(res.data)
               // 分享进入时若无 goodsId,用团上的商品再拉落地信息补全详情图
@@ -340,13 +376,36 @@ export default {
     buyAlone() {
       const id = this.goods.goodsId || this.goodsId
       if (!id) return
+      const hdId = this.resolveHdId()
       this.pageTo({
-        url: `/pages/goods/detail?id=${id}&account=${this.account}&hdId=${this.hdId}`
+        url: `/pages/goods/detail?id=${id}&account=${this.account}&hdId=${hdId}`
       })
     },
+    /**
+     * 解析当前门店 hdId:优先 option(接口 $set 回填),再读 storage
+     * 不用 computed 缓存,避免 setStorageSync / 非响应式写入后拿到过期空值
+     */
+    resolveHdId() {
+      const fromOption = this.option && this.option.hdId
+      const fromStore = uni.getStorageSync('hdId')
+      return Number(fromOption || fromStore || 0) || 0
+    },
+    /**
+     * 开团/参团前校验登录:未登录则弹出 wangCg,不发请求
+     * @returns {boolean} 是否已登录可继续
+     */
+    ensureLogin() {
+      if (!this.$util.isEmpty(this.loginInfo) && this.globalLoginStyle === 1) {
+        return true
+      }
+      this.globalLoginStyle = 0
+      this.$msg('请先登录')
+      return false
+    },
     /** 我要开团:默认自取下单,成功后去待付款 */
     startGroup() {
       if (this.submitting) return
+      if (!this.ensureLogin()) return
       const activityGoodsId = this.goods.activityGoodsId
       if (!activityGoodsId) {
         this.$msg('团购商品无效')
@@ -379,6 +438,7 @@ export default {
     },
     doJoin(groupBuyId) {
       if (this.submitting) return
+      if (!this.ensureLogin()) return
       this.submitting = true
       uni.showLoading({ title: '参团中', mask: true })
       joinGroup({
@@ -404,9 +464,11 @@ export default {
       const orderSn = data.orderSn || ''
       const id = data.id || 0
       const totalPrice = data.totalPrice || 0
+      // 实时解析 hdId,避免 computed 在接口回填后仍返回空值导致支付页被写成 0
+      const hdId = this.resolveHdId()
       this.$util.pageTo({
         url:
-          `/pages/callback/pay?pageStatus=1&orderSn=${orderSn}&hdId=${this.hdId}&account=${this.account}&id=${id}&couponId=0&totalPrice=${totalPrice}`,
+          `/pages/callback/pay?pageStatus=1&orderSn=${orderSn}&hdId=${hdId}&account=${this.account}&id=${id}&couponId=0&totalPrice=${totalPrice}`,
         type: 2
       })
     },

+ 2 - 0
mallApp/src/pages/login/account.vue

@@ -56,6 +56,8 @@ export default {
             store.commit('setLoginInfo', res.data.user)
             uni.navigateBack()
           }
+        } else {
+          this.$msg(res.msg)
         }
       })
     },

+ 18 - 25
mallApp/src/plugins/luch-request_0.0.7/request.js

@@ -1,15 +1,13 @@
 /**
- * Request 0.0.7
- * @Class uni-app request网络请求库
- * @Author lu-ch
- * @Date 2019-07-11
- * @Email webwork.s@qq.com
- * http://ext.dcloud.net.cn/plugin?id=392
- * **/
+ * Request 0.0.7(mallApp 网络请求封装)
+ * 用途:统一 uni.request,附带 token/account 等上下文
+ * 谁用:mallApp 各 api 模块通过 https.get / https.post 调用
+ * 说明:post 的 query 仅拼 account/hdId,业务参数只走 body,避免敏感字段进 URL
+ * 基于:luch-request 0.0.7 / http://ext.dcloud.net.cn/plugin?id=392
+ */
 import qs from 'qs'
 import constant from '@/constant/index'
 import { TOKEN_STORAGE_KEY } from '@/constant/storageKeys'
-// import common from '@/utils/common'
 import { parse } from '@/utils/util'
 import store from '@/store'
 
@@ -266,24 +264,19 @@ class Request {
 		return this.request(options, config)
 	}
 
+	/**
+	 * POST 请求:query 仅附带 account/hdId(店铺上下文),业务参数只放 body
+	 * 避免把 mobile/password 等敏感字段拼进 URL(日志、代理、Referer 可能泄漏)
+	 * @param {string} url 接口路径
+	 * @param {Object} data 业务请求体;若含 account/hdId 则优先用于 query
+	 * @param {Object} options uni.request 额外选项
+	 * @param {Object} config 业务控制参数 { loading?, hideError? }
+	 */
 	post(url, data = {}, options = {}, config = {}) {
-		const account = uni.getStorageSync('account') || 0;
-		const hdId = uni.getStorageSync('hdId') || 0;
-		if(account != 0 && hdId != 0){
-			options.url = url+'?account='+account+'&hdId='+hdId
-		} else {
-			//console.log("-----post----- data: ", data);
-			// 判断data中有无account和hdId
-			if(data.account && data.hdId){
-				options.url = url + parse({...data, 'account':data.account, 'hdId':data.hdId})
-			} else if(data.account && !data.hdId){
-				options.url = url + parse({...data, 'hdId':hdId})
-			} else if(!data.account && data.hdId){
-				options.url = url + parse({...data, 'account':account})
-			} else {
-				options.url = url + parse({...data, 'account':account, 'hdId':hdId})
-			}
-		}
+		// 优先用调用方传入的 account/hdId,否则回退本地缓存,无则 0
+		const account = (data && data.account) || uni.getStorageSync('account') || 0
+		const hdId = (data && data.hdId) || uni.getStorageSync('hdId') || 0
+		options.url = url + '?account=' + account + '&hdId=' + hdId
 		options.data = data
 		options.method = 'POST'
 		return this.request(options, config)

+ 3 - 2
mallApp/src/utils/launchScene.js

@@ -100,6 +100,8 @@ export function buildLaunchUrl(path, query) {
 
 /**
  * 写入 account / hdId / currentQuery(与原 globalMixins 一致)
+ * hdId:仅当本次入参带有效值时写入;缺省不覆盖本地已有绑定,
+ * 避免分享进页(故意不带 hdId)或跳转漏参时把接口回填的 hdId 冲成 0
  */
 export function persistLaunchStorage(option) {
 	if (option.account) {
@@ -107,10 +109,9 @@ export function persistLaunchStorage(option) {
 	} else {
 		uni.setStorageSync('account', 0)
 	}
+	// 无有效 hdId 时保留 storage,防止全局 onLoad 误清空门店绑定
 	if (option.hdId) {
 		uni.setStorageSync('hdId', option.hdId)
-	} else {
-		uni.setStorageSync('hdId', 0)
 	}
 	uni.setStorageSync('currentQuery', JSON.stringify(option))
 }