业务需求的场景
需要在后台管理系统中的列表数据添加复制功能,复制成功的链接能够在手机浏览器或者微信中打开指定的小程序页面(pages/good/detail/index)
使用文档介绍
需要在手机浏览器或者微信中唤起小程序有两种方式, 小程序URL Scheme文档,开放范围:非个人主体小程序
- 通过加密 URL Scheme (需要后端获取加密 Scheme)
- 通过明文 URL Scheme (可前端直接拼接)
注意:iOS系统可以直接打开URL Scheme,Android系统需要使用 H5 页面中转
实现过程
当前项目使用的技术是 vite-vue-ts,使用明文 URL Scheme方式唤起小程序
安装项目:pnpm create vite my-project-name --template vue-ts
1.列表页复制按钮事件js,实现生成URL Scheme,并复制到剪切板
const handleClick = ()=>{
// URL Scheme需要进入的小程序页面
const path = 'pages/good/detail/index'
// URL Scheme携带的参数
const query = encodeURIComponent(`goodId=${record.goodId}&other=${record.other}`)
// 拼接完整的 URL Scheme
const url = encodeURIComponent(`weixin://dl/business/?appid=222222222222&path=${path}&query=${query}`)
// 跳转到新增的H5页面的url(看下文),mpAppId公众号id,appId小程序id
const goodUrl = `${location.origin}/good.html?scheme=${url}&mpAppId=11111111111111&appId=222222222222`
// 调用后端的接口生成短链接,并复制到剪切板
return myRequest(`api/short/url`, { url:goodUrl }).then((resp) => {
// 拷贝到剪切板(看下文utils工具)
copyText(resp.data)
Message.success('复制成功')
return resp.data
})
}
2.需要在现有的后台管理系统项目新建一个H5页面
在vite.config.ts中配置项build > rollupOptions > input 中新增配置项 good:
export default defineConfig({
// ... 其他配置
build:{
rollupOptions:{
// ... 其他配置
input: {
good: path.resolve(__dirname, 'good.html'), // 新建H5
index: path.resolve(__dirname, 'index.html'), // 原系统入口页
},
output: {
dir: path.resolve(__dirname, './dist'), // 打包输出问价
},
}
}
})
在vite.config.ts同级目录下新建good.html文件,并引入小程序微信开放标签JDK的js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="/logo.png"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>跳转中...</title>
// 小程序微信开放标签
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<div id="app"></div>
<script
type="module"
src="/src/good.ts"
></script>
</body>
</html>
在src下新建good.ts,挂载页面
import { createApp } from 'vue'
import Auth from './Login.vue'
import './themes/login.less'
createApp(Auth).mount('#app')
在src下新建good.vue页面(H5页面),用来跳转小程序(访问复制出来的URL会进入这个页面,然后在点击进入小程序)
<!-- eslint-disable vue/no-lone-template -->
<template>
<div class="good">
// 手机浏览器网页直接打开上文复制出来的url
<div
v-if="isWeiXin ? false : isMobile"
class="public-web-container"
>
<a
href="javascript:"
class="public-web-jump-button"
@click="openWxApp()"
>
打开小程序
</a>
</div>
// 微信中需要使用微信开发标签
<div
v-show="isWeiXin"
id="weChat-web-container"
class="weChat-web-container"
>
<wx-open-launch-weapp
id="launch-btn"
class="wx-open-launch-weapp"
:appid="appId"
:path="weappPath"
>
<component
:is="'script'"
type="text/wxtag-template"
>
<button style="width: 200px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color: #fff">
打开小程序
</button>
</component>
</wx-open-launch-weapp>
</div>
// 桌面端提示用手机网页
<div
v-if="isDesktop"
class="desktop-web-container"
>
<p>请使用手机打开网页</p>
</div>
</div>
</template>
<script lang="ts" setup>
// 开放标签获取签名(签名需要后端生成)
const getSign = (params: { url: string })=>{
return axios.get(`/api/get/sign?url=${params.url}`)
}
const isWeiXin = ref<boolean>(false)
const isMobile = ref<boolean>(false)
const isDesktop = ref<boolean>(false)
// mpAppId 公众号id, appId 小程序id
const { mpAppId = '11111111111111', appId = '222222222222', scheme } = Object.fromEntries(new URLSearchParams(location.search))
const weappPath = ref('')
const errorCb= (e: any) => {
console.error('错误原因:', e.detail.errMsg)
}
onMounted(() => {
// 微信页面需要注册开放标签
document.addEventListener('WeixinOpenTagsError', errorCb)
if (isWeiXin.value) {
getSign({ url: encodeURIComponent(location.href.split('#')[0]) }).then((res) => {
const { data } = res.data
;(window as any).wx.config({
debug: false,
appId: mpAppId,
timestamp: data.timestamp,
nonceStr: data.noncestr,
signature: data.sign,
jsApiList: ['uploadImage'],
openTagList: ['wx-open-launch-weapp'],
})
})
}
})
onUnmounted(() => document.removeEventListener('WeixinOpenTagsError', errorCb))
onBeforeMount(() => {
const params = new URLSearchParams(scheme)
const query = params.get('query') as string
weappPath.value = `${params.get('path')}?${query}`
// 区分平台
const ua: any = navigator.userAgent.toLowerCase()
const isWXWork = ua.match(/wxwork/i) === 'wxwork'
isWeiXin.value = !isWXWork && ua.match(/micromessenger/i) == 'micromessenger'
if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) {
isMobile.value = true
} else {
isDesktop.value = true
}
})
const openWxApp = () => {
// 非微信中网页
location.href = decodeURIComponent(scheme)
}
</script>
<style lang="less" scoped>
* {
padding: 0;
margin: 0;
}
.good{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.weChat-web-container,
.public-web-container,
.desktop-web-container {
display: flex;
flex-direction: column;
align-items: center;
}
.wx-open-launch-weapp {
position: absolute;
bottom: 50%;
left: 0;
right: 0;
display: flex;
flex-direction: column;
align-items: center;
transform: translateY(-50%);
}
.public-web-jump-button {
position: absolute;
bottom: 50%;
transform: translateY(-50%);
display: inline-block;
width: 184px;
margin-left: auto;
margin-right: auto;
padding: 8px 24px;
box-sizing: border-box;
background-color: #06ae56;
color: #fff;
font-weight: 700;
font-size: 17px;
text-align: center;
text-decoration: none;
line-height: 1.41176471;
border-radius: 4px;
overflow: hidden;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.desktop-web-container p {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
</style>
src > utils > index 工具文件
// 复制剪切板功能
function fallbackCopyTextToClipboard(text: string) {
let textArea = document.createElement('textarea')
textArea.value = text
// Avoid scrolling to bottom
textArea.style.top = '0'
textArea.style.left = '0'
textArea.style.position = 'fixed'
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
return new Promise<boolean>((resolve, reject) => {
try {
let successful = document.execCommand('copy')
resolve(successful)
} catch (err) {
reject(err)
} finally {
document.body.removeChild(textArea)
}
})
}
export const copyText = (text: string): Promise<boolean> => {
if (!navigator.clipboard) {
return fallbackCopyTextToClipboard(text)
}
return navigator.clipboard.writeText(text).then(() => true)
}
遇到的问题
- 链接没有加密,打开网页是空白的,使用encodeURIComponent解决
- 链接过长,打开网页是空白的,通过后端生成短链接解决
- 注册开放标签注册失败,也会导致网页是空白的
注意事项
- 一定要配置 :在MP平台->设置->隐私与安全->明文Scheme拉起此小程序声明
- 微信开发标签一定要配置:登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
相关文档
链接: URL Scheme文档
链接: 获取短链接
链接: 开放标签