Browse Source

feat:手机号绑定插件

yangfeng 5 months ago
parent
commit
0c46126c17

+ 111 - 47
plugins/bind-phone-mobile/README.md

@@ -1,42 +1,11 @@
-# jy-mobile
+# @jy/plugin-bind-phone
 
-## 待办
-
-1. 使用 swiper 替换 vue-awesome-swiper
-2. 提取 lodash
-3.
-
-## 移动端剑鱼项目 GitLab
-
-http://192.168.3.207:8929/efe/jy-mobile
-
-!原 Gogs 项目已启用分支保护,不接受代码提交。
-
-## 开发规范流程
-
-1. 注册 GitLab
-2. 访问 jy-mobile 项目 dev1.1
-3. Fork 项目或者新建 feature/xxxx 功能分支
-4. 完成需求后通过 GitLab 申请合并需求
-5. 等待 CI Lint、代码评审
-6. 通过后合入 dev1.1 分支
-
-因加入 git-hook,需删除 .git/hook、node_modules 重新安装依赖。
-
-### 代码约束
-
-http://192.168.3.11:10081/doc-serve/page_site/standard/
-
-### 组件预览
-
-http://192.168.3.11:10081/doc-serve/page_docs/
+> 移动端绑定手机号弹框插件,可通过指令、vue实例调用
 
 ## 目录结构
 
 ```
-.
-├── config          // UI预览工具配置
-│   └── storybook
+├── README.md
 ├── package.json
 ├── public
 │   ├── favicon.ico
@@ -46,22 +15,88 @@ http://192.168.3.11:10081/doc-serve/page_docs/
 │   ├── assets
 │   ├── components  // 项目业务组件
 │   ├── router
-│   ├── store
-│   ├── stories     // UI 预览示例
-│   ├── ui          // 可提取公共UI组件
 │   ├── utils
 │   └── views
-├── vue.config.js
+├── vite.config.js
 └── yarn.lock
 ```
 
-## UI 预览结构规范
+## 引入方式
+
+1. web项目内通过package.json工作空间引入
+
+```
+"@jy/plugin-bind-phone": "workspace:*"
+
+// 注册
+import { BindPhoneDirective, BindPhoneDialogPlugin } from '@jy/plugin-bind-phone'
+Vue.use(BindPhoneDialogPlugin)
+Vue.directive('bound-phone', BindPhoneDirective)
+```
+
+2. jy项目通过build后放置/common-module/plugins/目录下引入
+
+```
+<script src="/common-module/plugins/js/jy-bind-phone.umd.js"></script>
+// 引入后注册(必须在new Vue()前注册)
+Vue.use(BindPhone)
+```
+
+3.其它项目通过install私有包引入
+
+```
+pnpm add @jy/plugin-bind-phone@1.0.0
+
+import BindPhone from '@jy/plugin-bind-phone'
+Vue.use(BindPhone)
+```
+
+### Example
 
 ```
-├── UI/我的组件
-│   ├── index.vue
-│   ├── index.stories.js   // 基础 js or jsx 组件示例
-│   └── index.stories.mdx  // 用于更自由的 markdown jsx 组件示例
+<template>
+  <div>
+    <button v-bound-phone="bindPhone()">指令触发</button>
+    <button @click="handle">手动实例触发</button>
+  </div>
+</template>
+<script>
+export default {
+  methods: {
+    bindPhone() {
+      return {
+        props: {
+          name: '触发位置名称(统计需要)'
+        },
+        next: () => {
+          <!-- 绑定成功/已绑定 下一步操作 -->
+        },
+        bound: () => {
+          // 绑定成功 下一步操作
+          // 当绑定完手机号操作与next不一致时需要传入,一致时只需传入next即可
+        },
+        close: () => {
+          <!-- 关闭弹框 -->
+        }
+      }
+    },
+    handle() {
+      this.$bindPhoneDialog({
+        props: {
+          name: '触发位置名称',
+          visible: true // 显示弹框
+        },
+        next: () => {
+          <!-- 绑定成功/已绑定 下一步操作 -->
+        },
+        close: () => {
+          <!-- 关闭弹框 -->
+        }
+      })
+    }
+  }
+}
+</script>
 ```
 
 ## Project setup
@@ -82,12 +117,41 @@ yarn serve
 yarn build
 ```
 
-### Lints and fixes files
+### 配置package.json
+
+```
+指定打包路径
+"main": "./dist/jy-bind-phone.umd.js",
+"module": "./dist/jy-bind-phone.mjs",
+```
+
+### 配置私有库地址
+
+```
+pnpm set registry http://172.20.100.235:14873/
+```
+
+### 注册私有库
+
+```
+pnpm adduser --registry http://172.20.100.235:14873/
+```
+
+### 登录私有库
 
 ```
