├── README.md ├── demos ├── demo.png └── screenshots.jpg ├── package.json └── pin-prompt ├── assets ├── .DS_Store ├── dots.png └── step-2.png ├── pin-prompt.js ├── pin-prompt.json ├── pin-prompt.wxml ├── pin-prompt.wxss └── utils.js /README.md: -------------------------------------------------------------------------------- 1 | # pin-prompt “添加到我的小程序”提示框 2 | 3 | 微信小程序中,提示用户点击右上角按钮,**添加到我的小程序**。 4 | 5 | * 长条状无干扰方式展示 6 | 7 | * 卡片状带详细引导步骤展示 8 | 9 | * 支持自定义导航栏 10 | 11 | * 支持横竖屏 12 | 13 | * 支持自动提示 14 | 15 | 16 | 17 | ### 扫码体验 18 | ![demo-1](./demos/demo.png) 19 | 20 | ### 示例 21 | ![screenshots](./demos/screenshots.jpg) 22 | 23 | ### 安装 24 | 25 | #### 方式一:npm 26 | 27 | ``` 28 | npm i --save wx-pin-prompt 29 | ``` 30 | 31 | 然后,在微信开发者工具中执行 **「构建 npm 」** 32 | 33 | #### 方式二:直接下载源码 34 | 35 | 直接下载源码,添加到你的项目中 36 | 37 | 38 | 39 | ### 使用 40 | 41 | 在页面 json 文件 `usingComponents` 中添加组件 42 | ``` json 43 | "pin-prompt": "/miniprogram_npm/wx-pin-prompt/pin-prompt" 44 | ``` 45 | 46 | 在 wxml 文件中 47 | 48 | ```html 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | 60 | 61 | > ⚠️注意:通过 npm 安装,可能会遇到在开发者工具报错 “渲染层网络层错误” 。可忽略。在真机上可以正常运行。 62 | 63 | 64 | 65 | 66 | 67 | ### 参数 68 | 69 | | 属性 | 默认值 | 说明 | 70 | | ---------- | -------------------------------- | --------------------------------------------------------- | 71 | | text | 点击「添加小程序」,方便下次访问 | 提示语 | 72 | | type | bar | 【提示框类型】bar:长条型,点击会展示 card 型;card:卡片型(含详细引导步骤) | 73 | | background | \#fff (白色) | 提示框背景色 | 74 | | customNavbar | false | 页面是否使用自定义的导航栏,用于自动定位提示框 | 75 | | auto | false | 如果为 true,则自动在第一次打开时提示,之后不在展示 | 76 | | delay | 0 | 延迟展示的时间(秒)。默认不延迟 | 77 | | duration | 5 | 展示时长(秒)。之后自动隐藏。0 代表不自动隐藏 | 78 | | logo | 无 | 用于详细引导步骤中,展示自家小程序 logo | 79 | | name | 无 | 用于详细引导步骤中,展示自家小程序名称 | 80 | | show | false | 展示提示框 | 81 | | showWhenNotAdded | false | 当未添加时展示 | 82 | | showDetail | false | 展示详细步骤提示框 | 83 | 84 | -------------------------------------------------------------------------------- /demos/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudding/wx-pin-prompt/48cc43fe254ff27e551794be0508a40f12a77b27/demos/demo.png -------------------------------------------------------------------------------- /demos/screenshots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudding/wx-pin-prompt/48cc43fe254ff27e551794be0508a40f12a77b27/demos/screenshots.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wx-pin-prompt", 3 | "version": "1.4.6", 4 | "description": "微信小程序 「添加到我的小程序」提示", 5 | "files": [ 6 | "pin-prompt" 7 | ], 8 | "miniprogram": "pin-prompt", 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/liudding/wx-pin-prompt.git" 15 | }, 16 | "keywords": [ 17 | "小程序", 18 | "mini-program", 19 | "miniprogram", 20 | "wechat", 21 | "component", 22 | "pin-prompt" 23 | ], 24 | "author": "liuding", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/liudding/wx-pin-prompt/issues" 28 | }, 29 | "homepage": "https://github.com/liudding/wx-pin-prompt" 30 | } -------------------------------------------------------------------------------- /pin-prompt/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudding/wx-pin-prompt/48cc43fe254ff27e551794be0508a40f12a77b27/pin-prompt/assets/.DS_Store -------------------------------------------------------------------------------- /pin-prompt/assets/dots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudding/wx-pin-prompt/48cc43fe254ff27e551794be0508a40f12a77b27/pin-prompt/assets/dots.png -------------------------------------------------------------------------------- /pin-prompt/assets/step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudding/wx-pin-prompt/48cc43fe254ff27e551794be0508a40f12a77b27/pin-prompt/assets/step-2.png -------------------------------------------------------------------------------- /pin-prompt/pin-prompt.js: -------------------------------------------------------------------------------- 1 | const STORAGE_KEY = 'PIN_PROMPT_DATE'; 2 | 3 | import { checkIsAdded } from './utils' 4 | 5 | Component({ 6 | properties: { 7 | text: { 8 | type: String, 9 | value: '点击「添加小程序」,方便下次访问' 10 | }, 11 | 12 | /** 13 | * 展示类型 14 | */ 15 | type: { 16 | type: String, 17 | value: 'bar' // bar, card 18 | }, 19 | 20 | show: { 21 | type: Boolean, 22 | value: false, 23 | observer: function (val) { 24 | val && this.show(); 25 | !val && this.close() 26 | } 27 | }, 28 | 29 | showWhenNotAdded: { 30 | type: Boolean, 31 | value: false, 32 | }, 33 | 34 | showDetail: { 35 | type: Boolean, 36 | value: false, 37 | observer: function (val) { 38 | val && this.showDetail(); 39 | } 40 | }, 41 | 42 | auto: { 43 | type: Boolean, 44 | value: false, 45 | }, 46 | 47 | background: { 48 | type: String, 49 | value: '#fff' 50 | }, 51 | 52 | color: { 53 | type: String, 54 | value: '#000' 55 | }, 56 | 57 | /** 58 | * 页面使用了自定义导航栏 59 | */ 60 | customNavbar: { 61 | type: Boolean, 62 | value: false, 63 | }, 64 | 65 | logo: { 66 | type: String, 67 | }, 68 | name: { 69 | type: String 70 | }, 71 | 72 | /** 73 | * 延迟展示 74 | */ 75 | delay: { 76 | type: Number, 77 | value: 0 // seconds 78 | }, 79 | 80 | /** 81 | * 展示时长 82 | */ 83 | duration: { 84 | type: Number, 85 | value: 5 // seconds 86 | }, 87 | 88 | backdrop: { 89 | type: Boolean, 90 | value: false 91 | } 92 | }, 93 | 94 | data: { 95 | showHint: false, 96 | showBackdrop: false, 97 | 98 | position: { 99 | top: 0, 100 | right: 0 101 | }, 102 | 103 | timer: null 104 | }, 105 | 106 | lifetimes: { 107 | attached: async function () { 108 | await this._attached(); 109 | }, 110 | }, 111 | 112 | pageLifetimes: { 113 | resize: function (size) { 114 | // 页面尺寸变化 115 | this._updatePosition() 116 | } 117 | }, 118 | 119 | attached: function () { 120 | this._attached(); 121 | }, 122 | 123 | methods: { 124 | onTapBackdrop() { 125 | this.close() 126 | }, 127 | 128 | onTapClose() { 129 | this.close() 130 | }, 131 | 132 | show() { 133 | this.setData({ 134 | show: true, 135 | showHint: true, 136 | showBackdrop: this.data.backdrop || this.data.type === 'card' 137 | }); 138 | 139 | if (this.data.type === 'bar' && this.data.duration > 0) { 140 | this.data.timer = setTimeout(() => { 141 | if (this.data.type === 'bar') { 142 | this.close(); 143 | } 144 | }, this.data.duration * 1000) 145 | } 146 | 147 | this.triggerEvent('show') 148 | }, 149 | 150 | close() { 151 | this.setData({ 152 | show: false, 153 | showHint: false, 154 | showBackdrop: false 155 | }); 156 | 157 | wx.setStorageSync(STORAGE_KEY, Date.now()) 158 | 159 | this.triggerEvent('close') 160 | }, 161 | 162 | showDetail: function () { 163 | this.data.timer && clearTimeout(this.data.timer) 164 | 165 | this.setData({ 166 | show: true, 167 | showHint: true, 168 | showBackdrop: true, 169 | type: 'card' 170 | }); 171 | 172 | this.triggerEvent('showDetail') 173 | }, 174 | 175 | async shouldShow() { 176 | if (this.data.showWhenNotAdded) { 177 | const isAdded = await checkIsAdded() 178 | if (isAdded !== null) { 179 | return !isAdded 180 | } 181 | } 182 | 183 | if (this.data.auto) { 184 | const alreadyShown = wx.getStorageSync(STORAGE_KEY) 185 | 186 | return !alreadyShown; 187 | } 188 | 189 | return this.data.show; 190 | }, 191 | 192 | async _attached() { 193 | this._updatePosition() 194 | 195 | if (await this.shouldShow()) { 196 | setTimeout(() => { 197 | this.show(); 198 | }, this.data.delay * 1000) 199 | } 200 | }, 201 | 202 | _updatePosition() { 203 | const isSupport = !!wx.getMenuButtonBoundingClientRect 204 | const rect = isSupport ? wx.getMenuButtonBoundingClientRect() : {} 205 | 206 | wx.getSystemInfo({ 207 | success: (res) => { 208 | this.setData({ 209 | position: { 210 | top: this.data.customNavbar ? rect.bottom : 0, 211 | right: res.screenWidth - rect.left - rect.width * 3 / 4 212 | } 213 | }) 214 | } 215 | }) 216 | 217 | } 218 | } 219 | }) -------------------------------------------------------------------------------- /pin-prompt/pin-prompt.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /pin-prompt/pin-prompt.wxml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | {{text}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 1 14 | 15 | 点击右上角 16 | 17 | 18 | 19 | 20 | 21 | 2 22 | 23 | 选择「添加到我的小程序」 24 | 26 | 27 | 28 | 29 | 3 30 | 31 | 微信首页下拉,快速进入小程序 32 | 33 | 我的小程序 34 | 35 | 36 | {{name}} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 我知道了 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /pin-prompt/pin-prompt.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: fixed; 3 | top: 0px; 4 | right: 0; 5 | z-index: 10001; 6 | 7 | width: 600rpx; 8 | 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: flex-end; 12 | align-items: flex-end; 13 | } 14 | 15 | 16 | .arrow { 17 | width: 0; 18 | height: 0; 19 | margin-right: 30px; 20 | border-width: 10px; 21 | border-style: solid; 22 | border-color: transparent transparent #fff transparent; 23 | } 24 | 25 | .body { 26 | min-height: 40px; 27 | } 28 | 29 | .bar { 30 | background: #fff; 31 | padding: 4px 8px; 32 | border-radius: 8px; 33 | } 34 | 35 | .bar .text { 36 | color: rgb(0, 0, 0); 37 | font-size: 12px; 38 | font-weight: 400; 39 | } 40 | 41 | .card { 42 | background-color: #fff; 43 | box-shadow: 0 10rpx 20rpx -10rpx #fff; 44 | border-radius: 16px; 45 | min-width: 200rpx; 46 | padding: 24px; 47 | display: flex; 48 | flex-direction: column; 49 | box-sizing: border-box; 50 | } 51 | 52 | .card .step { 53 | width: 100%; 54 | display: flex; 55 | flex-direction: row; 56 | justify-items: start; 57 | align-items: flex-start; 58 | padding: 8px 0; 59 | font-size: 14px; 60 | } 61 | 62 | .card .label { 63 | background: rgb(241, 130, 130); 64 | width: 20px; 65 | min-width: 20px; 66 | height: 20px; 67 | min-height: 20px; 68 | border-radius: 100%; 69 | text-align: center; 70 | line-height: 20px; 71 | font-size: 12px; 72 | margin-right: 8px; 73 | } 74 | 75 | .card .step-1__desc { 76 | display: flex; 77 | } 78 | 79 | .card .image { 80 | margin-top: 4px; 81 | border-radius: 8px; 82 | } 83 | 84 | .quick-entry { 85 | margin-top: 4px; 86 | position: relative; 87 | height: 70px; 88 | background: rgb(68, 65, 88); 89 | border-radius: 8px; 90 | color: white; 91 | padding: 8px 16px; 92 | } 93 | 94 | .quick-entry .quick-entry-title { 95 | color: gray; 96 | font-size: 10px; 97 | } 98 | 99 | .quick-entry .mini-app { 100 | width: 45px; 101 | display: flex; 102 | flex-direction: column; 103 | align-items: center; 104 | } 105 | 106 | .quick-entry .mini-app .logo { 107 | width: 30px; 108 | height: 30px; 109 | margin-top: 8px; 110 | border-radius: 100%; 111 | } 112 | 113 | .quick-entry .mini-app .name { 114 | margin-top: 4px; 115 | text-align: center; 116 | color: white; 117 | font-size: 8px; 118 | } 119 | 120 | .tail { 121 | width: 100%; 122 | text-align: center; 123 | padding: 8px 0; 124 | 125 | display: flex; 126 | flex-direction: column; 127 | align-items: center; 128 | } 129 | 130 | .got-it { 131 | width: 80px; 132 | background: transparent; 133 | border-radius: 1000px; 134 | border: 1px solid white; 135 | color: white; 136 | } 137 | 138 | .backdrop { 139 | position: fixed; 140 | top: 0; 141 | right: 0; 142 | bottom: 0; 143 | left: 0; 144 | z-index: 10000; 145 | background: rgba(0, 0, 0, 0.5); 146 | } -------------------------------------------------------------------------------- /pin-prompt/utils.js: -------------------------------------------------------------------------------- 1 | 2 | export async function checkIsAdded() { 3 | return new Promise((resolve, reject) => { 4 | if (!wx.checkIsAddedToMyMiniProgram) { 5 | resolve(null) 6 | } 7 | wx.checkIsAddedToMyMiniProgram({ 8 | success: (res) => { 9 | resolve(res.added) 10 | }, 11 | fail: (err) => { 12 | reject(err) 13 | } 14 | }) 15 | }) 16 | } --------------------------------------------------------------------------------