初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)
零、关于开发思路
(一)拿到工作任务,先理清楚需求
1.逻辑部分
不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问
2.页面部分(含国际化)
整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用
(时间充分的前提下,不要一味先复制粘贴再提取;要先提取直接使用)
开发过程中就国际化,可以仅把中文国际化做了zh.json,英文的最后找ai直接翻译一下给到en.json文件,这样一定程度上节约了时间,并且准确率极高
(否则容易出错-如用cursor直接最后国际化页面里的文本,漏提取or直接中文文本改错,等等问题)
3.swagger接口部分
遇到无厘头但可能相关的接口,问后端!
比如下面这个,登陆的时候勾选同意xxx协议,登陆成功后就要调用这个“用户协议操作记录接口”给到后端,后端记录一下。
(二)理清需求后,调研哪些点可能有阻碍
比如微信小程序权限-系统级别、微信APP级别、微信小程序级别(弄清楚产品要求各情况怎么处理)
比如原后端上传图片接口是否适配微信小程序uploadFile()方式
这些地方都要弄清楚,避免走弯路,尽早将可能遇到的困难问题摆出来,找更有经验的开发or百度or官网寻找解决方案,跟pm明确技术风险,并合理安排时间
避免由于忽视,前期困难点未理清楚导致错误排期and前后端走弯路
一、关于oss图片上传
(一)上传图片本身接口限制
1.微信小程序上传前提
微信小程序本身不直接支持原生的 FormData
对象,但可以通过以下几种方式实现类似 form-data
格式的文件上传功能(这里使用了第一种官方推荐的,其他两种没尝试)
2.具体场景分析
原接口1:APP司机注册
POST /xxx/driver-app/v1/users
表单和上传图片文件都在一起用一个接口(post、formData)上传,2个参数,单文件
这里虽然都是单张图片,但是要分别传人像照和国徽照,且非必传
而uni.uploadFile(obj)的obj中,filePath/files必填,不能为null或者“”
(这里我尝试了用一个固定的下载了线上图片得到的临时地址,可以实现,但并不推荐)
原接口2:APP端运单货物操作: 提货、确认到达
POST /xxx/driver-app/v1/waybills/{id}/actions/loadings
表单和上传图片文件都在一起用一个接口(post、formData)上传,2个参数,多文件数组
(这里swagger写的有问题,事实上是数组,且必填)
这里传的是俩file数组,更不能直接用这个接口了。
后端尝试两种解决:
第一种:oss直传(未采纳)
刚开始后端直接抄的鸿蒙app实现,结果差不多的问题,不支持
鸿蒙前端是用put传的文件,微信小程序不支持的

