微信小程序开发学习-8

微信小程序学习-08

1 公告

1.1 微信小程序端

html 复制代码
<!--pages/notice/notice.wxml-->
<view class="container">
    <!-- 使用wx:for循环遍历社区公告列表 -->
    <view wx:for="{{noticeList}}" wx:key="index" class="notice-item">
        <!-- 左侧图片 -->
        <image class="notice-image" src='{{item.img}}' mode="aspectFill"></image>
        <!-- 右侧内容 -->
        <view class="notice-content">
            <view class="notice-title">{{item.title}}</view>
            <view class="notice-time">{{item.create_time}}</view>
            <view class="notice-details">{{item.content}}</view>
        </view>
    </view>
</view>
css 复制代码
/* pages/notice/notice.wxss */
/* 页面整体布局 */
page {
    background-color: #fff;
    font-size: 28rpx;
  }
  
  /* 公告列表容器 */
  .container {
    padding: 20rpx;
  }
  
  /* 单条公告项 */
  .notice-item {
    display: flex;
    padding: 20rpx 0;
    border-bottom: 1rpx solid #eee;
  }
  
  /* 公告图片 */
  .notice-image {
    width: 120rpx;
    height: 120rpx;
    border-radius: 8rpx;
    margin-right: 20rpx;
  }
  
  /* 公告内容区域 */
  .notice-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
  
  /* 公告标题 */
  .notice-title {
    font-size: 32rpx;
    font-weight: bold;
    color: #333;
  }
  
  /* 公告时间 */
  .notice-time {
    font-size: 26rpx;
    color: #999;
    margin-top: 8rpx;
  }
  
  /* 公告详情 */
  .notice-details {
    font-size: 28rpx;
    color: #666;
    margin-top: 8rpx;
    line-height: 40rpx;
  }
js 复制代码
// pages/notice/notice.js
import api_url from '../../config/settings' 
Page({
    data: {
        noticeList: [{
                title: '公告标题1',
                create_time: '2025-11-15',
                content: '公告内容描述1,公告内容描述1,公告内容描述1,', // 可以根据实际情况添加更多内容
                img: '/images/notice/notice1.jpeg' // 图片路径,根据实际情况修改
            },
            {
                title: '公告标题2',
                create_time: '2025-11-16',
                content: '公告内容描述2,公告内容描述2,公告内容描述2,', // 可以根据实际情况添加更多内容
                img: '/images/notice/notice2.jpeg' // 图片路径,根据实际情况修改
            }
        ]
        // 可以添加更多社区公告数据
    },
    onLoad(){
        this.refresh()
    },
    refresh(){
        wx.showLoading({mask:true})
        wx.request({
          url: api_url.notice,
          method:'GET',
          success:(res)=>{
              this.setData({
                  noticeList:res.data
              })
          },
          complete:()=>{
              wx.hideLoading()
          }
        })
    }
})
js 复制代码
// settings.js
notice: rootUrl + '/notice/'

1.2 后端接口

python 复制代码
###############serializer.py###############################
class NoticeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Notice
        # fields = '__all__'
        fields = ['id', 'title', 'img', 'create_time', 'content']
        # create_time 只想要年月日,不要时分秒
        extra_kwargs = {
            'create_time':{'format':'%Y-%m-%d'}
        }

###################urls.py###########################
route.register('notice', Notice_View, 'notice')

###################views.py###########################
### 公告接口
from .models import Notice
from .serializer import NoticeSerializer
class Notice_View(GenericViewSet, ListModelMixin):
    queryset = Notice.objects.all().order_by('create_time')
    serializer_class = NoticeSerializer

2 活动列表

2.1 微信小程序端

html 复制代码
<!--pages/activity/activity.wxml-->
<view class="container">
<!-- 使用wx:for循环遍历活动报名列表 -->
    <view wx:for="{{activityList}}" wx:key="index" class="activity-item">
        <!-- 活动内容 -->
        <view class="activity-content">
            <view class="activity-title">{{item.title}}</view>
            <view class="activity-enrollment">报名人数:{{item.count}} | 总人数:{{item.total_count}}</view>
            <view class="activity-time">获得积分:{{item.score}}</view>
            <view class="activity-time">{{item.date}}</view>
            <view class="activity-description">{{item.text}}</view>
        </view>
        <!-- 报名按钮 -->
        <button class="signup-btn" bind:tap="handleSignup">报名</button>
    </view>
