Эх сурвалжийг харах

feat:体验手机号绑定插件优化

yangfeng 4 сар өмнө
parent
commit
a51958d8d2

+ 3 - 2
plugins/bind-phone-mobile/src/components/BindPhoneDialog.vue

@@ -198,6 +198,7 @@ export default {
     } else {
       this.getAd([this.adCode])
     }
+    this.getImgCaptcha()
   },
   mounted() {
     try {
@@ -229,8 +230,8 @@ export default {
         const cacheData = localStorage.getItem(cacheKey)
         if (cacheData) {
           const { data, timestamp } = JSON.parse(cacheData)
-          // 检查缓存是否在5分钟内(300000毫秒)
-          if (Date.now() - timestamp < 300000) {
+          // 检查缓存是否在10分钟内(600000毫秒)
+          if (Date.now() - timestamp < 600000) {
             return { info: data }
           }
         }

+ 6 - 39
plugins/bind-phone-mobile/src/entry.js

@@ -3,50 +3,17 @@
  */
 
 import BindPhoneDialog from './components/BindPhoneDialog.vue'
-import BoundPhoneDirective from './utils/directives/bind-phone.js'
+// import BindPhoneDirective from './utils/directives/bind-phone.js'
+import DirectiveBindPhonePlugin from './utils/plugins/directive-bind-phone.js'
+import PrototypeBindPhonePlugin from './utils/plugins/prototype-bind-phone.js'
 import './utils/prototype/env.js'
 import './assets/style.css'
 
 const install = (Vue) => {
-  // 注册全局组件
-  Vue.component('bind-phone-dialog', BindPhoneDialog)
-
-  // 注册全局指令
-  Vue.directive('bound-phone', BoundPhoneDirective)
-
-  // 创建弹窗实例
-  const ModalConstructor = Vue.extend(BindPhoneDialog)
-  Vue.prototype.$bindPhoneDialog = function (options) {
-    const instance = new ModalConstructor({
-      propsData: options.props
-    })
-    if (options.on) {
-      Object.keys(options.on).forEach((event) => {
-        instance.$on(event, options.on[event], (instance.visible = false))
-      })
-    }
-    // 支持插槽内容
-    if (options.slots) {
-      Object.keys(options.slots).forEach((slotName) => {
-        instance.$slots[slotName] = options.slots[slotName]
-      })
-    }
-    instance.$mount()
-    document.body.appendChild(instance.$el)
-    instance.visible = true
-    // 弹框弹起时埋点abtest
-    try {
-      window.__EasyJTrack.addTrack(options.props.name, {
-        break_data: 'abtest',
-        source: options.props.name
-      })
-    } catch (error) {}
-    return instance
-  }
+  Vue.use(DirectiveBindPhonePlugin)
+  Vue.use(PrototypeBindPhonePlugin)
 }
-
 export default {
   install,
-  BindPhoneDialog,
-  BoundPhoneDirective
+  BindPhoneDialog
 }

+ 15 - 10
plugins/bind-phone-mobile/src/index.js

@@ -1,15 +1,20 @@
-import BindPhoneDialogPlugin from './utils/plugins/index.js'
+import DirectiveBindPhonePlugin from './utils/plugins/directive-bind-phone.js'
+import PrototypeBindPhonePlugin from './utils/plugins/prototype-bind-phone.js'
 import BindPhoneDirective from './utils/directives/bind-phone'
 import BindPhoneDialog from './components/BindPhoneDialog.vue'
 import './assets/style.css'
-// import install from './entry.js'
+import './utils/prototype/env.js'
 
-// const BindPhonePlugin = {
-//   install,
-//   BindPhoneDialogPlugin,
-//   BindPhoneDirective,
-//   BindPhoneDialog
-// }
-// export default install
+export {
+  DirectiveBindPhonePlugin,
+  BindPhoneDirective,
+  BindPhoneDialog,
+  PrototypeBindPhonePlugin
+}
 
-export { BindPhoneDialogPlugin, BindPhoneDirective, BindPhoneDialog }
+const TestBindPhone = (Vue) => {
+  Vue.use(DirectiveBindPhonePlugin)
+  Vue.use(PrototypeBindPhonePlugin)
+}
+
+export default TestBindPhone

+ 7 - 2
plugins/bind-phone-mobile/src/main.js

@@ -3,10 +3,15 @@ import { Dialog, Lazyload, Toast } from 'vant'
 import App from './App.vue'
 import 'vant/lib/index.less'
 import router from './router'
-import BindPhoneDialogPlugin from './utils/plugins/index.js'
+import DirectiveBindPhonePlugin from './utils/plugins/directive-bind-phone.js'
+import PrototypeBindPhonePlugin from './utils/plugins/prototype-bind-phone.js'
 import './utils/prototype/env.js'
 
-Vue.use(Toast).use(Lazyload).use(Dialog).use(BindPhoneDialogPlugin)
+Vue.use(Toast)
+  .use(Lazyload)
+  .use(Dialog)
+  .use(DirectiveBindPhonePlugin)
+  .use(PrototypeBindPhonePlugin)
 
 // 设置默认 loading 配置项
 Toast.setDefaultOptions('loading', {

+ 40 - 20
plugins/bind-phone-mobile/src/utils/directives/bind-phone.js

@@ -4,35 +4,42 @@ const BindPhoneDirective = {
   inserted(el, binding, vNode) {
     const { value } = binding
     const vm = vNode.context
-    /**
-     * props: 接受的传参
-     * pass: 无需校验是否绑定即可进行下一步,场景:金刚区(部分产品需要绑定手机号、部分产品不需要绑定手机号)
-     * next: 绑定过手机号的下一步操作(必传)
-     * close: 关闭弹框操作
-     * bound:弹框绑定完手机号回调操作(大多数场景与next方法逻辑一致,部分场景有额外需求的会不一致,当绑定完成回调与next不一致时,需要传入bound方法)
-     */
 
-    el.addEventListener('click', async (event) => {
+    el.isVerified = false
+
+    const clickHandler = async (event) => {
+      if (el.isVerified) return
       const { props = {}, pass, bound, close, next } = value
+      /**
+       * props: 接受的传参
+       * pass: 无需校验是否绑定即可进行下一步,场景:金刚区(部分产品需要绑定手机号、部分产品不需要绑定手机号)
+       * next: 绑定过手机号的下一步操作(必传)
+       * close: 关闭弹框操作
+       * bound:弹框绑定完手机号回调操作(大多数场景与next方法逻辑一致,部分场景有额外需求的会不一致,当绑定完成回调与next不一致时,需要传入bound方法)
+       */
       console.log(
         `pass: ${pass}, bound: ${bound},next: ${next}, props: ${JSON.stringify(
           props
         )}`
       )
-
-      event.stopPropagation()
-      event.preventDefault()
-      // 从cookie中获取是否绑定过手机号
-      const needPhoneBound = Cookies.get('EXPERIENCESIGN') === 'experiencing'
-      // 无需绑定手机号
+      // 无需校验/无需绑定手机号
       if (typeof pass === 'function') {
         pass()
+        el.isVerified = true
         return
       }
+
+      // 从cookie中获取是否绑定过手机号
+      const needPhoneBound = Cookies.get('EXPERIENCESIGN') === 'experiencing'
       // 已绑定过手机号
       if (!needPhoneBound) {
         typeof next === 'function' && next()
+        el.isVerified = true
+        return
       } else {
+        event.stopPropagation()
+        event.preventDefault()
+        event.stopImmediatePropagation()
         props.visible = true
         // 未绑定过手机号
         vm.$bindPhoneDialog({
@@ -40,11 +47,7 @@ const BindPhoneDirective = {
           slots: {},
           on: {
             bound: () => {
-              if (bound && typeof bound === 'function') {
-                bound()
-              } else {
-                typeof next === 'function' && next()
-              }
+              el.isVerified = true
               // 绑定完清除cookie
               Cookies.remove('EXPERIENCESIGN')
               // 绑定成功埋点
@@ -54,6 +57,17 @@ const BindPhoneDirective = {
                   source: props.name + '-绑定成功'
                 })
               } catch (error) {}
+
+              if (bound && typeof bound === 'function') {
+                bound()
+              } else {
+                if (next) {
+                  typeof next === 'function' && next()
+                } else {
+                  // 没有bound和next方法时,自动触发当前点击dom的click事件(工作台、我的页面不细分唤起,点击任意地方都唤起)
+                  event.target.click()
+                }
+              }
             },
             close: () => {
               if (typeof close === 'function') {
@@ -65,7 +79,13 @@ const BindPhoneDirective = {
           }
         })
       }
-    })
+    }
+    // 绑定主点击事件
+    el.addEventListener('click', clickHandler, true)
+    el._clickHandler = clickHandler
+  },
+  unbind(el) {
+    el.removeEventListener('click', el._clickHandler)
   }
 }
 

+ 11 - 2
plugins/bind-phone-mobile/src/utils/plugins/index.js → plugins/bind-phone-mobile/src/utils/plugins/directive-bind-phone.js

@@ -2,7 +2,7 @@ import BindPhoneDialog from '../../components/BindPhoneDialog.vue'
 import BindPhoneDirective from '../../utils/directives/bind-phone.js'
 import '../../assets/style.css'
 
-const BindPhoneDialogPlugin = {
+const DirectiveBindPhonePlugin = {
   install(Vue) {
     // 注册全局组件
     Vue.component('bind-phone-dialog', BindPhoneDialog)
@@ -11,6 +11,7 @@ const BindPhoneDialogPlugin = {
 
     const DialogConstructor = Vue.extend(BindPhoneDialog)
     Vue.prototype.$bindPhoneDialog = function (options) {
+      options.props.visible = true
       const instance = new DialogConstructor({
         propsData: options.props
       })
@@ -28,6 +29,14 @@ const BindPhoneDialogPlugin = {
       instance.$mount()
       document.body.appendChild(instance.$el)
       instance.visible = true
+      // 添加销毁钩子
+      instance.$on('close', () => {
+        // 关闭动画结束后再销毁
+        setTimeout(() => {
+          instance.$el.parentNode.removeChild(instance.$el)
+          instance.$destroy()
+        }, 300)
+      })
       // 弹框弹起时埋点abtest
       try {
         window.__EasyJTrack.addTrack(options.props.name, {
@@ -40,4 +49,4 @@ const BindPhoneDialogPlugin = {
   }
 }
 
-export default BindPhoneDialogPlugin
+export default DirectiveBindPhonePlugin

+ 92 - 0
plugins/bind-phone-mobile/src/utils/plugins/prototype-bind-phone.js

@@ -0,0 +1,92 @@
+import BindPhoneDialog from '../../components/BindPhoneDialog.vue'
+import '../../assets/style.css'
+import Cookies from 'js-cookie'
+
+const PrototypeBindPhonePlugin = {
+  install(Vue) {
+    // 注册全局组件
+    Vue.component('bind-phone-dialog', BindPhoneDialog)
+
+    // 创建绑定手机号弹框实例的函数封装
+    function createBindPhoneDialog(instance, options) {
+      if (options.on) {
+        Object.keys(options.on).forEach((event) => {
+          instance.$on(event, options.on[event])
+        })
+      }
+
+      instance.$mount()
+      document.body.appendChild(instance.$el)
+      instance.visible = true
+      return instance
+    }
+
+    Vue.prototype.$testBindPhone = function (options) {
+      const { props = {}, slots, pass, bound, next, close } = options
+      props.visible = true
+      const needPhoneBound = Cookies.get('EXPERIENCESIGN') === 'experiencing'
+      if (typeof pass === 'function') {
+        pass()
+        return
+      }
+      if (needPhoneBound) {
+        const DialogConstructor = Vue.extend(BindPhoneDialog)
+        const instance = new DialogConstructor({ propsData: props })
+
+        // 封装插槽处理逻辑
+        if (slots) {
+          Object.entries(slots).forEach(([slotName, slotContent]) => {
+            instance.$slots[slotName] = slotContent
+          })
+        }
+        options.on = {
+          bound: () => {
+            if (bound && typeof bound === 'function') {
+              bound()
+            } else {
+              typeof next === 'function' && next()
+            }
+            // 绑定完清除cookie
+            Cookies.remove('EXPERIENCESIGN')
+            // 绑定成功埋点
+            try {
+              window.__EasyJTrack.addTrack(props.name, {
+                break_data: 'abtest',
+                source: props.name + '-绑定成功'
+              })
+            } catch (error) {}
+          },
+          close: () => {
+            if (typeof close === 'function') {
+              close()
+            } else {
+              Vue.prototype.$toast('请先绑定手机号')
+            }
+          }
+        }
+        // 创建对话框实例
+        const dialog = createBindPhoneDialog(instance, options)
+        // 埋点
+        if (window.__EasyJTrack) {
+          window.__EasyJTrack.addTrack(props.name, {
+            break_data: 'abtest',
+            source: props.name
+          })
+        }
+        // 添加销毁钩子
+        dialog.$on('close', () => {
+          // 关闭动画结束后再销毁
+          setTimeout(() => {
+            dialog.$el.parentNode.removeChild(dialog.$el)
+            dialog.$destroy()
+          }, 300)
+        })
+        return dialog
+      } else {
+        next?.()
+      }
+    }
+  }
+}
+
+export default PrototypeBindPhonePlugin

+ 43 - 3
plugins/bind-phone-mobile/src/views/test.vue

@@ -1,11 +1,21 @@
 <template>
   <div class="btn-group">
-    <button class="btn" v-bound-phone="bindPhone()">指令触发绑定弹框</button>
-    <button class="btn" @click="handle">手动触发绑定弹框</button>
+    <div class="flex">
+      <button class="btn" @click="setCookie">生成cookie</button>
+      <button class="btn" @click="removeCookie">清除cookie</button>
+    </div>
+    <button class="btn" v-bound-phone="bindPhone()">
+      指令触发绑定弹框(校验cookie)
+    </button>
+    <button class="btn" @click="handle">手动触发绑定弹框(未校验cookie)</button>
+    <button class="btn" @click="globalFn">
+      全局方法触发绑定弹框(校验cookie)
+    </button>
   </div>
 </template>
 
 <script>
+import Cookies from 'js-cookie'
 export default {
   name: 'TestPage',
   data() {
@@ -13,7 +23,23 @@ export default {
       visible: false
     }
   },
+  mounted() {
+    this.$testBindPhone({
+      props: {
+        name: '测试弹框-mounted触发'
+      },
+      next: () => {
+        this.$toast('next')
+      }
+    })
+  },
   methods: {
+    setCookie() {
+      Cookies.set('EXPERIENCESIGN', 'experiencing', { expires: 7, path: '' })
+    },
+    removeCookie() {
+      Cookies.remove('EXPERIENCESIGN')
+    },
     bindPhone() {
       return {
         props: {
@@ -28,7 +54,6 @@ export default {
     handle() {
       this.$bindPhoneDialog({
         props: {
-          visible: true,
           name: '测试弹框-手动触发'
         },
         on: {
@@ -43,6 +68,17 @@ export default {
           }
         }
       })
+    },
+    globalFn() {
+      this.$testBindPhone({
+        props: {
+          visible: true,
+          name: '测试弹框-全局方法触发'
+        },
+        next: () => {
+          this.$toast('next')
+        }
+      })
     }
   }
 }
@@ -56,5 +92,9 @@ export default {
   .btn {
     margin: 16px;
   }
+  .flex {
+    display: flex;
+    align-items: center;
+  }
 }
 </style>

+ 1 - 1
plugins/bind-phone-mobile/vite.config.js

@@ -34,7 +34,7 @@ export default defineConfig(({ mode, command }) => {
       emptyOutDir: true,
       lib: {
         entry: './src/entry.js', // 入口文件
-        name: 'BindPhone', // 库的全局变量名
+        name: 'TestBindPhone', // 库的全局变量名
         fileName: 'jy-bind-phone' // 输出的文件名
       },
       rollupOptions: {