开通Cms可视化网页管理后台
我们上面源码导入成功,并把云开发环境初始化成功以后,接下来就来开通cms可视化网页后台,现在cms与云环境还是单向的,即cms同步到云上,但云同步不到cms中(导入数据除外),所以数据库我们还是要在cms中初始化好。
如下图两张所示,直接点击开通内容管理(CMS)即可,两个位置点开
添加账号与密码
登陆cms
创建项目,进入xiongshaowen651后
点击内容模型—新建模型,会在云数据库中看到xsw表了。
添加字段举例
注意:在云环境后台的云数据库中添加的记录或修改记录,不会同步到CMS中,但cms中添加记录或修改记录会同步到云环境后台中
##小程序与cms通信
------实际上我们用得最多还是数据库,也就是说,操作cms建立的数据,与直接访问数据库一样,首先到云环境后台设置操作权限。
// pages/testcms/testcms.js
Page({
onLoad:function(options){
wx.cloud.database().collection("news").get()
.then(res=>{
console.log("新闻列表",res)
this.setData({
list:res.data
})
})
}
})
//xxx.wxml
<view wx:for="{{list}}">
<view>{{item.title}}</view>
<image src="{{item.img}}"></image>
</view>
使用cms问题
1.导入内容模型(数据表)
------新升级的小程序开发工具,同时也升级了cms,现在新的cms只可导入5张表,并具不可同步到云环境后台,当然了这是要交钱才可以的,但我不想交钱呀!
[{"fields":[{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isSystem":true,"name":"_createTime","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"},{"displayName":"图片","id":"n1ywhthvr3pywyldiyzjonjzrigsueub","name":"picUrl","order":2,"resourceLinkType":"fileId","type":"Image"}],"collectionName":"lunbotu","displayName":"轮播图","description":"首页顶部轮播图,推荐热门菜品","_id":"b00064a7602371f8040333e766175a2e"},{"fields":[{"displayName":"菜品分类","enumElementType":"string","enumElements":[{"label":"店长推荐","value":"店长推荐"},{"label":"新品推荐","value":"新品推荐"},{"label":"活动推荐","value":"活动推荐"},{"label":"经典套餐","value":"经典套餐"},{"label":"粥品汤类","value":"粥品汤类"},{"label":"面食","value":"面食"},{"label":"酒水饮料","value":"酒水饮料"},{"label":"主食主菜","value":"主食主菜"}],"id":"zt5mivizg4tgdof0pp9q9d5roes6ke55","isRequired":true,"name":"fenlei","order":0,"type":"Enum"},{"displayName":"菜品名","id":"64e29ko0o0ycdduknshj6p5558g2kpa5","name":"name","order":1,"type":"String"},{"displayName":"菜品价格","id":"4iroi681tok4tr34ebwa9w0npnevu3ng","name":"price","order":2,"type":"Number"},{"displayName":"菜品上架状态","enumElementType":"string","enumElements":[{"label":"上架","value":"上架"},{"label":"下架","value":"下架"}],"id":"zr8p3hej61dtertt9d756fsdjt919fnq","name":"status","order":3,"type":"Enum"},{"defaultValue":0,"displayName":"销量","id":"kn2i058g3vsq8ygf8otj7h7u99ep119b","name":"sell","order":4,"type":"Number"},{"displayName":"菜品图片","id":"wmmq6qxra3bjsp2t9239l6zq6v6s0m34","name":"icon","order":5,"resourceLinkType":"fileId","type":"Image"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isSystem":true,"name":"_createTime","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"}],"collectionName":"food","displayName":"菜品","description":"菜品数据","_id":"79550af26023746d03d8630759fc5694"},{"fields":[{"displayName":"用户微信昵称","id":"m8q1vjsz2c959nbgo88y5fyb7fsk6gsk","name":"name","order":0,"type":"String"},{"defaultValue":"0","description":"-1订单取消\n0新下单待上餐\n1待用户评价\n2订单已完成","displayName":"订单状态","enumElementType":"number","enumElements":[{"label":"订单取消","value":-1},{"label":"新下单待上餐","value":0},{"label":"已上餐待用户评价","value":1},{"label":"订单已完成","value":2}],"id":"f8sy1pq0eqkdx6umtvhhpj9s96k5h46z","name":"status","order":1,"type":"Enum"},{"displayName":"地址或桌号","id":"1cjixjfwmtd7eh00hm9hdks5r00o4v0o","name":"address","order":2,"type":"String"},{"displayName":"备注","id":"nf32xgjh2ah23vk9i10gevjme86p46fp","name":"beizhu","order":3,"type":"String"},{"defaultValue":1,"displayName":"就餐人数","id":"ubvhplplc1abw5vbtd81wf6nic15sce5","name":"renshu","order":4,"type":"Number"},{"displayName":"用户购买菜品","id":"0b31f3fqahpa28b0vmg0lqh939yg70e4","name":"orderList","order":5,"type":"Object"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isOrderField":false,"isSystem":true,"name":"_createTime","orderDirection":"desc","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"},{"displayName":"订单总价格","id":"vm2elywssee8ukabunv55xmb40xfm69k","name":"totalPrice","order":8,"type":"String"}],"collectionName":"order","displayName":"订单表","_id":"28ee4e3e6023cca1044f88c54cc7ca97"},{"fields":[{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isSystem":true,"name":"_createTime","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"},{"displayName":"用户名","id":"r224f33upej3jk98qti3rk18joh8lz4n","name":"name","order":2,"type":"String"},{"displayName":"订单号","id":"5sv5nbjy56xlqnlibg3lek2z694ic3sl","name":"orderId","order":3,"type":"String"},{"displayName":"用户头像","id":"uv9rsqgoexrraykjq3v4btbyooszyoco","name":"avatarUrl","order":4,"resourceLinkType":"https","type":"Image"},{"displayName":"评论内容","id":"5y71d4sptzgp0xsayj28lhv263bcvgf1","name":"content","order":5,"type":"MultiLineString"}],"collectionName":"pinglun","displayName":"评论","_id":"b00064a76023d836041d3bf4771dfc1f"},{"fields":[{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isSystem":true,"name":"_createTime","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"},{"displayName":"账号名","id":"t1av0wc8jim7a8ea98p3jju81dh47yn8","name":"name","order":2,"type":"String"},{"displayName":"密码","id":"izpe4yji6md4i5ycwwcm85gzjxzw5bow","name":"password","order":3,"type":"String"}],"collectionName":"admin","displayName":"管理员","description":"小程序端后厨管理订单,管理排号。","_id":"79550af2602889fc04b23efa147abb02"},{"fields":[{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"创建时间","id":"_createTime","isSystem":true,"name":"_createTime","type":"DateTime"},{"dateFormatType":"timestamp-ms","description":"系统字段,请勿随意修改","displayName":"修改时间","id":"_updateTime","isSystem":true,"name":"_updateTime","type":"DateTime"},{"displayName":"小桌排号","id":"03pvviz7ihcx3jb401ynhgmqg5ciff79","name":"xiaozhuo","order":2,"type":"Object"},{"displayName":"大桌排号","id":"rl9pjp41nhg6tmkerzsck38aoiqt1pei","name":"dazhuo","order":3,"type":"Object"},{"defaultValue":0,"displayName":"小桌当前可就餐号码","id":"4hftsjo6ret4og53hw360k8vwd7g0my6","name":"xiaozhuonum","order":4,"type":"Number"},{"defaultValue":0,"displayName":"大桌当前可就餐号码","id":"ij0seo67dn6gbi64w33uds43ndzu8e9k","name":"dazhuonum","order":5,"type":"Number"}],"collectionName":"paihao","displayName":"排号","_id":"79550af260290e9604ccd27f5405ac2b"}]
上面的‘内容模型.json‘是事先建立了6个表后,导出的,我们也写不来。这里我们用一下,但导入6个表(内容模型)不成功。
解决办法:即安装扩展,返回到cms旧版本,具体步骤如下
按步就班,写一下账号和密码,完成后,把访问地址,账号,密码,都记一下,以便下次用。
访问地址:http://xiongshaowen65179334-2b8d7b7a64b-1253188164.tcloudbaseapp.com/wx-cms/
账号:xiongshaowen
密码:cexoit1983
project
效果图:
一, app.json中设置整个项目所有页的导航栏色,和标题,和页面
{
"pages": [
"pages/home/home","pages/food/food",
"pages/food2/food2",
"pages/address/address",
"pages/pay/pay",
"pages/mycomment/mycomment",
"pages/myOrder/myOrder",
"pages/me/me",
"pages/paihao/paihao",
"pages/admin/admin",
"pages/adminHouchu/adminHouchu",
"pages/adminPaihao/adminPaihao"
],
"window": {
"backgroundColor": "#F6F6F6",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "点餐小程序",
"navigationBarBackgroundColor": "#f4c903"
},
"tabBar": {
"color": "#Fc0",
"selectedColor": "#f4c903",
"borderStyle": "white",
"list": [{
"selectedIconPath": "image/tab1-ok.png",
"iconPath": "image/tab1.png",
"pagePath": "pages/home/home",
"text": "首页"
},
{
"selectedIconPath": "image/tab2-ok.png",
"iconPath": "image/tab2.png",
"pagePath": "pages/me/me",
"text": "我的"
}
]
},
"permission": {
"scope.userLocation": {
"desc": "导航需要"
}
},
"sitemapLocation": "sitemap.json"
}
二,app.js,绑定云开发环境
App({
onLaunch() {
wx.cloud.init({
env:'xiongshaowen65179334-2b8d7b7a64b'
})
this.getOpenid();
},
}
三,云函数目录设置,在’project.config.json’的顶部加了如下一行代码,就把云函数所在的目录云化了,文件夹图标上会显示云朵。
{
"cloudfunctionRoot": "cloud/",
....
}
1–home
底部首页,也是我们一打开小程序就显示的页面。
home.json
{
"navigationBarTitleText": "扫码点餐",
"usingComponents": {}
}
这里只需要看一个重点知识点的文档即可,其余的知识点我在基础课里都有做讲解
轮播图片
home.wxml
<!-- 顶部轮播图 -->
<swiper indicator-color="#f4c903" indicator-dots="true" autoplay="true" circular="true">
<block wx:for="{{banner}}" wx:key="banner">
<swiper-item bindtap="goFood">
<image src="{{item.picUrl}}" mode="widthFix" style="width:100%;height:100%;"></image>
</swiper-item>
</block>
</swiper>
home.js
let searchKey = ''
let db = wx.cloud.database()
Page({
data: { //网络不好时,初始化显示的图片
banner: [{
picUrl: '/image/1.jpg'
}, {
picUrl: '/image/2.jpg'
}, {
picUrl: '/image/3.jpg'
}],
foodlist: []
},
onLoad() {
},
/**
* 周期函数,是系统自定义的
*/
onShow() {
this.getTopBanner() //显显轮播图,网络好时,调用数据库表中的图片
this.getHotList() //显示热推荐
},
//获取首页顶部轮播图(从表lunbotu)
getTopBanner() {
wx.cloud.database().collection('lunbotu')
.get()
.then(res => {
if (res.data && res.data.length > 0) {
this.setData({
banner: res.data
})
}
})
.catch(res => {
console.log("获取轮播图列表banner失败", res)
})
},
###搜索功能
- 模糊搜索关键知识点文档:
https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/Database.RegExp.html
核心代码如下
// 数据库正则对象
db.collection('food').where({
description: db.RegExp({ //description写数据表的字段名
regexp: 'miniprogram',
options: 'i',
})
}).get()
.then(res=>{
console.log("成功",res)
})
.catch(res=>{
console.log("失败",res)
})
例:搜索含有’鱼‘的菜品,模糊查询,food表在上面已经由cms导入了完成了。
<!-- 搜索框 -->
<view class="searchRoot">
<input class="searchInput" bindconfirm='goSearch' confirm-type='search' bindinput="getSearchKey" placeholder="搜索菜品" />
<image class="searchImg" bindtap="goSearch" src="/image/sousuo.png"></image>
</view>
<!-- 在搜索框下面显示的,测试用,我们真正的开发中要跳转页面-->
<block wx:for="{{foodlist}}" wx:key="foodlist">
<view>
<text>菜名:{{item.name}}</text>
<image src="{{item.icon}}"></image>
</view>
</block>
//xxx.wxss
/* 搜索框 */
.searchRoot {
background: rgb(255, 255, 255);
padding: 25rpx;
display: flex;
flex-direction: row; /* 设置容器里的按行显示 */
justify-content: space-between; /* */
}
.searchInput {
flex: 2; /* 输入框可以撑满行剩下的空间,值只要大于0即可 */
height: 70rpx;
padding-left: 30rpx;
border: 1px solid #f4c903;
border-radius: 30rpx; /* 方框的四角以图形显示 */
}
.searchImg {
width: 70rpx;
height: 70rpx;
margin: 0 15rpx;
}
//xxxx.js
let searchKey = ''
const db = wx.cloud.database()
Page(
//获取用户输入的搜索词
getSearchKey(e) {
searchKey = e.detail.value
console.log(searchKey)
},
//搜索点击事件触发方法
goSearch() {
console.log("触发了搜", searchKey)
if (searchKey && searchKey.length > 0) {
// 数据库正则对象
db.collection("food").where({
name: db.RegExp({
regexp: searchKey,
options: 'i'
})
}).get()
.then(res=>{
console.log("搜索成功",res)
this.setData({
foodlist:res.data
})
})
.catch(res=>{
console.log("搜索失败",res)
})
}
},
九空格实现菜单选项
<!-- 九宫格 -->
<view class="category_root">
<view bindtap='diancan' class="category_item">
<image class="category_item_image" src="/image/home1.png" />
<text>{{'去点餐'}}</text>
</view>
<view bindtap='goToFood' class="category_item">
<image class="category_item_image" src="/image/home2.png" />
<text>菜单浏览</text>
</view>
<view bindtap='paihao' class="category_item">
<image class="category_item_image" src="/image/home3.png" />
<text>排号等位</text>
</view>
<view bindtap='goToAddress' class="category_item">
<image class="category_item_image" src="/image/home4.png" />
<text>饭店信息</text>
</view>
</view>
/* 九空格区域,即显示四个菜单图标 */
.category_root {
height:90px;
width:100%;
display:flex;
color: #666;
flex-direction: row;
font-size:11px;
justify-content: space-around;
}
.category_item {
display: flex;
flex-direction: column;
text-align: center;
}
.category_item_image {
width:50px;
height: 50px;
padding:15px 12px 0 6px;
}
###热门推荐
即在主页下面显示当前销量最多的5个商品,按降排序显示
<!-- 热销菜品依据销量,列出前5个菜品 -->
<!-- <view wx:if="{{goodList&&goodList.length>0}}"> -->
<view class="hot_tip">
热门推荐
<text class="more" bindtap="goToFood">更多></text>
</view>
<block wx:for="{{goodList}}" wx:key="index">
<view class="good_item" bindtap="goToFood">
<image class="good_img" src="{{item.icon}}" />
<view class="good_right">
<view class="good_title" data-index="{{index}}">{{item.name}}:少文游司边特色展</view>
<view class="good_sell">销量:{{item.sell}} </view>
<view class="good_price" data-index="{{index}}">{{item.price}}</view>
</view>
</view>
</block>
<!-- </view> -->
home.wxss
/* 热门推荐商品 */
.hot_tip {
font-size: 45rpx;
color: #f4c903;
display: flex;
justify-content: space-between;
margin: 35rpx 5rpx 15rpx 30rpx;
}
.more {
font-size: 36rpx;
padding: 10rpx 30rpx;
}
.good_item {
/*列表 */
display: flex;
margin: 5rpx 40rpx;
border-bottom: 1px gainsboro solid;
}
.good_img {
width: 120rpx;
height: 120rpx;
min-width: 120rpx;
margin-left: 10rpx;
margin-right: 30rpx;
}
.good_title {
font-size: 38rpx;
width: 500rpx; /*这里必须设置文字宽度*/
white-space:nowrap; /*文本不换行*/
text-overflow:ellipsis; /*设置超出部分显示...*/
overflow: hidden;
}
.good_sell {
font-size: 30rpx;
color: gray;
}
.good_price {
/*价格*/
font-weight: bold;
font-size: 32rpx;
color: #ff9600;
}
.good_price::before {
/*人民币符号*/
content: "¥";
font-size: 14px;
}
home.js
Page({
//热门推荐
getHotList() {
/*db.collection('food')
.where({
status: '上架' //只查上架的
})
.orderBy('sell', 'desc') //降序排列
.limit(5)
.get()
.then(res => {
console.log('菜品推荐查询成功', res)
})
*/
//云函数getFoodList来获取菜品信息,该函数有三个功能:全部,上架的,下架的
wx.cloud.callFunction({
name:'getFoodList',
data:{
action:'getHot', //通过event.action传到云函数的参数中。
}
}).then(res=>{
console.log('菜品推荐查询成功', res)
this.setData({
goodList:res.result.data
})
})
}
})
注意点
- 列表中的文字过长时,不能换行显示,不然不好看
.good_title {
font-size: 38rpx;
width: 500rpx; /*这里必须设置文字宽度*/
white-space:nowrap; /*文本不换行*/
text-overflow:ellipsis; /*设置超出部分显示...*/
overflow: hidden;
}
- 人民币符号,处理
.good_price::before {
/*人民币符号*/
content: "¥";
font-size: 14px;
}
- 用云函数查询数据库,因为它可以突破20条查询权限,也可节省大量代码,如,查询条件(上架的,全部的,模糊的全可放一起)。
//getFoodList中的index.js代码,调用时,写如:name:'getFoodList'.
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
// 云函数入口函数
exports.main = async (event, context) => {
if (event.action == 'search' && event.searchKey) { //搜索菜品
return await db.collection('food').where({
name: db.RegExp({
regexp: event.searchKey,
options: 'i'
}),
status: '上架'
}).get()
} else if (event.action == 'getHot') { //获取首页推荐位热门商品
return await db.collection('food').where({
status: '上架'
})
.orderBy('sell', 'desc')
.limit(5)
.get()
} else { //获取100条菜品
return await db.collection('food')
.where({
status: '上架'
}).get()
}
}
2–me
底部’我的’页面。
3–address显示地图
<map style="width:100%; height:1000rpx;" longitude="{{longitude}}" latitude="{{latitude}}" scale="17"
markers="{{markers}}" bindmarkertap="navRoad" data-marker="{{markers[0]}}"
show-location />
<view class="phone" bindtap="Call">
地址:南昌市青山湖区江纺宅三区11号
</view>
<view class="phone" bindtap="Call">
电话 13065156391(可点击拨打)
</view>
<view class="phone" bindtap="Copy">
微信:x13065156391(可点击复制)
</view>
Page({
data: {
//店铺经纬度
latitude: 28.719223,
longitude: 115.945917,
//标记点
markers: [{
id: 0,
name: "编程小石头",
address: "南昌市青山湖区江纺宅三区11号",
latitude: 28.719223,
longitude: 115.945917,
width: 50,
height: 50
}]
},
//拨打电话
Call() {
wx.makePhoneCall({
phoneNumber: '13065156391' //仅为示例,这个号码也是石头哥的微信号
})
},
//复制微信
Copy() {
wx.setClipboardData({
data: 'x13065156391',
})
},
//导航
navRoad(event) {
console.log(event)
wx.getLocation({ //获取当前经纬度
type: 'wgs84', //返回可以用于wx.openLocation的经纬度,
success: function (res) {
wx.openLocation({ //使用微信内置地图查看位置。
latitude: event.currentTarget.dataset.marker.latitude, //要去的纬度-地址
longitude: event.currentTarget.dataset.marker.longitude, //要去的经度-地址
name: event.currentTarget.dataset.marker.name,
address: event.currentTarget.dataset.marker.address
})
},
fail: res => {
console.log('授权失败', res)
wx.showModal({
title: '需要授权位置信息',
success: res => {
if (res.confirm) {
wx.openSetting()
}
}
})
}
})
}
})
发布上线小程序,地图遇到的问题
以下接口未正确配置在app.json文件中,勾选协议可继续提交,该版本发布后,用户将无法使用相关接口能力。接口未正确配置:wx.getLocation
处理:app.json中
{
"pages": [
.........
],
"requiredPrivateInfos": [
"getLocation",
"choosePoi",
"chooseAddress"
],
"window": {
........
4—授权登陆与退出开发
4-1.认识wx.getUserProfile方法
对应的文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html
--------获取用户信息。页面产生点击事件(例如 button 上 bindtap 的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回 userInfo对象,该对象中有用户如:呢称(nickName),头像链接(avatarUrl----https://…),地址等。该接口用于替换 wx.getUserInfo(2020年就不用了),详见 用户信息接口调整说明。
//开放接口 /用户信息 /wx.getUserProfile
wx.getUserProfile(Object object)
//------用户头像昵称获取规则已调整,参考 小程序用户头像昵称获取规则调整公告
//基础库 2.10.4 开始支持(本人2024年用2.16),低版本需做兼容处理。
-----2024年该方法又作了调整了,现在开发时可以获取用户信息,但是发布或测试版不可获用户信息了。后续会讲2024最新版授权登陆处理。
授权弹窗
-----一般我的使用上面的wx.getUserProfile方法获取用户信息时,需要用户授权的。一般授权弹窗如下。
只有用户点击允许以后才可以获取用户信息。
不弹起授权弹窗解决方案
-----有的用这个方法时,不会弹起上面的弹窗,有可能是因为基础库版本太低,这里建议升级到最新版的基础库。
例:最简单的授权登陆后,获取用户头像和昵称显示在页面上
<!--xxxx.wxml-->
<button wx:if="{{noLogin}}" bindtap = "login" >授权登陆</button>
<view wx:else class="root">
<image class="touxiang" src="{{avatarUrl}}"></image>
<text class="nicheng">{{nickName}}</text>
</view>
<!--xxxx.wxss-->
.root{
display: flex; /* 弹性布局,默认会利用现有空间布置所有子元素 */
flex-direction: column;
align-items: center;
}
.touxiang{
width: 250rpx;
height: 250rpx;
border-radius: 50%;
}
//xxxx.js
Page({
data:{
nickName:'',
avatarUrl: '',
noLogin: true //没登陆时显示用户信息
},
login(){
wx.getUserProfile({
desc:'必须授权才可以继续用',
success:res=>{
console.log(res)
this.setData({
nickName: res.userInfo.nickName,
avatarUrl: res.userInfo.avatarUrl,
noLogin: false
})
},
fail: res=>{
console.log("授权失败",res)
}
})
}
})
改进一下上面的代码,我们把userInfo封装成一个对象
Page({
data:{
userInfo:''
},
login(){
wx.getUserProfile({
desc:'必须授权才可以继续用',
success:res=>{
console.log(res)
let user = res.userInfo
this.setData({
userInfo: user //这里封装
})
},
fail: res=>{
console.log("授权失败",res)
}
})
}
}),
//退出登陆,就是设置为空,但这登陆退出状态没有保存下来没做处理
loginOut(){
this.setData({
userInfo:''
})
}
<button wx:if="{{!userInfo}}" bindtap = "login" >授权登陆</button>
<view wx:else class="root">
<image class="touxiang" src="{{userInfo.avatarUrl}}"></image>
<text class="nicheng">{{userInfo.nickName}}</text>
<button bindtap="loginOut">退出登陆</button>
</view>
###4-2登陆状态缓存到本地
这里缓存我们主要用到了wx.setStorageSync 对应的官方文档: https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html
-----
onLoad(){ //onLoad()方法,是系统自带的,页面初始化时加载执行之,这里我们是测试获取本地缓存变量
let user =wx.getStorageSync('user')
console.log("本地缓存中的user",user)
},
login(){
wx.getUserProfile({
desc:'必须授权才可以继续用',
success:res=>{
console.log("已授权",res)
let user = res.userInfo
wx.setStorageSync('user', user) //缓存登陆用户信息到本地缓存,下次进入小程序可以读来不用再授权
-------
4-3利用1和2实现登陆退出
----大体思路:
一,初始化页时,从本地缓存中查询是否有用户登陆过的信息,如果有,则处于登陆授权状态
二,如果本地缓存中没有或为空,则显示授权登陆按钮,点击之,授权之,若授权成功,则把用户信息缓存到本地(user)
三,退出时,则清空或置null给本地用户缓存变量。
Page({
data:{
userInfo:''
},
//onLoad()系统自带的初始化接口(api)
onLoad(){
let user =wx.getStorageSync('user')
console.log("本地缓存中的user",user)
this.setData({
userInfo:user
})
},
//登陆授权
login(){
wx.getUserProfile({
desc:'必须授权才可以继续用',
success:res=>{
console.log("已授权",res)
let user = res.userInfo
wx.setStorageSync('user', user) //缓存登陆用户信息到本地缓存,下次进入小程序可以读来不用再授权
this.setData({
userInfo: user
})
},
fail: res=>{
console.log("授权失败",res)
}
})
},
//退出登陆
loginOut(){
this.setData({
userInfo:''
})
wx.setStorageSync('user', null/* '' */)
}
##2024新版登陆获取昵称与头像
-----上面的例子,获取头像与呢称,只要wx.getUserProfile()即可弹出窗口点击获取之。但这种方式在2022年11月就被抛异了。
------最近好多人在学习小程序课程的时候,遇到了下面这样的问题,在小程序授权获取用户头像和昵称时,获取到的是下面这样的。
到底是什么原因导致的呢,去小程序官方文档一看,又是官方改规则了。
点进去一看,原来小程序官方又把获取用户头像的接口回收了。
对应学习链接如下
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html
-----真是我的地盘我做主啊,我说怎么样就怎么样啊。有点店大欺客的嫌疑了。。。 但是呢,作为我们苦命的小程序开发者,官方虐我千百遍,我待官方如初恋。没办法啊,我们还是得用小程序不是吗。。。。
所以我们这里给大家提供几种解决方案。
一开始我们用的是wx.getUserInfo,
再后来官方给我们换成了wx.getUserProfile
而现在获取用户头像和昵称,只能用下面这个方法了。
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}"></image>
</button>
<input type="nickname" bindblur="getNickname" class="weui-input" placeholder="请输入昵称"/>
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
Page({
data: {
avatarUrl: defaultAvatarUrl,
},
onChooseAvatar(e) {
const { avatarUrl } = e.detail
this.setData({
avatarUrl,
})
},
//获取官方昵称
getNickname(e){
console.log("微信呢称:",e.detail.value)
this.setData({
nickName: e.detail.value
})
}
})
------但是官方给的方式有点不太方便使用,那就给大家改造下,供大家使用。学习之前你要去下载一下开发者工具。
使用方法
头像选择
-----需要将 button 组件 open-type 的值设置为 chooseAvatar,当用户选择需要使用的头像之后,可以通过 bindchooseavatar 事件回调获取到头像信息的临时路径。从基础库2.24.4版本起,若用户上传的图片未通过安全监测,不触发bindchooseavatar 事件。
昵称填写
-----需要将 input 组件 type 的值设置为 nickname,当用户在此input进行输入时,键盘上方会展示微信昵称。
----从基础库2.24.4版本起,在onBlur 事件触发时,微信将异步对用户输入的内容进行安全监测,若未通过安全监测,微信将清空用户输入的内容,建议开发者通过 form 中form-type 为submit 的button 组件收集用户输入的内容。
###前期准备
—新版登陆获取用户信息不仅可以获取登陆的版信对应的头像和呢称,还可以自已自定义呢称和头像,所以如果自定义的我们要把数据放到表中,以便随时用之。
1云开发环境
2.云环境表user2,权限设置‘仅创建者可读写,这样的好处是,获取数据时,获取第一个记录即可用,不用循环语句了,要不然读取多条记录,要用大量的语句来判断是某个用户的
Page({
data:{
userInfo:''
},
onLoad() {
let user= wx.getStorageSync("user1")
console.log("local user",user)
if(user){
this.setData({
userInfo: user
})
}
},
//退出登录
tuichu() {
this.setData({
userInfo: null,
})
let user = wx.getStorageSync('user1')
if(user){
wx.cloud.database().collection('user2').doc(user._id).remove()
.then(res=>{
console.log("删除记录成功",res)
})
.catch(res =>{
console.log("删除记录失败",res)
})
}
wx.setStorageSync('user1', null)
},
/**
* 关闭/打开弹框
*/
closeTank(e) {
if (!this.data.userInfo_tank) { //userInfo_tank
wx.cloud.database().collection('user2')
.get()
.then(res => {
console.log("用户信息====", res);
if (res.data.length) {
this.setData({
userInfo: res.data[0],
userInfo_tank: false,
})
wx.setStorageSync('user1', res.data[0])
} else {
console.log("还未注册====", res)
this.setData({
userInfo_tank: true
})
}
}).catch(res => {
console.log('编程小石头提醒你请添加user2表')
})
} else {
this.setData({
userInfo_tank: false
})
}
},
/**
* 获取头像
*/
onChooseAvatar(e) {
console.log(e);
this.setData({
avatarUrl: e.detail.avatarUrl
})
},
/**
* 获取用户昵称
*/
getNickName(e) {
console.log(e);
this.setData({
nickName: e.detail.value
})
},
/**
* 提交
*/
submit(e) {
if (!this.data.avatarUrl) {
return wx.showToast({
title: '请选择头像',
icon: 'error'
})
}
if (!this.data.nickName) {
return wx.showToast({
title: '请输入昵称',
icon: 'error'
})
}
this.setData({
userInfo_tank: false
})
wx.showLoading({
title: '正在注册',
mask: 'true'
})
let tempPath = this.data.avatarUrl
let suffix = /\.[^\.]+$/.exec(tempPath)[0];
console.log(suffix);
//上传到云存储
wx.cloud.uploadFile({
cloudPath: 'userimg/' + new Date().getTime() + suffix, //在云端的文件名称
filePath: tempPath, // 临时文件路径
success: res => {
console.log('上传成功', res)
let fileID = res.fileID
wx.hideLoading()
wx.cloud.database().collection('user2')
.add({
data: {
avatarUrl: fileID,
nickName: this.data.nickName
}
}).then(res => {
let user = {
avatarUrl: fileID,
nickName: this.data.nickName
}
// 注册成功
console.log('注册成功')
this.setData({
userInfo: user,
})
}).catch(res => {
console.log('注册失败', res)
wx.showToast({
icon: 'error',
title: '注册失败',
})
})
},
fail: err => {
wx.hideLoading()
console.log('上传失败', res)
wx.showToast({
icon: 'error',
title: '上传头像错误',
})
}
})
},
})
.header {
width: 100%;
display: flex;
padding: 30rpx 55rpx;
align-items: center;
background: #1ED76D;
}
.userinfo_avatar {
border-radius: 128rpx;
width: 128rpx;
height: 128rpx;
margin-right: 30rpx;
}
.userinfo_nickname {
font-size: 36rpx;
margin-bottom: 5rpx;
}
.tuichu {
font-size: 30rpx;
color: gray;
}
/* 授权弹窗 */
.userInfo_tank_bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.3;
background: #000;
z-index: 666;
}
.userInfo_tank {
position: fixed;
bottom: 0;
background-color: white;
width: 100%;
border-radius: 30rpx 30rpx 0 0;
padding: 30rpx;
box-sizing: border-box;
z-index: 999999;
}
.transfromjoin {
transition: all 0.3s;
margin-bottom: 0;
}
.transfromout {
transition: all 0.3s;
margin-bottom: -700rpx;
}
.avatar_url {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.tank_content {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eee;
height: 100rpx;
}
.tank_content text {
color: #787376;
}
.tank_content input {
width: 80%;
text-align: right;
}
.tank_title {
border-bottom: 1px solid #eee;
padding-bottom: 30rpx;
font-weight: 700;
}
.confirm_button {
display: flex;
margin: 50rpx 0;
box-sizing: border-box;
align-items: center;
justify-content: center;
}
.confirm_button view {
display: flex;
justify-content: center;
align-items: center;
width: 50%;
}
.pos_photo {
position: absolute;
bottom: 0;
right: -10rpx;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%;
width: 40rpx;
height: 40rpx;
display: flex;
justify-content: center;
align-items: center;
}
.pos_photo text {
font-size: 25rpx !important;
color: #fff;
}
.avatar {
position: relative;
}
.confirm_button view button {
width: 90%;
}
.default_button {
width: none !important;
margin: 0 !important;
padding: 10rpx 20rpx !important;
width: 260rpx !important;
font-weight: 600 !important;
font-size: 32rpx !important;
}
.avatar_button {
padding: 0 !important;
margin: 0 !important;
width: 80rpx !important;
height: 80rpx !important;
border-radius: 50% !important;
font-size: 32rpx !important;
overflow: visible !important;
}
<view class="header">
<!-- 未登陆 -->
<image wx:if="{{!userInfo.avatarUrl}}" bindtap="closeTank" class="userinfo_avatar" src="/image/no_login.png"></image>
<view wx:if="{{!userInfo.avatarUrl}}" bindtap="closeTank" class="userinfo_nickname">点击登陆</view>
<!-- 已登陆 -->
<image wx:if="{{userInfo.avatarUrl}}" class="userinfo_avatar" src="{{userInfo.avatarUrl}}"></image>
<view wx:if="{{userInfo.avatarUrl}}" class="header_right">
<view class="userinfo_nickname">{{userInfo.nickName}}</view>
<text class="tuichu" bindtap="tuichu">退出登录</text>
</view>
</view>
<!-- 头像昵称基本信息弹框 -->
<view>
<view class="userInfo_tank_bg" bindtap="closeTank" wx:if="{{userInfo_tank}}"></view>
<view class="userInfo_tank {{userInfo_tank?'transfromjoin':'transfromout'}}">
<view class="tank_title">
<text>申请获取您的头像、昵称</text>
</view>
<view class="tank_content">
<text>头像:</text>
<button class="avatar_button" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar_url" src="{{avatarUrl}}"></image>
<view class="pos_photo">
<text class="iconfont icon-paizhao"></text>
</view>
</button>
</view>
<view class="tank_content">
<text>昵称:</text>
<input form-type='submit' bindblur="getNickName" placeholder="请输入昵称" type="nickname" />
</view>
<view class="confirm_button">
<view>
<button bindtap="closeTank">拒绝</button>
</view>
<view>
<button bindtap="submit" type="primary">允许</button>
</view>
</view>
</view>
</view>