开发文档
https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%8A%80%E6%9C%AF%E5%8F%91%E5%B1%95%E5%8F%B2
安装开发工具
下载地址:
https://dldir1.qq.com/WechatWebDev/release/be1ec64cf6184b0fa64091919793f068/wechat_devtools_1.06.2306020_win32_x64.exe
安装其实就是疯狂下一步,注意,不要安装到中文目录,不要安装到c盘,目录的名字用字母表示,不要写数字,不要空格,不要特殊字符 ,所有的开发软件,大家都要使用管理员权限进行安装
创建小程序
1.注册小程序账号
https://mp.weixin.qq.com/wxopen/waregister?action=step1
个人:身份证,姓名,邮箱,电话 功能有限制,比如没有支付
企业:营业执照,法人身份证
2.登陆小程序账号
https://mp.weixin.qq.com/wxamp/home/guide?lang=zh_CN&token=1069050260
3.找到appid
开发管理—》开发设置–》appid
appid是由使用权限的。如果不是自己申请的appid,就需要别人给你的微信授权才可以。
成员管理–》添加成员–》微信号搜索–》授权
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zn00Ss1h-1690969402111)(1685958376345.png)]
目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmyLh0rs-1690969402112)(1686021397994.png)]
小程序上线
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sSMJlsgK-1690969402114)(1685959468174.png)]
上传–》
版本管理–》
设为体验版本–》
提交审核–》
不通过,修改小程序信息,重新提交审核
通过,提交上线
全局配置
app.json文件格式
{
"pages": [
],
"window": {
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
配置一:pages
pages的值通常是一个字符串数组,内容是页面的路径
pages中的第一个路径,通常作为默认展示页面
"pages": [
"pages/index/index",
"pages/logs/index"
]
配置二:启动页
配置了启动页之后,会显示首页图标,点击首页,会跳转到pages中的第一个路径
"entryPagePath": "pages/index/index",
配置三:window
window = navigationBar + background + page
navigationBar的配置
"navigationBarBackgroundColor": "#abc000",
"navigationBarTitleText": "吉星高照",
"navigationBarTextStyle": "black"
background的配置
"backgroundColor": "#defccc", 下拉刷新的背景色
"backgroundTextStyle": "dark", 下拉刷新三个点的颜色
page配置
"enablePullDownRefresh":true 为所有页面开启下拉刷新
"onReachBottomDistance":50 当页面上拉距离底部50px的时候,会自动触发触底事件
"pageOrientation":"landscape" 屏幕的方向 auto:自动 landscape:横屏 portrait:竖屏
restartStrategy 重新启动策略配置
initialRenderingCache 页面初始渲染缓存配置,支持 static / dynamic
visualEffectInBackground 切入系统后台时,隐藏页面内容,保护用户隐私。支持 hidden/none
handleWebviewPreload 控制预加载下个页面的时机。支持 static / manual / auto
配置四:tabbar
属性 | 类型 | 必填 | 默认值 | 描述 | 最低版本 |
---|---|---|---|---|---|
color | HexColor | 是 | tab 上的文字默认颜色,仅支持十六进制颜色 | ||
selectedColor | HexColor | 是 | tab 上的文字选中时的颜色,仅支持十六进制颜色 | ||
backgroundColor | HexColor | 是 | tab 的背景色,仅支持十六进制颜色 | ||
borderStyle | string | 否 | black | tabbar 上边框的颜色, 仅支持 black / white | |
list | Array | 是 | tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab | ||
position | string | 否 | bottom | tabBar 的位置,仅支持 bottom / top | |
custom | boolean | 否 | false | 自定义 tabBar,见详情 |
配置五: subpackages
分包结构配置 。小程序写完之后,点击上传,就会打包成一个文件,上传到微信服务器,微信帮我们发布,然后用户就可以访问了。
但是微信对于我们打包的文件大小有限制,它限制我们,每个文件的大小最多不超过2M。如果超过,就上传不上去。
那么我们在开发的时候,就要严格注意,不要让我们的代码包的体积太大。那是不是意味着,我们的代码最多只能是2M呢。
不是的。微信说了,我们每个包的体积上限时2M。但是,代码包个数的上限是8个。他说的对。
配置六:插件
在这里配置的插件,属于全局插件,就是任何地方都可以使用的插件
plugins:{
"myPlugin": {
"version": "1.0.0",
"provider": "wxidxxxxxxxxxxxxxxxxappid"
}
}
配置七:注册组件
注册全局组件
usingComponents
配置八:权限配置
permission
下面的代码可以获取授权
wx.getSetting({
success(res) {
if (!res.authSetting['scope.userLocation']) {
wx.authorize({
scope: 'scope.userLocation',
success () {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord()
}
})
}
}
})
页面配置
页面中配置项会覆盖 app.json
的 window
中相同的配置项
{
。。。大部分跟app.js中window里面的配置是一样的(navigation+background+page)
disableScroll:true 设置为 true 则页面整体不能上下滚动。
"usingComponents": {}, 引用组件
"enablePullDownRefresh":true 开启下拉刷新
}
sitmap配置
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/sitemap.html
新建页面
wxml : 组件
组件的详细介绍文档:https://developers.weixin.qq.com/miniprogram/dev/component/
了解一些常用的组件(标签)
div
<view></view>
a navigator
<navigator open-type="switchTab" url="/pages/index/index">去到index</navigator>
img
<image src="图片路径"/>
button button
<button size="" type="primary">内容</button>
input input
<input />
span
<text></text>
textarea
<textarea></textarea>
单选框
<radio/>
复选框
<checkbox/>
下拉框
<picker value="{{1}}" range="{{[1,2,3,4,5]}}">
<button type="primary">请选择</button>
</picker>
wxss : 样式
为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。
与 CSS 相比,WXSS 扩展的特性有:
尺寸单位
-
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
1rpx = 屏幕宽度/750
样式导入
@import "./abc.wxss";
js : 逻辑代码 (数据,方法,生命周期…)
json : 配置(下拉刷新,页面头部)
js:逻辑
通过{{}}实现数据渲染
在js文件的data中定义数据,然后在wxml中可以使用插值表达式,来渲染data中的结果
<text>hello {{username}}</text>
<view>hello {{obj.dog.dname}}</view>
<view>{{1>2}}</view>
<view>{{3>2?"上天":"下海"}}</view>
<view>{{mydate}}</view>
data中数据的定义
Page({
data: {
username:"jack",
obj:{
dog:{
dname:"小包"
}
},
mydate:''
},
})
在js中使用data中的数据,需要通过this.data来访问
比如 this.data.username this.data.obj.dog.dname
但是,在wxml中使用的时候,不用加this.data;
比如{{username}}
data中值得设置,需要通过setData方法才能修改(直接this.data.mydate=xx;,不触发视图更新)
this.setData({
mydate:new Date().toTimeString()
})
通过wx:if实现判断,
<view wx:if="{{age < 16}}">小孩儿</view>
<view wx:elif="{{age < 40}}">大人</view>
<view wx:elif="{{age < 70}}">老头</view>
<view wx:else>古董</view>
通过wx:for实现列表渲染
<view wx:for="{{books}}"
wx:for-item="v"
wx:for-index="ix"
>
编号:{{ix}} 名称:{{v.bname}} 作者:{{v.bauthor}}
</view>
通过v-bind实现属性绑定
<view style="{{data}}"></view>
通过bind或者catch实现事件绑定
https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html
事件的基本使用
绑定事件
<button bindtap="addNum" data-m="2" data-n="3">+++{{num}}</button>
事件编写
addNum(param){
//获取事件的参数
let {n,m} = param.target.dataset;
n = parseInt(n);
m = parseInt(m);
// ++this.data.num
this.setData({
num:this.data.num+n+m
})
}
小程序,将事件进行了分类
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
冒泡事件,有可能从内到外一次执行多个事件
bind+事件名
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
只执行主动触发的那个事件。如果主动触发的元素没有事件,就不执行
catch+事件名
互斥事件: 所有 mut-bind
是“互斥”的,只会有其中一个绑定函数被触发。同时,它完全不影响 bind
和 catch
的绑定效果。
从主动触发的事件开始,如果有就执行,没有就找父元素去执行,父元素没有继续往上…
<view mut-bind:tap="m0">
<view mut-bind:tap="m1">
<view mut-bind:tap="m3"></view>
</view>
</view>
更多事件
类型 | 触发条件 | 最低版本 |
---|---|---|
touchstart | 手指触摸动作开始 | |
touchmove | 手指触摸后移动 | |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
touchend | 手指触摸动作结束 | |
tap | 手指触摸后马上离开 | |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) | |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | |
animationstart | 会在一个 WXSS animation 动画开始时触发 | |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | |
animationend | 会在一个 WXSS animation 动画完成时触发 | |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 | 1.9.90 |
通过v-model实现双向绑定
正确的用法
<input style="border:2px solid #ccc;" model:value="{{mv}}" bindinput="hehe"/>
{{mv}}
注意:
- 只能是一个单一字段的绑定,下面的用法都是错误的
<input model:value="值为 {{value}}" />
<input model:value="{{ a + b }}" />
- 目前,尚不能 data 路径,下面的写法也是错误的
<input model:value="{{ a.b }}" />
生命周期
https://blog.csdn.net/qq_35546787/article/details/107955304
应用的生命周期
主要配置在app.js当中
onLaunch: 初始化小程序时触发,全局只触发一次
onShow: 小程序初始化完成或用户从后台切换到前台显示时触发
onHide: 用户从前台切换到后台隐藏时触发
onError: 小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
页面的生命周期
**生命周期函数–监听页面加载 ** (mounted)
onLoad(options) { },
生命周期函数–监听页面初次渲染完成
onReady() {},
生命周期函数–监听页面显示
onShow() { },
生命周期函数–监听页面隐藏
onHide() { }
生命周期函数–监听页面卸载
onUnload() { }
页面相关事件处理函数–监听用户下拉动作
onPullDownRefresh() { }
页面上拉触底事件的处理函数
onReachBottom() { }
用户点击右上角分享
onShareAppMessage() { }
路由
小程序的跳转,根据页面性质不一样,需要使用不同的跳转方式
跳转到非tabbar页面(open-type=“navigate”)
说明:跳转到 非tabBar 页面,并隐藏当前页面,路径后面可以带参数
<navigator url="/pages/six/six?pname=jack" open-type="navigate">
<button>点我跳转到six页面</button>
</navigator>
获取参数
onLoad({pname}){
console.log("获取到的参数,",pname)
}
改成api调用
wx.navigateTo({
url:""
})
跳转到tabbar页面
注意:跳转到tabbar页面,不支持传参( 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 )
<navigator url="/pages/san/san" open-type="switchTab">
<button>点我跳转到san页面</button>
</navigator>
改成api调用
wx.switchTab({
url: '/pages/two/two',
})
跳转到非tabbar,关闭当前页面,可以传参
open-type="redirect"
改成api调用
wx.redirectTo({
url:""
})
先关闭所有页面,然后打开指定页面,支持传参
open-type="reLaunch"
改成api调用
wx.reLaunch({
url: '/pages/san/san',
})
返回上一个页面
open-type="navigateBack"
改成api调用
wx.navigateBack({
delta:2 //2表示返回两步
})
打开其他小程序
<!-- 打开其他小程序 -->
<navigator open-type="navigate" target="miniProgram" app-id="wx2c348cf579062e56">
来来来,去美团
</navigator>
组件
创建组件
新建一个目录–》右键–》新建Component–>。。。。
注册组件
局部注册:哪个页面需要引用组件,就去对应页面的json文件中引用组件
全局注册:在app.json中引用组件,项目中的所有页面都可以直接引用组件
"usingComponents": {
"z2":"./components/z2/z2"
}
调用组件
<z2></z2>
组件传参
父传子,当父的值发生改变,子的值也会发生改变
父的写法
<z1 zname="{{dzname}}" zage="98"></z1>
子的写法
properties: {
zname: {
type:String,
value:"zname..init...val" //初始值
},
zage:{
type:Number,
value:123
}
},
子传父
子的写法
this.triggerEvent("shijian",{n1:this.data.z2num,n2:Math.random()})
父的写法
<z2 bindshijian="reciveValue"></z2>
reciveValue(v){
console.log("vv,",v)
console.log("v,",v.detail);
this.setData({
vd:v.detail
})
}
单向数据流
数据可以从父流向子,但是不能主动从子流向父
占位组件
当目标组件由于某些原因不能及时加载的时候可以先用占位组件来替代。不影响页面的渲染
{
"usingComponents": {
"comp-a": "../comp/compA",
"comp-b": "../comp/compB",
"comp-c": "../comp/compC"
},
"componentPlaceholder": {
"comp-a": "view", <view></view>
"comp-b": "comp-c" <comp-c></comp-c>
}
}
插槽(slot)
在调用组件的时候,组件的开始和结尾的内容,可以在目标组件中预先定义slot,达到渲染的目的
调用组件
<zujian>内容</zujian>
在zujian内部,定义slot
<view>1</view>
<view>2</view>
<slot/>
<view>3</view>
渲染的结果
1
2
内容
3
生命周期
组件的生命周期
生命周期 | 参数 | 描述 | 最低版本 |
---|---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 | 1.6.3 |
attached | 无 | 在组件实例进入页面节点树时执行 | 1.6.3 |
ready | 无 | 在组件在视图层布局完成后执行 | 1.6.3 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 | 1.6.3 |
detached | 无 | 在组件实例被从页面节点树移除时执行 | 1.6.3 |
error | Object Error | 每当组件方法抛出错误时执行 | 2.4.1 |
组件所在页面的生命周期
还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes
定义段中定义。其中可用的生命周期包括:
生命周期 | 参数 | 描述 | 最低版本 |
---|---|---|---|
show | 无 | 组件所在的页面被展示时执行 | 2.2.3 |
hide | 无 | 组件所在的页面被隐藏时执行 | 2.2.3 |
resize | Object Size | 组件所在的页面尺寸变化时执行 | 2.4.0 |
routeDone | 无 | 组件所在页面路由动画完成时执行 | 2.31.2 |
小程序使用sass
创建项目的时候选择指定模板即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJkTYs07-1690969402117)(1686207548638.png)]
多页面数据共享
自定义tabbar
在小程序的app.json同级目录下,添加一个文件夹
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zMesqrFd-1690969402119)(1686214084029.png)]
在文件件中创建一个index组件(组件的内容就已经接管了tabbar,前提是app.json中tabbar的custom配置为true)
注意,在页面切换,改变选中的样式的时候,需要在对应页面为页面所在tabbar的数据初始化
onShow(){
this.getTabBar && this.getTabBar() &&
this.getTabBar().setData({
currentTab: "个人中心"
})
}
使用iconfont字体图标
1.在网站上下载
https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4040543
2.在小程序app.json所在路径,创建iconfont文件夹
3.将下载的内容复制到iconfont文件夹,并将文件夹里面的iconfont.css改成iconfont.scss
4.检查iconfont.scss文件夹中的路径,进行修改
5.在app.scss中进行引用
6.在项目中使用字体图标
<view class="iconfont icon-home">首页</view>
应用的结构
应用=前台(界面)+后台(负责连接界面和数据库)+数据库(存储数据的仓库)
界面和后台进行数据交互,是通过接口和后端交互的。(ajax,axios)
后端:负责提供接口,接收前端的调用
负责,连接数据库,和数据库进行交互
数据库:管理数据(对外提供增删改查的方法)
云开发数据库指令文档
https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/Command.html
提出需求:用户管理
能够添加用户
能够编辑用户信息
能够删除用户
能够展示用户列表
能够搜索用户
在数据库添加一个用户表,用来保存数据
能够添加用户
1.写了一个form。为添加按钮绑定了一个事件,在事件中得到参数,提交后台,得到响应,提示用户,回馈界面
2.新建一个云函数。在js文件中找到云函数的入口函数,将我们的业务逻辑添加到函数中即可
上传部署云函数。
创建传统云函数
缺点:每个接口,都需要单独创建一个云函数
对云函数适当封装
注意:调用云函数之前,需要先初始化
wx.cloud.init({
env: 'cloud1-4gcfklck9a888aeb', //填上你的云开发环境id
traceUser: true,
})
编写添加用户的云函数
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
});
const db = cloud.database();
exports.main = async (event, context) => {
//删除event里面的type
delete event.type;
delete event.userInfo;
const result = await db.collection('user_230609_sun').add({
// data 字段表示需新增的 JSON 数据
data:event
});
return result._id?{code:200,msg:"新增用户成功"}:{code:500,msg:"新增用户失败"}
};
编写添加用户前端逻辑
submitForm(e){
console.log("e,,",e);
let {value} = e.detail;
//拿到了这些参数,现在需要调用接口传递数据,获取结果,进行提示
wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"addUser",...value},
complete({result}){
if(result.code == 200){
//说明成功
wx.showToast({
icon:"success",
duration:2000,
title:result.msg,
complete(){
setTimeout(()=>{
wx.switchTab({
url: '/pages/index/index'
})
},2000)
}
})
}else{
wx.showToast({
icon:"error",
duration:2000,
title:result.msg
})
}
}
})
},
能够展示用户列表
展示页面加载的时候,请求云函数,获取用户列表,渲染数据
data: {
userlist:[]
},
async onShow(){
let data = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"listUser"}
});
this.setData({
userlist:data.result.data.data
})
}
在wxml中渲染userlist中的数据即可
云函数编写(接收请求,连接数据库,获取数据,返回结果)
exports.main = async (event, context) => {
let result = await db.collection("user_230609_sun").get();
return {code:200,msg:"",data:result};
}
能够删除用户
前端页面:提供删除按钮,发起请求,传参(用户标识id),后端返回成功或者失败,处理响应结果
页面按钮
<view class="v-col"><button size="mini" bindtap="delUser" data-index="{{index}}" data-id="{{item._id}}">删除</button></view>
逻辑代码
//删除用户的方法
async delUser(params){
let {id,index} = params.currentTarget.dataset;
console.log(index)
//调用接口传参,处理结果
const data = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"deleteUser",id}
})
// {code:200,msg:"删除用户成功"} {code:500,msg:"删除用户失败"}
if(data && data.result && data.result.code == 200){
this.data.userlist.splice(index,1);
this.setData({
userlist:this.data.userlist
})
wx.showToast({
title: '删除用户成功',
icon:"success",
duration:1000
})
} else{
wx.showToast({
title: '删除用户失败',
icon:"fail",
duration:1000
})
}
},
云函数(接收用户标识,从数据库删除对应记录,返回结果)
exports.main = async (event, context) => {
//得到event里面传过来的id值
let {id} = event;
const result = await db.collection('user_230609_sun').where({_id:id}).remove();
// return result._id?{code:200,msg:"新增用户成功"}:{code:500,msg:"新增用户失败"}
return (result && result.stats && result.stats.removed > 0) ? {code:200,msg:"删除用户成功"} : {code:500,msg:"删除用户失败"}
};
能够搜索用户
前端页面(写个框,加个按钮)
前端逻辑
点击搜索按钮,调用云函数,传参(搜索的关键字),得到结果(列表),渲染列表到界面
//搜索的方法
async searchFun(){
console.log("搜索的值,",this.data.searchValue)
//{xx:xx,result:{真实结果}}
const data = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"searchUser",value:this.data.searchValue}
})
//{code:200,msg:"zcheng",data:[]}
console.log("result,",data);
if(data && data.result && data.result.code == 200){
this.setData({
userlist:data.result.data
})
}else{
//没有查到数据或者请求失败
}
},
云函数(接收调用,接收参数关键字,根据关键字从数据库匹配结果,返回结果)
exports.main = async (event, context) => {
let {value} = event;
let query = db.collection("user_230609_sun");
console.log("value,",value);
if(value){
query = query.where({
name: db.RegExp({
regexp: value,
options: 'i', // 忽略大小写
})
})
}
let result = await query.get();
return result && result.data ? {code:200,msg:"",data:result.data} : {code:500,msg:"搜索失败"};
};
能够编辑用户信息
页面(提供编辑按钮,点击编辑按钮,打开一个页面,显示对应信息,然后修改提交)
<button size="mini" data-row="{{item}}" bindtap="editUser">编辑</button>
js中编写编辑按钮点击事件
//点击编辑
editUser(data){
let {row} = data.currentTarget.dataset;
this.setData({editRow:row})
this.setData({
...row
})
//弹个窗
this.changeWindow({currentTarget:{dataset:{value:true}}})
},
点击模态框中的提交按钮
<form catchsubmit="submitForm">
<button formType="submit" style="width:97%;">提交</button>
</form>
在js中编写提交按钮事件
//提交编辑内容的方法
async submitForm(data){
data = {...data.detail.value,_id:this.data._id}
console.log("数据,",data)
//将数据发给后台,由后台到数据库更新,返回结果给我即可
const rs = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"editUser",data}
})
if(rs && rs.result && rs.result.code == 200){
this.setData({isShow:false})
//修改数据 userlist
//修改userlist里边的哪一条数据
//直接改item
//直接改item有没有用
console.log("itemx2,",this.data.editRow);
this.data.userlist.forEach(item=>{
if(item._id == this.data.editRow._id){
Object.assign(item,data);
}
})
this.setData({userlist:this.data.userlist})
}
},
完善后端接口(云函数)
exports.main = async (event, context) => {
//得到event里面传过来的id值
let {data} = event;
const _id = data._id;
delete data._id;
const result = await db.collection('user_230609_sun')
.doc(_id)
.set({data})
return (result && result.stats && result.stats.updated > 0)
? {code:200,msg:"更新成功"}
: {code:500,msg:"更新失败"};
// return result._id?{code:200,msg:"新增用户成功"}:{code:500,msg:"新增用户失败"}
// return (result && result.stats && result.stats.removed > 0) ? {code:200,msg:"删除用户成功"} : {code:500,msg:"删除用户失败"}
};
小程序登陆
1.点击登陆
2.小程序客户端发送请求给微信,得到code
3.小程序客户端,将code交给后台
4.后台拿着code,找微信,获取openID,保存openID,生成token返回
5.小程序保存token,拿着token,到后台获取对应数据
code:微信生成的临时登陆凭证,code只能用一次,5分钟有效期
openID:指定微信在指定appID下的唯一标识
点击登陆按钮
myLogin(){
//调用微信的sdk来请求code
wx.login({
success: async (res) => {
console.log("code,",res);
//得到code之后,交给后台
const data = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"logUser",code:res.code}
})
const token = data && data.result && data.result.token;
wx.setStorageSync('token', token);
//跳转到首页
wx.switchTab({
url: '/pages/index/index'
})
},
})
}
云函数代码
let str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$@#$#%$^&*(){}|-=_+.,/<>";
str = [...str];
//随机生成一个字符串,作为token
const getTokenStr = (len)=>{
let rs = [];
for(let i=0;i<=len;i++){
let r = parseInt(Math.random()*str.length);
rs.push(str[r]);
}
return rs.join('');
}
exports.main = async (event, context) => {
//根据code,appid,appsecret访问微信api,得到openID。
//GET https://api.weixin.qq.com/sns/jscode2session
//因为咱们是云开发,不用去请求了
const {openId} = event.userInfo;
//查看数据库里面有没有这个人,如果没有,就保存进去
const _ = db.command;//表示指令
const rs = await db.collection("user_230609_sun").where({"openId":_.eq(openId)}).get()
//生成token,返回
let token = '';
//就需要新增一个用户
rs && rs.data && rs.data.length == 0 && await db.collection("user_230609_sun").add({data:{openId}})
token = getTokenStr(64)
//就要修改对应用户的token(哪个用户啊,根据openID来标识)
await db.collection("user_230609_sun")
.where({
openId:_.eq(openId)
})
//正式情况下,不可能只有openID一个值,可能还有很多其他的值
.update({data:{token}})
//将token和openID关联起来,并且要保证,其他请求,也能访问到这个数据
//将token保存到数据库里面去
return {code:200,msg:"假设登陆成功",token};
};
登陆完成,获取用户昵称头像
整体思路
没登录之前,那个小窗那里,应该显示一个登陆按钮
登陆之后,那个小窗那里,应该显示一个完善个人信息按钮
登陆之后,并且已经完善个人信息的情况下下,那个小窗那里,应该显示头像和昵称
小窗代码
<view wx:if="{{isLogin}}">
<button wx:if="{{app.globalData.userInfo.nickName}}" size="mini" bindtap="myLogin" style="background-color: #ccc;line-height: 100rpx; color:blue;">{{app.globalData.userInfo.nickName}}显示个人信息。。</button>
<view wx:else>
<!-- 说明还没完善个人信息 -->
<navigator url="/pages/userInfo/userInfo"><button>完善个人信息</button></navigator>
</view>
</view>
<view wx:else>
<button size="mini" bindtap="myLogin" style="background-color: #ccc;line-height: 100rpx; color:blue;">登陆</button>
</view>
完善个人信息页面
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}"></image>
</button>
<!-- 建议开发者通过 form 中 为submit 的button 组件收集用户输入的内容。 -->
<form bindsubmit="suibian">
<input type="nickname" name="nickName" bindinput="bip" class="weui-input" placeholder="请输入昵称"/>
<button form-type="submit">提交按钮</button>
</form>
<view style="height:100px;width:100%;"></view>
完善个人信息页面,对应事件
data: {
avatarUrl:''
},
async onChooseAvatar(e) {
const { avatarUrl } = e.detail
//this.setData是同步还是异步
this.setData({
avatarUrl
})
//还要设置到后台去。。
},
async suibian(data){
let {value} = data.detail;
value.avator = this.data.avatarUrl;
console.log("suibian,",value)
//调用云函数,将数据传过去,然后看心情是否弹窗,直接返回之前的页面即可
const token = wx.getStorageSync('token');
let rs = await wx.cloud.callFunction({
name:"userSunFunctions",
data:{type:"addUserInfo",value,token}
})
if(rs.result.code == 200){
//存储到本地
_app.globalData.userInfo=value
wx.navigateBack();
}else{
wx.showToast({
title: '更新信息失败,请重新提交',
icon:"error"
})
}
修改头像昵称的云函数
exports.main = async (event, context) => {
//拿到token,根据token,拿到openID
let {token,value} = event;
let rs = await db.collection("user_230609_sun").where({token}).get();
if(rs && rs.data && rs.data.length>0){
//说明找到了,说明对应token存在,我们就要根据这个token,我们就可以从data中获取到openID
let openId = rs.data[0].openId;
//得到openId之后,根据openId更新信息
await db.collection("user_230609_sun").where({openId}).update({data:value})
return {code:200,data:rs,msg:"更新信息成功"};
}
return {code:500,msg:"更新信息失败"};
};
修改登陆逻辑
应该登陆成功之后,云函数,主动返回当前登陆用户的个人信息。小程序得到个人信息之后,同步到globalData里面的userInfo里面去。
获取手机号
必须企业appid才可以获取手机号
获取手机号,需要有三个步骤
第一步:写一个按钮
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"
class="uploader-text">快速填写本机号码</button>
第二步:写一个函数
getPhoneNumber(e){
let cloudID = e.detail.cloudID //开放数据ID
if (!cloudID) {
app.showToast('用户未授权')
return
}
// 调用云函数获取手机号
wx.cloud.callFunction({
name: 'getPhoneNumber',
data: {
weRunData: wx.cloud.CloudID(e.detail.cloudID),
}
})
.then( res => {
console.log('手机号', res)
this.setData({
tel:res.result
})
})
.catch( err => {
console.log('手机号err', err)
})
},
第三步:写一个云函数
exports.main = async (event, context) => {
var moblie = event.weRunData.data.phoneNumber;
return moblie
}
api
应用程序接口:application programing interface
一个应用程序有好多接口
一个应用程序有好多api
一个api有三大要素(请求路径+请求参数+返回值)
怎么调用api呢
调用api之前,需要搞清楚api的三要素
new XmlHttpRequest()
有这么一个场景,比如微信支付。大家开发应用,想要接入微信支付,就需要调用很多微信的api。那么,微信就需要开放这些api的资料。比如路径,参数等等。但是,这样一来,开发者就需要了解很多接口。而且别人接口大量开放,相对来说,也不安全。
所以,别人就想了个好办法,那就是生成sdk给用户进行调用。用于不需要直接调用api,也不需要了解api接口,直接通过sdk就可以调用了。
sdk
SDK是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等创建应用软件的开发工具的集合
表单的校验
对需要校验的输入的内容,进行校验。只有输入的内容都通过了校验,才能提交表单。如果没有通过校验,对应的地方就要报错提示。并且,阻止提交。这就是校验的目的。
一般校验逻辑,先得到输入的内容,然后对输入的内容进行判断,返回通过或者不通过的结果。over,
下载插件,放到utils目录下
- WxValidate.js
在需要使用校验的文件里,引入js文件
import WxValidate from '../../utils/WxValidate.js'
在form表单中添加name属性
<form catchsubmit="submitForm">
名字
<input name="name" data-tag="name" bindblur="checkVal"/>
年龄
<input name="age" data-tag="age" bindblur="checkVal"/>
<button formType="submit" style="width:97%;">提交</button>
</form>
设置校验规则
Page({
data:{
WxValidate:''
}
initValidate(){
const rules = {
name:{required:true,minlength:2},
age:{required:true,},
}
const messages = {
name:{ required:"名字不能为空", minlength:"长度不能小于2"}
age:{ required:"年龄不能为空"}
}
this.WxValidate = new WxValidate(rules,messages);
},
})
定义弹窗方法
page({
showModal(error){
wx.showModal({
content:error.msg,
showCancel:false
})
}
})
页面加载的时候,需要调用我们自己定义的校验方法
page({
onLoad() {
this.initValidate();
}
})
接下来就可以调用WxValidate对象完成校验了
我们还封装了一个校验方法
checkForm(params){
//定义一个数据
let mData = {
name:'hello',
age:18,
sex:'男',
job:'巫师',
tel:'118',
avator:"haha"
}
//params {sex:'男'}
Object.assign(mData,params)
//先校验
if(!this.WxValidate.checkForm(mData)){
const error = this.WxValidate.errorList[0];
this.showModal(error);
return false;
}
return true;
},
失去焦点的时候,调用封装好的校验方法
checkVal(data){
let params = {[data.target.dataset.tag]:data.detail.value};
this.checkForm(params);
},
点击提交的时候,调用封装好的校验方法
submitForm(e){
const params = e.detail.value; //{name:xx,age:xx,sex:xx}
let xxs = this.checkForm(params);
if(!xxs){
return false;
}
....此处省略一万字。。。
}
用户名不能重复
思路:得到录入的值,发给接口,接口返回重复或者不重复。然后按照结果提示用户即可。
实现:修改了失去焦点事件的方法,拦截name框的事件,增加请求,处理结果
async checkVal(data){
let params = {[data.target.dataset.tag]:data.detail.value};
let rs = this.checkForm(params);
//如果rs是true,说明校验通过,(不为空,长度不少于2位等)
if(rs){
//看一下params,是不是{name:xx}
let r = Object.keys(params).includes("name");
if(!r)return;
//如果是name,就要找数据库匹配去
let r2 = await wx.cloud.callFunction({
name:"userSunFunctions", //params {name:"xx"}
data:{type:"checkUsername",...params}
})
if((r2 && r2.result) && r2.result.code == 500){
console.log("提示重复")
this.showModal({msg:"用户名已存在,请重新输入"});
this.setData({name:null})
}
}
},
对应云函数代码
let checkUsername = async (event, context) => {
//得到上传上来的用户名
let {name} = event;
//上数据库匹配
let rs = await db.collection("user_230609_sun").where({name}).get();
// if(rs)return rs;
if(rs && rs.data && rs.data.length>0){
return {code:500,msg:"用户名已存在"}
}
return {code:200,msg:"用户名可用"};
};
文件上传
前端:需要提供一个选择框,用来选择上传的内容,然后再提供一个上传按钮,点击调用后台接口,并携带文件内容,然后处理上传的结果,over。
后端:接收调用,拿到传过来的参数(文件数据),
将数据,写入到本地文件,将本地文件对应的访问路径,保存到数据库。
将数据,发送到文件服务器,文件服务器保存文件,返回访问路径,保存到数据库
数据库:在用户那里,添加头像信息
调用代码
Page({
data:{
avatorUrl:''
},
updateAvator(){
wx.chooseImage({
count:1,
success:(data)=>{
//选择的文件,已经在包含data里面了。
//上传。。发给后台,后台解决路径问题即可
//上传。。发给云函数,云函数直接通过云存储,保存到本地,得到访问路径。
// 前端小程序 过云存储
let filename = this.getTokenStr(10);
let tempPath = data.tempFilePaths[0];
let ext = tempPath.substr(tempPath.lastIndexOf("."));
filename += ext;
wx.cloud.uploadFile({
cloudPath: filename, // 上传至云端的路径
filePath: tempPath, // 小程序临时文件路径
success: res => {
// 返回文件 ID
let {fileID} = res;
//fileID其实就是我们的用户头像啊
this.setData({
avatorUrl:fileID
})
},
fail: console.error
})
}
});
},
})
文件上传,添加加载中动画
什么时候展示加载中的动画,(点击上传的时候)
wx.showLoading({
title: '正在上传'
})
什么时候停止加载中动画(拿到返回的图片路径的时候)
wx.hideLoading()
管理员审核
openID可以唯一标识一个用户
写3个tabbar,判断当前用户的角色,普通用户,显示2个,其他显示3个。
问题:怎么知道当前用户的角色
随便调用一个云函数,将token传过去。后端通过token,得到openId,根据openId,返回对应角色
问题:怎么分配角色
填写微信号,上数据库找这个微信号,(根据微信号找到openId),然后修改那条记录的权限为管理员就行了
登陆功
eventBus通讯
第一步,新建一个utils/eventBus.js文件
const eventBus = {
// 存储所有事件和对应的订阅者
eventList: new Map(),
// 订阅事件
on(event, callback) {
if (!this.eventList.has(event)) {
this.eventList.set(event, new Set())
}
this.eventList.get(event).add(callback)
},
// 取消订阅事件
off(event, callback) {
if (this.eventList.has(event)) {
const callbacks = this.eventList.get(event)
callbacks.delete(callback)
if (callbacks.size === 0) {
this.eventList.delete(event)
}
}
},
// 发布事件
emit(event, ...args) {
if (this.eventList.has(event)) {
const callbacks = this.eventList.get(event)
callbacks.forEach((callback) => {
callback.call(null, ...args)
})
}
}
}
module.exports = eventBus
第二步:在app.js中引入eventBus
const eventBus =require( './utils/eventBus')
App({
eventBus
})
第三步:发消息的地方这样写
getApp().eventBus.emit("手机号","发送的数据")
第四步:收消息的地方这样写
getApp();.eventBus.on("手机号",data=>{console.log("收到的数据,",data)})
动态tabbar
效果:点击登陆,根据不同的role,动态渲染不同的tabbar
点击登陆,登陆成功,通过eventBus发送role的值
getApp().eventBus.emit('rightChange', data.result.data.role)
在custom-tab-bar的js文件中,使用eventBus监听,动态改变tabbar的值
attached(){
app.eventBus.on("rightChange",data=>{
let temp = [...this.data.list1];
if(data)temp = [...this.data.list1,...this.data.list2];
this.setData({
listData:temp
})
})
}
注意:这里有一个问题,点击管理中心之后,tabbar重新变成了两个。原因是因为,点击管理中心,然后显示管理中心页面,会创建一个新的自定义tabbar组件,新的自定义tabbar组件的数据又变成了两个。
解决方案:在自定义的tabbar组件中,的attached事件里面,获取role信息,根据role信息,动态为tabbar赋值。
attached(){
//每创建一个新的tabbar,我们就判断role的值
//如果role没有值,新的tabbar里面的list的值就是两个
//如果role有值,新的tabbar里面的list的值就是三个
let userInfo = app.globalData.userInfo;
let temp = [...this.data.list1];
if(userInfo && userInfo.role){
temp = [...this.data.list1,...this.data.list2];
}
this.setData({
listData:temp
})
}
角色不同,管理界面不同
登陆成功,通过eventBus
完全可以根据role的值来渲染
设置管理员
设置管理员按钮
点击设置管理员按钮,跳转到一个新的页面
新的页面,展示所有的管理员
新增
名字 | 微信号 | 角色 | 操作 |
---|---|---|---|
小王 | WgKps | 管理员 | 删除按钮 |
点击新增按钮,弹个窗,窗里面大概长这样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2RW0N1SJ-1690969402132)(1686904504798.png)]
审核
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hGFD1aiW-1690969402133)(1686905509705.png)]
entList.get(event)
callbacks.delete(callback)
if (callbacks.size === 0) {
this.eventList.delete(event)
}
}
},
// 发布事件
emit(event, …args) {
if (this.eventList.has(event)) {
const callbacks = this.eventList.get(event)
callbacks.forEach((callback) => {
callback.call(null, …args)
})
}
}
}
module.exports = eventBus
第二步:在app.js中引入eventBus
const eventBus =require( ‘./utils/eventBus’)
App({
eventBus
})
第三步:发消息的地方这样写
getApp().eventBus.emit(“手机号”,“发送的数据”)
第四步:收消息的地方这样写
getApp();.eventBus.on(“手机号”,data=>{console.log(“收到的数据,”,data)})
## 动态tabbar
效果:点击登陆,根据不同的role,动态渲染不同的tabbar
点击登陆,登陆成功,通过eventBus发送role的值
getApp().eventBus.emit(‘rightChange’, data.result.data.role)
在custom-tab-bar的js文件中,使用eventBus监听,动态改变tabbar的值
attached(){
app.eventBus.on(“rightChange”,data=>{
let temp = […this.data.list1];
if(data)temp = […this.data.list1,…this.data.list2];
this.setData({
listData:temp
})
})
}
注意:这里有一个问题,点击管理中心之后,tabbar重新变成了两个。原因是因为,点击管理中心,然后显示管理中心页面,会创建一个新的自定义tabbar组件,新的自定义tabbar组件的数据又变成了两个。
解决方案:在自定义的tabbar组件中,的attached事件里面,获取role信息,根据role信息,动态为tabbar赋值。
attached(){
//每创建一个新的tabbar,我们就判断role的值
//如果role没有值,新的tabbar里面的list的值就是两个
//如果role有值,新的tabbar里面的list的值就是三个
let userInfo = app.globalData.userInfo;
let temp = […this.data.list1];
if(userInfo && userInfo.role){
temp = […this.data.list1,…this.data.list2];
}
this.setData({
listData:temp
})
}
## 角色不同,管理界面不同
登陆成功,通过eventBus
完全可以根据role的值来渲染
## 设置管理员
设置管理员按钮
点击设置管理员按钮,跳转到一个新的页面
新的页面,展示所有的管理员
***新增***
| 名字 | 微信号 | 角色 | 操作 |
| ---- | ------ | ------ | ------------ |
| 小王 | WgKps | 管理员 | **删除按钮** |
点击新增按钮,弹个窗,窗里面大概长这样
![在这里插入图片描述](https://img-blog.csdnimg.cn/ee99f8cd08524d42998e7293fa6c04db.png#pic_center)
## 审核
![在这里插入图片描述](https://img-blog.csdnimg.cn/179f528c9fdc4ab3af83fae3e2160b5f.png#pic_center)