-yarn lint
+pnpm login --registry http://172.20.100.235:14873/
 ```
 
-### Customize configuration
+### 修改版本号
 
-See [Configuration Reference](https://cli.vuejs.org/config/).
+```
+手动修改package.json版本号
+"version": "1.0.3",
+```
+
+### 发布私有库
+
+```
+pnpm publish --no-git-checks
+```

+ 11 - 16
plugins/bind-phone-mobile/package.json

@@ -1,11 +1,14 @@
 {
-  "name": "@jy/bind-phone",
-  "version": "0.1.1",
-  "private": true,
-  "main": "src/index.js",
+  "name": "@jy/plugin-bind-phone",
+  "version": "1.0.4",
+  "private": false,
+  "description": "移动端绑定手机弹框插件",
   "files": [
-    "src"
+    "dist"
   ],
+  "main": "./dist/jy-bind-phone.umd.js",
+  "module": "./dist/jy-bind-phone.mjs",
+  "exports": "src/index.js",
   "scripts": {
     "dev": "vite",
     "build": "pnpm run update && pnpm run build:vite",
@@ -15,23 +18,14 @@
     "format": "prettier --write \"./**/*.{,vue,ts,js,json,md}\""
   },
   "dependencies": {
-    "@jy/data-models": "workspace:^",
-    "@jy/util": "workspace:^",
-    "@jy/vue-anti": "workspace:^",
     "js-cookie": "^3.0.1",
-    "lodash": "^4.17.21",
     "qs": "^6.11.2",
-    "vant": "2.12.44",
-    "vite-plugin-css-injected-by-js": "^3.1.0",
-    "vuex": "^3.6.2"
+    "vant": "2.12.44"
   },
   "devDependencies": {
     "@jonny1994/postcss-px-to-viewport": "^1.1.0",
     "@nabla/vite-plugin-eslint": "^2.0.2",
     "@rushstack/eslint-patch": "^1.1.0",
-    "@sentry/vite-plugin": "^2.21.1",
-    "@unocss/transformer-variant-group": "^0.58.5",
-    "@vitejs/plugin-legacy": "^4.0.4",
     "@vitejs/plugin-vue2": "^2.2.0",
     "@vue/eslint-config-prettier": "^7.0.0",
     "autoprefixer": "^10.4.14",
@@ -46,6 +40,7 @@
     "vite": "^4.5.3",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "1.6.4",
-    "vite-plugin-externals": "^0.6.2"
+    "vite-plugin-externals": "^0.6.2",
+    "vite-plugin-css-injected-by-js": "^3.1.0"
   }
 }

+ 0 - 100
plugins/bind-phone-mobile/public/decrypt-js.html

@@ -1,100 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title></title>
-</head>
-<body>
-    <script src='https://cdn-common.jianyu360.com/cdn/lib/jsencrypt/3.3.2/jsencrypt.min.js'></script>
-    <script>
-        const config = {
-            plainKey: '', // rsa解密后的key
-            privateKey: `-----BEGIN PRIVATE KEY-----
-                MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOhM0pNOfGeiBr+t
-                nunphCHReY3RiS4Fuc2nD3cbjKNdLezeViGmsZwHsb2SVUb6rpPHyX0+3xjXYn//
-                n39/Q8uPjWRA332TtN8MDEkSR2HMbn8ufRRt2TnlfsFDFTgBywSP7cwd0CiEdvBX
-                5w8Jifc9VbedwbeplBWyDeLLqjRjAgMBAAECgYB4es+EAuLWxNwHMb8Hxkr3VzNZ
-                8GDbc7DIDmsg9TLdz4fwH+hAD7pyGDOBBJIh/AXrM2U3BhKjSaIWjLdmYtT/kzg8
-                BxQDr9YoO7u2jvTcEE+/6p2YugYX/ngpinawFJqyM+N7Or8yRABaw6Aq8VuKtv6p
-                980Y2BBVVYn+/KorYQJBAP+9lu8iolzKRzJrFt/rosdWkOpNg5ujcSCwbxhYnYC0
-                UY85sPLsMvnLgegkpO8jocSAt586BmcsA+Q9o97qVCkCQQDoiSVegtOvG3U0mNlN
-                rCVpPEL22s9Kkwps3ZCdTl3VtUtNiyfhE8rbw/qOGti3VxMCRhpKi9hTIgeq13UG
-                67WrAkEA/WQ1c5XGd9f4eU1AKffInmf4SB8rgn+L7I7EVMQgstB3a0kHOXqs+3IX
-                shL01PliJFhBF+QfSgSDipdEke9uGQJBAOcw46xxmhDw1bizdulYi+Fy/oj7xzi3
-                tJfEObGMZpLBKtsvzThkOz4APS3n1yuBMO8Dz8PqAeu1W7YpfLqiwv0CQF68N244
-                dFebDSoZLl1hbCExpbtC7SDBpYxlIVNVqwN7ymr+Z0rIcAMVv5Ldp/bJEWaXJs9C
-                0sPCBpjDnyK9Z04=
-                -----END PRIVATE KEY-----`
-        }
-    </script>
-
-    <script>
-        var decryptTools = {
-            // rsa解密
-            rsaDecrypt: function (cipherText, privateKey) {
-                // 解密
-                var decrypt = new JSEncrypt()//创建解密对象实例
-                decrypt.setPrivateKey(privateKey)//设置秘钥
-                var uncrypted = decrypt.decrypt(cipherText)//解密之前拿公钥加密的内容
-                return uncrypted
-            },
-            // AES解密
-            async AESDecrypt(content, base64Key) {
-                const key = new TextEncoder().encode(base64Key)
-                const encryptedBase64 = content;
-                const encryptedData = Uint8Array.from(window.atob(encryptedBase64), c => c.charCodeAt(0));
-
-                const decryptData = async () => {
-                    const iv = encryptedData.slice(0, 16);
-                    const ciphertext = encryptedData.slice(16);
-
-                    const aesKey = await crypto.subtle.importKey("raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
-
-                    const decryptedData = await crypto.subtle.decrypt({name: "AES-CTR", counter: iv, length: 128}, aesKey, ciphertext);
-                    const decryptedText = new TextDecoder().decode(decryptedData);
-
-                    return {
-                        value: decryptedText
-                    }
-                };
-
-                const result = await decryptData()
-                return result
-            }
-        }
-
-        window.addEventListener('message', async (e) => {
-            if (e.data.type !== 'decrypt') {
-                return
-            }
-            if (window === e.source) {
-                return
-            }
-
-            var base64Key = e.data.base64Key
-            var cipherText = e.data.cipherText
-            // 1. 先解密base64Key
-            var plainKey = decryptTools.rsaDecrypt(base64Key, config.privateKey)
-            config.plainKey = plainKey
-            // 2. 再用key解密cipherText
-            var plainText = await decryptTools.AESDecrypt(cipherText, plainKey)
-
-            const result = {
-                ...e.data,
-                plainKey: plainKey,
-                plainText: plainText.value,
-            }
-            sendPostMessage(e, result)
-        })
-        function sendPostMessage(e, result) {
-            const win = e.source
-            var payload = {
-                ...result,
-                type: 'after-decrypt'
-            }
-            window.parent.postMessage(payload, payload.fromOrigin)
-        }
-    </script>
-</body>
-</html>

BIN
plugins/bind-phone-mobile/public/favicon.ico


+ 19 - 6
plugins/bind-phone-mobile/src/assets/style.css

@@ -47,7 +47,8 @@
   display: flex;
 }
 .bind-phone-dialog .j-button-group {
-  padding: 12px 20px;
+  padding: 12px 20px !important;
+  height: auto !important;
   display: flex;
   justify-content: space-between;
   align-items: center;
@@ -65,7 +66,7 @@
   font-size: 18px;
   line-height: inherit;
   text-align: center;
-  border-radius: 8px;
+  border-radius: 8px !important;
   border: 0;
   outline: 0;
 }
@@ -82,18 +83,21 @@
 .bind-phone-dialog .j-button-cancel[disabled] {
   opacity: 0.5;
 }
+.bind-phone-dialog .van-dialog__content {
+  padding: 0 !important;
+}
 
-.reveal-box {
+.bind-phone-dialog .reveal-box {
   position: relative;
   width: 100%;
   height: 100%;
 }
-.reveal-box .van-image,
-.reveal-box .van-icon__image {
+.bind-phone-dialog .reveal-box .van-image,
+.bind-phone-dialog .reveal-box .van-icon__image {
   width: 100%;
   height: 100%;
 }
-.reveal-box .tag-text {
+.bind-phone-dialog .reveal-box .tag-text {
   position: absolute;
   color: rgba(255, 255, 255, 0.8);
   background: rgba(0, 0, 0, 0.16);
@@ -104,3 +108,12 @@
   padding: 3px 6px;
   border-radius: 8px 0px 8px 0px;
 }
+.bind-phone-dialog input {
+  caret-color: #2abed1;
+}
+.bind-phone-dialog input::placeholder {
+  color: #c0c4cc;
+}
+.bind-phone-dialog input {
+  color: #171826;
+}

+ 10 - 1
plugins/bind-phone-mobile/src/components/BindPhoneDialog.vue

@@ -20,6 +20,7 @@
     </div>
     <div class="bind-form">
       <van-field
+        ref="phoneRef"
         v-model.trim="info.phone"
         label="手机号"
         type="tel"
@@ -182,7 +183,7 @@ export default {
       }
     }
   },
-  mounted() {
+  created() {
     if (!this.adCode) {
       const code = this.$envs.inWX
         ? 'wx-bind-phone-dialog'
@@ -192,6 +193,13 @@ export default {
       this.getAd([this.adCode])
     }
   },
+  mounted() {
+    try {
+      setTimeout(() => {
+        this.$refs.phoneRef.focus()
+      }, 500)
+    } catch (error) {}
+  },
   watch: {
     visible(val) {
       console.log(val, 'visible')
@@ -350,6 +358,7 @@ export default {
       if (code === 0 && data) {
         // 绑定成功
         this.$emit('bound')
+        this.show = false
       } else {
         this.$toast(msg || '请求失败')
       }

+ 10 - 1
plugins/bind-phone-mobile/src/entry.js

@@ -34,10 +34,19 @@ const install = (Vue) => {
     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
   }
 }
 
 export default {
-  install
+  install,
+  BindPhoneDialog,
+  BoundPhoneDirective
 }

+ 9 - 0
plugins/bind-phone-mobile/src/index.js

@@ -2,5 +2,14 @@ import BindPhoneDialogPlugin from './utils/plugins/index.js'
 import BindPhoneDirective from './utils/directives/bind-phone'
 import BindPhoneDialog from './components/BindPhoneDialog.vue'
 import './assets/style.css'
+// import install from './entry.js'
+
+// const BindPhonePlugin = {
+//   install,
+//   BindPhoneDialogPlugin,
+//   BindPhoneDirective,
+//   BindPhoneDialog
+// }
+// export default install
 
 export { BindPhoneDialogPlugin, BindPhoneDirective, BindPhoneDialog }

+ 9 - 2
plugins/bind-phone-mobile/src/utils/directives/bind-phone.js

@@ -23,14 +23,14 @@ const BindPhoneDirective = {
       event.stopPropagation()
       event.preventDefault()
       // 从cookie中获取是否绑定过手机号
-      const isPhoneBound = Cookies.get('EXPERIENCESIGN')
+      const needPhoneBound = Cookies.get('EXPERIENCESIGN')
       // 无需绑定手机号
       if (typeof pass === 'function') {
         pass()
         return
       }
       // 已绑定过手机号
-      if (isPhoneBound) {
+      if (!needPhoneBound) {
         typeof next === 'function' && next()
       } else {
         props.visible = true
@@ -47,6 +47,13 @@ const BindPhoneDirective = {
               }
               // 绑定完清除cookie
               Cookies.remove('EXPERIENCESIGN')
+              // 绑定成功埋点
+              try {
+                window.__EasyJTrack.addTrack(props.name, {
+                  break_data: 'abtest',
+                  source: props.name + '-绑定成功'
+                })
+              } catch (error) {}
             },
             close: () => {
               if (typeof close === 'function') {

+ 7 - 0
plugins/bind-phone-mobile/src/utils/plugins/index.js

@@ -28,6 +28,13 @@ const BindPhoneDialogPlugin = {
       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
     }
   }

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

@@ -16,16 +16,20 @@ export default {
   methods: {
     bindPhone() {
       return {
-        props: {},
+        props: {
+          name: '测试弹框-指令触发'
+        },
         next: () => {
-          console.log('1111')
+          console.log('指令触发-下一步')
         }
       }
     },
+    // 手动触发绑定弹框
     handle() {
       this.$bindPhoneDialog({
         props: {
-          visible: true
+          visible: true,
+          name: '测试弹框-手动触发'
         },
         on: {
           bound: () => {

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

@@ -35,8 +35,7 @@ export default defineConfig(({ mode, command }) => {
       lib: {
         entry: './src/entry.js', // 入口文件
         name: 'BindPhone', // 库的全局变量名
-        fileName: 'jy-bind-phone', // 输出的文件名
-        formats: ['umd'] // 打包格式
+        fileName: 'jy-bind-phone' // 输出的文件名
       },
       rollupOptions: {
         // 确保外部化处理 Vue,避免将 Vue等 打包进库
@@ -46,7 +45,8 @@ export default defineConfig(({ mode, command }) => {
             vue: 'Vue'
           }
         }
-      }
+      },
+      target: 'es2015' // 指定目标语法版本
     },
     plugins: [
       splitVendorChunkPlugin(),