</view>
css 复制代码
/* pages/activity/activity.wxss */
/* 页面整体样式 */
page {
    background-color: #fff;
    font-size: 28rpx;
  }
  
  /* 容器样式 */
  .container {
    padding: 20rpx;
  }
  
  /* 单个活动项样式 */
  .activity-item {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 20rpx 0;
    border-bottom: 1rpx solid #eee;
  }
  
  /* 活动内容区域样式 */
  .activity-content {
    flex: 1;
    margin-right: 20rpx;
  }
  
  /* 活动标题样式 */
  .activity-title {
    font-size: 32rpx;
    font-weight: bold;
    color: #333;
    margin-bottom: 10rpx;
  }
  
  /* 报名人数、总人数样式 */
  .activity-enrollment {
    font-size: 26rpx;
    color: #666;
    margin-bottom: 10rpx;
  }
  
  /* 积分、日期样式 */
  .activity-time {
    font-size: 26rpx;
    color: #999;
    margin-bottom: 10rpx;
  }
  
  /* 活动描述样式 */
  .activity-description {
    font-size: 28rpx;
    color: #666;
    line-height: 40rpx;
  }
  
  /* 报名按钮样式 调整宽度和文字显示 */
  .signup-btn {
    min-width: 80rpx; /* 最小宽度保证文字显示 */
    padding: 0 12rpx; /* 左右内边距 */
    height: 60rpx;
    line-height: 60rpx;
    background-color: #2196f3;
    color: #fff;
    border-radius: 8rpx;
    font-size: 26rpx; /* 调整字体大小适配按钮 */
    display: flex;
    align-items: center;
    justify-content: center;
  }
js 复制代码
// pages/activity/activity.js
import api_url from '../../config/settings' 
Page({

    /**
     * 页面的初始数据
     */
    data: {
        activityList:[]
    },
    onLoad(){
        this.refresh()
    },
    refresh(){
        wx.showLoading({mask:true})
        wx.request({
          url: api_url.activity,
          method:'GET',
          success:(res)=>{
              this.setData({
                activityList:res.data
              })
          },
          complete:()=>{
              wx.hideLoading()
          }
        })
    }

    


})
js 复制代码
// settings.js
activity: rootUrl + '/activity/'

2.2 后端接口

python 复制代码
###############serializer.py###############################
class ActivitySerializer(serializers.ModelSerializer):
    class Meta:
        model = Activity
        fields = ['id', 'title', 'text', 'date', 'count', 'score', 'total_count']
        extra_kwargs = {
            'date':{'format':'%Y-%m-%d'}
        }

###################urls.py###########################
route.register('activity', Activity_View, 'activity')

###################views.py###########################
### 活动接口
from .models import Activity
from .serializer import ActivitySerializer
class Activity_View(GenericViewSet, ListModelMixin):
    queryset = Activity.objects.all().order_by('date')
    serializer_class = ActivitySerializer

###################models.py###########################
# 用户表
class UserInfo(models.Model):
    name = models.CharField(verbose_name='姓名', max_length=32)
    avatar = models.FileField(verbose_name='头像', max_length=128, upload_to='avatar')
    create_date = models.DateField(verbose_name='日期', auto_now_add=True)
    score = models.IntegerField(verbose_name='积分', default=0)
    # 用户需要手机号登录--》手机号字段
    mobile = models.CharField(verbose_name='手机号', max_length=11, null=True)

    class Meta:
        verbose_name_plural = '用户表'
    def __str__(self):
        return self.name

# 活动表
class Activity(models.Model):
    title = models.CharField(verbose_name='活动标题', max_length=128)
    text = models.TextField(verbose_name='活动描述', null=True, blank=True)
    date = models.DateField(verbose_name='举办活动日期')
    count = models.IntegerField(verbose_name='报名人数', default=0)
    total_count = models.IntegerField(verbose_name='总人数', default=0)
    score = models.IntegerField(verbose_name='积分', default=0, null=True)
    # 一个用户可以报名多个活动
    join_record = models.ManyToManyField(verbose_name='参与者', through='JoinRecord', through_fields=('activity','user'), to='UserInfo')
    class Meta:
        verbose_name_plural = '活动表'
    def __str__(self):
        return self.title

