微信小程序+Django+DRF个人笔记
跳转
对标签绑定点击事件
<view class="item" bindtap="onclick" data-nid="123">
其中bindtap为点击事件,onclick为点击后js执行的函数,使用data-参数名 来传参;
js响应函数
onclick:function(e){
var nid=e.currentTarget.dataset.nid;
console.log(nid);
}
其中e为一个对象,可以通过下层的currentTarget下的dataset就可以找到需要传递的参数
e的层次代码图如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTWR4Jkl-1630375528307)(./代码截图/e事件.png)]
页面跳转
只需要在响应函数内添加如下代码,另外需要注意跳转页面不能是设置过tabbar的页面:
wx.navigateTo({
url: '/pages/redired/redired',
})
如果需要用到拼接的方式获取页面路径则可以使用如下方式:
wx.navigateTo({
url: '/pages/redired/redired?id='+nid,
})
页面跳转可以接受参数,在跳转到的页面中的onload函数中接受
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log(options);
},
通过标签跳转
<navigator url="/pages/redired/redired?id=666">跳转到新页面</navigator>
#数据绑定
前端中vue.js的绑定格式
<div id="app">
<p> {{ counter }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
counter: 0
}
})
</script>
微信的数据绑定和前端一样,如下面例子
<view>数据:{{message}}</view>
/**
* 页面的初始数据
*/
data: {
message:"这里是数据双向绑定"
},
通过函数获取data中的message,this指当前的page
ChangeData:function(){
console.log(this.data.message);
},
修改数据:使用setData函数
ChangeData:function(){
//获取数据
console.log(this.data.message);
//修改数据(错误,这里改变后端的值)
//this.data.message="这里是message"
this.setData({message:"修改成功数据"})
console.log(this.data.message);
},
获取用户信息
如果只是展示用户头像昵称,可以使用 组件
<open-data type="userAvatarUrl"></open-data>
<open-data type="userNickName"></open-data>
需要使用 button 来授权登录
<button wx:if="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button>
<view wx:else>请升级微信版本</view>
Page({
data: {
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
onLoad: function() {
// 查看是否授权
wx.getSetting({
success (res){
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称
wx.getUserInfo({
success: function(res) {
console.log(res.userInfo)
}
})
}
}
})
},
bindGetUserInfo (e) {
console.log(e.detail.userInfo)//获取具体信息使用字典['key']
}
})
获取定位信息
这里的 that=this 是将page引入到该函数中,不然下面的函数中的this不是指向page。
getLocalPath:function(){
var that=this
wx.chooseLocation({
success:function(res){
console.log(res.address)
that.setData({localpath:res.address})
}
})
},
for指令
####循环的是列表
<text>商品列表</text>
<view>
<view wx:for="{{datalist}}">{{index}}-{{item}}</view>
</view>
data: {
datalist:['袜子','鞋子','衣服','华为','小米']
},
其中item和index是死的,不能乱改,但是可以采用下面的形式来修改
<text>商品列表</text>
<view>
<view wx:for="{{datalist}}" wx:for-index="idx" wx:for-item="x">{{idx}}-{{x}}</view>
</view>
####循环的是字典
<view>用户信息(循环字典)</view>
<view>
<view>{{userinfo.name}}</view>
<view>{{userinfo.age}}</view>
</view>
<view wx:for="{{userinfo}}">{{index}}-{{item}}</view>
data: {
datalist:['袜子','鞋子','衣服','华为','小米'],
userinfo:{
name:'小青',
age:'19'
}
},
上传图片
<text bindtap="upLoadImage">请上传头像</text>
<view>
<image wx:for="{{piclist}}" src="{{item}}"></image>
</view>
data: {
piclist:['../image/head.jpg','../image/算法图.jpg']
},
upLoadImage:function(){
var that=this
wx.chooseImage({
count:9,//最多选择9张图片
sizeType:['original', 'compressed'],//原图;压缩图
sourceType:['album', 'camera'],//选择相册和相机
success:function(res){//成功后
console.log(res)
that.setData({piclist: res.tempFilePaths})
},
fail:function(res){//失败后
},
complete:function(res){//无论成功失败都执行
}
});
},
上传图片追加的方式
success:function(res){//成功后
var newlist=that.data.piclist.concat(res.tempFilePaths);//concat将两个列表合并,但是不改变原来列表的值,他们重新复制给新的列表
that.setData({piclist:newlist})
},
数据绑定(双向绑定)
input的双向绑定
<text>你输入了:{{message}}</text>
<input value="{{message}}" bindinput="bindText"></input>
data: {
message:"你好"
},
bindText:function(e){
console.log(e.detail.value)
this.setData({message:e.detail.value})
},
登录(前后端)
<text>手机号</text>
<input value="{{phone}}" bindinput="bindText" placeholder="请输入手机号"></input>
<text>验证码: <text>点击获取验证码</text></text>
<input value="{{code}}" bindinput="bindCode" placeholder="请输入验证码"></input>
<button bindtap="login">登录</button>
js请求部分
data: {
phone:"",
code:'',
},
/*获取并且设置输入的phone*/
bindText:function(e){
console.log(e.detail.value)
this.setData({phone:e.detail.value})
},
/**获取并且设置code */
bindCode:function(e){
console.log(e.detail.value)
this.setData({code:e.detail.value})
},
/**登录 */
login:function(){
console.log(this.data.phone,this.data.code)
/**将手机号和验证码发送到后端,后端进行登录 */
wx.request({
url: 'http://127.0.0.1:8000/app01/login/',
data: {phone:this.data.phone,code:this.data.code},
method: 'POST',
success: (result) => {console.log(result);},
})
},
在使用 wx.request等网络请求的时候,需要遵循:
- 网络地址为https
- 后台必须设置需要访问的域名
后端api设置
- 新建django项目
- 路由分发urls
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/',include('app01.urls'))
]
- 新建app01
- cbv实现简单返回
from django.shortcuts import render,HttpResponse,redirect
from rest_framework.views import APIView
from rest_framework.response import Response
class LoginView(APIView):
def post(self,request,*args,**kwargs):
print(request.data)
return Response({'status':True})
def get(self,request):
print(request.data)
return Response({"status":201})
- 在app01新建urls.py
from django.contrib import admin
from django.urls import path,include
from app01 import views
urlpatterns = [
path('login/',views.LoginView.as_view())
]
- 实验连接
手机号格式验证
使用toast和re
if(this.data.phone.length!=11){
wx.showToast({
title: '手机号长度错误',
icon:"none"/*loading success none error 支持插图片*/
})
return;
}
//正则匹配手机格式
var reg=/^(1[3|4|7|8|9]\d(9)$)/;
if( !reg.test(this.data.phone)){
wx.showToast({
title: '手机号格式错误',
icon:"none"
})
return;
}
验证手机号
正则 序列化 cbv redis 腾讯云短信验证
from django.shortcuts import render,HttpResponse,redirect
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
import re
def phone_validator(value):
if not re.match(r'^1[3|4|5|7|8][0-9]{9}$',value):
raise ValidationError('手机格式错误')
class MessageSerializer(serializers.Serializer):
phone=serializers.CharField(label="手机号",validators=[phone_validator,])
class MessageView(APIView):
def get(self,request,*args,**kwargs):
#1.获取手机号
#2.手机格式校验 序列化
ser=MessageSerializer(data=request.query_params)
if not ser.is_valid():
return Response({"status":False,'message':'手机号格式错误'})
phone=ser.validated_data.get('phone')
#3.生成随机验证码
import random
random_code=random.randint(1000,9999)
#4.发送到手机上,购买服务器发送短信,阿里云/腾讯云
'''
注册腾讯云开通短信服务
创建应用
sdk appid:1400555798
申请签名 使用公众号
签名管理 id 名称
申请模板 id 名称
申请腾讯云的appid secrect key
调用相关接口去发送短信 sdk 写好相关
'''
#TODO tencent.send_message.get(phone,random_code)
#5.校验验证码和手机号保留起来,超时时间为(30s过期)
# 5.1 搭建redis服务器
#5.2 diango中安装方便使用redis的模块django-redis
#配置:setting.py
'''
import redis
pool=redis.ConnectionPool(host='127.0.0.1',port=8000)
conn=redis.Redis(connection_pool=pool)
conn.set(phone,random_code,ex=30)
'''
from django_redis import get_redis_connection
coon=get_redis_connection()
coon.set(phone,random_code,ex=30)
# redis coon.set('19187495242','1675',ex=30)
#conn=conn.get('19187495242')
return Response({"status":True,'message':'发送成功'})
使用邮箱了
- redis
在settings.py中
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "密码",
}
}
}
启动redis
去找f盘的redis
redis-server redis.windows.conf
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e3tRgjHi-1630375528308)(./代码截图/redis启动.png)]
https://www.cnblogs.com/yunqing/p/10605934.html
然后使用:剩下使用邮箱代替上面部分即可
QQ邮箱设置
stteings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com' # 腾讯QQ邮箱 SMTP 服务器地址
EMAIL_PORT = 25 # SMTP服务的端口号
EMAIL_HOST_USER = '208633445@qq.com' # 你的qq邮箱,邮件发送者的邮箱
EMAIL_HOST_PASSWORD = 'slbsatkpmlmqcbbe' # 你申请的授权码(略)
EMAIL_USE_TLS = False # 与SMTP服务器通信时,是否启用安全模式
调用:
from django.core.mail import send_mail
subject = '爱读书' # 主题
from_email = settings.EMAIL_HOST_USER # 发件人,在settings.py中已经配置
to_email = qqemail # 邮件接收者列表
# 发送的消息
message = f'你本次的验证码:{random_code},有效时间为30秒' # 发送普通的消息使用的时候message
send_mail(subject, message, from_email, [to_email],
fail_silently=False, auth_user=None, auth_password=None,
connection=None, html_message=None)
验证登录
- 序列化器
class LoginSerializer(serializers.Serializer): # 对用户提交数据校验 code = serializers.CharField(label="qq验证码", ) qqemail = serializers.CharField(label="qq邮箱", validators=[qqemail_validator, ]) def validate_code(self,value): if len(value) != 4: raise ValidationError('短信格式错误') if not value.isdecimal(): raise ValidationError('短信格式错误') qqemail = self.initial_data.get('qqemail') coon = get_redis_connection() code = coon.get(qqemail) if not code: raise ValidationError('验证码过期') if value != code.decode('utf-8'): raise ValidationError('验证码错误') print(value) return value
- POST处理
class LoginView(APIView):
def post(self, request, *args, **kwargs):
# 校验QQ号是否违法
# 校验验证码
# 1.无验证码
# 2有验证码
# 3.输入错误
# 去数据库中获取用户信息(获取创建)
# 将一些信息范湖小程序
ser = LoginSerializer(data=request.data)
print(request.data)
print(ser.is_valid())
# 成功
if ser.is_valid():
pass
else:
return Response({'status': False, 'message': '验证错误'})
qqemail = ser.validated_data.get('qqemail')
user_object, flag = models.UserInfo.objects.get_or_create(qqemail=qqemail)
user_object.token = str(uuid.uuid4())
user_object.save()
return Response({'status': True, 'data': {'token': user_object.token, 'qqemail': qqemail}}, )
def get(self, request):
print(request.data)
return Response({"status": 201})
- 小程序发送请求
wx.request({
url: 'http://127.0.0.1:8000/login/',
data: {qqemail: this.data.qqemail,code:this.data.code},
method: 'POST',
dataType:'json',
success: function(result){
console.log(result.data)
//登录成功
//将qq号放到全局的位置
//1.去公共的app。js中获取globalData
app.globalData.qqemail=result.data.data.qqemail
console.log(app.globalData.qqemail)
if (result.data.status){
//跳转到上一级页面
var pages=getCurrentPages();
var prepage=pages[pages.length-2]
/*wx.navigateTo({
url: prepage,
})*/
wx.navigateBack({});
}
//登录失败
else{
wx.showToast({
title: '登录失败',
icon:"none"
})
}
},
})
},
- 全局变量
首先在app.js中gloableData声明变量
然后其他的页面调用:
var app=getApp()
app.gloableData.xxx
-
返回上页面
wx.navigateBack({});
-
接口请求qq昵称头像
var qq=this.data.qqemail var qqnum=(qq+'').replace('@qq.com',"") var url='http://q1.qlogo.cn/g?b=qq&nk=xx&s=100'.replace('xx',qqnum) this.setData({head:url}) console.log(this.data.head)
-
昵称(有乱码)
本地存储
- 存储
- wx.setStorageSync(‘UserInfo’,result);
- 取值
- var userinfo=wx.getStorageSync(‘UserInfo’);
- 删除
- wx.removeStorageSync(‘UserInfo’)
发布–子页面向向父页面传值
父页面
<view bindtap="getTopic">{{topicText}}</view>
data: {
topicText:"请选择话题",
topicid:null
},
//修改子页面传过来的值
pagesetData:function(res){
this.setData({topicText:res.title,topicid:res.id})
},
子页面
<view class='item' wx:for="{{topiclist}}" bindtap="choseTopic" data-xx="{{item}}">
<text>{{item.title}} </text>
<text>{{item.count}}</text>
</view>
data: {
topiclist:[{id:1,title:'#吴亦凡太垃圾了',count:100},
{id:2,title:'#金牌第二 ',count:100},
{id:3,title:'#喜欢听歌',count:100},
{id:4,title:'#网易云了',count:100}]
},
choseTopic:function(e){
var topicinfo=e.currentTarget.dataset.xx;
//将值传递给上一个页面
var pages=getCurrentPages();//获取当前页面
var prevPage=pages[pages.length-2]//获取上一页面的对象
prevPage.pagesetData(topicinfo)
wx.navigateBack({}),
console.log(topicinfo)
},
腾讯云的对象存储oss
SecretId: AKIDd5HCrGQt76oFQL0SeZEcjbDYcQxEExOa
SecretKey: wby5vUmnZ4IFvKSItCEzSfbFcjYWEfqj
APPID:1306812331
首先登陆腾讯云
然后选择对象存储
然后选择产看小程序sdk
然后在按照文档说明
使用的时候推荐方案一
通过后端回去秘钥返回
代码:
- wx:
upload:function(){
//方法1
// var cos = new COS({
// SecretId: 'AKIDd5HCrGQt76oFQL0SeZEcjbDYcQxEExOa',
// SecretKey: 'wby5vUmnZ4IFvKSItCEzSfbFcjYWEfqj',
// });
//方法2 获取临时秘钥
var cos = new COS({
// 必选参数
getAuthorization: function (options, callback) {
// 服务端 JS 和 PHP 示例:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/
// 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk
// STS 详细文档指引看:https://cloud.tencent.com/document/product/436/14048
wx.request({
url: 'http://127.0.0.1:8000/credential/',
data: {
// 可从 options 取需要的参数
},
success: function (result) {
var data = result.data;
var credentials = data && data.credentials;
if (!data || !credentials) return console.error('credentials invalid');
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
XCosSecurityToken: credentials.sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900
});
}
});
}
});
console.log(this.data.piclist);
for (var index in this.data.piclist){
var filePath=this.data.piclist[index]//获取本地上传图片路径
// 先选择文件,得到临时路径
cos.postObject({
Bucket: 'mini-1306812331',
Region: 'ap-chengdu',
Key:index+'x1.png',
FilePath: filePath,
onProgress: function (info) {
console.log('进度条',JSON.stringify(info));
}
}, function (err, data) {
console.log(err || data);
});
}
},
- 后端
class Credential(APIView):
def get(self,request,*args,**kwargs):
from django.conf import settings
config = {
'url': 'https://sts.tencentcloudapi.com/',
# 域名,非必须,默认为 sts.tencentcloudapi.com
'domain': 'sts.tencentcloudapi.com',
# 临时密钥有效时长,单位是秒
'duration_seconds': 1800,
'secret_id': 'AKIDd5HCrGQt76oFQL0SeZEcjbDYcQxEExOa',
# 固定密钥
'secret_key': 'wby5vUmnZ4IFvKSItCEzSfbFcjYWEfqj',
# 换成你的 bucket
'bucket': 'mini-1306812331',
# 换成 bucket 所在地区
'region': 'ap-chengdu',
# 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
# 例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
'allow_prefix': '*',
# 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
'allow_actions': [
'name/cos:PostObject',
--------相关权限-------------
],
}
sts=Sts(config)
response=sts.get_credential()
return Response(response)
发布问题
方式1.
打开图片进行本地预览
输入文字以及相应的信息
点击发布按钮
- 将本地的图片上传到腾讯云对象存储中cos 并将cos中的地址返回。
- 将cos中的url和文字等信息一起提交到后台
- 可能拿不到cos中的图片
在发送请求的时候是异步的,不会阻塞
方式2.(推荐)
打开图片进行本地浏览
将本地图片上传到cos
输入文字,选择信息
发不完以后才可以点击按钮
进度条组件
这里显示上传图片的进度条为例
<progress percent="20" ></progress>
<view>进度:20%</view>
<progress percent="20" color="#dc1239"></progress>
修改data的局部数据
data: {
persent:20,
piclist:[
{id:1,title:'图片1',percent:20},
{id:2,title:'图片2',percent:40},
{id:3,title:'图片3',percent:70},
]
},
change:function(){
/*方式1 数据多了就很慢
var datalist=this.data.piclist
datalist[0].percent=80
this.setData({piclist:datalist})*/
/*方式2 推荐*/
var num=2
this.setData({['piclist[0].percent']:90,
['piclist[1].percent']:90,
['piclist['+num+'].percent']:90})//字符串拼接
},
闭包
可以解决异步index问题,如进度条
api封装
新建文件夹config/api.js和page同级别
var indexUrl="http://127.0.0.1:8000"
module.exports={
getnews:indexUrl+"/getnews",
}
- 引入
var api=(’…/…/config/api.js’)
发布
小程序:
<!--编辑区-->
<textarea name="sd" id="" cols="30" rows="10" placeholder="请输入你的talk"></textarea>
<view class="tu">
<!--上传图片区-->
<view class="pic" wx:for="{{piclist}}">
<image class="picm" src="{{item}}"></image>
<view class="delete-btn" ><image data-index="{{index}}" catchtap="deleteImg" src="../image/delete.png"></image></view>
<progress class="progress" percent="{{imgs[index].percent}}"></progress>
</view>
<!--上传图片按钮-->
<image bindtap="upLoadImage" class="uppic" src="../image/uppicture.png"></image>
</view>
<!--标签-->
<view class="label">
<view class="label1" bindtap="getTopic"><image src="../image/place (2).png"></image> <view class="text">{{topicText}}</view></view>
<view class="label1" bindtap="getLocalPath"><image src="../image/place (1).png" ></image> <view class="text">{{sublocalpath}}</view></view>
</view>
<view class="line"></view>
<view class="fabu">
<button bindtap="upload" class="but">发表</button>
</view>
<view class="bac">
<view>每天开心一点点</view>
</view>
js逻辑
data
topicText:"添加标签",
localpath:"添加地点",
sublocalpath:"添加地点",//截取后的位置信息
piclist:[],//上传下载使用
urls:[],//图片地址
imgs:[],//渲染使用
progress:0,
qqemail:null,
qqname:null,
head:'../image/head.jpg',
UserInfo:'',
body:null,
获取话题
getTopic:function(){
wx.navigateTo({
url: '/pages/topic/topic',
})
},
pagesetData:function(res){
this.setData({topicText:res.title,topicid:res.id})
},
获取定位
getLocalPath:function(){
console.log("1")
var that=this
wx.chooseLocation({
success:function(res){
console.log(res.address)
that.setData({localpath:res.address,sublocalpath:res.address.substring(0,4)+"...",localpath:res.address})
}
})
},
删除图片
//删除图片
deleteImg: function (e) {
//删除本地
var that=this
var imgs = that.data.piclist;
var index = e.currentTarget.dataset.index;
imgs.splice(index, 1);
that.setData({
piclist: imgs
});
//删除对象存储
cos.deleteObject({
Bucket: 'mini-1306812331',
Region: 'ap-chengdu',
Key: that.data.imgs[index].key,
}, function (err, data) {
console.log(err || data);
});
var url = cos.getObjectUrl({
Bucket: 'mini-1306812331',
Region: 'ap-chengdu',
Key: that.data.imgs[index].key,
});
console.log('url=',url)
},
获取日期
onLoad: function (options) {
var myDate = new Date();
var riqi=myDate.toLocaleDateString(); //获取日期
console.log(riqi)
},
选择本地图片并且上传到oss
upLoadImage:function(){
var that=this
var imgs = this.data.imgs;
wx.chooseImage({
count:9,//最多选择9张图片
sizeType:['original', 'compressed'],//原图;压缩图
sourceType:['album', 'camera'],//选择相册和相机
success:function(res){//成功后
var oldlength=that.data.piclist.length;
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
let tempFilePaths = res.tempFilePaths;
let totalcount=res.tempFilePaths.length+oldlength;//总照片数
if(totalcount>9){
wx.showToast({
title: '图片最多选择9张',
icon:'none'
});
return
};
var piclist=that.data.piclist.concat(res.tempFilePaths);//concat将两个列表合并,但是不改变原来列表的值,他///们重新复制给心得列表
/**本地预览 */
that.setData({piclist:piclist})
//方法2 获取临时秘钥
console.log(that.data.piclist);
for (let index in res.tempFilePaths){
var filePath=res.tempFilePaths[index]//http://tmp/t4TusS4AKmhG70b46dc9e64ad9aa477e1fff6cf646b7.jpg
var filePathSplit=filePath.split('.');
console.log("xxxxxxx",filePathSplit)//http://tmp/t4TusS4AKmhG70b46dc9e64ad9aa477e1fff6cf646b7
var ext=filePathSplit[filePathSplit.length-1]
console.log("ext=",ext)//jpg
//创建随机字符串
let randomString=Math.random().toString(36).slice(-8)+String(new Date().getDate())
console.log("随机字符串",randomString)
var filekey=randomString+"."+ext;//自己组和的xxx.jpg
//将新的名字复制给piclist[key]=filekey
that.setData({
['imgs['+oldlength+parseInt(index)+'].key']:filekey})
cos.postObject({
Bucket: 'mini-1306812331',
Region: 'ap-chengdu',
Key:filekey,
FilePath: filePath,
onProgress: function (info) {
that.setData({
['imgs['+oldlength+parseInt(index)+'].percent']:info.percent*100
})
//如果进度条满了就可以删除
if(imgs[index].percent==100){
that.setData({
['imgs['+oldlength+parseInt(index)+'].delpic']:info.percent*100
})
}
}
}, function (err, data) {
console.log(err || data);
});
}
},
});
},
细节处理
css:
使用 overflow: scroll;
可以让图片隐藏滑动,适用于很多的图片
api设计
1.数据库设计
- 话题
class Topic(models.Model):
topic=models.CharField(verbose_name="话题",max_length=32)
hot=models.IntegerField(verbose_name="热度")
- 动态
class News(models.Model):
"""
动态
"""
cover = models.CharField(verbose_name='封面', max_length=128)
content = models.CharField(verbose_name='内容', max_length=255)
topic = models.ForeignKey(verbose_name='话题', to='Topic',on_delete=models.CASCADE, null=True, blank=True)
address = models.CharField(verbose_name='位置', max_length=128, null=True, blank=True)
user = models.ForeignKey(verbose_name='发布者', to='UserInfo', related_name='news',on_delete=models.CASCADE)
favor_count = models.PositiveIntegerField(verbose_name='赞数', default=0)
# favor = models.ManyToManyField(verbose_name='点赞记录', to='UserInfo', related_name="news_favor")
viewer_count = models.PositiveIntegerField(verbose_name='浏览数', default=0)
# viewer = models.ManyToManyField(verbose_name='浏览器记录', to='UserInfo', related_name='news_viewer')
comment_count = models.PositiveIntegerField(verbose_name='评论数', default=0)
create_date = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
- 动态详细,外键为动态
class NewsDetail(models.Model):
"""
动态详细
"""
key = models.CharField(verbose_name='腾讯对象存储中的文件名', max_length=128, help_text="用于以后在腾讯对象存储中删除")
cos_path = models.CharField(verbose_name='腾讯对象存储中图片路径', max_length=128)
news = models.ForeignKey(verbose_name='动态', to='News',on_delete=models.CASCADE)
def __str__(self):
return str([self.cos_path,self.key])
序列化
这里涉及嵌套的反序列化,用于create
使用外键的序列化,注意序列化内部使用序列化
class CreateNewsTopicModelSerializer(serializers.Serializer):
key = serializers.CharField()
cos_path = serializers.CharField()
class CreateNewsModelSerializer(serializers.ModelSerializer):
imageList = CreateNewsTopicModelSerializer(many=True)
class Meta:
model = models.News
exclude = ['user', 'viewer_count', 'comment_count']
def create(self, validated_data):
image_list = validated_data.pop('imageList')
news_object = models.News.objects.create(**validated_data)
data_list = models.NewsDetail.objects.bulk_create(
[models.NewsDetail(**info, news=news_object) for info in image_list]
)
news_object.imageList = data_list
if news_object.topic:
news_object.topic.hot += 1
news_object.save()
return news_object
class NewsDetailSer(serializers.Serializer):
key = serializers.CharField()
cos_path = serializers.CharField()
##获取news
class NewsSer(serializers.Serializer):
cover =serializers.CharField()
content =serializers.CharField()
topic =TopicSer()
address = serializers.CharField()
user =serializers.StringRelatedField(read_only=True)
favor_count =serializers.IntegerField()
# favor = models.ManyToManyField(verbose_name='点赞记录', to='UserInfo', related_name="news_favor")
viewer_count = serializers.IntegerField()
# viewer = models.ManyToManyField(verbose_name='浏览器记录', to='UserInfo', related_name='news_viewer')
comment_count = serializers.IntegerField()
create_date = serializers.DateTimeField()
newsdetail_set=serializers.StringRelatedField(read_only=True,many=True)
外键序列化 (重点)
在序列化字段的是时候使用:
- 方式1
PrimaryKeyRelatedField()
这里参数:only_read=True or queryset=…OBJ.all() - 方式2
在模型类里面使用
__str__()
序列化字段时:
StringRelatedFiled()
- 方式3
在字段里使用序列化类
先定义一个序列化类,然后在序列化字段时使用
在一方中序列化另外有外键的一方(灰常好使,不要怕错)
使用小写类名_set=serlizers.PrimaryKeyRealetd(only=True,many=True)
- views的设计
class NewsView(CreateAPIView,ListAPIView):
""" 创建动态的API """
serializer_class = CreateNewsModelSerializer
queryset = models.News.objects.all()
def perform_create(self, serializer):
new_object=serializer.save(user_id=1)
return new_object
from .ser import NewsSer,NewsDetailSer
class GetNews(APIView):
def get(self, request, *args, **kwargs):
queryset = models.News.objects.all().order_by("-id")[0:10]
ser = NewsSer(instance=queryset, many=True)
return Response(ser.data)
class GetNewsDetail(APIView):
def get(self, request, *args, **kwargs):
queryset = models.NewsDetail.objects.all()
ser = NewsDetailSer(instance=queryset, many=True)
return Response(ser.data)
class GetNewsDetailOnly(APIView):
def get(self, request,pk):
queryset = models.NewsDetail.objects.filter(news_id=pk)
ser = NewsDetailSer(instance=queryset, many=True)
return Response(ser.data)
使用脚本对数据库进行初始化
import os
import sys
import django
base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE","template.settings")
django.setup()
#====================数据库初始化脚本======================
from app03 import models
for i in range(1,37):
models.Topic.objects.create(
topic="今天很热",hot=100
)
models.News.objects.create(
cover="https://mini-1306812331.cos.ap-chengdu.myqcloud.com/lme85byh23.jpg",
content ="第{}只羊".format(i),
topic_id =i,
address ="云南",
user_id =1,
)
models.NewsDetail.objects.create(
key= 'lme85byh23.jpg',
cos_path='https://mini-1306812331.cos.ap-chengdu.myqcloud.com/lme85byh23.jpg',
news_id=i
)
models.NewsDetail.objects.create(
key='lme85byh23.jpg',
cos_path='https://mini-1306812331.cos.ap-chengdu.myqcloud.com/lme85byh23.jpg',
news_id=i
)
models.NewsDetail.objects.create(
key='jrpzjj8w16.jpg',
cos_path='https://mini-1306812331.cos.ap-chengdu.myqcloud.com/jrpzjj8w16.jpg ',
news_id=i
)
箭头函数
可以不用将this转换成that
上拉刷新–下拉刷新
- js
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
var that=this
wx.request({
//获取新闻列表
url:'http://127.0.0.1:8000/get1topic/',
data:{
maxid:that.data.maxid
},
dataType:"json",
responseType:'text',
method: 'GET',
success: (result) => {console.log(result.data)
if(result.data.length){
wx.showToast({
title: '已经是最新数据',
})
}
wx.stopPullDownRefresh({
success: (res) => {return},
})
var newdata=result.data.reverse()//将数据反转
that.setData({data:newdata.concat(that.data.data),maxid:newdata[0].id})
},
})
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
var that=this
wx.request({
//获取新闻列表
url:'http://127.0.0.1:8000/get1topic/',
data:{
minid:that.data.minid
},
dataType:"json",
responseType:'text',
method: 'GET',
success: (result) => {console.log(result.data)
if(result){
that.setData({data:that.data.data.concat(result.data),minid:result.data[result.data.length-1].id})
}
},
})
},
- json
{
"usingComponents": {},
"enablePullDownRefresh": true
}
- api
class GetNews(APIView):
def get(self, request, *args, **kwargs):
minid=request.query_params.get('minid')
maxid = request.query_params.get('maxid')
if not minid:
queryset = models.News.objects.all().order_by("-id")[0:10]
elif maxid:
queryset = models.News.objects.filter(id__gt=maxid).order_by("id")[0:10]
else:
queryset = models.News.objects.filter(id__lt=minid).order_by("-id")[0:10]
ser = NewsSer(instance=queryset, many=True)
return Response(ser.data)
- api优化
可以使用分页器和filterbackend来共同完成
注意
数据库删除问题:
删除时必须将表django_migrations中的相关文件删除
这东西浪费了我很久时间!
轮播图组件
- js
// components/theSwiper.js
Component({
/**
* 组件的属性列表
*/
properties: {
imgUrls: Array,
},
/**
* 组件的初始数据
*/
data: {
currentIndex: 0
},
/**
* 组件的方法列表
*/
methods: {
swiperChange(e) {
this.setData({
currentIndex: e.detail.current
});
}
}
});
/*
<view class="dots-box own-class">
<view class="dots {{currentIndex == index ? 'bg-333' : ''}}" wx: for="{{ imgUrls }}" wx:key="{{ index }}"></view>
</view >
*/
```json
json
{
"component": true,
"usingComponents": {}
}
wxml
```wxml
<swiper indicator-dots="false"
autoplay="{{true}}"
interval="5000"
indicator-dots="{{false}}"
indicator-color='#8a8a8a'
indicator-active-color='#333'
circular="true"
class="swiper-block"
bindchange="swiperChange"
previous-margin="100rpx"
next-margin="100rpx"
current="{{0}}">
<block wx:for="{{imgUrls}}" wx:index="{{index}}" wx:key="{{index}}">
<swiper-item class="swiper-item ">
<image mode="aspectFill" src="{{item}}" class="slide-image {{currentIndex == index ? 'active' : 'common'}}" />
</swiper-item>
</block>
</swiper>
- wxss
page{
background-color: #fff;
}
.swiper-block {
background: #fff;
height: 500rpx;
width: 100%;
}
.swiper-item{
display: flex;
flex-direction: column;
justify-content: start;
align-items: flex-start;
overflow: unset;
width: 550rpx;
height: 450rpx;
padding-top: 70rpx;
padding-bottom: 20rpx;
box-sizing: border-box;
}
.slide-image{
height: 300rpx;
width: 450rpx;
border-radius: 10rpx;
margin: 0rpx 50rpx ;
z-index: 1;
box-shadow: 10rpx 5px 40rpx rgba(0, 0, 0,0.5);
}
.active{
transform: scale(1.3);
transition: all .5s ease-in 0s;
z-index: 20;
opacity: 1;
}
.common{
transform: scale(1);
transition: all .5s ease-in 0s;
z-index: 0;
opacity: 0.4;
}
.dots-box{
display: flex;
justify-content: center;
align-items: center;
}
.dots{
width: 30rpx;
height: 6rpx;
margin: 0 4rpx;
background-color: #aaa;
margin-top: -80rpx;
}
.bg-333{
background-color: #333;
}
----------------如何使用?------------------------
使用界面的 wxml 添加
json
这里组件地址写自己放组件的地址就行
{
"usingComponents": {
"custom-swiper": "../../components/customSwiper/customSwiper"
},
}
js中的data添加数据:
carouselImgUrls:[
"https://wx1.sinaimg.cn/mw690/006cV2kkly1g90322akslj30on1hcjvf.jpg",
"https://wx2.sinaimg.cn/mw690/006cV2kkly1g9032310y9j30on1hcdkw.jpg",
"https://wx3.sinaimg.cn/mw690/006cV2kkly1g90323z18oj30on1hc77z.jpg",
"https://wx1.sinaimg.cn/mw690/006cV2kkly1g90324d2mrj30on1hcwic.jpg",
"https://wx3.sinaimg.cn/mw690/006cV2kkly1g903258itpj30on1hctby.jpg"
],
将对象转换成字典
modle_to_dict()
评论功能
- 数据库设计
class CommentRecored(models.Model):
news=models.ForeignKey(verbose_name="动态",to='NewsDetail',on_delete=models.DO_NOTHING)
content=models.CharField(verbose_name='评论内容',max_length=255)
user=models.ForeignKey(verbose_name='评论者',to='UserInfo',on_delete=models.DO_NOTHING)
create_date=models.DateTimeField(verbose_name='评论时间',auto_now_add=True)
reply=models.ForeignKey(verbose_name='回复',to='self',null=True,blank=True,related_name='replys',on_delete=models.SET_NULL)
depth=models.PositiveIntegerField(verbose_name='评论层级数',default=1)
root=models.ForeignKey(verbose_name='跟评论',to='self',null=True,blank=True,related_name='roots',on_delete=models.SET_NULL)
favor_count=models.PositiveIntegerField(verbose_name='赞数',default=0)
里面不是递归,而是自相连
使用自相连的时候需要注意related——name需要填充
- 序列化
- 获取相关新闻及其评论信息
#获取news
class NewsSer(serializers.Serializer):
id=serializers.IntegerField()
cover =serializers.CharField()
content =serializers.CharField()
topic =TopicSer()
address = serializers.CharField()
user =serializers.StringRelatedField(read_only=True)
favor_count =serializers.IntegerField()
# favor = models.ManyToManyField(verbose_name='点赞记录', to='UserInfo', related_name="news_favor")
viewer_count = serializers.IntegerField()
# viewer = models.ManyToManyField(verbose_name='浏览器记录', to='UserInfo', related_name='news_viewer')
comment_count = serializers.IntegerField()
create_date = serializers.DateTimeField(format="%Y-%m-%d")
newsdetail_set=serializers.StringRelatedField(read_only=True,many=True)
comment=serializers.SerializerMethodField()
def get_comment(self,obj):
#一级评论
first_comment=models.CommentRecored.objects.filter(news_id=obj.id,depth=1).order_by('id')[0:10].values(
'id',
'content',
'user',
'create_date',
'favor_count',
'depth'
)
first_comment_id=[item['id'] for item in first_comment]
print(first_comment_id)
#二级评论
from django.db.models import Max #获取二级评论最新的一条
#使用一级id分组,查询最新的二级评论的id,即获取每条一级评论下的最新一条二级评论
result= models.CommentRecored.objects.filter(depth=2,reply_id__in=first_comment_id).values('id').annotate(max_id=Max('id'))
second_id=[item['max_id'] for item in result]
print(second_id)
second_comment = models.CommentRecored.objects.filter(id__in=second_id).values(
'id',
'content',
'user__qqemail',
'create_date',
'reply_id',
'favor_count',
'depth'
)
print(second_comment)
##合并一二级评论在一个关键字里
#使用字典
first_dict={}
for item in first_comment:
item['create_date']=item['create_date'].strftime('%Y-%m-%d')#2010-x-xx格式的日期
first_dict[item['id']]=item
for node in second_comment:
node['create_date'] = node['create_date'].strftime('%Y-%m-%d') # 2010-x-xx格式的日期
first_dict[node['reply_id']]['child']=[node,]
return list(first_dict.values())
- 获取回复信息
class getcomment(serializers.Serializer):
id=serializers.IntegerField()
content=serializers.CharField()
user__qqemail=serializers.CharField(source='user.qqemail')
create_date=serializers.DateTimeField(format="%Y-%m-%d")
reply_id=serializers.IntegerField()
favor_count=serializers.IntegerField()
depth=serializers.IntegerField()
- 创建回复信息
class createfirstcomment(serializers.ModelSerializer):
class Meta:
model = models.CommentRecored
fields="__all__"
- views
from .ser import NewsSer,NewsDetailSer,getcomment
class GetNews(APIView):
def get(self, request, *args, **kwargs):
minid=request.query_params.get('minid')
maxid = request.query_params.get('maxid')
newid=request.query_params.get('newid')
if minid:
queryset = models.News.objects.filter(id__lt=minid).order_by("-id")[0:10]
elif maxid:
queryset = models.News.objects.filter(id__gt=maxid).order_by("id")[0:10]
elif newid:
queryset = models.News.objects.filter(id=newid)
vc=queryset.values_list('viewer_count')[0][0]
queryset.update(viewer_count=vc+1)
else:
queryset = models.News.objects.all().order_by("-id")[0:10]
ser = NewsSer(instance=queryset, many=True)
return Response(ser.data)
class GetNewsDetail(APIView):
def get(self, request, *args, **kwargs):
queryset = models.NewsDetail.objects.all()
ser = NewsDetailSer(instance=queryset, many=True)
return Response(ser.data)
class GetComment(APIView):
def get(self,requset,*args,**kwargs):
itid=requset.GET.get('itemid')
obj=models.CommentRecored.objects.filter(reply_id=itid)
ser=getcomment(instance=obj,many=True)
return Response(ser.data)
from .ser import createfirstcomment
class CreateComment(CreateAPIView):
serializer_class = createfirstcomment
<!--pages/newsdetail/newsdetail.wxml-->
<custom-swiper wx:if="{{carouselImgUrls.length}}" imgUrls="{{carouselImgUrls}}" />
<view class="user">
<image src="../image/fabubac.jpg"></image>
<text>多啦的碑文</text>
</view>
<view> {{data.content}}#{{data.topic.topic}}</view>
<view>浏览量:{{data.viewer_count}}</view>
<!--虚线-->
<view class="com">
<view class="line"></view>
<text>评论</text>
<view class="line"></view>
</view>
<!--评论-->
<view class="pinglun">
<view wx:for="{{data.comment}}">
<view class="first">
<view class="head">
<image src="../image/fabubac.jpg"></image>
</view>
<view class="sec">
<view style="font-weight:600">多啦的碑文</view>
<view class="ba">{{item.create_date}}<view class="back" bindtap="huifu" data-uer="多啦的碑文" data-replyid="{{item.id}}">回复</view></view>
</view>
<view class="dianzan">
<image src="../image/dianzan.png"></image>
<text>{{item.favor_count}}</text>
</view>
</view>
<view class="content">
<view class="te">{{item.content}}<view class="huifu" bindtap="hui" data-itemid="{{item.id}}" data-idx="{{index}}">更多评论 ></view>
<view wx:for="{{item.child}}" wx:for-item="idxs">
<view class="first">
<view class="head">
<image src="../image/fabubac.jpg"></image>
</view>
<view class="sec">
<view>多啦的碑文</view>
<view>{{idxs.create_date}}</view>
</view>
<view class="dianzan">
<image src="../image/dianzan.png"></image>
<text>{{idxs.favor_count}}</text>
</view>
</view>
<view class="te">{{idxs.content}}</view>
</view>
</view>
</view>
</view>
</view>
<view class="fpl">
<textarea placeholder="{{tolk}}" class="texa" bindinput="textcont"></textarea>
<button bindtap="ok" data-data="{{data}}">确认</button>
</view>
- 样式css
/* pages/newsdetail/newsdetail.wxss */
.user{
display: flex;
justify-content: space-between;
align-items: center;
width: 40%;
}
image{
float: left;
margin-left: 10rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.com{
margin-top: 20rpx;
display: flex;
justify-content: space-around;
align-items: center;
}
.line{
width: 43%;
border-bottom: 0.5rpx dotted gray;
}
.first{
width: 100%;
height: 80rpx;
margin-top: 25rpx;
}
.head{
width: 80rpx;
height: 80rpx;
border-radius: 50% ;
float: left;
}
.head image{
width: 100%;
height: 100%;
}
.sec{
float: left;
margin-left: 15rpx;
}
.dianzan{
float: right;
width: 15%;
height: 80rpx;
margin-right: 15rpx;
}
.dianzan image{
margin-top: 10rpx;
width: 50%;
height: 65%;
}
.dianzan text{
line-height: 80rpx;
}
.content{
float: none;
width: 100%;
margin-top: 5rpx;
}
.te{
margin-top: 10rpx;
background-color: rgb(236, 236, 236);
white-space:pre-wrap;
margin-left: 12%;
}
.fpl{
width: 100%;
height: 100rpx;
position: fixed;
bottom: 0rpx;
border: 1rpx sandybrown;
background-color: rgb(224, 224, 224);
display: flex;
justify-content: space-between;
flex-direction: row;
}
.fpl textarea{
white-space:pre-wrap ;
width: 70%;
background-color: #f5f6f8;
height: 80%;
margin-left: 20rpx;
border-radius: 5rpx;
margin-top: 10rpx;
}
.fpl button{
margin-top: 10rpx;
line-height: 80rpx;
width:20%;
height: 80%;
float:right;
right: 20rpx;
background-color: rgb(235, 192, 144);
border-radius: 8rpx;
}
.pinglun{
margin-bottom:200rpx;
}
.back{
float: right;
font: weight 500;
font-size: 30rpx;
}
- js
// pages/newsdetail/newsdetail.js
Page({
/**
* 页面的初始数据
*/
data: {
carouselImgUrls:null,
data:[],
head:'../image/head.jpg',
UserInfo:'',
ishuifu:true,
isfu:false,
option:null,
tolk:"留下点你来过的痕迹吧",
/*以下是发送的评论*/
textaea:null,
reply:null,
depth:1,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log("optoions",options)
this.setData({option:options})
var that=this
wx.request({
//获取新闻列表
url:'http://127.0.0.1:8000/get1topic/',
dataType:"json",
data:{
newid:options.newsid
},
responseType:'text',
method: 'GET',
success: (result) => {console.log("result1data=",result.data)
that.setData({data:result.data[0],carouselImgUrls:result.data[0].newsdetail_set})
console.log("res====>",result.data[0].newsdetail_set)
},
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
hui:function(e){
console.log("data---",this.data.data)
var that=this
//console.log(this.data.data.comment[1].child[0])
var itemid=e.currentTarget.dataset.itemid
var idx=e.currentTarget.dataset.idx
console.log("idex=",idx)
console.log("itemid=",itemid)
wx.request({
url: 'http://127.0.0.1:8000/getcom/?itemid='+itemid,
dataType:'json',
method: 'GET',
responseType: 'text',
timeout: 0,
success: (result) => {console.log('result=',result)
that.setData({
['data.comment['+idx+'].child']:result.data,isfu:true
})
console.log("childdata=",this.data.data)
},
})
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/*评论内容*/
textcont:function(e){
console.log(e.detail.value)
this.setData({textaea:e.detail.value})
},
ok:function(){
var that=this
console.log("id=",this.data.data.id)
console.log(that.data.reply)
wx.request({
//获取新闻列表
url:'http://127.0.0.1:8000/upcomment/',
dataType:"json",
data:{
"news":that.data.data.id,
"content":that.data.textaea,
"user":1,
"depth":that.data.depth,
"reply":that.data.reply
},
responseType:'text',
method: 'POST',
success: (result) => {
console.log('ok')
that.onLoad(that.data.option)
},
})
},
/**回复 */
huifu:function(e){
var replyid=e.currentTarget.dataset.replyid
console.log(replyid)
this.setData({tolk:"回复:多啦的碑文",depth:2,reply:replyid})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
用户认证
使用请求头将token传递过去
header{
Authorization
}
然后get请求里使用
token=request.META.get(‘HTTP_AUTHORIZATION’,None)
None所在的参数是如果没有取到token就为空,或者可以不用设为nONE,设置为其他的字符
自定义认证组件
在app下新建 author.py
然后
from rest_framework.authentication import BaseAuthentication
from .models import UserInfo
import models
class GeneralAuthentication(BaseAuthentication):
"""
通用认证,如果成功返回数据,不成功不处理,交给下一组件处理
"""
def authenticate(self, request):
token=request.META.get('HTTP_AUTHORIZATION',None)
if not token:
return None
user_object=models.UserInfo.objects.filter(token=token).first()
if not user_object:
return None
#成功
return (user_object,token)
在seetings.py中设置
REST_FRAMEWORK={
"UNAUTHENTICATED_USER":None,
"UNAUTHENTICATED_TOKEN":None
}
然后在需要认证的接口视图中添加
authentication_classes = [GeneralAuthentication,]
使用
在请求方式里面
request.user
request.token
-
设置成全局的认证 在settings.py中添加默认的认证类,
REST_FRAMEWORK={
“DEFAULT_AUTHENTICATION_CLASS”:[‘app03.author.GeneralAuthentication’,],
“UNAUTHENTICATED_USER”:None,
“UNAUTHENTICATED_TOKEN”:None
} -
用户的认证–>登录认证
class UserAuthentication(BaseAuthentication):
"""
用户认证,如果成功返回数据,不成功抛异常
"""
def authenticate(self, request):
token=request.META.get('HTTP_AUTHORIZATION',None)
if not token:
raise exceptions.AuthenticationFailed()#返回403
user_object=models.UserInfo.objects.filter(token=token).first()
if not user_object:
raise exceptions.AuthenticationFailed()#返回403
#成功
return (user_object,token)
使用
- 判断是post
- 然后在使用该认证类
在视图函数中
if self.request.method=='POST':
return [UserAuthentication(),]
return [GeneralAuthentication(),]
自定义tabbar
- 步骤1
导入官方文档里的自定义tabbar案例,导入到compononts/tabbar中- js
var app=getApp()
Component({
properties:{
selected: {
type:Number,
value:0
}
},
data: {
color: "#7A7E83",
selectedColor: "#3cc51f",
list: [
{
pagePath: "/pages/news/news",
//iconPath: "/image/icon_component.png",
//selectedIconPath: "/image/icon_component_HL.png",
text: "首页"
}, {
text: "发布"
}, {
pagePath: "/pages/main/main",
//iconPath: "/image/icon_component.png",
//selectedIconPath: "/image/icon_component_HL.png",
text: "我的"
},]
},
attached() {
},
methods: {
switchTab(e) {
const data = e.currentTarget.dataset
const url = data.path
if(url){
wx.switchTab({url})
}
else{
if(app.globalData.UserInfo){
wx.switchTab({
url: '/pages/publish/publish',
})}
else{
wx.navigateTo({
url: '/pages/home/home',
})
}
}
}
}
})
需要注意 wx.switchTab()跳转的是tababr页面
wx.navigateTo()是非tabbar页面
- wsml
<cover-view class="tab-bar">
<cover-view class="tab-bar-border"></cover-view>
<cover-view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
<block wx:if="{{item.text=='发布'}}" >
<cover-view class="pub" style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</cover-view>
</block>
<block wx:else>
<cover-image src="{{selected === index ? item.selectedIconPath : item.iconPath}}"></cover-image>
<cover-view style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</cover-view>
</block>
</cover-view>
</cover-view>
-
json
{
“component”: true
} -
css
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 48px;
background: white;
display: flex;
padding-bottom: env(safe-area-inset-bottom);
}
.tab-bar-border {
background-color: rgba(0, 0, 0, 0.33);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
}
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.tab-bar-item cover-image {
width: 27px;
height: 27px;
}
.tab-bar-item cover-view {
font-size: 10px;
}
.pub{
background-color: #d39a32;
height: 80rpx;
width: 80rpx;
border-radius: 50%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
-
第2步骤
导入使用 -
json
"usingComponents": {
"tabbar":"/components/tabbar/tabbar"
},
- wxml
<tabbar selected="{{0}}"></tabbar>
注意 selested里面的值是锁引值,数据是第几个,点击后会变色
在序列化类里获取request
xx=serlizers.serlizerMtthod()
def get_xx(self):
request=self.context['request']
支付
1.沙箱环境
微信里没有
2.微信小程序支付
2.1微信支付平台
- 个人
- 企业
2.2商户平台(企业)
暂时跳过
celery
处理任务的的python模块
-
场景1:
对于耗时的任务,将任务添加到broker(队列中),然后立即给用户一个任务id,当任务添加到broker之后,由worker去broker获取并处理任务
任务完成后,再将结果放到backend中
用户想要检查结果,提供任务id,我们可以去backen中去帮他查找。 -
场景2:
定时任务:定时发布,定时拍卖
celery是一个基于python开发的模块,可以对任务进行分发和处理
- 安装(注意版本的区别)
-
pip3 install celery
-
安装broker需要redis(已经安装)或者 rabbitMQ
-
pip3 install redis/pika
-
windows 需要另外安装
-
使用,参考网上教程
django-celery
为了使用jango和celery结合起来,使用jango-celery模块
- 安装
- 在seetings。py中配置
https证书申请
- 阿里云 免费个人 ssl 下载
项目部署
找一台云服务器 安装nginx
yum install nginx -y