事实上阿里云分别提供了各种设备的实践demo,各不相同,适配各种环境。
所以以后要注意不能直接鸿蒙抄到微信小程序,不一定适配的。
如何在微信小程序环境下将文件上传到OSS_对象存储(OSS)-阿里云帮助中心微信小程序可以将图片、文档、视频等文件上传到OSS,实现文件的云端存储和分发。https://help.aliyun.com/zh/oss/use-cases/wechat-applet-uploads-files-directly-to-oss?spm=a2c4g.11186623.help-menu-31815.d_6_1_2_0.2944285cQPObLQ&scm=20140722.H_92883._.OR_help-T_cn~zh-V_1
最终后端研究之后决定不采用直传->前端传文件给阿里云这种形式了(觉得比较麻烦),选择单拎文件上传接口慢慢上传->前端传文件给后端,后端传文件给阿里云
第二种:单独创建上传文件的接口+创建小程序司机注册、货物操作接口(采纳)
上传文件接口
POST /xxx/driver-app/v1/media/upload
注册、货物操作-小程序接口
小程序的传参file/file[]改成了传string/string[]
(二)ios微信小程序照片不显示(安卓正常显示)
1.原因
2.解决方案
看一下后端返回的图片地址是否是http开头的,是的话需要让后端配置一下改成https或者前端处理http->https
A 后端处理(问的ai,并没尝试)-后端适配会更合适
B 前端处理(需确保 Bucket 支持 HTTPS)-本次采纳
oss_url = "http://bucket.oss-cn-hangzhou.aliyuncs.com/image.jpg"
https_url = oss_url.replace("http://", "https://")
(三)微信小程序上传下载文件需配置域名白名单
记得在小程序后台开发管理页配置uploadFile和downloadFile用到的合法域名
二、关于权限
(一)相机和相册权限(仍可能在某些设备如红米等机型出现问题)
1.uniapp封装的组件-uni-file-picker(内置了微信app级别的权限判断)
uni-app官网
实现的效果——微信app有权限即能正常使用,无权限的话内置提示
如下图(安卓ios显示不同问题不大,但是安卓红米K80的相机没有弹出首次提醒)
2.自己写微信级别、微信小程序级别的的权限
代码判断什么的自己写,跟定位权限差不多,参考下文。权限从定位改成相机、相册,不用管系统级别权限而已。
(二)定位权限
1.微信小程序权限
uni.authorize(OBJECT) | uni-app官网
uni.openSetting(OBJECT) | uni-app官网
2.系统/微信应用权限
系统信息的概念 | uni-app官网
3.代码
流程图
a.先微信小程序级别权限
/** 1.定位* @param callback* @param isChange 是否转换坐标系* @param type 提示语使用哪个
*/
export const getPosition = (isChange: boolean = true) => {return new Promise((resolve) => {// 授权获取地理位置uni.authorize({scope: 'scope.userLocation',success() {getLocation((res: any) => {resolve(res);}, isChange)},// 小程序定位拒绝fail() {uni.showModal({title: i18n.global.t('locationPermissionTitle'),content: i18n.global.t('locationPermissionContent'),confirmText: i18n.global.t('allow'),cancelText: i18n.global.t('deny'),confirmColor: '#218BFE',cancelColor: '#999',success: res => {if (res.confirm) {uni.openSetting({success(res) {if (!res.authSetting['scope.userLocation']) {uni.showModal({content: i18n.global.t('locationNotEnabled'),showCancel: false,confirmText: i18n.global.t('iKnow'),confirmColor: '#1677FF',complete: () => {resolve(undefined);}})} else {resolve(undefined);}}});} else if (res.cancel) {// 不允许resolve(undefined);}},fail: () => {resolve(undefined);}});}});})
};
b.再系统/微信级别权限+获取定位
/*** 2.获取定位*/
export const getLocation = (callback: Function, isChange: boolean = true) => {uni.getLocation({type: 'gcj02',isHighAccuracy: true,success: function (res) {let result: any = {};const {longitude, latitude} = res;if (isEmpty(longitude) || isEmpty(latitude)) {const messageError = i18n.global.t('getLocationFailed');uni.showToast({title: messageError,icon: 'none',duration: 2000,complete: () => {// 提示关闭后进行回调操作, 这里callback入参是undefinedsetTimeout(callback, 2000);}});} else {if (isChange) {result = qqMapTransBMap(longitude, latitude);} else {result = {longitude,latitude};}callback(result);}},// 一般是微信定位拒绝fail: (error) => {const { locationAuthorized } = uni.getSystemInfoSync();console.error('getLocation error', error);const tip = !locationAuthorized ? i18n.global.t('wechatLocationDenied') : i18n.global.t('getLocationFailedGeneral');uni.showToast({title: tip,icon: 'none',duration: 2000,complete: () => {// 提示关闭后进行回调操作setTimeout(callback, 2000);}});}});
}
三、微信小程序坐标系及地理逆编码调研
(一)坐标系及三方
微信小程序的getLocation可以两种坐标系,国际gps或者国标局gcj02(推荐国标局gcj02,wx.openLoction()要求用gcj02坐标系,且完美适配腾讯/高德地图sdk,百度地图也提供了官方转换方式)
自带没有api实现地理逆编码,要想实现的话需要使用三方sdk/api,比如腾讯/百度/高德地图
微信小程序JavaScript SDK | 腾讯位置服务
微信小程序JavaScript API | 百度地图API SDK
概述-微信小程序插件 | 高德地图API
(二)代码
腾讯gcj02百度互转
// 腾讯地图gcj02经纬度转百度地图
export function qqMapTransBMap(lng: number, lat: number) {let x_pi = 3.14159265358979324 * 3000.0 / 180.0;let x = lng;let y = lat;let z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);let theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);let lngs = z * Math.cos(theta) + 0.0065;let lats = z * Math.sin(theta) + 0.006;return {latitude: lats,longitude: lngs}
}
// 百度地图经纬度转腾讯地图gcj02
export function BMapTransQQMap(lng: number, lat: number) {let x_pi = 3.14159265358979324 * 3000.0 / 180.0;let x = lng - 0.0065;let y = lat - 0.006;let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);let lngs = z * Math.cos(theta);let lats = z * Math.sin(theta);return {latitude: lats,longitude: lngs}
}
四、关于input
正则过滤输入,不支持pattern,pattern属性html才有
最后使用的是@input过滤+debounce实现(todo:寻找更好的方法)
五、关于toast
上一个toast会被下一个toast无缝替换(看不出前面的toast,只能看到最后一个toast)
小程序的提示uni.showToas会在页面跳转时消失,导致一闪而过看不清提示的情况--需加setTimeout延迟一下
六、关于防爆
一直以为就调接口需要防爆???
结果拍照页面点击上传也需要???
业务代码里用到了事件总线,由于异步,接口loading也没用,需要手动设置标志位(类似锁)防爆
ps:使用事件总线$on,$emit——因为navigateBack不能传参,又想特定时候更新上一页数据