# 活动报名记录表--》跟用户多对多关系--》一个用户可以报名多个活动
class JoinRecord(models.Model):
    user = models.ForeignKey(verbose_name='用户',to='UserInfo', on_delete=models.CASCADE)
    activity = models.ForeignKey(verbose_name='活动', to='Activity', on_delete=models.CASCADE, related_name='ac')
    exchange = models.BooleanField(verbose_name='是否已兑换', default=False)
    class Meta:
        verbose_name_plural = '活动报名记录表'

###################admin.py###########################
admin.site.register(Activity)

3 登录功能

3.1 登录分析

tex 复制代码
(个人类型用不了,需要企业)
# 1 一键登录--》使用微信的手机号
	小程序可以获取微信手机号--》code--》向后端发送请求--》后端通过code访问微信开放平台--》换取手机号--》去自己数据校验用户是否存在,如果存在--》说明是我们的用户--》直接签发token--》如果不存在--》说明第一次用该小程序--》使用这个手机号注册--》签发token
# 2 其他手机号登录
	登录页面--》手机号--》发送验证码(腾讯验证码)--》手机号+验证码--》后端--》去自己数据库校验用户是否存在,如果存在--》说明是我们的用户--》直接签发token--》如果不存在--》说明第一次用该小程序--》使用这个手机号注册--》签发token
# 发送短信接口
	-腾讯发送短信

3.2 my页面

html 复制代码
<block wx:if="{{userInfo==null}}">
    <!-- 显示手机号快捷登录和其他手机号登录 -->
    <view class="container1">
        <view class="main">
            <view class="icon-view">
                <!-- 应用图标 -->
                <image src="/images/my/景色.jpeg" class="app-icon"></image>
                <text class="title">智慧社区</text>
            </view>
        </view>
        <van-cell-group>
            <van-cell>
                <button type="warn" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" class="btn">手机号快捷登录</button>
            </van-cell>
        </van-cell-group>
        <!-- 其他手机号登录 -->
        <van-cell-group>
            <van-cell>
                <button type="primary" plain bind:tap="handleOtherLogin" class="btn">其他手机号登录</button>
            </van-cell>
        </van-cell-group>
        <!-- 用户协议同意 -->
        <view class="agreement-container">
            <checkbox class="checkbox" value="{{agreed}}" bindchange='handleAgreeChange'></checkbox>
            <text class="agreement-text">我已阅读并同意</text>
            <navigator url="" class="agreement-link">《用户协议》</navigator>
        </view>
    </view>
</block>
<block wx:else>
  <view class="container">
    <!-- 顶部用户信息区域 -->
    <view class="top-view">
      <view class="user">
        <view class="row">
          <image class="avatar" src="{{userInfo.avatar}}"></image>
          <view class="name">
            <view bind:tap="logout">{{userInfo.name}}</view>
          </view>
        </view>
      </view>
      <view class="numbers">
        <view class="row">
          <text class="num">{{userInfo.score}}</text>
          <text class="label">积分</text>
        </view>
        <view class="row">
          <text class="num">55</text>
          <text class="label">其他</text>
        </view>
        <view class="row">
          <text class="num">77</text>
          <text class="label">其他</text>
        </view>
        <view class="row">
          <text class="num">56</text>
          <text class="label">其他</text>
        </view>
      </view>
    </view>

    <!-- 列表区域(使用 van-cell 需确保已引入 Vant Weapp 组件库) -->
    <van-list>
      <van-cell title="积分兑换记录" is-link />
      <van-cell title="我参加的活动" is-link />
      <van-cell title="分享应用" is-link />
      <van-cell title="联系客服" is-link />
      <van-cell title="退出" is-link bind:tap="handleLogout" />
    </van-list>
  </view>
