一 路由跳转携带参数问题
1:单个参数用? 后面参数用&
`/index/proof?id=${id}&name=${name}`
2:小程序参数传递,是类似于get传参,所以对单个字段,如果太长,会自动进行截取,那么传递之前,对长数据做一个编码
。接收页面再做一个解码
let newProofFileId = encodeURIComponent(obj.ProofFileId);
encodeURIComponent: 函数可把字符串作为 URI 组件进行编码。
在接收参数页面,对数据进行解码
decodeURIComponent(options.newProofFileId)
二 跳转方式
小程序点的层级太多,有时候点击返回,想直接返回原始页。
1.1 最常见的跳转
保留当前页面,跳转到应用内的某个页面。返回时,一层一层的返回
wx.navigateTo({
url: ‘/pages/index/index’,
});
1.2 只能跳转到tabBar配置页面
注意:只能是系统定义的tabBar,如果是自定义tabBar。此方法不管用
wx.switchTab({
url: ‘/pages/index/index’,
});
1.3 设置返回的页数级别
delta:返回的页面数,如果 delta 大于现有页面数,则返回到首页,默认值为1
wx.navigateBack({
delta: 2
})
1.4 关闭当前页面,跳转到应用内的某个页面
单纯的关闭当前页面
wx.redirectTo({
url: ‘/pages/index/index’,
});
1.5 关闭所有页面,打开到应用内的某个页面。
此种跳转,会在页面顶部有一个home图标。点击之后,直接返回首页
wx.reLaunch({
url: ‘/pages/index/index’,
});
三 登录流程,解密UnionID
- 登录按钮必须安排上
- wx.login获取
code
- wx.getUserInfo 获取
userInfo,encryptedData,iv
好奇点:
<button open-type="getUserInfo" lang="zh_CN" @getuserinfo="onGotUserInfo">
已经有了onGotUserInfo ,就可以获取到userInfo,encryptedData,iv`。
下面为什么还要再次调用
wx.getUserInfo.
因为解密的话,得先走code,不然encryptedData会失效。
button里面的 @getuserinfo="onGotUserInfo 只是走个形式。
最终流程还是wx.login -》wx.getUserInfo
要想获取UnionID。小程序必须得关联微信开放平台,
切记切结`:是微信公众平台 不是微信公众平台
代码
async onGotUserInfo() {
if (!this.WxUuid) {
wx.getSetting({
success: async (res) => {
console.log(res);
console.log(res);
// 判断用户是否拒绝了授权
if (!res.authSetting["scope.userInfo"]) {
// 用户拒绝了授权 什么也不操作了就
} else {
wx.login({
success: async (res) => {
if (res.code) {
this.code = res.code;
wx.getUserInfo({
success: async (e) => {
console.log("wwww", e);
const loginData = {
AppId: "",
Secret: "",
Iv: e.iv,
encryptedData: e.encryptedData,
Code: this.code,
};
console.log("loginData", loginData);
const login = await wxuserinfoApi(loginData);
console.log("login", login);
uni.setStorageSync("WxUuid", login);
// 同意授权,掉接口,跳转、
uni.reLaunch({
url: "/pages/proof/index",
});
},
});
}
},
});
}
},
});
} else {
uni.reLaunch({
url: "/pages/proof/index",
});
}
},
录音的授权
wx.getSetting({
success: (res) => {
console.log(res);
if (!res.authSetting["scope.record"]) {
wx.authorize({
scope: "scope.record",
success: (res) => {
console.log("第一次录音授权成功", res);
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
this.isStart = false;
console.log("this.isStart", this.isStart);
this.start();
const startObj = {
duration: 60000,
};
recorderManager.start(startObj);
},
fail: (res) => {
wx.showModal({
title: "温馨提示",
content: "您已拒绝授权,是否去设置打开?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
console.log(res);
if (res.confirm) {
console.log("用户点击确认");
wx.openSetting({
success: (res) => {
console.log(res);
res.authSetting = {
"scope.record": true,
};
console.log("openSetting: success");
},
});
} else {
console.log("用户点击取消");
}
},
});
},
});
} else {
wx.authorize({
scope: "scope.record",
success: (res) => {
console.log("第二次授权成功的录音授权成功", res);
//第二次授权成功的
this.isStart = false;
console.log("this.isStart", this.isStart);
this.start();
const startObj = {
duration: 60000,
};
recorderManager.start(startObj);
},
});
}
},
});
位置授权,获取经纬度
wx.getSetting({
success: (res) => {
console.log(res);
if (!res.authSetting["scope.userLocation"]) {
wx.getLocation({
type: "wgs84",
success: (res) => {
console.log("授权weizhi", res);
console.log("当前位置的经度:" + res.longitude);
console.log("当前位置的纬度:" + res.latitude);
uni.setStorageSync("longitude", res.longitude);
uni.setStorageSync("latitude", res.latitude);
},
fail() {
wx.showModal({
title: "温馨提示",
content: "您已拒绝授权,是否去设置打开?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
console.log(res);
if (res.confirm) {
console.log("用户点击确认");
wx.openSetting({
success: (res) => {
console.log(res);
res.authSetting = {
"scope.userLocation": true,
};
console.log("openSetting: success");
},
});
} else {
console.log("用户点击取消");
}
},
});
},
});
} else {
wx.getLocation({
type: "wgs84",
success: (res) => {
console.log("授权weizhi", res);
console.log("当前位置的经度:" + res.longitude);
console.log("当前位置的纬度:" + res.latitude);
uni.setStorageSync("longitude", res.longitude);
uni.setStorageSync("latitude", res.latitude);
},
}
},
});
获取到经纬度之后,转化成,度 分 秒
对此功能做了单独的封装 data.js
const gps = function (d_data) {
let d = parseInt(d_data);
let m = parseInt(((d_data - d) * 60));
let s = parseInt((((d_data - d) * 60 - m) * 60));
return d + "°" + m + "′" + s + "″";
}
module.exports = {
gps
}
使用
import { gps } from "../../utilsProof/date";
data() {
return {
longitude: gps(uni.getStorageSync("longitude")),
latitude: gps(uni.getStorageSync("latitude")),
}
效果
文件转hash(sha256)
- 上传 拍照 图片:wx.chooseImage
- 上传文件:chooseMessageFile
- 上传,拍摄视频 chooseVideo
这些微信内置的方法。都会返回一个本地的临时路劲。我们可以将这个临时路劲转成二进制文件流
注意我们只要 ArrayBuffer 格式读取文件的二进制内容。
wx.chooseImage({
success: (res)=> {
// 这个适用于任何,不局限于wx.chooseImage
console.log(wx.getFileSystemManager().readFileSync(res.tempFilePaths[0]))
var buffer = wx.getFileSystemManager().readFileSync(res.tempFilePaths[0])
},
})
安装js-sha256
npm install js-sha256 --save
引入
import { sha256 } from "js-sha256";
使用
buffer = sha256(this.base);
如果要转成base64.则这样写
wx.chooseImage({
success: (res)=> {
console.log(wx.getFileSystemManager().readFileSync(res.tempFilePaths[0], "base64"))
},
})
总结 : 文件转hash加密
主要是文件类型是ArrayBuffer 格式。所以要将本地临时路径的文件读取
出来。然后利用sha256进行加密
将文件保存到到本地,并且设置临时地址
- 将base64转化为ArrayBuffer
- 获取文件的后缀。以便设置文件后缀
比如: ’ 37a7a3d0acbc715344e2f4a30a12b27ae510f24.mp4’
const temp = this.id.split(".")[this.id.split(".").length - 1]; // 根据. 进行分割。取. 后的所有字符 - 这样既设置了路劲,也将文件保存至本地。那么此时的文件路劲。我们就可以当做src.在音频,视屏 图片中 随意使用了
const buffer = uni.base64ToArrayBuffer(data[0]);
this.voicePath = `${wx.env.USER_DATA_PATH}/tmp_base64.${temp}`;
var fileManager = wx.getFileSystemManager();
fileManager.writeFile({
filePath: this.voicePath,
data: buffer,
success: (result) => {},
fail: () => {},
complete: () => {},
});
textarea 中字数统计
我也想偷懒,没想到官方没这个功能
<view class="contain-input">
<textarea
placeholder="500字以内"
:value="content"
:maxlength="500"
placeholder-style=" font-size: 31rpx;font-family: PingFangSC-Medium "
@input="conInput"
/>
<view class="max-num"> {{ content.length || 0 }}/500 </view>
</view>
//字数
conInput(e) {
this.content = e.detail.value;
},
动态设置标题,面导航条颜色,字体颜色
场景: 有五个功能,都要跳到同一个页面,进行取名操作。但是页面标题都是根据功能类型来显示。这个时候用动态标题,绝对妥。并且在已进入页面(onLoad中设置)
- 动态设置标题
onLoad(options) {
this.title = options.type;
uni.setNavigationBarTitle({
title: this.title,
});
},
- 设置页面导航条颜色
uni.setNavigationBarColor({
frontColor: '#ffffff', // 仅支持 #ffffff 和 #000000。
backgroundColor: '#ff0000', //背景颜色值,有效值为十六进制颜色,不拘束于黑白
//animation为可选项
animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
自定义tabbar
<template>
<view class="tarbar" :style="{ marginBottom: isFullSucreen ? '68rpx' : '' }">
<view
class=".tarbar-list"
:style="{
background: tabBar.backgroundColor,
color: tabBar.color,
'border-top':
tabBar.position == 'bottom' ? '1rpx solid ' + tabBar.borderStyle : 0,
'border-bottom':
tabBar.position == 'top' ? '1rpx solid ' + tabBar.borderStyle : 0,
}"
>
<view class="tarbar-list-ul">
<view
class="tarbar-list-li"
v-for="(item, index) in tabBar.list"
:key="index"
@click.stop="setSelected(item, index)"
>
<block>
<view class="tarbar-list-li-icon">
<image
:src="selected == index ? item.selectedIconPath : item.iconPath"
mode=""
></image>
</view>
<view
:style="selected == index ? 'color:' + tabBar.selectedColor : ''"
class="tarbar-list-li-name"
>{{ item.text }}</view
>
</block>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
isFullSucreen: false,
tabBar: {
color: "#999999",
selectedColor: "#999",
borderStyle: "white",
backgroundColor: "#fff",
position: "bottom",
list: [
{
pagePath: "/pages/proof/index",
iconPath: "/static/czHome.png",
selectedIconPath: "/static/czHomeSelect.png",
text: "首页",
},
{
pagePath: "/pages/proofList/index",
iconPath: "/static/czProof.png",
selectedIconPath: "/static/czProofSelect.png",
text: "证据",
},
],
},
selected: 0, //当前激活项
};
},
props: {
current: {
type: Number,
default: 0,
},
},
created() {
this.isFullSucreen = getApp().globalData.isFullSucreen;
console.log("this.isFullSucreen", this.isFullSucreen);
console.log("子组件czChange", this.current);
},
watch: {
current(newValue, oldValue) {
this.selected = newValue;
},
},
methods: {
setSelected(tab, index) {
console.log("11", tab);
this.selected = index;
console.log("选中", this.selected, "索引", index);
this.$emit("czChange", {
path: tab.pagePath,
index,
});
},
},
};
</script>
<style>
.tarbar {
width: 100%;
z-index: 9999;
position: fixed;
}
.tarbar-list {
width: 100%;
height: 98upx;
background: #4d586f;
position: fixed;
left: 0;
bottom: 0;
}
.tarbar-list-ul {
width: 100%;
height: 100%;
padding: 0upx 160upx;
display: flex;
justify-content: space-between;
box-sizing: border-box;
}
.tarbar-list-li {
width: 80upx;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.tarbar-list-li-icon {
width: 50upx;
height: 50upx;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.tarbar-list-li-icon image {
width: 36rpx;
height: 36rpx;
}
.tarbar-list-li-name {
width: 100%;
text-align: center;
line-height: 30upx;
font-size: 24upx;
height: 30upx;
}
.tarbar-list-li-center {
width: 100upx;
}
</style>
使用
<my-tabbar :current="current" @czChange="czChange"></my-tabbar>
czChange(obj) {
console.log("参数", obj);
this.current = +obj.index;
console.log("this.current", this.current);
if (this.current === 1) {
this.getQueryProof();
uni.setNavigationBarTitle({
title: "证据列表",
});
uni.setNavigationBarColor({
frontColor: "#000000",
backgroundColor: "#ffffff",
});
} else {
uni.setNavigationBarTitle({
title: "",
});
uni.setNavigationBarColor({
frontColor: "#ffffff",
backgroundColor: "#0a47b0",
});
}
console.log("类型", typeof obj.index);
console.log("索引", this.current);
},
次策略是万般无奈之举,由于自定义tabbar。每次都会和页面的加载和销毁发生变化。所以底部tabbar会闪一下。为了解决这个问题。把两个页面放在了一起。通过当前点击是tabbaer索引,开控制页面的展示
<view v-if="current === 0">
aaa页面
</view>
<view v-else>
bbb页面
</view>
小技巧
- 传值的时候,通过对象传参,取得时候,特别方便
- 父子组件传值,很容易出现监听不到问题。所以我这里用了监听器,来监听父组件传值的变化
watch: {
current(newValue, oldValue) {
this.selected = newValue;
},
},
- 底部适配全屏
const { safeArea, screenHeight } = wx.getSystemInfoSync()
this.fullScreen = screenHeight - safeArea.height > 40
全面屏动态加样式
:style="{ marginBottom: isFullSucreen ? '68rpx' : '' }"
几种提示框
- 简单的提示,间隔几秒就消失
uni.showToast({
title: "不支持该类型文件",
duration: 2000,
icon: "none",
});
- 数据加载前,加载后的提示
uni.showLoading({
title: '加载中'
});
uni.hideLoading();
- 操作模态框
uni.showModal({
title: '提示',
content: '这是一个模态弹窗',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
上传文件
wx.uploadFile
将本地资源上传到服务器。客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data。
wx.chooseImage({
success (res) {
const tempFilePaths = res.tempFilePaths
wx.uploadFile({
url: 'https://example.weixin.qq.com/upload', //仅为示例,非真实的接口地址
filePath: tempFilePaths[0],
name: 'file',
formData: {
'user': 'test'
},
success (res){
const data = res.data
//do something
}
})
}
})
视屏自动全屏播放
看那个时机需要,就放这段话,期中myvideo,是video组件的id
var videoContext = wx.createVideoContext("myvideo", this);
videoContext.requestFullScreen(); //执行全屏方法
<view v-if="hasVidao">
<video
:src="voicePath"
controls
autoplay
isFullscreen
id="myvideo"
@fullscreenchange="screenChange"
></video>
</view>
检测是否全屏 @fullscreenchange
screenChange(e) {
console.log("e全屏非全屏", e);
let fullScreen = e.detail.fullScreen; //值true为进入全屏,false为退出全屏
if (!fullScreen) {
//退出全屏
this.hasVidao = false;
} else {
//进入全屏
this.hasVidao = true;
}
},