app.js
App({ /** * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次) */ onLaunch: function () { }, globalData:{}, })
wxml
<view class="view_contain"> <!-- 第一部分 --> <view class="view_1"> <view class="view_image_text"> <image class="image_radius" src="/statlc/images/a.jpg" /> <navigator url="/pages/atuh/atuh">{{phone}}</navigator> </view> </view> <!-- 第二部分 --> <view class="view_2"> <view class="view_tupianwenzi"> <image class="image_tupian" src="/statlc/images/a.jpg"></image> <text class="text_saoyisao">待付款</text> </view> <view class="view_tupianwenzi"> <image class="image_tupian" src="/statlc/images/a.jpg"></image> <text class="text_saoyisao">待发货</text> </view> <view class="view_tupianwenzi"> <image class="image_tupian" src="/statlc/images/a.jpg"></image> <text class="text_saoyisao">待收货</text> </view> <view class="view_tupianwenzi"> <image class="image_tupian" src="/statlc/images/a.jpg"></image> <text class="text_saoyisao">待评价</text> </view> </view> <!-- 第三部分 --> <view class="view_3"> <view class="list-item"> <image class="item-image" src="/statlc/images/a.jpg"></image> <text class="item-text">我的收藏</text> <image class="image-jiantou" src="/statlc/images/a.jpg"></image> </view> <view class="line"></view> <view class="list-item"> <image class="item-image" src="/statlc/images/a.jpg"></image> <text class="item-text">我的评价</text> <image class="image-jiantou" src="/statlc/images/a.jpg"></image> </view> <view class="line"></view> <view class="list-item"> <image class="item-image" src="/statlc/images/a.jpg"></image> <text class="item-text">版本更新</text> <image class="image-jiantou" src="/statlc/images/a.jpg"></image> </view> <view class="line"></view> <view class="list-item"> <image class="item-image" src="/statlc/images/a.jpg"></image> <text class="item-text">售后记录</text> <image class="image-jiantou" src="/statlc/images/a.jpg"></image> </view> <view class="line"></view> <view class="list-item"> <image class="item-image" src="/statlc/images/a.jpg"></image> <text class="item-text">分享邀请</text> <image class="image-jiantou" src="/statlc/images/a.jpg"></image> </view> <view class="line"></view> </view> </view>
wxss
/* 使用page就是为了保证 满屏 */ page { width: 100%; height: 100%; } .view_contain { width: 100%; height: 100%; background: #f0eeed } /* 第一部分 */ .view_1 { display: flex; justify-content: center; width: 100%; height: 25%; background: #a0deee; } .view_image_text { width: 100%; display: flex; align-items: center; justify-content: center; flex-direction: column; } .image_radius { height: 50px; width: 50px; border-radius: 30px; } /* 第二部分 */ .view_2 { width: 100%; height: 15%; display: flex; flex-direction: row; align-items: center; justify-content: center; background: white; } .view_tupianwenzi { display: flex; flex-direction: column; width: 120rpx; align-items: center; margin-left: 25rpx; margin-right: 25rpx; } .image_tupian { display: flex; width: 100rpx; height: 100rpx; } /* 第三部分 */ .view_3 { width: 100%; height: 50%; /* background: #f0eeed; */ } .list-item { display: flex; flex-direction: row; align-items: center; width: 100%; height: 80rpx; margin-top: 20rpx; position: relative; /*父元素位置要设置为相对*/ background: white; } .item-image { width: 50rpx; height: 50rpx; margin: 20rpx; } .item-text { color: gray; font-size: 35rpx; margin-left: 20rpx; } .image-jiantou { width: 20rpx; height: 35rpx; position: absolute; /* 要约束所在位置的子元素的位置要设置成绝对 */ right: 0; /* 靠右调节 */ margin-right: 35rpx; } /* 黑线 使得产生阴影效果 */ .line { width: 100%; height: 3rpx; background: lightgray; margin-left: 90rpx; }
js
//获取全局app文件的数据 var app = getApp(); Page({ /** * 页面的初始数据 */ data: { phone:"登录" }, /** * 生命周期函数--监听页面加载(第一次访问时执行) */ onLoad(options) {}, /** * 生命周期函数--监听页面初次渲染完成(第一次访问时执行) */ onReady() {}, /** * 生命周期函数--监听页面显示(每次访问时执行) */ onShow() { //本地storage中获取值 var phone = wx.getStorageSync('phone'); if(phone){ this.setData({ //查全局文件的globalData // phone:app.globalData.phone phone:phone }) }else{ this.setData({ phone:"登录" }) } }, })
wxml
<view class="main_candidate"> <view class="inputbox flex"> <text class="input-label">手机号</text> <input name="name" placeholder="请输入手机号" bindinput="bindPhone" maxlength="11" class="primary" value="{{phone}}"/> </view> <view class="inputbox flex"> <text class="input-label" >验证码</text> <input name="code" placeholder="请输入验证码" bindinput="bindCode" maxlength="4" class="primary" value="{{code}}"/> <button class="getCode" bindtap="messageCode">发送验证码</button> </view> <button class="login" form-type="submit"bindtap="login">立即登录</button> <view class="register"> <text bindtap="register">没有账号?去注册</text> </view> </view>
wxss
.input-label { color: #888; font-size: 12pt; height: 25rpx; line-height: 25rpx; padding: 0 25rpx; border-right: 1px solid #d8d8d8; } .main_candidate{ width: 100%; height: 100%; background-color: #ffffff; margin-top: 30px; } .inputbox{ padding-left: 6px; box-sizing: border-box; border-bottom: 1px solid #dadada; width: 100%; height: 50px; line-height: 50px; font-size: 14px; background-color: #fff; } .flex{ border-radius: 5px; border: 2px solid #f4f4f4; display: flex; align-items: center; margin: 40rpx 0; } .primary{ flex:1; } .inputbox button{ width: 110px; height: 38px; color:#fff; background-color: #5dd5c8; font-size: 16px; } .login{ margin-top: 20px; background-color: #5dd5c8; color: #fff; font-size: 20px; } .register{ color: blue; font-size: 16px; margin: 0 auto; width: 40%; margin-top: 10px; }
js
Page({ /** * 页面的初始数据 */ data: { phone:"18993538183", code:"" }, bindPhone(e){ this.setData({phone:e.detail.value}); }, bindCode(e){ this.setData({code:e.detail.value}); } })
js
/** * 发送短信验证码 */ messageCode(e){ //*****************1.判断手机号长度*************** // 输入的数据不等于11执行 if (this.data.phone.length !=11){ //弹窗 wx.showToast({title: '手机号长度错误',icon:"none"}) return;} //*****************2.判断手机号是否正规*************** //正则匹配手机格式 //"/xxx/"中写正则 var reg = /^(1[3|4|5|6|7|8|9]\d{9})$/; if(!reg.test(this.data.phone)){ //弹窗 wx.showToast({title: '手机号长度错误',icon:"none"}) return;} //*****************3.通过request发送到后台*************** //发送短信验证码,登陆成功之后获取jwt和微信用户信息,保存到globalData和本地存储中。 wx.request({ url: 'http://127.0.0.1:8000/api/message/', data: {phone:this.data.phone}, method: "GET", success: (result)=>{ //返回的数据模型{"status":True/False,"message":"错误信息"} if(result.data.status){ //倒计时计数器 wx.showToast({title: result.data.message,icon:"none"}) }else{ //短信发送失败 wx.showToast({title: result.data.message,icon:"none"}) } }, }) }
auction/urls
from django.contrib import admin from django.urls import path,re_path,include from api1 import urls urlpatterns = [ path('admin/', admin.site.urls), re_path("^api/", include("api1.urls")), ]
api/urls
from django.urls import path,re_path,include from api1 import views urlpatterns = [ re_path('^login/', views.ListView.as_view()), re_path('^message/', views.MessageView.as_view()), ]
auction/settings
from pathlib import Path INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'api1.apps.Api1Config' ] DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # 配置django-redis CACHES = { "default":{ "BACKEND":"django_redis.cache.RedisCache", # 主机ip和端口 "LOCATION":"redis://127.0.0.1:6000", "OPTIONS":{ "CLIENT_CLASS":"django_redis.client.DefaultClient", # 最大访问数 "CONNECTION_POOL_KWARGS":{"max_connections":100} # 密码 # "POASSWORD":"密码", } } }
api/serializer/account
from rest_framework import serializers from .validators import phone_validator # 我们没有号码对应的表所以需要继承serializers.Serializer class MessageSerialiaer(serializers.Serializer): # 数据进来后先去判断是否为空,再去判断列表中的函数 phone = serializers.CharField(label="手机号",validators=[phone_validator,])
api/serizlizer/validators
import re from rest_framework.exceptions import ValidationError def phone_validator(value): if not re.match("^(1[3|4|5|6|7|8|9]\d{9})$",value): # 主动推送特定的错误 raise ValidationError("手机号格式错误")
api/utils/tencent/msg
from tencentcloud.common import credential from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException from tencentcloud.sms.v20190711 import sms_client,models def send_message(phone,code): # TODO tencent.send_message(phone,random_code) """ 1.注册腾讯云,开通腾讯云短信。 2.创建应用 sdk appid = 1400705782 3.申请签名(个人:小程序) 476491 python小程序 4.申请模板 1473776 普通短信 5.申请腾讯云api https://console.cloud.tencent.com/cam/capi appid 1312898916 SecretId AKIDzAB4nInCPOAkdDrMhoheMogIgqjNn35s SecretKey c09IAm9HCxZ4VduAwTSFsPq6A2f7MK5b 6.调用相关接口去发送短信 pip install --upgrade tencentcloud-sdk-python sdk,写好的工具 """ try: phone = "{}{}".format("+86",phone) cred = credential.Credential("AKIDW3Rgszw84ylQxMzNn7KOJ6kFPSLSL5c5MU","GQSMXmtsjR0QhuIalzTp250nU6digZSD") client = sms_client.SmsClient(cred,"ap-guangzhou") req = models.SendSmsRequest() # 短信应用id req.SmsSdkAppid = "1400302209" # 短信签名内容 req.Sign = "python之路" # 下发手机号+86标准 req.PhoneNumberSet = [phone] # 模板id req.TemplateID = "516680" # 模板参数 req.TemplateParamSet = [code] # 发送短信 resp = client.SendSms(req) # 输出json格式的字符串回包 if resp.SendStatusSet[0].Code == "Ok": return True #print(resp.to_json_string(indent=2)) except TencentCloudSDKException as err: #print(err) pass
api/view
from rest_framework.response import Response from rest_framework.views import APIView import random from .serializer import account from .utils.tencent import msg class MessageView(APIView): def get (self, request, *args, **kwargs): # ********************1.获取手机号和手机格式校验************************* ser = account.MessageSerialiaer(data=request.query_params) if not ser.is_valid(): return Response({"status":False,"message":"手机格式错误"}) phone = ser.validated_data.get("phone") # *****3.生成随机验证码和验证码发送到手机上,购买服务器进行发送短信:腾讯云****** random_code = random.randint(1000,9999) result = msg.send_message(phone,random_code) if not result: return Response({"status":False,"message":"短信发送失败"}) # ******5.吧验证码+手机号保留(过期时间30s)和搭建redis服务器(云redis)****** from django_redis import get_redis_connection conn = get_redis_connection() conn.set(phone,random_code,ex=60) return Response({"status":True,"message":"发送成功"})
js
// pages/atuh/atuh.js var app = getApp(); Page({ /** * 页面的初始数据 */ data: { phone:"18993538183", code:"" }, /** * 用户登陆 */ login(){ // xw.request向后台发送 wx.request({ url: 'http://127.0.0.1:8000/api/login/', data: {phone:this.data.phone,code:this.data.code}, method: "POST", success: (result) => { if(result.data.status){ //1.去公共的app.js中调用globalData,里面赋值 //app.globalData.phone = result.data.data.phone; //console.log(app.globalData); // 在本地“cookie”中赋值(并不是cookie只是功能一样,就这样叫) wx.setStorageSync('phone',result.data.data.phone); //登陆成功跳到上一级 wx.navigateBack({}); //查看跳转层级记录 //var pages = getCurrentPages(); //prevPage = pages[pages.length-2] }else{ wx.showToast({title: result.data.message,}) } } }) }, })
api/models
from django.db import models class UserInfo(models.Model): #创建唯一索引 phone = models.CharField(verbose_name="手机号",max_length=11,unique=True) token = models.CharField(verbose_name="用户TOKEN",max_length=64,null=True,blank=True)
api/serializer/account
# !/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework import serializers from .validators import phone_validator from django_redis import get_redis_connection from rest_framework.exceptions import ValidationError class LoginSerializer(serializers.Serializer): #这些都是自定义的验证字段 # 数据进来后先去判断是否为空,再去判断列表中的函数 phone = serializers.CharField(label="手机号",validators=[phone_validator,]) code = serializers.CharField(label="短信验证码",) #钩子验证code字段validate_+字段名 def validate_code(self,value): #应该写开应为主动推送的不一样 if len(value) !=4 or not value.isdecimal(): #主动推送错误信息 raise ValidationError("短信格式错误") #在函数内部取值时,使用initial_data应为用了request.data后会将其赋值给它 phone = self.initial_data.get("phone") #去取验证码 conn = get_redis_connection() code = conn.get() if not code: raise ValidationError("验证码过期") if value != code.decode("utf-8"): raise ValidationError("验证码错误") return value
api/view
from rest_framework.response import Response from rest_framework.views import APIView from . import models import uuid from .serializer import account # Create your views here. # value就是指定字段的指定数据 class ListView(APIView): def post (self, request, *args, **kwargs): print(request.data) """ 1.校验手机号是否合法 2.校验验证码,redis 验证码获取状态:无数据/有错误/有无错误 3.去数据库中获取用户信息 4.将一些信息返回给小程序 """ ser = account.LoginSerializer(data=request.data) if not ser.is_valid(): return Response({"status":False,"message":"验证码错误"}) # 验证后使用它获取数据 phone = ser.validated_data.get("phone") # 会帮你做如果有则返回一个查询对象的参数和一个逻辑值有是真没有是假,如果没有则创建并返回数据和False user_object,flag = models.UserInfo.objects. get_or_create(phone=phone) # 修改或添加token的值 user_object.token = str(uuid.uuid4()) user_object.save() return Response({"status":True,"data":{"token":user_object.token,"phone":phone}})
我的.wxml
<!-- 第一部分 --> <view class="view_1"> <view class="view_image_text"> <image class="image_radius" src="{{userinfo.avatarUrl}}" /> <navigator url="{{userinfo.url}}">{{userinfo.nickName}}</navigator> </view> </view>
我的.js
// pages/logs/logs.js var app = getApp(); Page({ /** * 页面的初始数据 */ data: { userinfo:"登录" }, /** * 生命周期函数--监听页面显示(每次访问时执行) */ onShow() { //本地storage中获取值 this.setData({ userinfo:app.globalData.userinfo }) }, })
登录.wxml
<view class="main_candidate"> ... <button class="login" form-type="submit"bindtap="onClicksubmit">立即登录</button> ... </view>
登录.js
// pages/atuh/atuh.js var app = getApp(); Page({ /** * 用户登陆 */ onClicksubmit(){ // xw.request向后台发送 wx.request({ url: 'http://127.0.0.1:8000/api/login/', data: {phone:this.data.phone,code:this.data.code}, method: "POST", success: (result) => { if(result.data.status){ //获取用户信息 wx.getUserProfile({ desc: '获取用户信息', success:(res) => { // 初始化用户信息 app.initUserInfo(result.data.data,res.userInfo); //登陆成功跳到上一级 wx.navigateBack({}); } }) }else{ wx.showToast({title: result.data.message,}) } } }) }, })
app.js
App({ /** * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次) */ onLaunch: function () { //清理在session的数据 //wx.removeStorageSync('userinfo') var userinfo = wx.getStorageSync('userinfo'); if(userinfo){ //在内存中存储一份,加快读取速度。 this.globalData.userinfo=userinfo } }, globalData:{ userinfo:{nickName:"登录", url:"/pages/atuh/atuh" } }, initUserInfo(res,localInfo){ var info = { token:res.token, phone:res.phone, nickName:localInfo.nickName, avatarUrl:localInfo.avatarUrl, url:"null" } //1.去公共的app.js中调用globalData,里面赋值 this.globalData.userinfo = info; // 在本地“cookie”中赋值(并不是cookie只是功能一样,就这样叫) wx.setStorageSync('userinfo',info); }, })
全局数据和局部数据
app.js中的app{globalData}中存放全局应用数据。
用它获取全局数据getApp();
每个页面中有.js的文件中的page{data}中存放局部数据。
用它获取局部数据this.data(this要看是谁的对象)
使用到的api(详细请到官网浏览)
登录时用到的wx.wx.request({})向后台发送请求。
提醒用户时用到wx.showToast({})弹出提示框(无交互)。
在类似cookie中存储数据用到wx.setStorageSync('key',value)(在本地存储,并不是cookie只是功能一样,就这样叫)。
清理在session的数据用到wx.removeStorageSync('key')。
获取在session的数据用到wx.getStorageSync('key')。
用到wx.getUserProfile({})来获取用户信息。
执行后页面跳回上一个页面用到wx.navigateBack({})在要执行的函数中加入。
全局事件和局部事件
app.js中有onLaunch: function () {}当小程序初始化完成时,会触发(全局只触发一次)。
每个页面都会的
显示前触发
页面的初始数据data: {}
生命周期函数--监听页面加载onLoad(options) {}
生命周期函数--监听页面初次渲染完成onReady() {}
用户加载/跳转/关闭时触发
生命周期函数--监听页面显示onShow() {}
生命周期函数--监听页面隐藏onHide() {}
生命周期函数--监听页面卸载onUnload() {}
用户触发
页面相关事件处理函数--监听用户下拉动作onPullDownRefresh() {}
页面上拉触底事件的处理函数onReachBottom() {}
用户点击右上角分享onShareAppMessage() {}
父页面
wxml
<text bindtap="getTopic">{{topicText}}</text>
js
Page({ data: { topicText:"请选择话题", topiId:null }, getTopic (){ //跳转 wx.navigateTo({ url: '/pages/topic/topic', }) }, setTopicData(res){ //设置data中的数据 this.setData({ topicText:res.title, topiId:res.id }); }, })
子页面
wxml
<view class="container"> <view class="item" wx:for="{{topicList}}" bindtap="chostTopic" data-xx="{{item}}"> <text>{{item.title}}</text> <text>{{item.count}}</text> </view> </view>
wxss
.item{ padding: 40rpx; display: flex; flex-direction: row; justify-content: space-between; }
js
// pages/topic/topic.js Page({ /** * 页面的初始数据 */ data: { topicList:[ {id:1,title:"你吧把",count:10000}, {id:1,title:"吧把",count:10}, {id:1,title:"wo吧把",count:1010}, {id:1,title:"我吧把",count:1200}, {id:1,title:"你把",count:100}, ] }, chostTopic(e){ //获取触发事件时传入的指定值 var topicInfo = e.currentTarget.dataset.xx; // 把这个值传递给它的副页面 // 他会按顺序拿到每个页面的对象。 var pages = getCurrentPages(); var prevPage = pages[pages.length-2]; //调用对象的指定事件 prevPage.setTopicData(topicInfo); //放回上级页面 wx.navigateBack({}); } })
第一阶段:文件服务器,将文件存储在某个指定的服务器上(支持目录结构的划分)。
第二阶段:分为文件服务器和对象服务器,对象存储优化了存储和操作但是无目录结构。
第三阶段:使用云服务存储,实现为每个人提供存储服务器。
登录腾讯云并访问https://console.cloud.tencent.com/cos/bucket。
点击存储桶列表并创建桶。
右上角选择sdk文档中的小程序中详细介绍。
wxml
<!--pages/publish/publish.wxml--> <text>pages/publish/publish.wxml</text> <view bindtap="uploadImage">请上传图片</view> <view> <image style="width: 200rpx;height: 200rpx; margin-right: 5rpx;" wx:for="{{imageList}}" src="{{item}}"></image> </view> <view bindtap="uploadFile">点击上传</view>
js
// pages/publish/publish.js var COS = require("../../utils/cos-wx-sdk-v5.js") Page({ /** * 页面的初始数据 */ data: { imageList:[], onlineImageList:[] }, uploadImage(){ var that = this; wx.chooseImage({ count: 9, sizeType:["compressed","original"], sourceType:["album","camera"], success(res){ //设置imagelist,页面上自动修改 // that.setData({ // imageList:res.tempFilePaths // }); //先列表中添加单条数据 // that.data.imageList.push("/statlc/images/a.jpg"); //怎加图片并保留之前图片 that.setData({ imageList:that.data.imageList.concat(res.tempFilePaths) }) } }) }, uploadFile(){ // 存储图片在服务器上的url var onlineImageList = []; var that = this; //创建连接对象 var cos = new COS({ // 必选参数 getAuthorization: function (options, callback) { // 服务端其他语言参考 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/api/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, // v1.2.0之前版本的sdk使用XCosSecurityToken而不是SecurityToken SecurityToken: credentials.sessionToken, // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 StartTime: data.startTime, // 时间戳,单位秒,如:1580000000 ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900 }); } }); } }); // var cos = new COS({ // SecretId: 'AKIDzAB4nInCPOAk5DrMhoheMogIgqjNn35s', // SecretKey: 'c09IAm9HCxZ4Vu9AwTSFsPq6A2f7MK5b', // }); for(var index in this.data.imageList){ var filePath = this.data.imageList[index]; // 先选择文件,得到临时路径 cos.postObject({ Bucket:"static-1312898916", Region:"ap-nanjing", Key:index+"xzx.png", FilePath:filePath, onProgress(info){ console.log(JSON.stringify(info)); } }, function (err,data){ console.log(data); onlineImageList.push(data.Location); }) } }, })
api/urls
from django.urls import re_path from api1 import views urlpatterns = [ re_path('^credential/', views.CredentialView.as_view()), ]
api/view
from rest_framework.response import Response from rest_framework.views import APIView class CredentialView(APIView): def get (self,*args,**kwargs): import json import os from sts.sts import Sts config = { 'url': 'https://sts.tencentcloudapi.com/', # 域名,非必须,默认为 sts.tencentcloudapi.com 'domain': 'sts.tencentcloudapi.com', # 临时密钥有效时长,单位是秒 'duration_seconds': 1800, 'secret_id': 'AKIDzAB4nInCPOAk5DrMhoheMogIgqjNn35s', # 固定密钥 'secret_key': 'c09IAm9HCxZ4Vu9AwTSFsPq6A2f7MK5b', # 换成你的 bucket 'bucket': 'static-1312898916', # 换成 bucket 所在地区 'region': 'ap-nanjing', # 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径 # 例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用) 'allow_prefix': '*', # 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923 'allow_actions': [ # 简单上传 'name/cos:PostObject', ], } try: sts = Sts(config) response = sts.get_credential() print('get data : ' + json.dumps(dict(response), indent=4)) return Response(response) except Exception as e: print(e)