开发背景
最近在优化一个两三年的老项目,奈何UI小姐姐出的图太高大上,用了好久的微信自带网络加载api,没法满足需求,但是在每个页面上写太多相同的setData太low,于是下定决心封装一个。
插件实现功能
- 1 网络加载框
- 2 选择对话框
- 3 吐司对话框
本文要点
- 1 插件效果演示(amazing)
- 2 插件集成使用(import)
- 2 小程序插件的开发与使用(component)
- 3 基于插件相关功能Util类封装(loadToastUtil)
- 4 总结(summary)
上干货
插件效果演示(amazing)
插件集成使用(import)
插件下载地址在文末,欢迎下载,欢迎star
在小程序页面的json文件中引入该插件
home.json
{
"usingComponents": {
"jkDialog": "../../view/jk-dialog/jk-dialog"
}
}
在js文件中引入jkDialogUtil,注意构造方法中的三个参数
home.js
import {
jkDialogUtil
} from "../../view/jk-dialog/dialogUtil";
let dialogUtil;
//三个参数分别对应page对象,网络请求基础url,对应插件id
dialogUtil = new jkDialogUtil(this, 'https://www.baidu.com', '#loadToast');
//调用吐司对话框
dialogUtil.showToast('提示用户\n换行');
//调用选择对话框
dialogUtil.showSelectDialog({
selectTip: '测试',
selectContent: '测试选择对话框\njust soso',
}, (res) => {
dialogUtil.showToast(res == 'confirm' ? '确认操作' : '取消操作');
});
//显示隐藏网络加载框
dialogUtil.showLoading();
dialogUtil.hiddenLoading();
原理分析
小程序插件的开发与使用(component)
在项目文件中定义view文件夹,用来存放自定义组件
右键新建Component,以我的为例取名为jk-dialog
这个时候开发者工具就会生成四个相应的文件
话不多说,直接上代码
jk-dialog.json
json文件,这里需要注意的就是component字段,声明这个模块是一个插件
{
"component": true, //声明组件
"usingComponents": {}
}
jk-dialog.wxml
wxml文件,插件页面文件,这个分成三个部分,网络加载框 选择对话框 吐司对话框,分别用三个boolean变量控制显隐
{{selectTip}}
{{selectContent}}
{{confirmText}}
{{cancelText}}
{{toastContent}}
jk-dialog.wxss
wxss文件,页面样式,其中用到了一些动画效果,有想法的可以看看,虽然我也是在网上找的代码。
.load-container {
pointer-events: auto;
color: #000;
font-size: 20px;
z-index: 9999;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.load-container image {
width: 50px;
height: 50px;
animation: loading-rotate 1s linear infinite;
}
@keyframes loading-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.pop-container {
pointer-events: auto;
color: #000;
font-size: 20px;
z-index: 10;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.row-center {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.delay-con {
width: 80%;
border-radius: 25rpx;
background: white;
font-size: 32rpx;
margin-bottom: 200rpx;
-webkit-animation: fadeleftIn .4s;
animation: fadeleftIn .4s;
-webkit-animation-name: popIn;
animation-name: popIn;
}
.delay-title {
padding-top: 30rpx;
padding-bottom: 20rpx;
}
.column-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.delay-content {
padding-bottom: 30rpx;
line-height: 45rpx;
text-align: center;
}
.delay-bottom {
width: 100%;
border-top: 1px solid #d6d6d6;
color: #027bfe;
}
.delay-bottom view {
width: 50%;
padding-top: 25rpx;
padding-bottom: 25rpx;
text-align: center;
}
.toast-con {
width: calc(85% - 60px);
padding-top: 20px;
padding-bottom: 20px;
padding-left: 30px;
padding-right: 30px;
border-radius: 10px;
background: rgba(0, 0, 0, 0.8);
margin-bottom: 200px;
color: white;
text-align: center;
font-size: 17px;
-webkit-animation: fadelogIn .2s;
animation: fadelogIn .2s;
}
@-webkit-keyframes popIn {
0% {
-webkit-transform: scale3d(0, 0, 0);
transform: scale3d(0.5, 0.5, 0.5);
opacity: 0;
}
50% {
-webkit-animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
-webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
opacity: 1;
}
}
@keyframes popIn {
0% {
-webkit-transform: scale3d(0, 0, 0);
transform: scale3d(0.5, 0.5, 0.5);
opacity: 0;
}
50% {
-webkit-animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
-webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
opacity: 1;
}
}
@keyframes fadelogIn {
0% {
-webkit-transform: translate3d(0, 100%, 0);
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
100% {
-webkit-transform: none;
transform: none;
}
}
@-webkit-keyframes fadelogIn {
0% {
-webkit-transform: translate3d(0, 100%, 0);
}
100% {
-webkit-transform: none;
}
}
jk-dialog.js
js文件,封装了三个对话框的显示隐藏
let systemInfo, selectCallBack;
import {
selectLabel
} from './config'
Component({
/**
* 组件的属性列表
*/
properties: {
toastText: {
type: String,
}
},
/**
* 组件的初始数据
*/
data: {
statusBarHeight: 0,
showLoading: false,
showToast: false,
toastContent: '',
showSelect: false,
selectTip: '提示',
selectContent: '该操作不可逆\n是否继续',
confirmText: '确认',
cancelText: '取消'
},
ready: function () {
let that = this;
that.initData();
},
/**
* 组件的方法列表
*/
methods: {
/*用户提示对话框
\n表示换行
*/
showToast: function (content, duration) {
let that = this;
that.setData({
showLoading: false,
showSelect: false,
showToast: true,
toastContent: content
});
let timer = setTimeout((res) => {
that.setData({
showToast: false,
});
clearTimeout(timer);
}, duration == undefined ? 1500 : duration)
},
/* 展示自定义加载框
*/
showLoading: function () {
this.setData({
showSelect: false,
showToast: false,
showLoading: true,
})
},
/* 隐藏自定义加载框
*/
hiddenLoading: function () {
this.setData({
showLoading: false,
})
},
/* 初始化数据
*/
initData: function () {
let that = this;
wx.getSystemInfo({
success: function (res) {
console.log(res, 'system');
if (res.errMsg === 'getSystemInfo:ok') {
that.setData({
statusBarHeight: res.statusBarHeight
});
res.lang = res.language.indexOf('zh') !== -1 ? 'zh' : 'en';
systemInfo = res;
}
},
});
},
/* 展示选择对话框
*/
showSelectDailog: function (params, callBack) {
let that = this;
that.setData({
showToast: false,
showLoading: false,
showSelect: true,
selectTip: params.selectTip != undefined ? params.selectTip : selectLabel[systemInfo.lang]['selectTip'],
selectContent: params.selectContent != undefined ? params.selectContent : selectLabel[systemInfo.lang]['content'],
confirmText: params.confirmText != undefined ? params.confirmText : selectLabel[systemInfo.lang]['confirmText'],
cancelText: params.cancelText != undefined ? params.cancelText : selectLabel[systemInfo.lang]['cancelText'],
});
selectCallBack = callBack;
},
hiddeenSelectDialog: function (e) {
let that = this;
that.setData({
showSelect: false
});
selectCallBack(e.currentTarget.dataset.state);
}
}
})
基于插件相关功能Util类封装(loadToastUtil)
插件页面基本已经好了,还差一个工具类,调用比较方便。
dialogUtil.js
我是直接封装成了一个class类,在构造方法中传入小程序Page对象,baseUrl以及插件id,简单暴露了一些方法供每一个页面调用。
需要注意的是小程序中的一个api selectComponent。
通过这个获取到插件对象,以此来调用插件js文件中写好的方法
const app = getApp();
export class jkDialogUtil {
/*
构造方法
that:page对象
baseUrl:网络请求基础url
loadToast:对应插件id
*/
constructor(that, baseUrl, loadToast) {
this.that = that;
this.baseUrl = baseUrl;
this.loadToast = that.selectComponent(loadToast);
if (this.loadToast != null)
console.log('插件加载成功');
else
console.log('插件加载失败');
}
/*展示网络加载框 */
showLoading() {
if (this.loadToast != null)
this.loadToast.showLoading();
}
/*隐藏网络加载框 */
hiddenLoading() {
if (this.loadToast != null)
this.loadToast.hiddenLoading();
}
/**
* 网络请求
*let params = {
url: '/authorization/v1/token/decode',
data: data,
method: 'POST',
isCommon: true,
isLoad: true
}
successCallback:成功回调
errorCallback:失败回调
*/
baseRequest(params, successCallback, errorCallback) {
if (this.loadToast == null) {
return;
}
let that = this;
let realUrl = that.baseUrl + params.url;
if (params.type == 'realUrl') {
realUrl = params.realUrl;
}
if (params.isLoad) {
that.loadToast.showLoading();
};
let token = wx.getStorageSync('accessToken');
let header = {
'Authorization': 'Bearer ' + token,
'X-TrackingId': that.getXTrackingId() + ''
};
if (params.header != undefined) {
header = Object.assign({}, params.header, header);
}
wx.request({
url: realUrl,
method: params.method,
header: header,
data: params.data,
success: res => {
if (params.isLoad) {
that.loadToast.hiddenLoading();
}
if (res.data.responseCode === undefined) {
that.baseToast('未知错误');
return;
}
if (res.data.responseCode === -2) {
wx.login({
success: res => {
jKLogin(res.code, function () {
that.baseRequest(params, successCallback, errorCallback)
})
}
});
return;
}
if (!params.isCommon) {
successCallback(res.data);
return;
}
if (res.data.responseCode != undefined) {
if (res.data.responseCode === 0) {
successCallback(res.data.data);
return;
} else {
that.baseToast(res.data.responseMessage);
errorCallback == undefined ? '' : errorCallback(res.data);
}
return;
}
baseToast('未知错误');
},
error: res => {
that.loadToast.hiddenLoading();
console.log(res, 'error');
that.baseToast('未知错误')
}
})
}
/*展示选择对话框 */
showSelectDialog(params, callBack) {
if (this.loadToast != null)
this.loadToast.showSelectDailog(params, callBack);
}
/* 展示吐司对话框 */
showToast(content) {
if (this.loadToast != null)
this.loadToast.showToast(content);
}
baseToast(content) {
wx.showToast({
title: content,
icon: 'none',
duration: 3000
})
}
getXTrackingId() {
let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
let XTrackingId = "";
for (let i = 0; i < 32; i++) {
let id = parseInt(Math.random() * 61);
XTrackingId += chars[id];
}
return XTrackingId;
}
}
源码地址点我前往
总结(summary)
在工具类中其实还封装了baseRequest的网络请求方法,大家可以按照具体需求自行修改。
没bug 祝好运 ~