小程序的总结和一些坑
持续更新中
1. 解决VS Code中小程序的wxml文件的注释是双斜杠

重启vscode就可以了
2. vscode安装easy less

在settings.json中添加如下 "less.compile"

"less.compile": {
"compress": false,//是否压缩
"sourceMap": false,//是否生成map文件,有了这个可以在调试台看到less行数
"out": true, // 是否输出css文件,false为不输出
"outExt": ".css", // 输出文件的后缀
}
3. vscode如何自动格式化代码
安装插件 Prettier -Code formatter

在settings.json中添加如下 "editor.formatOnSave": true

4. 小程序标签绑定点击事件
.wxml文件
<view bindtap="changeInputSizeUp">123</view>
.wxss文件
changeInputSizeUp: function() {
do something
}
5. 弹窗时 禁止背景页面滑动操作
弹框标签添加 catchtouchmove="return"
<view wx:if="{{isShowConfirm}}" catchtouchmove="return">
6. 输入框标签聚焦或键盘输入时触发
bindinput 键盘输入时触发
bindfocus 输入框聚焦时触发
<textarea maxlength="70" bindinput='setValue' data-name='stuEidtName'></textarea>
7.日期选择器
<view class="section">
<view class="section__title">日期选择器</view>
<picker mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">
<view class="picker">
当前选择: {{date}}
</view>
</picker>
</view>
bindDateChange: function(e) {
console.log('picker发送选择改变,携带值为', e.detail.value)
this.setData({
date: e.detail.value
})
},
8. 文本超出显示省略号
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
9. 小程序下拉刷新之后停止下拉框的显示
加载完数据后添加 wx.stopPullDownRefresh()
onPullDownRefresh: function () {
this.loadTodoList();
wx.stopPullDownRefresh();
},
10. 给后端服务器发送http请求 支持es7的async await
- 跟目录下创建 request/index.js
// 同时发送异步代码的次数
let ajaxTimes = 0;
export const request = (params) => {
ajaxTimes++;
// 显示加载中loading 效果
wx.showLoading({
title: "加载中",
mask: true,
});
return new Promise((resolve, reject) => {
wx.request({
...params,
success: (result) => {
resolve(result.data);
},
fail: (err) => {
reject(err);
},
complete: () => {
ajaxTimes--;
if (ajaxTimes === 0) {
// 关闭正在等待的图标
wx.hideLoading();
}
},
});
});
};
-
根目录下创建 lib/runtime/runtime.js
将 regenerator/packages/regenerator-runtime/runtime.js 的代码拷到自己的 runtime.js里
-
需要使用async语法的页面js导入 哪个页面使用就哪个页面导入
import { request } from "../../request/index.js";
import regeneratorRuntime from "../../lib/runtime/runtime";
async loadTodoList() {
var that = this;
var url = "http://127.0.0.1:8000/api/add"
const result = await request({
url: url,
data: this.SubmitParams,
method: "post",
header: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (result.code == 0) {
pass
} else {
pass
}
},
11. 监听用户下拉操作
// 监听用户下拉操作
onPullDownRefresh: function () {
// 1 重置数组
this.setData({
todo_show: [],
});
// 2 重置页码
this.SubmitParams.page_num = 1;
// 3 发送请求
this.loadTodoList();
wx.stopPullDownRefresh();
},
12. 页面上拉触底事件的处理函数 如加载下一页
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
// 1 判断还有没有下一页数据
// console.log(this.SubmitParams.page_num, this.totalPages);
if (this.SubmitParams.page_num >= this.totalPages) {
// 没有下一页数据
// console.log('%c'+"没有下一页数据","color:red;font-size:100px;background-image:linear-gradient(to right,#0094ff,pink)");
wx.showToast({ title: "没有下一页数据" });
} else {
// 还有下一页数据
// console.log('%c'+"有下一页数据","color:red;font-size:100px;background-image:linear-gradient(to right,#0094ff,pink)");
this.SubmitParams.page_num++;
this.loadTodoList();
}
}
13. 出现遮罩层后禁止背景页面上下拉动

在遮罩层的标签中添加 catchtouchmove="noneEnoughPeople"
注意:在开发平台上可能不起效果 在手机上测试会起作用

14. button透明 放到图片或view等标签下面
.login_btn 就是button按钮 放到了.user_icon标签下面了
.user_icon {
position: relative;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
.login_btn {
position: absolute;
top: 0;
left: 0;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
opacity: 0;
}
15. 下面的标签 部分压住上面的标签效果实现

// 下面的白色标签 要绝对定位 然后 top指定为负数
.user_content {
position: relative;
.user_business_info_wrap {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -40rpx;
width: 94.6%;
height: 100rpx;
background-color: #ffffff;
}
}
16. async a函数 在别的函数b里需要同步调用
给b变成async 然后在调用a前await一下
async a() {
......
}
async b() {
// 如果不添加await a() 和 other执行顺序不定 会出现问题
await a();
do other......
}
17. 外部this不能被success内部使用问题

18. 小程序登陆功能
小程序调用 wx.login({}) success后请求后端的登陆接口 将wx.login返回的 res.code 传给后端使用
后端服务器的login接口 去请求微信接口(需要code参数),将返回的用户wx_token存储到数据库 用来当作用户的身份验证
可以将wx_token进行编码或加密 md5或base64

auth.js
authLogin(nickName) {
wx.login({
success: (res) => {
if (res.code) {
//发起网络请求
var backend_url = app.globalData.backend_url;
var url = backend_url.base_url + backend_url.login;
wx.request({
url: url, //仅为示例,并非真实的接口地址
data: {
code: res.code,
nickName: nickName,
},
header: {
"content-type": "application/json", // 默认值
},
success: (res) => {
if (res.data.code === 0) {
// 登陆成功
}
},
});
}
},
});
},
auth.py
def login(request):
try:
code = request.GET.get('code')
nick_name = request.GET.get('nickName')
url = "https://api.weixin.qq.com/sns/jscode2session?" \
"appid=%s&secret=%s&js_code=%s&grant_type=authorization_code" % ("xxxxxx",
"xxxxxx",
code)
result = requests.get(url)
result = result.json()
open_id = result['openid']
wx_token = "对open_id进行加密"
session_key = result['session_key']
user, _ = User.objects.update_or_create(wx_token=wx_token,
defaults={'session_key': session_key,
'username': wx_token,
'nick_name': nick_name,
'open_id': open_id})
except Exception as e:
logger.error(e)
return JsonResponse({'code': 1, 'message': '服务器异常,请联系管理员'})
return JsonResponse({'code': 0, 'wx_token': wx_token, 'user_id': user.id})
19. 下载excel
小程序发起下载请求,后端服务器生成excel 返回给小程序excel的网络地址
然后小程序请求这个网络地址进行下载 wx.downloadFile( ) +wx.openDocument( )
wx.downloadFile({
// 示例 url,并非真实存在
url: 'http://example.com/somefile.pdf',
success: function (res) {
const filePath = res.tempFilePath
wx.openDocument({
filePath: filePath,
success: function (res) {
console.log('打开文档成功')
}
})
}
})
20. 七牛云接口存储图片
from PIL import Image
from qiniu import Auth, put_data
from qiniu import BucketManager
AK = "七牛云 ak"
SK = "七牛云 secret_ky"
# 构建鉴权对象
q = Auth(AK, SK)
# 要上传的空间
bucket_name = 'aaaaa'
# 七牛云上传图片
def upload_file(localfile):
try:
m = hashlib.md5() # 括号内也可以传值,类型也要求是bytes类型
key = 'df' + str(int(time.time())) + str(random.randint(10000, 100000))
m.update(key.encode('utf-8'))
# 上传后保存的文件名
key = m.hexdigest() + '.jpg'
# 生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)
ret, info = put_data(token, key, localfile)
if info.status_code == 200:
sign_url = "http://qjbb5wdkg.hb-bkt.clouddn.com/" + key
return sign_url
except Exception as e:
logger.error(e)
# 七牛云删除图片
def delete_file(key):
"""
只需要传名字就行xxx.jpg 不需要传域名路径
"""
try:
# 初始化BucketManager
bucket = BucketManager(q)
# 你要测试的空间, 并且这个key在你空间中存在
# 删除bucket_name 中的文件 key
ret, info = bucket.delete(bucket_name, key)
assert ret == {}
except Exception as e:
logger.error(e)
# 视图函数
def upload_view(request):
sign_pic = request.FILES.get('sign_pic')
im_pic = Image.open(sign_pic)
# im_pic = im_pic.transpose(Image.ROTATE_90) # 引用固定的常量值 旋转90度 非必须
# 创建一个字节流管道
img_bytes = io.BytesIO()
# 将图片数据存入字节流管道, format可以按照具体文件的格式填写
im_pic.save(img_bytes, format="PNG")
# 从字节流管道中获取二进制
image_bytes = img_bytes.getvalue()
url = upload_file(image_bytes)
delete_file(url.split('/')[-1])
21. 盒子阴影
直接在标签里加入css名 <div class="shadow"></div>
/* 盒子阴影 */
.shadow {
position: relative;
max-width: 100%;
box-shadow: 0px 1.5px 3px rgba(0, 0, 0, 0.3),
0px 0px 20px rgba(0, 0, 0, 0.1) inset;
}
.shadow::before,
.shadow::after {
content: "";
position: absolute;
z-index: -1;
}
.shadow::before,
.shadow::after {
content: "";
position: absolute;
z-index: -1;
bottom: 15px;
left: 10px;
width: 50%;
height: 20%;
}
.shadow::before,
.shadow::after {
content: "";
position: absolute;
z-index: -1;
bottom: 15px;
left: 10px;
width: 50%;
height: 20%;
box-shadow: 0 10px 7px rgba(0, 0, 0, 0.7);
transform: rotate(-3deg);
}
.shadow::after {
right: 10px;
left: auto;
transform: rotate(3deg);
}
22. 点击订阅功能
index.wxml
<!-- 订阅开关 -->
<image
class="sub_status"
src="xxxxxx"
mode="widthFix"
bindtap="subContentBtn"
data-itemid="{{item.id}}"
data-userid="{{item.user_id}}"
data-idx="{{idx}}"
data-endtime="{{item.end_time}}"
data-subscribe="{{item.subscribe_status}}"
></image>
index.js
// 点击订阅
subContentBtn(e) {
wx.showModal({
title: "提示",
content: "订阅此条记录?",
showCancel: true,
cancelText: "取消",
cancelColor: "#000000",
confirmText: "确定",
confirmColor: "#3CC51F",
success: (result4) => {
if (result4.confirm) {
wx.requestSubscribeMessage({
tmplIds: ["订阅模版id"],
success: (res) => {
if (res["订阅模版id"] ==="accept") {
// 发送请求
this.sendSub(
url,
{
id: item_id,
subscribe_status: subscribe_status,
},
index
);
}
},
fail(err) {
console.error(err);
},
});
}
},
});
// 请求订阅接口
async sendSub(url, params, index) {
const result = await request({
url: url,
data: params,
});
if (result.code == 0) {
wx.showToast({
title: "操作成功",
icon: "success",
duration: 2000,
mask: true,
success: (result) => {},
});
} else {
wx.showToast({
title: result.message,
});
}
},
23. 保存图片到本地 和 图片预览
//保存到相册
async saveCanvasAsImg() {
try {
// 1 获取 权限状态
const res1 = await getSetting();
const scopewritePhotosAlbum = res1.authSetting["scope.writePhotosAlbum"];
// 2 判断 权限状态
if (scopewritePhotosAlbum === false) {
await openSetting();
}
wx.canvasToTempFilePath({
canvasId: "handWriting",
fileType: "png",
quality: 1, //图片质量
success(res) {
// console.log(res.tempFilePath, 'canvas生成图片地址');
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
wx.showToast({
title: "已保存到相册",
duration: 2000,
});
},
fail: function (res) {
console.log(res);
if (res.errMsg === "saveImageToPhotosAlbum:fail auth deny") {
console.log("打开设置窗口");
}
},
});
},
});
} catch (error) {
console.log(error);
}
},
//预览
previewCanvasImg() {
wx.canvasToTempFilePath({
canvasId: "handWriting",
fileType: "jpg",
quality: 1, //图片质量
success(res) {
// console.log(res.tempFilePath, 'canvas生成图片地址');
wx.previewImage({
urls: [res.tempFilePath], //预览图片 数组
});
},
});
},
24. 长按保存
wxml文件
<image
wx:if="{{item.status!='审核中'}}"
data-url="{{item.sign_url}}"
bindlongpress="saveImage"
bindtap="imagePreview"
class="signUrl"
src="{{item.sign_url}}"
mode="widthFix"
/>
js文件
//单击图片预览
imagePreview: function (e) {
var current = e.target.dataset.url; //这里获取到的是一张本地的图片
wx.previewImage({
current: current, //需要预览的图片链接列表
urls: [current], //当前显示图片的链接
});
},
// 长按保存图片
saveImage(e) {
let url = e.currentTarget.dataset.url;
//用户需要授权
wx.getSetting({
success: (res) => {
if (!res.authSetting["scope.writePhotosAlbum"]) {
wx.authorize({
scope: "scope.writePhotosAlbum",
success: () => {
// 同意授权
this.saveImg1(url);
},
fail: (res) => {
console.log(res);
},
});
} else {
// 已经授权了
this.saveImg1(url);
}
},
fail: (res) => {
console.log(res);
},
});
},
saveImg1(url) {
wx.getImageInfo({
src: url,
success: (res) => {
let path = res.path;
wx.saveImageToPhotosAlbum({
filePath: path,
success: (res) => {
console.log(res);
wx.showToast({
title: "图片保存成功",
icon: "success",
duration: 2000,
mask: true,
});
},
fail: (res) => {
console.log(res);
},
});
},
fail: (res) => {
console.log(res);
},
});
},
25. 获取用户信息
必须指定 open-type="getUserInfo"
wxml文件
<button
class="login_btn"
type="primary"
plain
open-type="getUserInfo"
bindgetuserinfo="handleGetUserInfo"
>
js文件
handleGetUserInfo(e) {
// console.log(e);
const { userInfo } = e.detail;
wx.setStorageSync("user_info", userInfo);
this.setData({
user_info: userInfo,
});
this.authLogin(userInfo.nickName);
},
26. 获取当前页面的route
let pages = getCurrentPages();
let currPage = null;
// console.log(pages) 的到一个数组
if (pages.length) {
// 获取当前页面的对象(上边所获得的数组中最后一项就是当前页面的对象)
currPage = pages[pages.length - 1];
}
// 获取当前页面的路由 /pages/index/index
let route = currPage.route;
27. app.js里添加定时任务 在小程序运行期间一直定时执行
onLaunch: function () {
var that = this;
setInterval(that.showActionHistory, 5000);
},
28. 小程序设置tabBar右上角添加文本/移除文本样式
wx.setTabBarBadge({
index: 2,
text: '设置文本内容',
})
//移除taBar右上角的文本
wx.removeTabBarBadge({
index: 2, //tabBar下标
})
29. setTimeout 里无法调用this.setData问题
使用that或箭头函数
var that = this;
setTimeout(function () {
that.setData({
is_todo_pk_btn_disabled: false,
is_todo_pk_img: false,
is_todo_result: true,
});
}, 8000);
// 或者
setTimeout(() => {
this.setData({
is_todo_pk_btn_disabled: false,
is_todo_pk_img: false,
is_todo_result: true,
});
}, 8000);
30. 如果有用户自定义上传文本或图片的地方 需要进行内容的审核 否则上线不通过
请求微信接口对内容进行审核, 但是文档有点坑
- 返回值是
errmsg
而不是文档写的errMsg
- 不能直接
requests.post(url, data=data)
这样会一直返回ok,要对data进行处理,如下代码可以正常运行
data = {'content': '特3456书yuuo莞6543李zxcz蒜7782法fgnv级'}
requests.post(url, data=json.dumps(data, ensure_ascii=False).encode(), headers={'content-type': 'application/json'})