一、前言
起先是我们的产品小伙伴需要一个短信链接跳转到我们小程序中的一个静态页面,我想到没想就新增了一个页面,然后经过我一系列手动操作之后生成了短信链接,并把链接给到了产品小伙伴。
没过几天,产品小伙伴又来了,说需要一个新的短信链接,我拿到页面一看脑袋里全是问号,这不就是你之前的页面吗?产品小伙伴说你看文案不一样。这个时候我感觉事情不是我原来想的那么简单,于是我反问产品小伙伴,未来是不是还有类似需求,更新文案然后生成一个新的短信链接,产品小伙伴说就剩几个城市了。我半信半疑的将页面做成了通用的,根据url参数区分不同的页面内容,同时又手动生成了一个短信链接。
大概过了两周,产品小伙伴拿着电脑给我看了一个Excel表格,里面大概有10行,都是需要生成新的短信链接,可能未来随着业务的进入,还会有新的地区,需要新的短信链接,所以我花了点时间,写了一个小工具,可以一次性生成多个短信链接。
二、如何拥有一个小工具
2.1 功能交互
我根据我想要的功能画了一个简单的流程图。
2.2 页面UI
2.3 功能实现
2.3.1 实现短信打开小程序的方式
微信提供了3种方式实现短信打开小程序,具体可以查看官方文档
我是通过URL Link实现的,官方文档
注:
1)单个小程序每日生成 Link 上限为50万个(包含短期有效 Link 与长期有效 Link );
2)有效时间超过180天的 Link 或永久有效的 Link 为长期有效Link,单个小程序总共可生成长期有效 Link 上限为10万个,请谨慎调用;
3)有效时间不超过180天的 Link 为短期有效Link,单个小程序生成短期有效 Link 不设上限;
4)开放范围:针对非个人主体小程序开放;
5)只能生成已发布的小程序的 URL Link;
6)在微信内或者安卓手机打开 URL Link 时,默认会先跳转官方 H5 中间页,如果需要定制 H5 内容,可以使用云开发静态网站。
2.3.2 功能更新提醒(重要)
自 2022 年 4 月 11 日起,URL Link有效期最长 30 天,不再支持永久有效的URL Link、不再区分短期有效URL Link与长期有效URL Link。若在微信外打开,用户可以在浏览器页面点击进入小程序。每个独立的URL Link被用户访问后,仅此用户可以再次访问并打开对应小程序,其他用户无法再次通过相同URL Link打开该小程序。 在本次规则调整生效前已经生成的URL Link,如果有效期超过30天或长期会被降级为30天有效,只能被1个用户访问,开始时间从调整日期开始计算。 详细调整说明可见《小程序链接生成与使用规则调整公告》。
重要信息归纳
- 不再支持长期有效URL Link;
- 每个独立的URL Link仅支持一位用户访问,如果已经被访问过,其他用户无法再次访问该URL Link。
2.3.2 具体实现
我们的项目是基于React框架开发的,所以小工具也是JSX写法。
1)视图层
<Tabs className='tools-tabs'>
<Tab title='获取短信链接'>
<div className='list mt10'>
<div className='item'>
<textarea
className='tips text-ts text-gray textarea'
defaultValue={smsLinkText}
rows={8}
placeholder='短信链接跳转页面信息粘贴,path,query之间请用逗号隔开。 如:pages/home/home,type=bj。多行用换行隔开。'
onChange={e => getValue(e)}
/>
</div>
<div className='item'>获取的短新链接JSON值</div>
<div className='item'>
<textarea className='tips text-ts text-gray textarea' defaultValue={smsLinkListJson} rows={8} placeholder='获取的短新链接' />
</div>
<div className='item'>
<Button className='btn-large' type='primary' round onClick={getSMSLink}>
确定并一键复制结果
</Button>
</div>
</div>
</Tab>
</Tabs>
2)处理输入值
多行且是多个参数,所以使用字符串截取的方式,将字符串重组成最终我们想要数组数据。
/**
* 设置输入框的值
* @param {Event} e Event对象
* @return {void} 无
*/
const getValue = e => {
const val = e.target.value;
if (!val) return;
let list = val.split('\n');
let linkList = [];
list.forEach(link => {
let [path, query] = link.split(',');
if (path && query) {
linkList.push({
path: path,
query: query,
});
}
});
setSmsLinkText(val);
setSmsLinkList(linkList);
};
3)点击确定按钮获取小程序URL Link
点击确定之前我先校验了一下输入框内容是否为空,如果为空会给出提示;
如果不为空进行获取小程序的access_token,因为获取小程序URL Link的接口需要传入access_token;
将获取的access_token传入getGenerateurllink方法进而获取小程序URL Link,并放入list数组中;
将list数组转成json对象,进行页面回显,并复制到本地剪贴板中。
/**
* 复制链接
* @param {string} message 复制内容
* @return {void} 无
*/
const copyTextHandle = message => {
let input = document.createElement('input');
input.value = message;
document.body.appendChild(input);
input.select();
input.setSelectionRange(0, input.value.length), document.execCommand('Copy');
document.body.removeChild(input);
opentToast('复制成功');
};
/**
* 获取小程序 URL Link
* @param {void} 无
* @return {void} 无
*/
const getGenerateurllink = (access_token, link, callback) => {
const params = {
path: link.path,
query: link.query,
};
axios
.post(`https://api.weixin.qq.com/wxa/generate_urllink?access_token=${access_token}`, {
...params,
})
.then(function () {
//
})
.catch(function (error) {
const resData = error.resData;
let url_link = resData.url_link;
callback && callback(url_link);
});
};
/**
* 获取小程序的access_token
* @param {void} 无
* @return {void} 无
*/
const getCgibinToken = () => {
axios.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=需要跳转的小程序的appid&secret=需要跳转的小程序的secret').then(res => {
const resData = res.resData;
let access_token = resData.access_token;
let list = [];
smsLinkList.forEach(link => {
// 获取小程序 URL Link
getGenerateurllink(access_token, link, url_link => {
list.push({
url_link: url_link,
path: link.path,
query: link.query,
});
if (list.length !== 0 && list.length == smsLinkList.length) {
let arrayData = {
list: [].concat(list),
};
let jsonData = JSON.stringify(arrayData, null, 4);
setSmsLinkListJson(jsonData);
copyTextHandle(jsonData);
}
});
});
});
};
/**
* 获取短信链接
* @param {void} 无
* @return {void} 无
*/
const getSMSLink = () => {
if (!smsLinkText) {
return opentToast('请填写短信链接跳转的页面信息');
}
getCgibinToken();
};
三、完整代码
tools.jsx
/**
* @description 批量获取跳转小程序的短信链接
*/
import React, { useState } from 'react';
import { Tab, Tabs, Button, Toast } from 'antd';
import axios from 'axios';
const Tools = () => {
let [smsLinkText, setSmsLinkText] = useState('');
let [smsLinkList, setSmsLinkList] = useState([]);
let [smsLinkListJson, setSmsLinkListJson] = useState([]);
/**
* 错误提示框
* @param {string} message 弹窗内容
* @return {void} 无
*/
const opentToast = message => {
Toast({ icon: 'failure', message: message }, 2000);
};
/**
* 设置输入框的值
* @param {Event} e Event对象
* @return {void} 无
*/
const getValue = e => {
const val = e.target.value;
if (!val) return;
let list = val.split('\n');
let linkList = [];
list.forEach(link => {
let [path, query] = link.split(',');
if (path && query) {
linkList.push({
path: path,
query: query,
});
}
});
setSmsLinkText(val);
setSmsLinkList(linkList);
};
/**
* 复制链接
* @param {string} message 复制内容
* @return {void} 无
*/
const copyTextHandle = message => {
let input = document.createElement('input');
input.value = message;
document.body.appendChild(input);
input.select();
input.setSelectionRange(0, input.value.length), document.execCommand('Copy');
document.body.removeChild(input);
opentToast('复制成功');
};
/**
* 获取小程序 URL Link
* @param {void} 无
* @return {void} 无
*/
const getGenerateurllink = (access_token, link, callback) => {
const params = {
path: link.path,
query: link.query,
};
axios
.post(`https://api.weixin.qq.com/wxa/generate_urllink?access_token=${access_token}`, {
...params,
})
.then(function () {
//
})
.catch(function (error) {
const resData = error.resData;
let url_link = resData.url_link;
callback && callback(url_link);
});
};
/**
* 获取小程序的access_token
* @param {void} 无
* @return {void} 无
*/
const getCgibinToken = () => {
axios.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=需要跳转的小程序的appid&secret=需要跳转的小程序的secret').then(res => {
const resData = res.resData;
let access_token = resData.access_token;
let list = [];
smsLinkList.forEach(link => {
// 获取小程序 URL Link
getGenerateurllink(access_token, link, url_link => {
list.push({
url_link: url_link,
path: link.path,
query: link.query,
});
if (list.length !== 0 && list.length == smsLinkList.length) {
let arrayData = {
list: [].concat(list),
};
let jsonData = JSON.stringify(arrayData, null, 4);
setSmsLinkListJson(jsonData);
copyTextHandle(jsonData);
}
});
});
});
};
/**
* 获取短信链接
* @param {void} 无
* @return {void} 无
*/
const getSMSLink = () => {
if (!smsLinkText) {
return opentToast('请填写短信链接跳转的页面信息');
}
getCgibinToken();
};
return (
<div>
<Tabs className='tools-tabs'>
<Tab title='获取短信链接'>
<div className='list mt10'>
<div className='item'>
<textarea
className='tips text-ts text-gray textarea'
defaultValue={smsLinkText}
rows={8}
placeholder='短信链接跳转页面信息粘贴,path,query之间请用逗号隔开。 如:pages/home/home,type=bj。多行用换行隔开。'
onChange={e => getValue(e)}
/>
</div>
<div className='item'>获取的短新链接JSON值</div>
<div className='item'>
<textarea className='tips text-ts text-gray textarea' defaultValue={smsLinkListJson} rows={8} placeholder='获取的短新链接' />
</div>
<div className='item'>
<Button className='btn-large' type='primary' round onClick={getSMSLink}>
确定并一键复制结果
</Button>
</div>
</div>
</Tab>
</Tabs>
</div>
);
};
export default Tools;
四、总结
仔细想来,有些需求看似简单,甚至是不断重复的劳动,其实是可以从中进行技术加工的,仅是一味的堆砌页面和功能显然不是咱们的追求。
这是2021年最后一次更文,感谢掘金这一年让我更加热爱开发,更加喜欢从日常工作中发现一些闪光点,最后送给所有的掘金的伙伴,我喜欢的航拍中国中的一段话:
我们与万物同行,星辰指引方向,云与光铺展成大地的模样。
加油2022。