</block>
css 复制代码
// my.wxss
page {
    height: 100%;
    background-color: #fff;
  }
  
  /* 未登录区域样式 */
  .container1 {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start; 
    padding: 80rpx 40rpx; 
    box-sizing: border-box;
    height: 100%;
  }
  
  .icon-view {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 60rpx; 
  }
  
  .app-icon {
    width: 120rpx;
    height: 120rpx;
    border-radius: 16rpx;
    margin-bottom: 20rpx;
  }
  
  .title {
    font-size: 36rpx;
    color: #333;
    font-weight: 500;
  }
  
  /* 按钮区域样式 */
  .van-cell-group {
    width: 100%;
    margin-bottom: 30rpx;
  }
  
  .van-cell {
    background-color: transparent;
    border: none;
    padding: 0;
  }
  
  button {
    width: 95%; /* 进一步加宽按钮宽度到95% */
    height: 90rpx;
    line-height: 90rpx;
    border-radius: 45rpx;
    margin: 0 auto; 
    font-size: 32rpx; /* 保持文字大小 */
  }
  
  button[type="warn"] {
    background-color: #e53935;
    color: #fff;
  }
  
  button[type="primary"].plain {
    background-color: #fff;
    color: #4caf50;
    border: 1rpx solid #4caf50;
  }
  
  /* 用户协议区域样式 */
  .agreement-container {
    display: flex;
    align-items: center;
    font-size: 24rpx;
    color: #999;
    margin-top: 40rpx; 
    justify-content: center; 
  }
  
  .checkbox {
    width: 32rpx;
    height: 32rpx;
    margin-right: 10rpx;
  }
  
  .agreement-link {
    color: #4285f4;
    margin-left: 6rpx;
  }
  
  /* 已登录区域样式(与原有样式合并调整) */
  .container {
    height: 100%;
  }
  
  .top-view {
    background-color: #fff;
    color: #333;
    padding: 40rpx 30rpx;
  }
  
  .top-view .user .row {
    display: flex;
    align-items: center;
    margin-bottom: 30rpx;
  }
  
  .top-view .user .avatar {
    width: 100rpx;
    height: 100rpx;
    border-radius: 50%;
  }
  
  .top-view .user .name {
    padding-left: 20rpx;
  }
  
  .top-view .user .name view {
    font-size: 36rpx;
    font-weight: 500;
    color: #333;
  }
  
  .top-view .numbers {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    font-size: 28rpx;
  }
  
  .top-view .numbers .row {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  
  .top-view .numbers .num {
    font-size: 40rpx;
    font-weight: bold;
    margin-bottom: 10rpx;
    color: #333;
  }
  
  .top-view .numbers .label {
    font-size: 24rpx;
    opacity: 0.7;
    color: #666;
  }
  
  /* 调整 van-cell 样式 */
  .van-list {
    margin-top: 20rpx;
  }
  
  .van-cell {
    background-color: #fff;
    margin-bottom: 1rpx;
    padding: 30rpx;
  }
  
  .van-cell__title {
    font-size: 30rpx;
    color: #333;
  }
  
  .van-cell__value {
    font-size: 30rpx;
    color: #999;
  }
  .btn {
    width: 600rpx; /* 最大化宽度,仅留少量边距 */
    height: 90rpx;
    line-height: 90rpx;
    border-radius: 30rpx;
    margin: 0 auto;
    font-size: 32rpx;
  }
js 复制代码
// pages/my/my.js
import api_url from '../../config/settings'
var app = getApp() // 取到app.js对象
Page({
    data: {
        userInfo: null
    },
    // 手机号快速登录
    getPhoneNumber(event) {
        console.log(event)
        // 通过获取手机号返回的code--》传递给后端--》后端调用微信小程序开发平台获取手机号的接口--》
        // 获取手机号--》够短签发token给前端
        wx.request({
            url: api_url.quick_login,
            method: 'POST',
            data: {
                code: event.detail.code
            },
            success: (res) => {
                console.log(res)
                // 在此返回登录信息
                var data = res.data
                console.log(data)
                if (data.code == 100) {
                    //登陆成功,保存用户信息
                    console.log(data)
                    var token = data.token
                    var name = data.name
                    var score = data.score
                    var avatar = data.avatar
                    // 保存到app.js中
                    app.initUserInfo(name, score, avatar, token)
                    var info = app.globalData.userInfo
                    console.log('globalData.userInfo', info)
                    if(info){
                        this.setData({
                            userInfo:info
                        })
                    }
                } else {
                    wx.showToast({
                        title: '登录失败',
                    })
                }
            }
        })
    },
    // 其他手机号登录
    handleOtherLogin() {
        wx.navigateTo({
            url: '/pages/login/login',
        })
    },
    onShow() {
        // 1 取出放在app.js中的用户信息,赋值到当前的userInfo中,userInfo只要有值,页面就显示用户信息了
        var info = app.globalData.userInfo
        console.log('globalData.userInfo', info)
        if (info) {
            this.setData({
                userInfo: info
            })
        }
    },
    handleLogout() {
        // 1 调用app.js的退出
        app.logoutUserInfo()
        // 2当前页面中的userInfo置为空
        this.setData({
            userInfo: null
        })
    }
})

3.3 发送短信、登录接口、快速登录接口

python 复制代码
###################urls.py###########################
route.register('user', Login_View, 'user')

###################views.py###########################
### 发送短信验证码接口--》快速登录--》普通手机号登录
from libs.send_tx_sms import get_code, send_sms_by_phone_simple 
from django.core.cache import cache
from rest_framework.decorators import action
from .models import UserInfo
from faker import Faker
from rest_framework_simplejwt.tokens import RefreshToken
class Login_View(GenericViewSet):
    # http://192.168.110.143:8000/smart/user/send_sms/?mobile=12345678901
    @action(methods=['GET'], detail=False)
    def send_sms(self, request, *args, **kwargs):
        # 1 取出前端传入手机号(get请求传过来的)
        mobile = request.query_params.get('mobile')
        # 2 获取手机验证码
        code = get_code()
        print('验证码', code)
        # 3 验证码放到缓存-->临时存储,能存,后期可以根据key取出来--》django提供的
        cache.set(f'sms_{mobile}', code)
        # 4 发送短信
        res = send_sms_by_phone_simple(mobile, code)
        if res:
            return Response({'code':100,'msg':'短信发送成功'})
        else:
            return Response({'code':101,'msg':'短信发送失败,请稍后再试'})
    
    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        # 1 取出手机号和验证码
        mobile = request.data.get('mobile')
        code = request.data.get('code')
        # 2 校验验证码是否正确
        old_code = cache.get(f'sms_{mobile}')
        if old_code == code:
            # 3 数据库查询用户,如果存在直接签发token登陆成功
            user = UserInfo.objects.filter(mobile=mobile).first()
            if not user:
                # 4 如果用户不存在,创建用户,再签发token
                # 随机生成一个中文名 pip install Faker
                fake = Faker('zh_CN')
                username = fake.name()
                user = UserInfo.objects.create(mobile=mobile, name=username)
            # 5 能查到用户,直接签发token--》simple-jwt--》pip install djangorestframework-simplejwt
            refresh = RefreshToken.for_user(user)
            return Response({'code':100, 'msg':'登陆成功', 'token':str(refresh.access_token), 'name':user.name,
                             'score':user.score, 'avatar':'http://192.168.8.40:8000/media/'+str(user.avatar)})
        else:
            return Response({'code':101, 'msg':'验证码错误'})

    @action(methods=['POST'], detail=False)
    def quick_login(self, request, *args, **kwargs):
        # 1 取出前端传入的code
        code = request.data.get('code')
        # 2 通过code,调用微信开发平台接口,换取手机号
        # 3 拿到手机号在自己库中查,能查到,签发token
        # 4 查不到注册再签发token
        # 假数据--》只要点快速登录就签发第一个用户
        user = UserInfo.objects.filter(pk=1).first()
        refresh = RefreshToken.for_user(user)
        return Response({'code':100, 'msg':'登陆成功', 'token':str(refresh.access_token), 'name':user.name,
                             'score':user.score, 'avatar':'http://192.168.8.40:8000/media/'+str(user.avatar)})
        

###################libs/send_tx_sms.py###########################
import random
import json

def get_code(length=6):
    """
    生成指定长度的数字验证码
    """
    code = ''.join([str(random.randint(0, 9)) for _ in range(length)])
    return code

# 没有使用腾讯云SDK,可以使用requests的简化版本
def send_sms_by_phone_simple(mobile, code):
    """
    简化版的短信发送(如果需要的话)
    """
    try:
        print(f"【模拟发送】向手机号 {mobile} 发送验证码: {code}")
        # 模拟发送成功
        return True
        
    except Exception as e:
        print(f"发送短信异常: {e}")
        return False

3.4 login页面

html 复制代码
<!--pages/login/login.wxml-->
<view class="container">
    <!-- 应用图标区域 -->
    <view class="icon-title">
        <image src="/images/login/景色.jpeg" class="app-icon"></image>
        <text class="title">智慧社区</text>
    </view>

    <!-- 手机号输入框 -->
    <van-field value='{{phone}}' bind:input="onPhoneInput" label='手机号' type='tel' placeholder='请输入手机号' clearable='{{true}}' class="field"></van-field>

    <!-- 验证码输入框 -->
    <van-field value='{{code}}' bind:input="onCodeInput" center clearable label='验证码' placeholder='请输入验证码' use-button-slot class="field">
        <van-button slot='button' size='small' type='primary' bind:tap="sendCode" disable='{{sendCodeDisabled}}' class="code-btn">{{buttonText}}</van-button>
    </van-field>

    <!-- 登录按钮 -->
    <van-button type="info" block='{{true}}' bind:tap="login" class="login-btn">登录</van-button>
</view>
css 复制代码
/* pages/login/login.wxss */
/* 页面整体布局 */
page {
    background-color: #fff;
    font-size: 28rpx;
  }
  
  /* 容器样式 */
  .container {
    padding: 40rpx 30rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%; /* 确保容器宽度占满 */
    box-sizing: border-box;
  }
  
  /* 应用图标和标题区域 */
  .icon-title {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 60rpx;
  }
  
  .app-icon {
    width: 120rpx;
    height: 120rpx;
    border-radius: 16rpx;
    margin-bottom: 20rpx;
  }
  
  .title {
    font-size: 36rpx;
    color: #333;
    font-weight: 500;
  }
  
  /* 输入框样式 */
  .field {
    width: 100%;
    margin-bottom: 30rpx;
  }
  
  /* 验证码按钮样式 */
  .code-btn {
    width: 160rpx !important;
    font-size: 26rpx !important;
  }
  
  /* 登录按钮样式(增加!important确保优先级) */
  .login-btn {
    width: 100% !important;
    height: 90rpx;
    line-height: 90rpx;
    border-radius: 45rpx;
    font-size: 32rpx;
    margin-top: 40rpx;
  }
js 复制代码
// pages/login/login.js
import api_url from '../../config/settings' 
var app = getApp() // 取到app.js对象
Page({
    data: {
        phone:'',
        code:'',
        agreed: false,
        sendCodeDisabled: false,
        buttonText: '发送验证码',
        loading: false,
        timer: null,
        countDown: 60
    },
    // 监听手机号输入
    onPhoneInput(event){
        this.setData({
            phone: event.detail
        })
    },
    // 监听验证码输入
    onCodeInput(event){
        this.setData({
            code: event.detail
        })
    },
    // 发送验证码
    sendCode(){
        // 在这里编写发送验证码的逻辑,此处仅做示例
        console.log('发送验证码', this.data.phone, this.data.code)
        if(this.data.phone){
            wx.request({
              url: api_url.send_sms+'?mobile='+this.data.phone,
              method:'GET',
              success:(res)=>{
                  wx.showToast({
                    title: res.data.msg,
                  })
              }
            })
            this.setData({
                sendCodeDisabled:true,
                timer: setInterval(this.countDown, 1000)
            })
        }else{
            wx.showToast({
              title: '请输入手机号',
            })
        }
    },
    // 验证码倒计时
    countDown(){
        let countDown = this.data.countDown
        if(countDown==0){
            clearInterval(this.data.timer)
            this.setData({
                buttonText:'发送验证码',
                sendCodeDisabled:false,
                countDown:60
            })
            return
        }
        this.setData({
            buttonText:countDown + 's',
            countDown: countDown - 1
        })
    },
    //页面移走,销毁定时器
    onUnload(){
        clearInterval(this.data.timer)
    },
    // 登录接口
    login(){
        if(this.data.phone && this.data.code){
            wx.request({
              url: api_url.login,
              method:'POST',
              data:{mobile:this.data.phone, code:this.data.code},
              success:(res)=>{
                  var data = res.data
                  if(data.code==100){
                      //登陆成功,保存用户信息
                      console.log(data)
                      var token = data.token
                      var name = data.name
                      var score = data.score
                      var avatar = data.avatar
                      // 保存到app.js中
                      app.initUserInfo(name, score, avatar, token)
                      var info = app.globalData.userInfo
                      console.log('globalData.userInfo', info)
                      wx.navigateBack()
                  }else{
                      wx.showToast({
                        title: '登录失败',
                      })
                  }
              }
            })
        }else{
            wx.showToast({
              title: '请填写信息',
            })
        }
    }

})

3.5 app.js

js 复制代码
// app.js
App({
    // 用来存放用户登录信息,以后在任意页面,通过var app = getApp() 都能取到当前app
    globalData:{
        userInfo:null
    },
    // 登录成功后调用,把用户登录信息保存到app.js中,并且存放到本地存储中
    initUserInfo:function(name, score, avatar, token){
        var info = {
            name: name,
            score: score,
            avatar: avatar,
            token: token
        }
        this.globalData.userInfo = info
        // 保存到本地存储
        wx.setStorageSync('userInfo', info)
    },
    // 退出功能
    logoutUserInfo:function(){
        wx.removeStorageSync('userInfo')
        this.globalData.userInfo=null
    },
    // 小程序一启动,本地存储中有登录数据,用户就是登录状态
    onLaunch(){
        var info = wx.getStorageSync('userInfo')
        console.log(info)
        this.globalData.userInfo = info
    }
})

4 活动报名

4.1 小程序端

html 复制代码
<!--pages/activity/activity.wxml-->
<button class="signup-btn" bind:tap="handleSignup" mark:id="{{item.id}}">报名</button>
js 复制代码
// pages/activity/activity.js
import api_url from '../../config/settings'
var app = getApp() // 取到app.js对象
Page({

    /**
     * 页面的初始数据
     */
    data: {
        activityList:[]
    },
    onLoad(){
        this.refresh()
    },
    refresh(){
        wx.showLoading({mask:true})
        wx.request({
          url: api_url.activity,
          method:'GET',
          success:(res)=>{
              this.setData({
                activityList:res.data
              })
          },
          complete:()=>{
              wx.hideLoading()
          }
        })
    },
    // 活动报名接口
    handleSignup(event){
        // 1 校验用户是否登录
        var info = app.globalData.userInfo
        if(info){
            // 2 处理报名按钮点击事件
            var index = event.mask.id // 获取当前点击的活动索引
            console.log('点击了报名按钮,索引为:', index)
            wx.request({
              url: api_url.join,
              method:'POST',
              data:{'id':index},
              header:{token:info.token}, // 用户登录信息,带在请求头中
              success:(res)=>{
                  wx.showToast({
                    title: res.data.msg,
                  })
              }
            })
        }else{
            wx.showToast({
              title: '请先登录',
            })
        }
        
    }
})
js 复制代码
// settings.js
join: rootUrl+'/join/join/'

4.2 后端接口

python 复制代码
###################urls.py###########################
route.register('join', ActivityJoinView, 'join')

###################views.py###########################
### 报名后端接口
from .auth import MyJSONWebTokenAuthentication
from .models import JoinRecord
class ActivityJoinView(GenericViewSet):
    print('请求收到')
    authentication_classes = [MyJSONWebTokenAuthentication]
    @action(methods=['POST'], detail=False)
    def join(self, request, *args, **kwargs):
        # 1 取出要参加的活动id
        activity_id = request.data.get('id')
        # 2 取出当前登录用户
        user = request.user
        # 3 查出当前活动
        activity = Activity.objects.filter(pk=activity_id).first()
        # 4 判断时间,判断人数
        # 5 判断是否报名过
        join_record = JoinRecord.objects.filter(activity_id=activity_id, user=user).first()
        if join_record:
            return Response({'code':101, 'msg':'已经报名过,不用重复报名'})
        else:
            # 6 报名人数+1,报名表存入
            activity.count = activity.count + 1
            activity.save()
            JoinRecord.objects.create(activity=activity, user=user)
            # 7 返回报名成功
            return Response({'code':100, 'msg':'报名成功'})


################### auth.py 认证类 ###########################
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import UserInfo

class MyJSONWebTokenAuthentication(JWTAuthentication):
    def authenticate(self, request):
        jwt_value = request.META.get('HTTP_TOKEN')
        if not jwt_value:
            raise AuthenticationFailed('token 字段是必须的')
        validated_token = self.get_validated_token(jwt_value)
        print(validated_token['user_id'])
        user = UserInfo.objects.filter(pk=validated_token['user_id']).first()
        return user, jwt_value

5 积分商城(没写)

5.1 小程序端

相关推荐
柳一航2 小时前
HTML笔记
前端·笔记·html
by__csdn2 小时前
Electron入门:跨平台桌面开发指南
前端·javascript·vue.js·typescript·electron·html
星离~11 小时前
Vue响应式原理详解:从零实现一个迷你Vue
前端·javascript·vue.js
2501_9160088911 小时前
手机抓包app大全:无需root的安卓抓包软件列表
android·ios·智能手机·小程序·uni-app·iphone·webview
一只小阿乐11 小时前
react 中的判断显示
前端·javascript·vue.js·react.js·react
小沐°11 小时前
React-页码组件
前端·javascript·react.js
低代码布道师13 小时前
医疗小程序10预约挂号日历切换
低代码·小程序